aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/amd/pm/powerplay
diff options
context:
space:
mode:
authorPeter Zijlstra <peterz@infradead.org>2020-11-26 13:16:55 +0100
committerPeter Zijlstra <peterz@infradead.org>2020-11-26 13:16:55 +0100
commit20c7775aecea04d8ca322039969d49dcf568e0e9 (patch)
tree138c057839197c9021043353e994815c0250e669 /drivers/gpu/drm/amd/pm/powerplay
parentperf/x86/intel: Add event constraint for CYCLE_ACTIVITY.STALLS_MEM_ANY (diff)
parentMerge tag 'media/v5.10-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media (diff)
downloadlinux-dev-20c7775aecea04d8ca322039969d49dcf568e0e9.tar.xz
linux-dev-20c7775aecea04d8ca322039969d49dcf568e0e9.zip
Merge remote-tracking branch 'origin/master' into perf/core
Further perf/core patches will depend on: d3f7b1bb2040 ("mm/gup: fix gup_fast with dynamic page table folding") which is already in Linus' tree.
Diffstat (limited to 'drivers/gpu/drm/amd/pm/powerplay')
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/Makefile39
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c1694
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/cik_dpm.h29
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/Makefile44
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/ci_baco.c196
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/ci_baco.h29
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/common_baco.c120
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/common_baco.h63
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/fiji_baco.c196
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/fiji_baco.h29
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/hardwaremanager.c547
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c564
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr_ppt.h116
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/polaris_baco.c222
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/polaris_baco.h29
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/pp_overdriver.c1288
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/pp_overdriver.h46
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/pp_psm.c296
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/pp_psm.h40
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c1562
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.h328
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.c710
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.h244
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppevvmath.h555
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/pppcielanes.c64
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/pppcielanes.h31
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/pptable_v1_0.h436
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c1337
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.h35
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c1732
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.h50
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.c1494
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.h322
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_inc.h43
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_baco.c91
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_baco.h32
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_clockpowergating.c437
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_clockpowergating.h38
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_dyn_defaults.h55
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c5236
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.h368
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_powertune.c1239
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_powertune.h62
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_thermal.c471
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_thermal.h58
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu8_hwmgr.c2049
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu8_hwmgr.h311
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu9_baco.c66
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu9_baco.h31
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.c767
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.h228
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/tonga_baco.c231
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/tonga_baco.h29
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_baco.c121
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_baco.h29
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c5482
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.h446
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_inc.h43
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_powertune.c1392
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_powertune.h82
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_pptable.h445
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c1398
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.h63
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_thermal.c669
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_thermal.h80
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_baco.c119
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_baco.h29
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c2868
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.h458
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_inc.h40
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_pptable.h109
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c402
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.h58
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_thermal.c317
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_thermal.h66
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_baco.c122
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_baco.h33
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c4409
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.h590
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_inc.h36
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_powertune.c72
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_powertune.h32
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_pptable.h142
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c970
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.h31
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_thermal.c358
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_thermal.h71
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/kv_dpm.c3382
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/kv_dpm.h229
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/kv_smc.c218
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/ppsmc.h200
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/r600_dpm.h127
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/si_dpm.c8079
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/si_dpm.h1015
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/si_smc.c273
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/sislands_smc.h423
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/Makefile33
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c2988
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.h76
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/fiji_smumgr.c2673
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/fiji_smumgr.h49
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/iceland_smumgr.c2686
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/iceland_smumgr.h70
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/polaris10_smumgr.c2590
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/polaris10_smumgr.h66
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/smu10_smumgr.c322
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/smu10_smumgr.h50
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/smu7_smumgr.c601
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/smu7_smumgr.h83
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/smu8_smumgr.c910
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/smu8_smumgr.h99
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/smu9_smumgr.c173
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/smu9_smumgr.h32
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/smumgr.c255
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/tonga_smumgr.c3268
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/tonga_smumgr.h74
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/vega10_smumgr.c398
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/vega10_smumgr.h51
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/vega12_smumgr.c416
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/vega12_smumgr.h57
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/vega20_smumgr.c646
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/vega20_smumgr.h63
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/vegam_smumgr.c2302
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/vegam_smumgr.h75
124 files changed, 82963 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/pm/powerplay/Makefile b/drivers/gpu/drm/amd/pm/powerplay/Makefile
new file mode 100644
index 000000000000..0fb114adc79f
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/Makefile
@@ -0,0 +1,39 @@
+#
+# Copyright 2020 Advanced Micro Devices, Inc.
+#
+# 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+#
+
+AMD_PP_PATH = ../pm/powerplay
+
+PP_LIBS = smumgr hwmgr
+
+AMD_POWERPLAY = $(addsuffix /Makefile,$(addprefix $(FULL_AMD_PATH)/pm/powerplay/,$(PP_LIBS)))
+
+include $(AMD_POWERPLAY)
+
+POWER_MGR-y = amd_powerplay.o
+
+POWER_MGR-$(CONFIG_DRM_AMDGPU_CIK)+= kv_dpm.o kv_smc.o
+
+POWER_MGR-$(CONFIG_DRM_AMDGPU_SI)+= si_dpm.o si_smc.o
+
+AMD_PP_POWER = $(addprefix $(AMD_PP_PATH)/,$(POWER_MGR-y))
+
+AMD_POWERPLAY_FILES += $(AMD_PP_POWER)
diff --git a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
new file mode 100644
index 000000000000..eab9768029c1
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
@@ -0,0 +1,1694 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "pp_debug.h"
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/gfp.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include "amd_shared.h"
+#include "amd_powerplay.h"
+#include "power_state.h"
+#include "amdgpu.h"
+#include "hwmgr.h"
+
+
+static const struct amd_pm_funcs pp_dpm_funcs;
+
+static int amd_powerplay_create(struct amdgpu_device *adev)
+{
+ struct pp_hwmgr *hwmgr;
+
+ if (adev == NULL)
+ return -EINVAL;
+
+ hwmgr = kzalloc(sizeof(struct pp_hwmgr), GFP_KERNEL);
+ if (hwmgr == NULL)
+ return -ENOMEM;
+
+ hwmgr->adev = adev;
+ hwmgr->not_vf = !amdgpu_sriov_vf(adev);
+ hwmgr->device = amdgpu_cgs_create_device(adev);
+ mutex_init(&hwmgr->smu_lock);
+ mutex_init(&hwmgr->msg_lock);
+ hwmgr->chip_family = adev->family;
+ hwmgr->chip_id = adev->asic_type;
+ hwmgr->feature_mask = adev->pm.pp_feature;
+ hwmgr->display_config = &adev->pm.pm_display_cfg;
+ adev->powerplay.pp_handle = hwmgr;
+ adev->powerplay.pp_funcs = &pp_dpm_funcs;
+ return 0;
+}
+
+
+static void amd_powerplay_destroy(struct amdgpu_device *adev)
+{
+ struct pp_hwmgr *hwmgr = adev->powerplay.pp_handle;
+
+ mutex_destroy(&hwmgr->msg_lock);
+
+ kfree(hwmgr->hardcode_pp_table);
+ hwmgr->hardcode_pp_table = NULL;
+
+ kfree(hwmgr);
+ hwmgr = NULL;
+}
+
+static int pp_early_init(void *handle)
+{
+ int ret;
+ struct amdgpu_device *adev = handle;
+
+ ret = amd_powerplay_create(adev);
+
+ if (ret != 0)
+ return ret;
+
+ ret = hwmgr_early_init(adev->powerplay.pp_handle);
+ if (ret)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int pp_sw_init(void *handle)
+{
+ struct amdgpu_device *adev = handle;
+ struct pp_hwmgr *hwmgr = adev->powerplay.pp_handle;
+ int ret = 0;
+
+ ret = hwmgr_sw_init(hwmgr);
+
+ pr_debug("powerplay sw init %s\n", ret ? "failed" : "successfully");
+
+ return ret;
+}
+
+static int pp_sw_fini(void *handle)
+{
+ struct amdgpu_device *adev = handle;
+ struct pp_hwmgr *hwmgr = adev->powerplay.pp_handle;
+
+ hwmgr_sw_fini(hwmgr);
+
+ release_firmware(adev->pm.fw);
+ adev->pm.fw = NULL;
+
+ return 0;
+}
+
+static int pp_hw_init(void *handle)
+{
+ int ret = 0;
+ struct amdgpu_device *adev = handle;
+ struct pp_hwmgr *hwmgr = adev->powerplay.pp_handle;
+
+ ret = hwmgr_hw_init(hwmgr);
+
+ if (ret)
+ pr_err("powerplay hw init failed\n");
+
+ return ret;
+}
+
+static int pp_hw_fini(void *handle)
+{
+ struct amdgpu_device *adev = handle;
+ struct pp_hwmgr *hwmgr = adev->powerplay.pp_handle;
+
+ hwmgr_hw_fini(hwmgr);
+
+ return 0;
+}
+
+static void pp_reserve_vram_for_smu(struct amdgpu_device *adev)
+{
+ int r = -EINVAL;
+ void *cpu_ptr = NULL;
+ uint64_t gpu_addr;
+ struct pp_hwmgr *hwmgr = adev->powerplay.pp_handle;
+
+ if (amdgpu_bo_create_kernel(adev, adev->pm.smu_prv_buffer_size,
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_GTT,
+ &adev->pm.smu_prv_buffer,
+ &gpu_addr,
+ &cpu_ptr)) {
+ DRM_ERROR("amdgpu: failed to create smu prv buffer\n");
+ return;
+ }
+
+ if (hwmgr->hwmgr_func->notify_cac_buffer_info)
+ r = hwmgr->hwmgr_func->notify_cac_buffer_info(hwmgr,
+ lower_32_bits((unsigned long)cpu_ptr),
+ upper_32_bits((unsigned long)cpu_ptr),
+ lower_32_bits(gpu_addr),
+ upper_32_bits(gpu_addr),
+ adev->pm.smu_prv_buffer_size);
+
+ if (r) {
+ amdgpu_bo_free_kernel(&adev->pm.smu_prv_buffer, NULL, NULL);
+ adev->pm.smu_prv_buffer = NULL;
+ DRM_ERROR("amdgpu: failed to notify SMU buffer address\n");
+ }
+}
+
+static int pp_late_init(void *handle)
+{
+ struct amdgpu_device *adev = handle;
+ struct pp_hwmgr *hwmgr = adev->powerplay.pp_handle;
+
+ if (hwmgr && hwmgr->pm_en) {
+ mutex_lock(&hwmgr->smu_lock);
+ hwmgr_handle_task(hwmgr,
+ AMD_PP_TASK_COMPLETE_INIT, NULL);
+ mutex_unlock(&hwmgr->smu_lock);
+ }
+ if (adev->pm.smu_prv_buffer_size != 0)
+ pp_reserve_vram_for_smu(adev);
+
+ return 0;
+}
+
+static void pp_late_fini(void *handle)
+{
+ struct amdgpu_device *adev = handle;
+
+ if (adev->pm.smu_prv_buffer)
+ amdgpu_bo_free_kernel(&adev->pm.smu_prv_buffer, NULL, NULL);
+ amd_powerplay_destroy(adev);
+}
+
+
+static bool pp_is_idle(void *handle)
+{
+ return false;
+}
+
+static int pp_wait_for_idle(void *handle)
+{
+ return 0;
+}
+
+static int pp_sw_reset(void *handle)
+{
+ return 0;
+}
+
+static int pp_set_powergating_state(void *handle,
+ enum amd_powergating_state state)
+{
+ return 0;
+}
+
+static int pp_suspend(void *handle)
+{
+ struct amdgpu_device *adev = handle;
+ struct pp_hwmgr *hwmgr = adev->powerplay.pp_handle;
+
+ return hwmgr_suspend(hwmgr);
+}
+
+static int pp_resume(void *handle)
+{
+ struct amdgpu_device *adev = handle;
+ struct pp_hwmgr *hwmgr = adev->powerplay.pp_handle;
+
+ return hwmgr_resume(hwmgr);
+}
+
+static int pp_set_clockgating_state(void *handle,
+ enum amd_clockgating_state state)
+{
+ return 0;
+}
+
+static const struct amd_ip_funcs pp_ip_funcs = {
+ .name = "powerplay",
+ .early_init = pp_early_init,
+ .late_init = pp_late_init,
+ .sw_init = pp_sw_init,
+ .sw_fini = pp_sw_fini,
+ .hw_init = pp_hw_init,
+ .hw_fini = pp_hw_fini,
+ .late_fini = pp_late_fini,
+ .suspend = pp_suspend,
+ .resume = pp_resume,
+ .is_idle = pp_is_idle,
+ .wait_for_idle = pp_wait_for_idle,
+ .soft_reset = pp_sw_reset,
+ .set_clockgating_state = pp_set_clockgating_state,
+ .set_powergating_state = pp_set_powergating_state,
+};
+
+const struct amdgpu_ip_block_version pp_smu_ip_block =
+{
+ .type = AMD_IP_BLOCK_TYPE_SMC,
+ .major = 1,
+ .minor = 0,
+ .rev = 0,
+ .funcs = &pp_ip_funcs,
+};
+
+/* This interface only be supported On Vi,
+ * because only smu7/8 can help to load gfx/sdma fw,
+ * smu need to be enabled before load other ip's fw.
+ * so call start smu to load smu7 fw and other ip's fw
+ */
+static int pp_dpm_load_fw(void *handle)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->smumgr_funcs || !hwmgr->smumgr_funcs->start_smu)
+ return -EINVAL;
+
+ if (hwmgr->smumgr_funcs->start_smu(hwmgr)) {
+ pr_err("fw load failed\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pp_dpm_fw_loading_complete(void *handle)
+{
+ return 0;
+}
+
+static int pp_set_clockgating_by_smu(void *handle, uint32_t msg_id)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->update_clock_gatings == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return 0;
+ }
+
+ return hwmgr->hwmgr_func->update_clock_gatings(hwmgr, &msg_id);
+}
+
+static void pp_dpm_en_umd_pstate(struct pp_hwmgr *hwmgr,
+ enum amd_dpm_forced_level *level)
+{
+ uint32_t profile_mode_mask = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD |
+ AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK |
+ AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK |
+ AMD_DPM_FORCED_LEVEL_PROFILE_PEAK;
+
+ if (!(hwmgr->dpm_level & profile_mode_mask)) {
+ /* enter umd pstate, save current level, disable gfx cg*/
+ if (*level & profile_mode_mask) {
+ hwmgr->saved_dpm_level = hwmgr->dpm_level;
+ hwmgr->en_umd_pstate = true;
+ amdgpu_device_ip_set_powergating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_GFX,
+ AMD_PG_STATE_UNGATE);
+ amdgpu_device_ip_set_clockgating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_GFX,
+ AMD_CG_STATE_UNGATE);
+ }
+ } else {
+ /* exit umd pstate, restore level, enable gfx cg*/
+ if (!(*level & profile_mode_mask)) {
+ if (*level == AMD_DPM_FORCED_LEVEL_PROFILE_EXIT)
+ *level = hwmgr->saved_dpm_level;
+ hwmgr->en_umd_pstate = false;
+ amdgpu_device_ip_set_clockgating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_GFX,
+ AMD_CG_STATE_GATE);
+ amdgpu_device_ip_set_powergating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_GFX,
+ AMD_PG_STATE_GATE);
+ }
+ }
+}
+
+static int pp_dpm_force_performance_level(void *handle,
+ enum amd_dpm_forced_level level)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (level == hwmgr->dpm_level)
+ return 0;
+
+ mutex_lock(&hwmgr->smu_lock);
+ pp_dpm_en_umd_pstate(hwmgr, &level);
+ hwmgr->request_dpm_level = level;
+ hwmgr_handle_task(hwmgr, AMD_PP_TASK_READJUST_POWER_STATE, NULL);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return 0;
+}
+
+static enum amd_dpm_forced_level pp_dpm_get_performance_level(
+ void *handle)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ enum amd_dpm_forced_level level;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+ level = hwmgr->dpm_level;
+ mutex_unlock(&hwmgr->smu_lock);
+ return level;
+}
+
+static uint32_t pp_dpm_get_sclk(void *handle, bool low)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ uint32_t clk = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return 0;
+
+ if (hwmgr->hwmgr_func->get_sclk == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return 0;
+ }
+ mutex_lock(&hwmgr->smu_lock);
+ clk = hwmgr->hwmgr_func->get_sclk(hwmgr, low);
+ mutex_unlock(&hwmgr->smu_lock);
+ return clk;
+}
+
+static uint32_t pp_dpm_get_mclk(void *handle, bool low)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ uint32_t clk = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return 0;
+
+ if (hwmgr->hwmgr_func->get_mclk == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return 0;
+ }
+ mutex_lock(&hwmgr->smu_lock);
+ clk = hwmgr->hwmgr_func->get_mclk(hwmgr, low);
+ mutex_unlock(&hwmgr->smu_lock);
+ return clk;
+}
+
+static void pp_dpm_powergate_vce(void *handle, bool gate)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return;
+
+ if (hwmgr->hwmgr_func->powergate_vce == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return;
+ }
+ mutex_lock(&hwmgr->smu_lock);
+ hwmgr->hwmgr_func->powergate_vce(hwmgr, gate);
+ mutex_unlock(&hwmgr->smu_lock);
+}
+
+static void pp_dpm_powergate_uvd(void *handle, bool gate)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return;
+
+ if (hwmgr->hwmgr_func->powergate_uvd == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return;
+ }
+ mutex_lock(&hwmgr->smu_lock);
+ hwmgr->hwmgr_func->powergate_uvd(hwmgr, gate);
+ mutex_unlock(&hwmgr->smu_lock);
+}
+
+static int pp_dpm_dispatch_tasks(void *handle, enum amd_pp_task task_id,
+ enum amd_pm_state_type *user_state)
+{
+ int ret = 0;
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+ ret = hwmgr_handle_task(hwmgr, task_id, user_state);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return ret;
+}
+
+static enum amd_pm_state_type pp_dpm_get_current_power_state(void *handle)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ struct pp_power_state *state;
+ enum amd_pm_state_type pm_type;
+
+ if (!hwmgr || !hwmgr->pm_en || !hwmgr->current_ps)
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+
+ state = hwmgr->current_ps;
+
+ switch (state->classification.ui_label) {
+ case PP_StateUILabel_Battery:
+ pm_type = POWER_STATE_TYPE_BATTERY;
+ break;
+ case PP_StateUILabel_Balanced:
+ pm_type = POWER_STATE_TYPE_BALANCED;
+ break;
+ case PP_StateUILabel_Performance:
+ pm_type = POWER_STATE_TYPE_PERFORMANCE;
+ break;
+ default:
+ if (state->classification.flags & PP_StateClassificationFlag_Boot)
+ pm_type = POWER_STATE_TYPE_INTERNAL_BOOT;
+ else
+ pm_type = POWER_STATE_TYPE_DEFAULT;
+ break;
+ }
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return pm_type;
+}
+
+static void pp_dpm_set_fan_control_mode(void *handle, uint32_t mode)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return;
+
+ if (hwmgr->hwmgr_func->set_fan_control_mode == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return;
+ }
+ mutex_lock(&hwmgr->smu_lock);
+ hwmgr->hwmgr_func->set_fan_control_mode(hwmgr, mode);
+ mutex_unlock(&hwmgr->smu_lock);
+}
+
+static uint32_t pp_dpm_get_fan_control_mode(void *handle)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ uint32_t mode = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return 0;
+
+ if (hwmgr->hwmgr_func->get_fan_control_mode == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return 0;
+ }
+ mutex_lock(&hwmgr->smu_lock);
+ mode = hwmgr->hwmgr_func->get_fan_control_mode(hwmgr);
+ mutex_unlock(&hwmgr->smu_lock);
+ return mode;
+}
+
+static int pp_dpm_set_fan_speed_percent(void *handle, uint32_t percent)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->set_fan_speed_percent == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return 0;
+ }
+ mutex_lock(&hwmgr->smu_lock);
+ ret = hwmgr->hwmgr_func->set_fan_speed_percent(hwmgr, percent);
+ mutex_unlock(&hwmgr->smu_lock);
+ return ret;
+}
+
+static int pp_dpm_get_fan_speed_percent(void *handle, uint32_t *speed)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->get_fan_speed_percent == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return 0;
+ }
+
+ mutex_lock(&hwmgr->smu_lock);
+ ret = hwmgr->hwmgr_func->get_fan_speed_percent(hwmgr, speed);
+ mutex_unlock(&hwmgr->smu_lock);
+ return ret;
+}
+
+static int pp_dpm_get_fan_speed_rpm(void *handle, uint32_t *rpm)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->get_fan_speed_rpm == NULL)
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+ ret = hwmgr->hwmgr_func->get_fan_speed_rpm(hwmgr, rpm);
+ mutex_unlock(&hwmgr->smu_lock);
+ return ret;
+}
+
+static int pp_dpm_set_fan_speed_rpm(void *handle, uint32_t rpm)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->set_fan_speed_rpm == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return 0;
+ }
+ mutex_lock(&hwmgr->smu_lock);
+ ret = hwmgr->hwmgr_func->set_fan_speed_rpm(hwmgr, rpm);
+ mutex_unlock(&hwmgr->smu_lock);
+ return ret;
+}
+
+static int pp_dpm_get_pp_num_states(void *handle,
+ struct pp_states_info *data)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int i;
+
+ memset(data, 0, sizeof(*data));
+
+ if (!hwmgr || !hwmgr->pm_en ||!hwmgr->ps)
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+
+ data->nums = hwmgr->num_ps;
+
+ for (i = 0; i < hwmgr->num_ps; i++) {
+ struct pp_power_state *state = (struct pp_power_state *)
+ ((unsigned long)hwmgr->ps + i * hwmgr->ps_size);
+ switch (state->classification.ui_label) {
+ case PP_StateUILabel_Battery:
+ data->states[i] = POWER_STATE_TYPE_BATTERY;
+ break;
+ case PP_StateUILabel_Balanced:
+ data->states[i] = POWER_STATE_TYPE_BALANCED;
+ break;
+ case PP_StateUILabel_Performance:
+ data->states[i] = POWER_STATE_TYPE_PERFORMANCE;
+ break;
+ default:
+ if (state->classification.flags & PP_StateClassificationFlag_Boot)
+ data->states[i] = POWER_STATE_TYPE_INTERNAL_BOOT;
+ else
+ data->states[i] = POWER_STATE_TYPE_DEFAULT;
+ }
+ }
+ mutex_unlock(&hwmgr->smu_lock);
+ return 0;
+}
+
+static int pp_dpm_get_pp_table(void *handle, char **table)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int size = 0;
+
+ if (!hwmgr || !hwmgr->pm_en ||!hwmgr->soft_pp_table)
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+ *table = (char *)hwmgr->soft_pp_table;
+ size = hwmgr->soft_pp_table_size;
+ mutex_unlock(&hwmgr->smu_lock);
+ return size;
+}
+
+static int amd_powerplay_reset(void *handle)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret;
+
+ ret = hwmgr_hw_fini(hwmgr);
+ if (ret)
+ return ret;
+
+ ret = hwmgr_hw_init(hwmgr);
+ if (ret)
+ return ret;
+
+ return hwmgr_handle_task(hwmgr, AMD_PP_TASK_COMPLETE_INIT, NULL);
+}
+
+static int pp_dpm_set_pp_table(void *handle, const char *buf, size_t size)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = -ENOMEM;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+ if (!hwmgr->hardcode_pp_table) {
+ hwmgr->hardcode_pp_table = kmemdup(hwmgr->soft_pp_table,
+ hwmgr->soft_pp_table_size,
+ GFP_KERNEL);
+ if (!hwmgr->hardcode_pp_table)
+ goto err;
+ }
+
+ memcpy(hwmgr->hardcode_pp_table, buf, size);
+
+ hwmgr->soft_pp_table = hwmgr->hardcode_pp_table;
+
+ ret = amd_powerplay_reset(handle);
+ if (ret)
+ goto err;
+
+ if (hwmgr->hwmgr_func->avfs_control) {
+ ret = hwmgr->hwmgr_func->avfs_control(hwmgr, false);
+ if (ret)
+ goto err;
+ }
+ mutex_unlock(&hwmgr->smu_lock);
+ return 0;
+err:
+ mutex_unlock(&hwmgr->smu_lock);
+ return ret;
+}
+
+static int pp_dpm_force_clock_level(void *handle,
+ enum pp_clock_type type, uint32_t mask)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->force_clock_level == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return 0;
+ }
+
+ if (hwmgr->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL) {
+ pr_debug("force clock level is for dpm manual mode only.\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&hwmgr->smu_lock);
+ ret = hwmgr->hwmgr_func->force_clock_level(hwmgr, type, mask);
+ mutex_unlock(&hwmgr->smu_lock);
+ return ret;
+}
+
+static int pp_dpm_print_clock_levels(void *handle,
+ enum pp_clock_type type, char *buf)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->print_clock_levels == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return 0;
+ }
+ mutex_lock(&hwmgr->smu_lock);
+ ret = hwmgr->hwmgr_func->print_clock_levels(hwmgr, type, buf);
+ mutex_unlock(&hwmgr->smu_lock);
+ return ret;
+}
+
+static int pp_dpm_get_sclk_od(void *handle)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->get_sclk_od == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return 0;
+ }
+ mutex_lock(&hwmgr->smu_lock);
+ ret = hwmgr->hwmgr_func->get_sclk_od(hwmgr);
+ mutex_unlock(&hwmgr->smu_lock);
+ return ret;
+}
+
+static int pp_dpm_set_sclk_od(void *handle, uint32_t value)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->set_sclk_od == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return 0;
+ }
+
+ mutex_lock(&hwmgr->smu_lock);
+ ret = hwmgr->hwmgr_func->set_sclk_od(hwmgr, value);
+ mutex_unlock(&hwmgr->smu_lock);
+ return ret;
+}
+
+static int pp_dpm_get_mclk_od(void *handle)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->get_mclk_od == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return 0;
+ }
+ mutex_lock(&hwmgr->smu_lock);
+ ret = hwmgr->hwmgr_func->get_mclk_od(hwmgr);
+ mutex_unlock(&hwmgr->smu_lock);
+ return ret;
+}
+
+static int pp_dpm_set_mclk_od(void *handle, uint32_t value)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->set_mclk_od == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return 0;
+ }
+ mutex_lock(&hwmgr->smu_lock);
+ ret = hwmgr->hwmgr_func->set_mclk_od(hwmgr, value);
+ mutex_unlock(&hwmgr->smu_lock);
+ return ret;
+}
+
+static int pp_dpm_read_sensor(void *handle, int idx,
+ void *value, int *size)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en || !value)
+ return -EINVAL;
+
+ switch (idx) {
+ case AMDGPU_PP_SENSOR_STABLE_PSTATE_SCLK:
+ *((uint32_t *)value) = hwmgr->pstate_sclk;
+ return 0;
+ case AMDGPU_PP_SENSOR_STABLE_PSTATE_MCLK:
+ *((uint32_t *)value) = hwmgr->pstate_mclk;
+ return 0;
+ case AMDGPU_PP_SENSOR_MIN_FAN_RPM:
+ *((uint32_t *)value) = hwmgr->thermal_controller.fanInfo.ulMinRPM;
+ return 0;
+ case AMDGPU_PP_SENSOR_MAX_FAN_RPM:
+ *((uint32_t *)value) = hwmgr->thermal_controller.fanInfo.ulMaxRPM;
+ return 0;
+ default:
+ mutex_lock(&hwmgr->smu_lock);
+ ret = hwmgr->hwmgr_func->read_sensor(hwmgr, idx, value, size);
+ mutex_unlock(&hwmgr->smu_lock);
+ return ret;
+ }
+}
+
+static struct amd_vce_state*
+pp_dpm_get_vce_clock_state(void *handle, unsigned idx)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return NULL;
+
+ if (idx < hwmgr->num_vce_state_tables)
+ return &hwmgr->vce_states[idx];
+ return NULL;
+}
+
+static int pp_get_power_profile_mode(void *handle, char *buf)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en || !buf)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->get_power_profile_mode == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return snprintf(buf, PAGE_SIZE, "\n");
+ }
+
+ return hwmgr->hwmgr_func->get_power_profile_mode(hwmgr, buf);
+}
+
+static int pp_set_power_profile_mode(void *handle, long *input, uint32_t size)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = -EINVAL;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return ret;
+
+ if (hwmgr->hwmgr_func->set_power_profile_mode == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return ret;
+ }
+
+ if (hwmgr->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL) {
+ pr_debug("power profile setting is for manual dpm mode only.\n");
+ return ret;
+ }
+
+ mutex_lock(&hwmgr->smu_lock);
+ ret = hwmgr->hwmgr_func->set_power_profile_mode(hwmgr, input, size);
+ mutex_unlock(&hwmgr->smu_lock);
+ return ret;
+}
+
+static int pp_set_fine_grain_clk_vol(void *handle, uint32_t type, long *input, uint32_t size)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->set_fine_grain_clk_vol == NULL)
+ return 0;
+
+ return hwmgr->hwmgr_func->set_fine_grain_clk_vol(hwmgr, type, input, size);
+}
+
+static int pp_odn_edit_dpm_table(void *handle, uint32_t type, long *input, uint32_t size)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->odn_edit_dpm_table == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return 0;
+ }
+
+ return hwmgr->hwmgr_func->odn_edit_dpm_table(hwmgr, type, input, size);
+}
+
+static int pp_dpm_set_mp1_state(void *handle, enum pp_mp1_state mp1_state)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr)
+ return -EINVAL;
+
+ if (!hwmgr->pm_en)
+ return 0;
+
+ if (hwmgr->hwmgr_func->set_mp1_state)
+ return hwmgr->hwmgr_func->set_mp1_state(hwmgr, mp1_state);
+
+ return 0;
+}
+
+static int pp_dpm_switch_power_profile(void *handle,
+ enum PP_SMC_POWER_PROFILE type, bool en)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ long workload;
+ uint32_t index;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->set_power_profile_mode == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!(type < PP_SMC_POWER_PROFILE_CUSTOM))
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+
+ if (!en) {
+ hwmgr->workload_mask &= ~(1 << hwmgr->workload_prority[type]);
+ index = fls(hwmgr->workload_mask);
+ index = index > 0 && index <= Workload_Policy_Max ? index - 1 : 0;
+ workload = hwmgr->workload_setting[index];
+ } else {
+ hwmgr->workload_mask |= (1 << hwmgr->workload_prority[type]);
+ index = fls(hwmgr->workload_mask);
+ index = index <= Workload_Policy_Max ? index - 1 : 0;
+ workload = hwmgr->workload_setting[index];
+ }
+
+ if (type == PP_SMC_POWER_PROFILE_COMPUTE &&
+ hwmgr->hwmgr_func->disable_power_features_for_compute_performance) {
+ if (hwmgr->hwmgr_func->disable_power_features_for_compute_performance(hwmgr, en)) {
+ mutex_unlock(&hwmgr->smu_lock);
+ return -EINVAL;
+ }
+ }
+
+ if (hwmgr->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL)
+ hwmgr->hwmgr_func->set_power_profile_mode(hwmgr, &workload, 0);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return 0;
+}
+
+static int pp_set_power_limit(void *handle, uint32_t limit)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ uint32_t max_power_limit;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->set_power_limit == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return -EINVAL;
+ }
+
+ if (limit == 0)
+ limit = hwmgr->default_power_limit;
+
+ max_power_limit = hwmgr->default_power_limit;
+ if (hwmgr->od_enabled) {
+ max_power_limit *= (100 + hwmgr->platform_descriptor.TDPODLimit);
+ max_power_limit /= 100;
+ }
+
+ if (limit > max_power_limit)
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+ hwmgr->hwmgr_func->set_power_limit(hwmgr, limit);
+ hwmgr->power_limit = limit;
+ mutex_unlock(&hwmgr->smu_lock);
+ return 0;
+}
+
+static int pp_get_power_limit(void *handle, uint32_t *limit, bool default_limit)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en ||!limit)
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+
+ if (default_limit) {
+ *limit = hwmgr->default_power_limit;
+ if (hwmgr->od_enabled) {
+ *limit *= (100 + hwmgr->platform_descriptor.TDPODLimit);
+ *limit /= 100;
+ }
+ }
+ else
+ *limit = hwmgr->power_limit;
+
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return 0;
+}
+
+static int pp_display_configuration_change(void *handle,
+ const struct amd_pp_display_configuration *display_config)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+ phm_store_dal_configuration_data(hwmgr, display_config);
+ mutex_unlock(&hwmgr->smu_lock);
+ return 0;
+}
+
+static int pp_get_display_power_level(void *handle,
+ struct amd_pp_simple_clock_info *output)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en ||!output)
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+ ret = phm_get_dal_power_level(hwmgr, output);
+ mutex_unlock(&hwmgr->smu_lock);
+ return ret;
+}
+
+static int pp_get_current_clocks(void *handle,
+ struct amd_pp_clock_info *clocks)
+{
+ struct amd_pp_simple_clock_info simple_clocks = { 0 };
+ struct pp_clock_info hw_clocks;
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+
+ phm_get_dal_power_level(hwmgr, &simple_clocks);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PowerContainment))
+ ret = phm_get_clock_info(hwmgr, &hwmgr->current_ps->hardware,
+ &hw_clocks, PHM_PerformanceLevelDesignation_PowerContainment);
+ else
+ ret = phm_get_clock_info(hwmgr, &hwmgr->current_ps->hardware,
+ &hw_clocks, PHM_PerformanceLevelDesignation_Activity);
+
+ if (ret) {
+ pr_debug("Error in phm_get_clock_info \n");
+ mutex_unlock(&hwmgr->smu_lock);
+ return -EINVAL;
+ }
+
+ clocks->min_engine_clock = hw_clocks.min_eng_clk;
+ clocks->max_engine_clock = hw_clocks.max_eng_clk;
+ clocks->min_memory_clock = hw_clocks.min_mem_clk;
+ clocks->max_memory_clock = hw_clocks.max_mem_clk;
+ clocks->min_bus_bandwidth = hw_clocks.min_bus_bandwidth;
+ clocks->max_bus_bandwidth = hw_clocks.max_bus_bandwidth;
+
+ clocks->max_engine_clock_in_sr = hw_clocks.max_eng_clk;
+ clocks->min_engine_clock_in_sr = hw_clocks.min_eng_clk;
+
+ if (simple_clocks.level == 0)
+ clocks->max_clocks_state = PP_DAL_POWERLEVEL_7;
+ else
+ clocks->max_clocks_state = simple_clocks.level;
+
+ if (0 == phm_get_current_shallow_sleep_clocks(hwmgr, &hwmgr->current_ps->hardware, &hw_clocks)) {
+ clocks->max_engine_clock_in_sr = hw_clocks.max_eng_clk;
+ clocks->min_engine_clock_in_sr = hw_clocks.min_eng_clk;
+ }
+ mutex_unlock(&hwmgr->smu_lock);
+ return 0;
+}
+
+static int pp_get_clock_by_type(void *handle, enum amd_pp_clock_type type, struct amd_pp_clocks *clocks)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (clocks == NULL)
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+ ret = phm_get_clock_by_type(hwmgr, type, clocks);
+ mutex_unlock(&hwmgr->smu_lock);
+ return ret;
+}
+
+static int pp_get_clock_by_type_with_latency(void *handle,
+ enum amd_pp_clock_type type,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en ||!clocks)
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+ ret = phm_get_clock_by_type_with_latency(hwmgr, type, clocks);
+ mutex_unlock(&hwmgr->smu_lock);
+ return ret;
+}
+
+static int pp_get_clock_by_type_with_voltage(void *handle,
+ enum amd_pp_clock_type type,
+ struct pp_clock_levels_with_voltage *clocks)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en ||!clocks)
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+
+ ret = phm_get_clock_by_type_with_voltage(hwmgr, type, clocks);
+
+ mutex_unlock(&hwmgr->smu_lock);
+ return ret;
+}
+
+static int pp_set_watermarks_for_clocks_ranges(void *handle,
+ void *clock_ranges)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en || !clock_ranges)
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+ ret = phm_set_watermarks_for_clocks_ranges(hwmgr,
+ clock_ranges);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return ret;
+}
+
+static int pp_display_clock_voltage_request(void *handle,
+ struct pp_display_clock_request *clock)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en ||!clock)
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+ ret = phm_display_clock_voltage_request(hwmgr, clock);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return ret;
+}
+
+static int pp_get_display_mode_validation_clocks(void *handle,
+ struct amd_pp_simple_clock_info *clocks)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en ||!clocks)
+ return -EINVAL;
+
+ clocks->level = PP_DAL_POWERLEVEL_7;
+
+ mutex_lock(&hwmgr->smu_lock);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DynamicPatchPowerState))
+ ret = phm_get_max_high_clocks(hwmgr, clocks);
+
+ mutex_unlock(&hwmgr->smu_lock);
+ return ret;
+}
+
+static int pp_dpm_powergate_mmhub(void *handle)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->powergate_mmhub == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return 0;
+ }
+
+ return hwmgr->hwmgr_func->powergate_mmhub(hwmgr);
+}
+
+static int pp_dpm_powergate_gfx(void *handle, bool gate)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return 0;
+
+ if (hwmgr->hwmgr_func->powergate_gfx == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return 0;
+ }
+
+ return hwmgr->hwmgr_func->powergate_gfx(hwmgr, gate);
+}
+
+static void pp_dpm_powergate_acp(void *handle, bool gate)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return;
+
+ if (hwmgr->hwmgr_func->powergate_acp == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return;
+ }
+
+ hwmgr->hwmgr_func->powergate_acp(hwmgr, gate);
+}
+
+static void pp_dpm_powergate_sdma(void *handle, bool gate)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr)
+ return;
+
+ if (hwmgr->hwmgr_func->powergate_sdma == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return;
+ }
+
+ hwmgr->hwmgr_func->powergate_sdma(hwmgr, gate);
+}
+
+static int pp_set_powergating_by_smu(void *handle,
+ uint32_t block_type, bool gate)
+{
+ int ret = 0;
+
+ switch (block_type) {
+ case AMD_IP_BLOCK_TYPE_UVD:
+ case AMD_IP_BLOCK_TYPE_VCN:
+ pp_dpm_powergate_uvd(handle, gate);
+ break;
+ case AMD_IP_BLOCK_TYPE_VCE:
+ pp_dpm_powergate_vce(handle, gate);
+ break;
+ case AMD_IP_BLOCK_TYPE_GMC:
+ pp_dpm_powergate_mmhub(handle);
+ break;
+ case AMD_IP_BLOCK_TYPE_GFX:
+ ret = pp_dpm_powergate_gfx(handle, gate);
+ break;
+ case AMD_IP_BLOCK_TYPE_ACP:
+ pp_dpm_powergate_acp(handle, gate);
+ break;
+ case AMD_IP_BLOCK_TYPE_SDMA:
+ pp_dpm_powergate_sdma(handle, gate);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static int pp_notify_smu_enable_pwe(void *handle)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->smus_notify_pwe == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&hwmgr->smu_lock);
+ hwmgr->hwmgr_func->smus_notify_pwe(hwmgr);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return 0;
+}
+
+static int pp_enable_mgpu_fan_boost(void *handle)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr)
+ return -EINVAL;
+
+ if (!hwmgr->pm_en ||
+ hwmgr->hwmgr_func->enable_mgpu_fan_boost == NULL)
+ return 0;
+
+ mutex_lock(&hwmgr->smu_lock);
+ hwmgr->hwmgr_func->enable_mgpu_fan_boost(hwmgr);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return 0;
+}
+
+static int pp_set_min_deep_sleep_dcefclk(void *handle, uint32_t clock)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->set_min_deep_sleep_dcefclk == NULL) {
+ pr_debug("%s was not implemented.\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&hwmgr->smu_lock);
+ hwmgr->hwmgr_func->set_min_deep_sleep_dcefclk(hwmgr, clock);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return 0;
+}
+
+static int pp_set_hard_min_dcefclk_by_freq(void *handle, uint32_t clock)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->set_hard_min_dcefclk_by_freq == NULL) {
+ pr_debug("%s was not implemented.\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&hwmgr->smu_lock);
+ hwmgr->hwmgr_func->set_hard_min_dcefclk_by_freq(hwmgr, clock);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return 0;
+}
+
+static int pp_set_hard_min_fclk_by_freq(void *handle, uint32_t clock)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->set_hard_min_fclk_by_freq == NULL) {
+ pr_debug("%s was not implemented.\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&hwmgr->smu_lock);
+ hwmgr->hwmgr_func->set_hard_min_fclk_by_freq(hwmgr, clock);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return 0;
+}
+
+static int pp_set_active_display_count(void *handle, uint32_t count)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->smu_lock);
+ ret = phm_set_active_display_count(hwmgr, count);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return ret;
+}
+
+static int pp_get_asic_baco_capability(void *handle, bool *cap)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ *cap = false;
+ if (!hwmgr)
+ return -EINVAL;
+
+ if (!(hwmgr->not_vf && amdgpu_dpm) ||
+ !hwmgr->hwmgr_func->get_asic_baco_capability)
+ return 0;
+
+ mutex_lock(&hwmgr->smu_lock);
+ hwmgr->hwmgr_func->get_asic_baco_capability(hwmgr, cap);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return 0;
+}
+
+static int pp_get_asic_baco_state(void *handle, int *state)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr)
+ return -EINVAL;
+
+ if (!hwmgr->pm_en || !hwmgr->hwmgr_func->get_asic_baco_state)
+ return 0;
+
+ mutex_lock(&hwmgr->smu_lock);
+ hwmgr->hwmgr_func->get_asic_baco_state(hwmgr, (enum BACO_STATE *)state);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return 0;
+}
+
+static int pp_set_asic_baco_state(void *handle, int state)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr)
+ return -EINVAL;
+
+ if (!(hwmgr->not_vf && amdgpu_dpm) ||
+ !hwmgr->hwmgr_func->set_asic_baco_state)
+ return 0;
+
+ mutex_lock(&hwmgr->smu_lock);
+ hwmgr->hwmgr_func->set_asic_baco_state(hwmgr, (enum BACO_STATE)state);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return 0;
+}
+
+static int pp_get_ppfeature_status(void *handle, char *buf)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en || !buf)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->get_ppfeature_status == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&hwmgr->smu_lock);
+ ret = hwmgr->hwmgr_func->get_ppfeature_status(hwmgr, buf);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return ret;
+}
+
+static int pp_set_ppfeature_status(void *handle, uint64_t ppfeature_masks)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->set_ppfeature_status == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&hwmgr->smu_lock);
+ ret = hwmgr->hwmgr_func->set_ppfeature_status(hwmgr, ppfeature_masks);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return ret;
+}
+
+static int pp_asic_reset_mode_2(void *handle)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->asic_reset == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&hwmgr->smu_lock);
+ ret = hwmgr->hwmgr_func->asic_reset(hwmgr, SMU_ASIC_RESET_MODE_2);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return ret;
+}
+
+static int pp_smu_i2c_bus_access(void *handle, bool acquire)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en)
+ return -EINVAL;
+
+ if (hwmgr->hwmgr_func->smu_i2c_bus_access == NULL) {
+ pr_info_ratelimited("%s was not implemented.\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&hwmgr->smu_lock);
+ ret = hwmgr->hwmgr_func->smu_i2c_bus_access(hwmgr, acquire);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return ret;
+}
+
+static int pp_set_df_cstate(void *handle, enum pp_df_cstate state)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr)
+ return -EINVAL;
+
+ if (!hwmgr->pm_en || !hwmgr->hwmgr_func->set_df_cstate)
+ return 0;
+
+ mutex_lock(&hwmgr->smu_lock);
+ hwmgr->hwmgr_func->set_df_cstate(hwmgr, state);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return 0;
+}
+
+static int pp_set_xgmi_pstate(void *handle, uint32_t pstate)
+{
+ struct pp_hwmgr *hwmgr = handle;
+
+ if (!hwmgr)
+ return -EINVAL;
+
+ if (!hwmgr->pm_en || !hwmgr->hwmgr_func->set_xgmi_pstate)
+ return 0;
+
+ mutex_lock(&hwmgr->smu_lock);
+ hwmgr->hwmgr_func->set_xgmi_pstate(hwmgr, pstate);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return 0;
+}
+
+static ssize_t pp_get_gpu_metrics(void *handle, void **table)
+{
+ struct pp_hwmgr *hwmgr = handle;
+ ssize_t size;
+
+ if (!hwmgr)
+ return -EINVAL;
+
+ if (!hwmgr->pm_en || !hwmgr->hwmgr_func->get_gpu_metrics)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&hwmgr->smu_lock);
+ size = hwmgr->hwmgr_func->get_gpu_metrics(hwmgr, table);
+ mutex_unlock(&hwmgr->smu_lock);
+
+ return size;
+}
+
+static const struct amd_pm_funcs pp_dpm_funcs = {
+ .load_firmware = pp_dpm_load_fw,
+ .wait_for_fw_loading_complete = pp_dpm_fw_loading_complete,
+ .force_performance_level = pp_dpm_force_performance_level,
+ .get_performance_level = pp_dpm_get_performance_level,
+ .get_current_power_state = pp_dpm_get_current_power_state,
+ .dispatch_tasks = pp_dpm_dispatch_tasks,
+ .set_fan_control_mode = pp_dpm_set_fan_control_mode,
+ .get_fan_control_mode = pp_dpm_get_fan_control_mode,
+ .set_fan_speed_percent = pp_dpm_set_fan_speed_percent,
+ .get_fan_speed_percent = pp_dpm_get_fan_speed_percent,
+ .get_fan_speed_rpm = pp_dpm_get_fan_speed_rpm,
+ .set_fan_speed_rpm = pp_dpm_set_fan_speed_rpm,
+ .get_pp_num_states = pp_dpm_get_pp_num_states,
+ .get_pp_table = pp_dpm_get_pp_table,
+ .set_pp_table = pp_dpm_set_pp_table,
+ .force_clock_level = pp_dpm_force_clock_level,
+ .print_clock_levels = pp_dpm_print_clock_levels,
+ .get_sclk_od = pp_dpm_get_sclk_od,
+ .set_sclk_od = pp_dpm_set_sclk_od,
+ .get_mclk_od = pp_dpm_get_mclk_od,
+ .set_mclk_od = pp_dpm_set_mclk_od,
+ .read_sensor = pp_dpm_read_sensor,
+ .get_vce_clock_state = pp_dpm_get_vce_clock_state,
+ .switch_power_profile = pp_dpm_switch_power_profile,
+ .set_clockgating_by_smu = pp_set_clockgating_by_smu,
+ .set_powergating_by_smu = pp_set_powergating_by_smu,
+ .get_power_profile_mode = pp_get_power_profile_mode,
+ .set_power_profile_mode = pp_set_power_profile_mode,
+ .set_fine_grain_clk_vol = pp_set_fine_grain_clk_vol,
+ .odn_edit_dpm_table = pp_odn_edit_dpm_table,
+ .set_mp1_state = pp_dpm_set_mp1_state,
+ .set_power_limit = pp_set_power_limit,
+ .get_power_limit = pp_get_power_limit,
+/* export to DC */
+ .get_sclk = pp_dpm_get_sclk,
+ .get_mclk = pp_dpm_get_mclk,
+ .display_configuration_change = pp_display_configuration_change,
+ .get_display_power_level = pp_get_display_power_level,
+ .get_current_clocks = pp_get_current_clocks,
+ .get_clock_by_type = pp_get_clock_by_type,
+ .get_clock_by_type_with_latency = pp_get_clock_by_type_with_latency,
+ .get_clock_by_type_with_voltage = pp_get_clock_by_type_with_voltage,
+ .set_watermarks_for_clocks_ranges = pp_set_watermarks_for_clocks_ranges,
+ .display_clock_voltage_request = pp_display_clock_voltage_request,
+ .get_display_mode_validation_clocks = pp_get_display_mode_validation_clocks,
+ .notify_smu_enable_pwe = pp_notify_smu_enable_pwe,
+ .enable_mgpu_fan_boost = pp_enable_mgpu_fan_boost,
+ .set_active_display_count = pp_set_active_display_count,
+ .set_min_deep_sleep_dcefclk = pp_set_min_deep_sleep_dcefclk,
+ .set_hard_min_dcefclk_by_freq = pp_set_hard_min_dcefclk_by_freq,
+ .set_hard_min_fclk_by_freq = pp_set_hard_min_fclk_by_freq,
+ .get_asic_baco_capability = pp_get_asic_baco_capability,
+ .get_asic_baco_state = pp_get_asic_baco_state,
+ .set_asic_baco_state = pp_set_asic_baco_state,
+ .get_ppfeature_status = pp_get_ppfeature_status,
+ .set_ppfeature_status = pp_set_ppfeature_status,
+ .asic_reset_mode_2 = pp_asic_reset_mode_2,
+ .smu_i2c_bus_access = pp_smu_i2c_bus_access,
+ .set_df_cstate = pp_set_df_cstate,
+ .set_xgmi_pstate = pp_set_xgmi_pstate,
+ .get_gpu_metrics = pp_get_gpu_metrics,
+};
diff --git a/drivers/gpu/drm/amd/pm/powerplay/cik_dpm.h b/drivers/gpu/drm/amd/pm/powerplay/cik_dpm.h
new file mode 100644
index 000000000000..2fcc4b60153c
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/cik_dpm.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 __CIK_DPM_H__
+#define __CIK_DPM_H__
+
+extern const struct amdgpu_ip_block_version kv_smu_ip_block;
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/Makefile b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/Makefile
new file mode 100644
index 000000000000..2773966ae434
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/Makefile
@@ -0,0 +1,44 @@
+#
+# Copyright 2017 Advanced Micro Devices, Inc.
+#
+# 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+#
+#
+# Makefile for the 'hw manager' sub-component of powerplay.
+# It provides the hardware management services for the driver.
+
+HARDWARE_MGR = hwmgr.o processpptables.o \
+ hardwaremanager.o smu8_hwmgr.o \
+ pppcielanes.o\
+ process_pptables_v1_0.o ppatomctrl.o ppatomfwctrl.o \
+ smu7_hwmgr.o smu7_powertune.o smu7_thermal.o \
+ smu7_clockpowergating.o \
+ vega10_processpptables.o vega10_hwmgr.o vega10_powertune.o \
+ vega10_thermal.o smu10_hwmgr.o pp_psm.o\
+ vega12_processpptables.o vega12_hwmgr.o \
+ vega12_thermal.o \
+ pp_overdriver.o smu_helper.o \
+ vega20_processpptables.o vega20_hwmgr.o vega20_powertune.o \
+ vega20_thermal.o common_baco.o vega10_baco.o vega20_baco.o \
+ vega12_baco.o smu9_baco.o tonga_baco.o polaris_baco.o fiji_baco.o \
+ ci_baco.o smu7_baco.o
+
+AMD_PP_HWMGR = $(addprefix $(AMD_PP_PATH)/hwmgr/,$(HARDWARE_MGR))
+
+AMD_POWERPLAY_FILES += $(AMD_PP_HWMGR)
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ci_baco.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ci_baco.c
new file mode 100644
index 000000000000..45f608838f6e
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ci_baco.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "amdgpu.h"
+#include "ci_baco.h"
+
+#include "gmc/gmc_7_1_d.h"
+#include "gmc/gmc_7_1_sh_mask.h"
+
+#include "bif/bif_4_1_d.h"
+#include "bif/bif_4_1_sh_mask.h"
+
+#include "dce/dce_8_0_d.h"
+#include "dce/dce_8_0_sh_mask.h"
+
+#include "smu/smu_7_0_1_d.h"
+#include "smu/smu_7_0_1_sh_mask.h"
+
+#include "gca/gfx_7_2_d.h"
+#include "gca/gfx_7_2_sh_mask.h"
+
+static const struct baco_cmd_entry gpio_tbl[] =
+{
+ { CMD_WRITE, mmGPIOPAD_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmGPIOPAD_PD_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmGPIOPAD_PU_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmGPIOPAD_MASK, 0, 0, 0, 0xff77ffff },
+ { CMD_WRITE, mmDC_GPIO_DVODATA_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmDC_GPIO_DVODATA_MASK, 0, 0, 0, 0xffffffff },
+ { CMD_WRITE, mmDC_GPIO_GENERIC_EN, 0, 0, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmDC_GPIO_GENERIC_MASK, 0, 0, 0, 0x03333333 },
+ { CMD_WRITE, mmDC_GPIO_SYNCA_EN, 0, 0, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmDC_GPIO_SYNCA_MASK, 0, 0, 0, 0x00001111 }
+};
+
+static const struct baco_cmd_entry enable_fb_req_rej_tbl[] =
+{
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, 0xC0300024 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x1, 0x0, 0, 0x1 },
+ { CMD_WRITE, mmBIF_FB_EN, 0, 0, 0, 0x0 }
+};
+
+static const struct baco_cmd_entry use_bclk_tbl[] =
+{
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_FUNC_CNTL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL__SPLL_BYPASS_EN_MASK, CG_SPLL_FUNC_CNTL__SPLL_BYPASS_EN__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_FUNC_CNTL_2 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL_2__SPLL_BYPASS_CHG_MASK, CG_SPLL_FUNC_CNTL_2__SPLL_BYPASS_CHG__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_STATUS },
+ { CMD_WAITFOR, mmGCK_SMC_IND_DATA, CG_SPLL_STATUS__SPLL_CHG_STATUS_MASK, 0, 0xffffffff, 0x2 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_FUNC_CNTL_2 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL_2__SPLL_BYPASS_CHG_MASK, CG_SPLL_FUNC_CNTL_2__SPLL_BYPASS_CHG__SHIFT, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL_2__SPLL_CTLREQ_CHG_MASK, CG_SPLL_FUNC_CNTL_2__SPLL_CTLREQ_CHG__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_STATUS },
+ { CMD_WAITFOR, mmGCK_SMC_IND_DATA, CG_SPLL_STATUS__SPLL_CHG_STATUS_MASK, 0, 0xffffffff, 0x2 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_FUNC_CNTL_2 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL_2__SPLL_CTLREQ_CHG_MASK, CG_SPLL_FUNC_CNTL_2__SPLL_CTLREQ_CHG__SHIFT, 0, 0x0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, 0xC0500170 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x4000000, 0x1a, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixMPLL_BYPASSCLK_SEL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, MPLL_BYPASSCLK_SEL__MPLL_CLKOUT_SEL_MASK, MPLL_BYPASSCLK_SEL__MPLL_CLKOUT_SEL__SHIFT, 0, 0x2 },
+ { CMD_READMODIFYWRITE, mmMPLL_CNTL_MODE, MPLL_CNTL_MODE__MPLL_SW_DIR_CONTROL_MASK, MPLL_CNTL_MODE__MPLL_SW_DIR_CONTROL__SHIFT, 0, 0x1 },
+ { CMD_READMODIFYWRITE, mmMPLL_CNTL_MODE, MPLL_CNTL_MODE__MPLL_MCLK_SEL_MASK, MPLL_CNTL_MODE__MPLL_MCLK_SEL__SHIFT, 0, 0x0 }
+};
+
+static const struct baco_cmd_entry turn_off_plls_tbl[] =
+{
+ { CMD_READMODIFYWRITE, mmDISPPLL_BG_CNTL, DISPPLL_BG_CNTL__DISPPLL_BG_PDN_MASK, DISPPLL_BG_CNTL__DISPPLL_BG_PDN__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_CLKPIN_CNTL_DC },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_CLKPIN_CNTL_DC__OSC_EN_MASK, CG_CLKPIN_CNTL_DC__OSC_EN__SHIFT, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_CLKPIN_CNTL_DC__XTALIN_SEL_MASK, CG_CLKPIN_CNTL_DC__XTALIN_SEL__SHIFT, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmPLL_CNTL, PLL_CNTL__PLL_RESET_MASK, PLL_CNTL__PLL_RESET__SHIFT, 0, 0x1 },
+ { CMD_READMODIFYWRITE, mmPLL_CNTL, PLL_CNTL__PLL_POWER_DOWN_MASK, PLL_CNTL__PLL_POWER_DOWN__SHIFT, 0, 0x1 },
+ { CMD_READMODIFYWRITE, mmPLL_CNTL, PLL_CNTL__PLL_BYPASS_CAL_MASK, PLL_CNTL__PLL_BYPASS_CAL__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_FUNC_CNTL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL__SPLL_RESET_MASK, CG_SPLL_FUNC_CNTL__SPLL_RESET__SHIFT, 0, 0x1 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL__SPLL_PWRON_MASK, CG_SPLL_FUNC_CNTL__SPLL_PWRON__SHIFT, 0, 0x0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, 0xC0500170 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x2000000, 0x19, 0, 0x1 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x8000000, 0x1b, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmMPLL_CNTL_MODE, MPLL_CNTL_MODE__GLOBAL_MPLL_RESET_MASK, MPLL_CNTL_MODE__GLOBAL_MPLL_RESET__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmMPLL_CONTROL, 0, 0, 0, 0x00000006 },
+ { CMD_WRITE, mmMC_IO_RXCNTL_DPHY0_D0, 0, 0, 0, 0x00007740 },
+ { CMD_WRITE, mmMC_IO_RXCNTL_DPHY0_D1, 0, 0, 0, 0x00007740 },
+ { CMD_WRITE, mmMC_IO_RXCNTL_DPHY1_D0, 0, 0, 0, 0x00007740 },
+ { CMD_WRITE, mmMC_IO_RXCNTL_DPHY1_D1, 0, 0, 0, 0x00007740 },
+ { CMD_READMODIFYWRITE, mmMCLK_PWRMGT_CNTL, MCLK_PWRMGT_CNTL__MRDCK0_PDNB_MASK, MCLK_PWRMGT_CNTL__MRDCK0_PDNB__SHIFT, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmMCLK_PWRMGT_CNTL, MCLK_PWRMGT_CNTL__MRDCK1_PDNB_MASK, MCLK_PWRMGT_CNTL__MRDCK1_PDNB__SHIFT, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmMC_SEQ_CNTL_2, MC_SEQ_CNTL_2__DRST_PU_MASK, MC_SEQ_CNTL_2__DRST_PU__SHIFT, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmMC_SEQ_CNTL_2, MC_SEQ_CNTL_2__DRST_PD_MASK, MC_SEQ_CNTL_2__DRST_PD__SHIFT, 0, 0x0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_CLKPIN_CNTL_2 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_CLKPIN_CNTL_2__FORCE_BIF_REFCLK_EN_MASK, CG_CLKPIN_CNTL_2__FORCE_BIF_REFCLK_EN__SHIFT, 0, 0x0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixMPLL_BYPASSCLK_SEL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, MPLL_BYPASSCLK_SEL__MPLL_CLKOUT_SEL_MASK, MPLL_BYPASSCLK_SEL__MPLL_CLKOUT_SEL__SHIFT, 0, 0x4 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixMISC_CLK_CTRL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, MISC_CLK_CTRL__DEEP_SLEEP_CLK_SEL_MASK, MISC_CLK_CTRL__DEEP_SLEEP_CLK_SEL__SHIFT, 0, 0x2 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, MISC_CLK_CTRL__ZCLK_SEL_MASK, MISC_CLK_CTRL__ZCLK_SEL__SHIFT, 0, 0x2 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, MISC_CLK_CTRL__DFT_SMS_PG_CLK_SEL_MASK, MISC_CLK_CTRL__DFT_SMS_PG_CLK_SEL__SHIFT, 0, 0x2 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixTHM_CLK_CNTL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, THM_CLK_CNTL__CMON_CLK_SEL_MASK, THM_CLK_CNTL__CMON_CLK_SEL__SHIFT, 0, 0x2 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, THM_CLK_CNTL__TMON_CLK_SEL_MASK, THM_CLK_CNTL__TMON_CLK_SEL__SHIFT, 0, 0x2 }
+};
+
+static const struct baco_cmd_entry enter_baco_tbl[] =
+{
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_EN_MASK, BACO_CNTL__BACO_EN__SHIFT, 0, 0x01 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_BCLK_OFF_MASK, BACO_CNTL__BACO_BCLK_OFF__SHIFT, 0, 0x01 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_BCLK_OFF_MASK, 0, 5, 0x02 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_ISO_DIS_MASK, BACO_CNTL__BACO_ISO_DIS__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_ISO_DIS_MASK, 0, 5, 0x00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_ANA_ISO_DIS_MASK, BACO_CNTL__BACO_ANA_ISO_DIS__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_ANA_ISO_DIS_MASK, 0, 5, 0x00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_POWER_OFF_MASK, BACO_CNTL__BACO_POWER_OFF__SHIFT, 0, 0x01 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_POWER_OFF_MASK, 0, 5, 0x08 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_MODE_MASK, 0, 0xffffffff, 0x40 }
+};
+
+#define BACO_CNTL__PWRGOOD_MASK BACO_CNTL__PWRGOOD_GPIO_MASK+BACO_CNTL__PWRGOOD_MEM_MASK+BACO_CNTL__PWRGOOD_DVO_MASK
+
+static const struct baco_cmd_entry exit_baco_tbl[] =
+{
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_RESET_EN_MASK, BACO_CNTL__BACO_RESET_EN__SHIFT, 0, 0x01 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_BCLK_OFF_MASK, BACO_CNTL__BACO_BCLK_OFF__SHIFT, 0, 0x00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_POWER_OFF_MASK, BACO_CNTL__BACO_POWER_OFF__SHIFT, 0, 0x00 },
+ { CMD_DELAY_MS, 0, 0, 0, 20, 0 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__PWRGOOD_BF_MASK, 0, 0xffffffff, 0x200 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_ISO_DIS_MASK, BACO_CNTL__BACO_ISO_DIS__SHIFT, 0, 0x01 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__PWRGOOD_MASK, 0, 5, 0x1c00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_ANA_ISO_DIS_MASK, BACO_CNTL__BACO_ANA_ISO_DIS__SHIFT, 0, 0x01 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_RESET_EN_MASK, BACO_CNTL__BACO_RESET_EN__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__RCU_BIF_CONFIG_DONE_MASK, 0, 5, 0x100 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_EN_MASK, BACO_CNTL__BACO_EN__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_MODE_MASK, 0, 0xffffffff, 0x00 }
+};
+
+static const struct baco_cmd_entry clean_baco_tbl[] =
+{
+ { CMD_WRITE, mmBIOS_SCRATCH_6, 0, 0, 0, 0 },
+ { CMD_WRITE, mmBIOS_SCRATCH_7, 0, 0, 0, 0 },
+ { CMD_WRITE, mmCP_PFP_UCODE_ADDR, 0, 0, 0, 0 }
+};
+
+int ci_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state)
+{
+ enum BACO_STATE cur_state;
+
+ smu7_baco_get_state(hwmgr, &cur_state);
+
+ if (cur_state == state)
+ /* aisc already in the target state */
+ return 0;
+
+ if (state == BACO_STATE_IN) {
+ baco_program_registers(hwmgr, gpio_tbl, ARRAY_SIZE(gpio_tbl));
+ baco_program_registers(hwmgr, enable_fb_req_rej_tbl,
+ ARRAY_SIZE(enable_fb_req_rej_tbl));
+ baco_program_registers(hwmgr, use_bclk_tbl, ARRAY_SIZE(use_bclk_tbl));
+ baco_program_registers(hwmgr, turn_off_plls_tbl,
+ ARRAY_SIZE(turn_off_plls_tbl));
+ if (baco_program_registers(hwmgr, enter_baco_tbl,
+ ARRAY_SIZE(enter_baco_tbl)))
+ return 0;
+
+ } else if (state == BACO_STATE_OUT) {
+ /* HW requires at least 20ms between regulator off and on */
+ msleep(20);
+ /* Execute Hardware BACO exit sequence */
+ if (baco_program_registers(hwmgr, exit_baco_tbl,
+ ARRAY_SIZE(exit_baco_tbl))) {
+ if (baco_program_registers(hwmgr, clean_baco_tbl,
+ ARRAY_SIZE(clean_baco_tbl)))
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ci_baco.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ci_baco.h
new file mode 100644
index 000000000000..17041f187020
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ci_baco.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 __CI_BACO_H__
+#define __CI_BACO_H__
+#include "smu7_baco.h"
+
+extern int ci_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/common_baco.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/common_baco.c
new file mode 100644
index 000000000000..1c73776bd606
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/common_baco.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "common_baco.h"
+
+
+static bool baco_wait_register(struct pp_hwmgr *hwmgr, u32 reg, u32 mask, u32 value)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
+ u32 timeout = 5000, data;
+
+ do {
+ msleep(1);
+ data = RREG32(reg);
+ timeout--;
+ } while (value != (data & mask) && (timeout != 0));
+
+ if (timeout == 0)
+ return false;
+
+ return true;
+}
+
+static bool baco_cmd_handler(struct pp_hwmgr *hwmgr, u32 command, u32 reg, u32 mask,
+ u32 shift, u32 value, u32 timeout)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
+ u32 data;
+ bool ret = true;
+
+ switch (command) {
+ case CMD_WRITE:
+ WREG32(reg, value << shift);
+ break;
+ case CMD_READMODIFYWRITE:
+ data = RREG32(reg);
+ data = (data & (~mask)) | (value << shift);
+ WREG32(reg, data);
+ break;
+ case CMD_WAITFOR:
+ ret = baco_wait_register(hwmgr, reg, mask, value);
+ break;
+ case CMD_DELAY_MS:
+ if (timeout)
+ /* Delay in milli Seconds */
+ msleep(timeout);
+ break;
+ case CMD_DELAY_US:
+ if (timeout)
+ /* Delay in micro Seconds */
+ udelay(timeout);
+ break;
+
+ default:
+ dev_warn(adev->dev, "Invalid BACO command.\n");
+ ret = false;
+ }
+
+ return ret;
+}
+
+bool baco_program_registers(struct pp_hwmgr *hwmgr,
+ const struct baco_cmd_entry *entry,
+ const u32 array_size)
+{
+ u32 i, reg = 0;
+
+ for (i = 0; i < array_size; i++) {
+ if ((entry[i].cmd == CMD_WRITE) ||
+ (entry[i].cmd == CMD_READMODIFYWRITE) ||
+ (entry[i].cmd == CMD_WAITFOR))
+ reg = entry[i].reg_offset;
+ if (!baco_cmd_handler(hwmgr, entry[i].cmd, reg, entry[i].mask,
+ entry[i].shift, entry[i].val, entry[i].timeout))
+ return false;
+ }
+
+ return true;
+}
+
+bool soc15_baco_program_registers(struct pp_hwmgr *hwmgr,
+ const struct soc15_baco_cmd_entry *entry,
+ const u32 array_size)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
+ u32 i, reg = 0;
+
+ for (i = 0; i < array_size; i++) {
+ if ((entry[i].cmd == CMD_WRITE) ||
+ (entry[i].cmd == CMD_READMODIFYWRITE) ||
+ (entry[i].cmd == CMD_WAITFOR))
+ reg = adev->reg_offset[entry[i].hwip][entry[i].inst][entry[i].seg]
+ + entry[i].reg_offset;
+ if (!baco_cmd_handler(hwmgr, entry[i].cmd, reg, entry[i].mask,
+ entry[i].shift, entry[i].val, entry[i].timeout))
+ return false;
+ }
+
+ return true;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/common_baco.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/common_baco.h
new file mode 100644
index 000000000000..8393eb62706d
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/common_baco.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 __COMMON_BOCO_H__
+#define __COMMON_BOCO_H__
+#include "hwmgr.h"
+
+
+enum baco_cmd_type {
+ CMD_WRITE = 0,
+ CMD_READMODIFYWRITE,
+ CMD_WAITFOR,
+ CMD_DELAY_MS,
+ CMD_DELAY_US,
+};
+
+struct baco_cmd_entry {
+ enum baco_cmd_type cmd;
+ uint32_t reg_offset;
+ uint32_t mask;
+ uint32_t shift;
+ uint32_t timeout;
+ uint32_t val;
+};
+
+struct soc15_baco_cmd_entry {
+ enum baco_cmd_type cmd;
+ uint32_t hwip;
+ uint32_t inst;
+ uint32_t seg;
+ uint32_t reg_offset;
+ uint32_t mask;
+ uint32_t shift;
+ uint32_t timeout;
+ uint32_t val;
+};
+
+extern bool baco_program_registers(struct pp_hwmgr *hwmgr,
+ const struct baco_cmd_entry *entry,
+ const u32 array_size);
+extern bool soc15_baco_program_registers(struct pp_hwmgr *hwmgr,
+ const struct soc15_baco_cmd_entry *entry,
+ const u32 array_size);
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/fiji_baco.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/fiji_baco.c
new file mode 100644
index 000000000000..c0368f2dfb21
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/fiji_baco.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "amdgpu.h"
+#include "fiji_baco.h"
+
+#include "gmc/gmc_8_1_d.h"
+#include "gmc/gmc_8_1_sh_mask.h"
+
+#include "bif/bif_5_0_d.h"
+#include "bif/bif_5_0_sh_mask.h"
+
+#include "dce/dce_10_0_d.h"
+#include "dce/dce_10_0_sh_mask.h"
+
+#include "smu/smu_7_1_3_d.h"
+#include "smu/smu_7_1_3_sh_mask.h"
+
+
+static const struct baco_cmd_entry gpio_tbl[] =
+{
+ { CMD_WRITE, mmGPIOPAD_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmGPIOPAD_PD_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmGPIOPAD_PU_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmGPIOPAD_MASK, 0, 0, 0, 0xff77ffff },
+ { CMD_WRITE, mmDC_GPIO_DVODATA_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmDC_GPIO_DVODATA_MASK, 0, 0, 0, 0xffffffff },
+ { CMD_WRITE, mmDC_GPIO_GENERIC_EN, 0, 0, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmDC_GPIO_GENERIC_MASK, 0, 0, 0, 0x03333333 },
+ { CMD_WRITE, mmDC_GPIO_SYNCA_EN, 0, 0, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmDC_GPIO_SYNCA_MASK, 0, 0, 0, 0x00001111 }
+};
+
+static const struct baco_cmd_entry enable_fb_req_rej_tbl[] =
+{
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, 0xC0300024 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x1, 0x0, 0, 0x1 },
+ { CMD_WRITE, mmBIF_FB_EN, 0, 0, 0, 0x0 }
+};
+
+static const struct baco_cmd_entry use_bclk_tbl[] =
+{
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_FUNC_CNTL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL__SPLL_BYPASS_EN_MASK, CG_SPLL_FUNC_CNTL__SPLL_BYPASS_EN__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_FUNC_CNTL_2 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL_2__SPLL_BYPASS_CHG_MASK, CG_SPLL_FUNC_CNTL_2__SPLL_BYPASS_CHG__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_STATUS },
+ { CMD_WAITFOR, mmGCK_SMC_IND_DATA, CG_SPLL_STATUS__SPLL_CHG_STATUS_MASK, 0, 0xffffffff, 0x2 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_FUNC_CNTL_2 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL_2__SPLL_BYPASS_CHG_MASK, CG_SPLL_FUNC_CNTL_2__SPLL_BYPASS_CHG__SHIFT, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL_2__SPLL_CTLREQ_CHG_MASK, CG_SPLL_FUNC_CNTL_2__SPLL_CTLREQ_CHG__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_STATUS },
+ { CMD_WAITFOR, mmGCK_SMC_IND_DATA, CG_SPLL_STATUS__SPLL_CHG_STATUS_MASK, 0, 0xffffffff, 0x2 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_FUNC_CNTL_2 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL_2__SPLL_CTLREQ_CHG_MASK, CG_SPLL_FUNC_CNTL_2__SPLL_CTLREQ_CHG__SHIFT, 0, 0x0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, 0xC0500170 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x4000000, 0x1a, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixMPLL_BYPASSCLK_SEL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, MPLL_BYPASSCLK_SEL__MPLL_CLKOUT_SEL_MASK, MPLL_BYPASSCLK_SEL__MPLL_CLKOUT_SEL__SHIFT, 0, 0x2 }
+};
+
+static const struct baco_cmd_entry turn_off_plls_tbl[] =
+{
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_FUNC_CNTL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL__SPLL_RESET_MASK, CG_SPLL_FUNC_CNTL__SPLL_RESET__SHIFT, 0, 0x1 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL__SPLL_PWRON_MASK, CG_SPLL_FUNC_CNTL__SPLL_PWRON__SHIFT, 0, 0x0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, 0xC0500170 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x2000000, 0x19, 0, 0x1 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x8000000, 0x1b, 0, 0x0 }
+};
+
+static const struct baco_cmd_entry clk_req_b_tbl[] =
+{
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_CLKPIN_CNTL_2 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_CLKPIN_CNTL_2__FORCE_BIF_REFCLK_EN_MASK, CG_CLKPIN_CNTL_2__FORCE_BIF_REFCLK_EN__SHIFT, 0, 0x0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixMPLL_BYPASSCLK_SEL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, MPLL_BYPASSCLK_SEL__MPLL_CLKOUT_SEL_MASK, MPLL_BYPASSCLK_SEL__MPLL_CLKOUT_SEL__SHIFT, 0, 0x4 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixMISC_CLK_CTRL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, MISC_CLK_CTRL__DEEP_SLEEP_CLK_SEL_MASK, MISC_CLK_CTRL__DEEP_SLEEP_CLK_SEL__SHIFT, 0, 0x1 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, MISC_CLK_CTRL__ZCLK_SEL_MASK, MISC_CLK_CTRL__ZCLK_SEL__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_CLKPIN_CNTL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_CLKPIN_CNTL__BCLK_AS_XCLK_MASK, CG_CLKPIN_CNTL__BCLK_AS_XCLK__SHIFT, 0, 0x0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixTHM_CLK_CNTL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, THM_CLK_CNTL__CMON_CLK_SEL_MASK, THM_CLK_CNTL__CMON_CLK_SEL__SHIFT, 0, 0x1 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, THM_CLK_CNTL__TMON_CLK_SEL_MASK, THM_CLK_CNTL__TMON_CLK_SEL__SHIFT, 0, 0x1 }
+};
+
+static const struct baco_cmd_entry enter_baco_tbl[] =
+{
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_EN_MASK, BACO_CNTL__BACO_EN__SHIFT, 0, 0x01 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_BIF_SCLK_SWITCH_MASK, BACO_CNTL__BACO_BIF_SCLK_SWITCH__SHIFT, 0, 0x01 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_BIF_SCLK_SWITCH_MASK, 0, 5, 0x40000 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_BCLK_OFF_MASK, BACO_CNTL__BACO_BCLK_OFF__SHIFT, 0, 0x01 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_BCLK_OFF_MASK, 0, 5, 0x02 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_ISO_DIS_MASK, BACO_CNTL__BACO_ISO_DIS__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_ISO_DIS_MASK, 0, 5, 0x00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_ANA_ISO_DIS_MASK, BACO_CNTL__BACO_ANA_ISO_DIS__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_ANA_ISO_DIS_MASK, 0, 5, 0x00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_POWER_OFF_MASK, BACO_CNTL__BACO_POWER_OFF__SHIFT, 0, 0x01 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_POWER_OFF_MASK, 0, 5, 0x08 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_MODE_MASK, 0, 0xffffffff, 0x40 }
+};
+
+#define BACO_CNTL__PWRGOOD_MASK BACO_CNTL__PWRGOOD_GPIO_MASK+BACO_CNTL__PWRGOOD_MEM_MASK+BACO_CNTL__PWRGOOD_DVO_MASK
+
+static const struct baco_cmd_entry exit_baco_tbl[] =
+{
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_RESET_EN_MASK, BACO_CNTL__BACO_RESET_EN__SHIFT, 0, 0x01 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_BCLK_OFF_MASK, BACO_CNTL__BACO_BCLK_OFF__SHIFT, 0, 0x00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_POWER_OFF_MASK, BACO_CNTL__BACO_POWER_OFF__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__PWRGOOD_BF_MASK, 0, 0xffffffff, 0x200 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_ISO_DIS_MASK, BACO_CNTL__BACO_ISO_DIS__SHIFT, 0, 0x01 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__PWRGOOD_MASK, 0, 5, 0x1c00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_ANA_ISO_DIS_MASK, BACO_CNTL__BACO_ANA_ISO_DIS__SHIFT, 0, 0x01 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_BIF_SCLK_SWITCH_MASK, BACO_CNTL__BACO_BIF_SCLK_SWITCH__SHIFT, 0, 0x00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_RESET_EN_MASK, BACO_CNTL__BACO_RESET_EN__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__RCU_BIF_CONFIG_DONE_MASK, 0, 5, 0x100 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_EN_MASK, BACO_CNTL__BACO_EN__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_MODE_MASK, 0, 0xffffffff, 0x00 }
+};
+
+static const struct baco_cmd_entry clean_baco_tbl[] =
+{
+ { CMD_WRITE, mmBIOS_SCRATCH_0, 0, 0, 0, 0 },
+ { CMD_WRITE, mmBIOS_SCRATCH_1, 0, 0, 0, 0 },
+ { CMD_WRITE, mmBIOS_SCRATCH_2, 0, 0, 0, 0 },
+ { CMD_WRITE, mmBIOS_SCRATCH_3, 0, 0, 0, 0 },
+ { CMD_WRITE, mmBIOS_SCRATCH_4, 0, 0, 0, 0 },
+ { CMD_WRITE, mmBIOS_SCRATCH_5, 0, 0, 0, 0 },
+ { CMD_WRITE, mmBIOS_SCRATCH_6, 0, 0, 0, 0 },
+ { CMD_WRITE, mmBIOS_SCRATCH_7, 0, 0, 0, 0 },
+ { CMD_WRITE, mmBIOS_SCRATCH_8, 0, 0, 0, 0 },
+ { CMD_WRITE, mmBIOS_SCRATCH_9, 0, 0, 0, 0 },
+ { CMD_WRITE, mmBIOS_SCRATCH_10, 0, 0, 0, 0 },
+ { CMD_WRITE, mmBIOS_SCRATCH_11, 0, 0, 0, 0 },
+ { CMD_WRITE, mmBIOS_SCRATCH_12, 0, 0, 0, 0 },
+ { CMD_WRITE, mmBIOS_SCRATCH_13, 0, 0, 0, 0 },
+ { CMD_WRITE, mmBIOS_SCRATCH_14, 0, 0, 0, 0 },
+ { CMD_WRITE, mmBIOS_SCRATCH_15, 0, 0, 0, 0 }
+};
+
+int fiji_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state)
+{
+ enum BACO_STATE cur_state;
+
+ smu7_baco_get_state(hwmgr, &cur_state);
+
+ if (cur_state == state)
+ /* aisc already in the target state */
+ return 0;
+
+ if (state == BACO_STATE_IN) {
+ baco_program_registers(hwmgr, gpio_tbl, ARRAY_SIZE(gpio_tbl));
+ baco_program_registers(hwmgr, enable_fb_req_rej_tbl,
+ ARRAY_SIZE(enable_fb_req_rej_tbl));
+ baco_program_registers(hwmgr, use_bclk_tbl, ARRAY_SIZE(use_bclk_tbl));
+ baco_program_registers(hwmgr, turn_off_plls_tbl,
+ ARRAY_SIZE(turn_off_plls_tbl));
+ baco_program_registers(hwmgr, clk_req_b_tbl, ARRAY_SIZE(clk_req_b_tbl));
+ if (baco_program_registers(hwmgr, enter_baco_tbl,
+ ARRAY_SIZE(enter_baco_tbl)))
+ return 0;
+
+ } else if (state == BACO_STATE_OUT) {
+ /* HW requires at least 20ms between regulator off and on */
+ msleep(20);
+ /* Execute Hardware BACO exit sequence */
+ if (baco_program_registers(hwmgr, exit_baco_tbl,
+ ARRAY_SIZE(exit_baco_tbl))) {
+ if (baco_program_registers(hwmgr, clean_baco_tbl,
+ ARRAY_SIZE(clean_baco_tbl)))
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/fiji_baco.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/fiji_baco.h
new file mode 100644
index 000000000000..47f402900bdb
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/fiji_baco.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 __FIJI_BACO_H__
+#define __FIJI_BACO_H__
+#include "smu7_baco.h"
+
+extern int fiji_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hardwaremanager.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hardwaremanager.c
new file mode 100644
index 000000000000..1f9b9facdf1f
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hardwaremanager.c
@@ -0,0 +1,547 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "pp_debug.h"
+#include <linux/errno.h>
+#include "hwmgr.h"
+#include "hardwaremanager.h"
+#include "power_state.h"
+
+
+#define TEMP_RANGE_MIN (0)
+#define TEMP_RANGE_MAX (80 * 1000)
+
+#define PHM_FUNC_CHECK(hw) \
+ do { \
+ if ((hw) == NULL || (hw)->hwmgr_func == NULL) \
+ return -EINVAL; \
+ } while (0)
+
+int phm_setup_asic(struct pp_hwmgr *hwmgr)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (NULL != hwmgr->hwmgr_func->asic_setup)
+ return hwmgr->hwmgr_func->asic_setup(hwmgr);
+
+ return 0;
+}
+
+int phm_power_down_asic(struct pp_hwmgr *hwmgr)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (NULL != hwmgr->hwmgr_func->power_off_asic)
+ return hwmgr->hwmgr_func->power_off_asic(hwmgr);
+
+ return 0;
+}
+
+int phm_set_power_state(struct pp_hwmgr *hwmgr,
+ const struct pp_hw_power_state *pcurrent_state,
+ const struct pp_hw_power_state *pnew_power_state)
+{
+ struct phm_set_power_state_input states;
+
+ PHM_FUNC_CHECK(hwmgr);
+
+ states.pcurrent_state = pcurrent_state;
+ states.pnew_state = pnew_power_state;
+
+ if (NULL != hwmgr->hwmgr_func->power_state_set)
+ return hwmgr->hwmgr_func->power_state_set(hwmgr, &states);
+
+ return 0;
+}
+
+int phm_enable_dynamic_state_management(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = NULL;
+ int ret = -EINVAL;
+ PHM_FUNC_CHECK(hwmgr);
+ adev = hwmgr->adev;
+
+ /* Skip for suspend/resume case */
+ if (!hwmgr->pp_one_vf && smum_is_dpm_running(hwmgr)
+ && !amdgpu_passthrough(adev) && adev->in_suspend) {
+ pr_info("dpm has been enabled\n");
+ return 0;
+ }
+
+ if (NULL != hwmgr->hwmgr_func->dynamic_state_management_enable)
+ ret = hwmgr->hwmgr_func->dynamic_state_management_enable(hwmgr);
+
+ return ret;
+}
+
+int phm_disable_dynamic_state_management(struct pp_hwmgr *hwmgr)
+{
+ int ret = -EINVAL;
+
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (!hwmgr->not_vf)
+ return 0;
+
+ if (!smum_is_dpm_running(hwmgr)) {
+ pr_info("dpm has been disabled\n");
+ return 0;
+ }
+
+ if (hwmgr->hwmgr_func->dynamic_state_management_disable)
+ ret = hwmgr->hwmgr_func->dynamic_state_management_disable(hwmgr);
+
+ return ret;
+}
+
+int phm_force_dpm_levels(struct pp_hwmgr *hwmgr, enum amd_dpm_forced_level level)
+{
+ int ret = 0;
+
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (hwmgr->hwmgr_func->force_dpm_level != NULL)
+ ret = hwmgr->hwmgr_func->force_dpm_level(hwmgr, level);
+
+ return ret;
+}
+
+int phm_apply_state_adjust_rules(struct pp_hwmgr *hwmgr,
+ struct pp_power_state *adjusted_ps,
+ const struct pp_power_state *current_ps)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (hwmgr->hwmgr_func->apply_state_adjust_rules != NULL)
+ return hwmgr->hwmgr_func->apply_state_adjust_rules(
+ hwmgr,
+ adjusted_ps,
+ current_ps);
+ return 0;
+}
+
+int phm_apply_clock_adjust_rules(struct pp_hwmgr *hwmgr)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (hwmgr->hwmgr_func->apply_clocks_adjust_rules != NULL)
+ return hwmgr->hwmgr_func->apply_clocks_adjust_rules(hwmgr);
+ return 0;
+}
+
+int phm_powerdown_uvd(struct pp_hwmgr *hwmgr)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (hwmgr->hwmgr_func->powerdown_uvd != NULL)
+ return hwmgr->hwmgr_func->powerdown_uvd(hwmgr);
+ return 0;
+}
+
+
+int phm_disable_clock_power_gatings(struct pp_hwmgr *hwmgr)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (NULL != hwmgr->hwmgr_func->disable_clock_power_gating)
+ return hwmgr->hwmgr_func->disable_clock_power_gating(hwmgr);
+
+ return 0;
+}
+
+int phm_pre_display_configuration_changed(struct pp_hwmgr *hwmgr)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (NULL != hwmgr->hwmgr_func->pre_display_config_changed)
+ hwmgr->hwmgr_func->pre_display_config_changed(hwmgr);
+
+ return 0;
+
+}
+
+int phm_display_configuration_changed(struct pp_hwmgr *hwmgr)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (NULL != hwmgr->hwmgr_func->display_config_changed)
+ hwmgr->hwmgr_func->display_config_changed(hwmgr);
+
+ return 0;
+}
+
+int phm_notify_smc_display_config_after_ps_adjustment(struct pp_hwmgr *hwmgr)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (NULL != hwmgr->hwmgr_func->notify_smc_display_config_after_ps_adjustment)
+ hwmgr->hwmgr_func->notify_smc_display_config_after_ps_adjustment(hwmgr);
+
+ return 0;
+}
+
+int phm_stop_thermal_controller(struct pp_hwmgr *hwmgr)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (!hwmgr->not_vf)
+ return 0;
+
+ if (hwmgr->hwmgr_func->stop_thermal_controller == NULL)
+ return -EINVAL;
+
+ return hwmgr->hwmgr_func->stop_thermal_controller(hwmgr);
+}
+
+int phm_register_irq_handlers(struct pp_hwmgr *hwmgr)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (hwmgr->hwmgr_func->register_irq_handlers != NULL)
+ return hwmgr->hwmgr_func->register_irq_handlers(hwmgr);
+
+ return 0;
+}
+
+/**
+* Initializes the thermal controller subsystem.
+*
+* @param pHwMgr the address of the powerplay hardware manager.
+* @exception PP_Result_Failed if any of the paramters is NULL, otherwise the return value from the dispatcher.
+*/
+int phm_start_thermal_controller(struct pp_hwmgr *hwmgr)
+{
+ int ret = 0;
+ struct PP_TemperatureRange range = {
+ TEMP_RANGE_MIN,
+ TEMP_RANGE_MAX,
+ TEMP_RANGE_MAX,
+ TEMP_RANGE_MIN,
+ TEMP_RANGE_MAX,
+ TEMP_RANGE_MAX,
+ TEMP_RANGE_MIN,
+ TEMP_RANGE_MAX,
+ TEMP_RANGE_MAX};
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ if (!hwmgr->not_vf)
+ return 0;
+
+ if (hwmgr->hwmgr_func->get_thermal_temperature_range)
+ hwmgr->hwmgr_func->get_thermal_temperature_range(
+ hwmgr, &range);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ThermalController)
+ && hwmgr->hwmgr_func->start_thermal_controller != NULL)
+ ret = hwmgr->hwmgr_func->start_thermal_controller(hwmgr, &range);
+
+ adev->pm.dpm.thermal.min_temp = range.min;
+ adev->pm.dpm.thermal.max_temp = range.max;
+ adev->pm.dpm.thermal.max_edge_emergency_temp = range.edge_emergency_max;
+ adev->pm.dpm.thermal.min_hotspot_temp = range.hotspot_min;
+ adev->pm.dpm.thermal.max_hotspot_crit_temp = range.hotspot_crit_max;
+ adev->pm.dpm.thermal.max_hotspot_emergency_temp = range.hotspot_emergency_max;
+ adev->pm.dpm.thermal.min_mem_temp = range.mem_min;
+ adev->pm.dpm.thermal.max_mem_crit_temp = range.mem_crit_max;
+ adev->pm.dpm.thermal.max_mem_emergency_temp = range.mem_emergency_max;
+
+ return ret;
+}
+
+
+bool phm_check_smc_update_required_for_display_configuration(struct pp_hwmgr *hwmgr)
+{
+ if (hwmgr == NULL ||
+ hwmgr->hwmgr_func == NULL)
+ return false;
+
+ if (hwmgr->pp_one_vf)
+ return false;
+
+ if (hwmgr->hwmgr_func->check_smc_update_required_for_display_configuration == NULL)
+ return false;
+
+ return hwmgr->hwmgr_func->check_smc_update_required_for_display_configuration(hwmgr);
+}
+
+
+int phm_check_states_equal(struct pp_hwmgr *hwmgr,
+ const struct pp_hw_power_state *pstate1,
+ const struct pp_hw_power_state *pstate2,
+ bool *equal)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (hwmgr->hwmgr_func->check_states_equal == NULL)
+ return -EINVAL;
+
+ return hwmgr->hwmgr_func->check_states_equal(hwmgr, pstate1, pstate2, equal);
+}
+
+int phm_store_dal_configuration_data(struct pp_hwmgr *hwmgr,
+ const struct amd_pp_display_configuration *display_config)
+{
+ int index = 0;
+ int number_of_active_display = 0;
+
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (display_config == NULL)
+ return -EINVAL;
+
+ if (NULL != hwmgr->hwmgr_func->set_min_deep_sleep_dcefclk)
+ hwmgr->hwmgr_func->set_min_deep_sleep_dcefclk(hwmgr, display_config->min_dcef_deep_sleep_set_clk);
+
+ for (index = 0; index < display_config->num_path_including_non_display; index++) {
+ if (display_config->displays[index].controller_id != 0)
+ number_of_active_display++;
+ }
+
+ if (NULL != hwmgr->hwmgr_func->set_active_display_count)
+ hwmgr->hwmgr_func->set_active_display_count(hwmgr, number_of_active_display);
+
+ if (hwmgr->hwmgr_func->store_cc6_data == NULL)
+ return -EINVAL;
+
+ /* TODO: pass other display configuration in the future */
+
+ if (hwmgr->hwmgr_func->store_cc6_data)
+ hwmgr->hwmgr_func->store_cc6_data(hwmgr,
+ display_config->cpu_pstate_separation_time,
+ display_config->cpu_cc6_disable,
+ display_config->cpu_pstate_disable,
+ display_config->nb_pstate_switch_disable);
+
+ return 0;
+}
+
+int phm_get_dal_power_level(struct pp_hwmgr *hwmgr,
+ struct amd_pp_simple_clock_info *info)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (info == NULL || hwmgr->hwmgr_func->get_dal_power_level == NULL)
+ return -EINVAL;
+ return hwmgr->hwmgr_func->get_dal_power_level(hwmgr, info);
+}
+
+int phm_set_cpu_power_state(struct pp_hwmgr *hwmgr)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (hwmgr->hwmgr_func->set_cpu_power_state != NULL)
+ return hwmgr->hwmgr_func->set_cpu_power_state(hwmgr);
+
+ return 0;
+}
+
+
+int phm_get_performance_level(struct pp_hwmgr *hwmgr, const struct pp_hw_power_state *state,
+ PHM_PerformanceLevelDesignation designation, uint32_t index,
+ PHM_PerformanceLevel *level)
+{
+ PHM_FUNC_CHECK(hwmgr);
+ if (hwmgr->hwmgr_func->get_performance_level == NULL)
+ return -EINVAL;
+
+ return hwmgr->hwmgr_func->get_performance_level(hwmgr, state, designation, index, level);
+
+
+}
+
+
+/**
+* Gets Clock Info.
+*
+* @param pHwMgr the address of the powerplay hardware manager.
+* @param pPowerState the address of the Power State structure.
+* @param pClockInfo the address of PP_ClockInfo structure where the result will be returned.
+* @exception PP_Result_Failed if any of the paramters is NULL, otherwise the return value from the back-end.
+*/
+int phm_get_clock_info(struct pp_hwmgr *hwmgr, const struct pp_hw_power_state *state, struct pp_clock_info *pclock_info,
+ PHM_PerformanceLevelDesignation designation)
+{
+ int result;
+ PHM_PerformanceLevel performance_level = {0};
+
+ PHM_FUNC_CHECK(hwmgr);
+
+ PP_ASSERT_WITH_CODE((NULL != state), "Invalid Input!", return -EINVAL);
+ PP_ASSERT_WITH_CODE((NULL != pclock_info), "Invalid Input!", return -EINVAL);
+
+ result = phm_get_performance_level(hwmgr, state, PHM_PerformanceLevelDesignation_Activity, 0, &performance_level);
+
+ PP_ASSERT_WITH_CODE((0 == result), "Failed to retrieve minimum clocks.", return result);
+
+
+ pclock_info->min_mem_clk = performance_level.memory_clock;
+ pclock_info->min_eng_clk = performance_level.coreClock;
+ pclock_info->min_bus_bandwidth = performance_level.nonLocalMemoryFreq * performance_level.nonLocalMemoryWidth;
+
+
+ result = phm_get_performance_level(hwmgr, state, designation,
+ (hwmgr->platform_descriptor.hardwareActivityPerformanceLevels - 1), &performance_level);
+
+ PP_ASSERT_WITH_CODE((0 == result), "Failed to retrieve maximum clocks.", return result);
+
+ pclock_info->max_mem_clk = performance_level.memory_clock;
+ pclock_info->max_eng_clk = performance_level.coreClock;
+ pclock_info->max_bus_bandwidth = performance_level.nonLocalMemoryFreq * performance_level.nonLocalMemoryWidth;
+
+ return 0;
+}
+
+int phm_get_current_shallow_sleep_clocks(struct pp_hwmgr *hwmgr, const struct pp_hw_power_state *state, struct pp_clock_info *clock_info)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (hwmgr->hwmgr_func->get_current_shallow_sleep_clocks == NULL)
+ return -EINVAL;
+
+ return hwmgr->hwmgr_func->get_current_shallow_sleep_clocks(hwmgr, state, clock_info);
+
+}
+
+int phm_get_clock_by_type(struct pp_hwmgr *hwmgr, enum amd_pp_clock_type type, struct amd_pp_clocks *clocks)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (hwmgr->hwmgr_func->get_clock_by_type == NULL)
+ return -EINVAL;
+
+ return hwmgr->hwmgr_func->get_clock_by_type(hwmgr, type, clocks);
+
+}
+
+int phm_get_clock_by_type_with_latency(struct pp_hwmgr *hwmgr,
+ enum amd_pp_clock_type type,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (hwmgr->hwmgr_func->get_clock_by_type_with_latency == NULL)
+ return -EINVAL;
+
+ return hwmgr->hwmgr_func->get_clock_by_type_with_latency(hwmgr, type, clocks);
+
+}
+
+int phm_get_clock_by_type_with_voltage(struct pp_hwmgr *hwmgr,
+ enum amd_pp_clock_type type,
+ struct pp_clock_levels_with_voltage *clocks)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (hwmgr->hwmgr_func->get_clock_by_type_with_voltage == NULL)
+ return -EINVAL;
+
+ return hwmgr->hwmgr_func->get_clock_by_type_with_voltage(hwmgr, type, clocks);
+
+}
+
+int phm_set_watermarks_for_clocks_ranges(struct pp_hwmgr *hwmgr,
+ void *clock_ranges)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (!hwmgr->hwmgr_func->set_watermarks_for_clocks_ranges)
+ return -EINVAL;
+
+ return hwmgr->hwmgr_func->set_watermarks_for_clocks_ranges(hwmgr,
+ clock_ranges);
+}
+
+int phm_display_clock_voltage_request(struct pp_hwmgr *hwmgr,
+ struct pp_display_clock_request *clock)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (!hwmgr->hwmgr_func->display_clock_voltage_request)
+ return -EINVAL;
+
+ return hwmgr->hwmgr_func->display_clock_voltage_request(hwmgr, clock);
+}
+
+int phm_get_max_high_clocks(struct pp_hwmgr *hwmgr, struct amd_pp_simple_clock_info *clocks)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (hwmgr->hwmgr_func->get_max_high_clocks == NULL)
+ return -EINVAL;
+
+ return hwmgr->hwmgr_func->get_max_high_clocks(hwmgr, clocks);
+}
+
+int phm_disable_smc_firmware_ctf(struct pp_hwmgr *hwmgr)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (!hwmgr->not_vf)
+ return 0;
+
+ if (hwmgr->hwmgr_func->disable_smc_firmware_ctf == NULL)
+ return -EINVAL;
+
+ return hwmgr->hwmgr_func->disable_smc_firmware_ctf(hwmgr);
+}
+
+int phm_set_active_display_count(struct pp_hwmgr *hwmgr, uint32_t count)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (!hwmgr->hwmgr_func->set_active_display_count)
+ return -EINVAL;
+
+ return hwmgr->hwmgr_func->set_active_display_count(hwmgr, count);
+}
+
+int phm_set_min_deep_sleep_dcefclk(struct pp_hwmgr *hwmgr, uint32_t clock)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (!hwmgr->hwmgr_func->set_min_deep_sleep_dcefclk)
+ return -EINVAL;
+
+ return hwmgr->hwmgr_func->set_min_deep_sleep_dcefclk(hwmgr, clock);
+}
+
+int phm_set_hard_min_dcefclk_by_freq(struct pp_hwmgr *hwmgr, uint32_t clock)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (!hwmgr->hwmgr_func->set_hard_min_dcefclk_by_freq)
+ return -EINVAL;
+
+ return hwmgr->hwmgr_func->set_hard_min_dcefclk_by_freq(hwmgr, clock);
+}
+
+int phm_set_hard_min_fclk_by_freq(struct pp_hwmgr *hwmgr, uint32_t clock)
+{
+ PHM_FUNC_CHECK(hwmgr);
+
+ if (!hwmgr->hwmgr_func->set_hard_min_fclk_by_freq)
+ return -EINVAL;
+
+ return hwmgr->hwmgr_func->set_hard_min_fclk_by_freq(hwmgr, clock);
+}
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c
new file mode 100644
index 000000000000..f48fdc7f0382
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c
@@ -0,0 +1,564 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "pp_debug.h"
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <drm/amdgpu_drm.h>
+#include "power_state.h"
+#include "hwmgr.h"
+#include "ppsmc.h"
+#include "amd_acpi.h"
+#include "pp_psm.h"
+
+extern const struct pp_smumgr_func ci_smu_funcs;
+extern const struct pp_smumgr_func smu8_smu_funcs;
+extern const struct pp_smumgr_func iceland_smu_funcs;
+extern const struct pp_smumgr_func tonga_smu_funcs;
+extern const struct pp_smumgr_func fiji_smu_funcs;
+extern const struct pp_smumgr_func polaris10_smu_funcs;
+extern const struct pp_smumgr_func vegam_smu_funcs;
+extern const struct pp_smumgr_func vega10_smu_funcs;
+extern const struct pp_smumgr_func vega12_smu_funcs;
+extern const struct pp_smumgr_func smu10_smu_funcs;
+extern const struct pp_smumgr_func vega20_smu_funcs;
+
+extern int smu7_init_function_pointers(struct pp_hwmgr *hwmgr);
+extern int smu8_init_function_pointers(struct pp_hwmgr *hwmgr);
+extern int vega10_hwmgr_init(struct pp_hwmgr *hwmgr);
+extern int vega12_hwmgr_init(struct pp_hwmgr *hwmgr);
+extern int vega20_hwmgr_init(struct pp_hwmgr *hwmgr);
+extern int smu10_init_function_pointers(struct pp_hwmgr *hwmgr);
+
+static int polaris_set_asic_special_caps(struct pp_hwmgr *hwmgr);
+static void hwmgr_init_default_caps(struct pp_hwmgr *hwmgr);
+static int hwmgr_set_user_specify_caps(struct pp_hwmgr *hwmgr);
+static int fiji_set_asic_special_caps(struct pp_hwmgr *hwmgr);
+static int tonga_set_asic_special_caps(struct pp_hwmgr *hwmgr);
+static int topaz_set_asic_special_caps(struct pp_hwmgr *hwmgr);
+static int ci_set_asic_special_caps(struct pp_hwmgr *hwmgr);
+
+
+static void hwmgr_init_workload_prority(struct pp_hwmgr *hwmgr)
+{
+ hwmgr->workload_prority[PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT] = 0;
+ hwmgr->workload_prority[PP_SMC_POWER_PROFILE_FULLSCREEN3D] = 1;
+ hwmgr->workload_prority[PP_SMC_POWER_PROFILE_POWERSAVING] = 2;
+ hwmgr->workload_prority[PP_SMC_POWER_PROFILE_VIDEO] = 3;
+ hwmgr->workload_prority[PP_SMC_POWER_PROFILE_VR] = 4;
+ hwmgr->workload_prority[PP_SMC_POWER_PROFILE_COMPUTE] = 5;
+
+ hwmgr->workload_setting[0] = PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT;
+ hwmgr->workload_setting[1] = PP_SMC_POWER_PROFILE_FULLSCREEN3D;
+ hwmgr->workload_setting[2] = PP_SMC_POWER_PROFILE_POWERSAVING;
+ hwmgr->workload_setting[3] = PP_SMC_POWER_PROFILE_VIDEO;
+ hwmgr->workload_setting[4] = PP_SMC_POWER_PROFILE_VR;
+ hwmgr->workload_setting[5] = PP_SMC_POWER_PROFILE_COMPUTE;
+}
+
+int hwmgr_early_init(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev;
+
+ if (!hwmgr)
+ return -EINVAL;
+
+ hwmgr->usec_timeout = AMD_MAX_USEC_TIMEOUT;
+ hwmgr->pp_table_version = PP_TABLE_V1;
+ hwmgr->dpm_level = AMD_DPM_FORCED_LEVEL_AUTO;
+ hwmgr->request_dpm_level = AMD_DPM_FORCED_LEVEL_AUTO;
+ hwmgr_init_default_caps(hwmgr);
+ hwmgr_set_user_specify_caps(hwmgr);
+ hwmgr->fan_ctrl_is_in_default_mode = true;
+ hwmgr_init_workload_prority(hwmgr);
+ hwmgr->gfxoff_state_changed_by_workload = false;
+
+ adev = hwmgr->adev;
+
+ switch (hwmgr->chip_family) {
+ case AMDGPU_FAMILY_CI:
+ adev->pm.pp_feature &= ~PP_GFXOFF_MASK;
+ hwmgr->smumgr_funcs = &ci_smu_funcs;
+ ci_set_asic_special_caps(hwmgr);
+ hwmgr->feature_mask &= ~(PP_VBI_TIME_SUPPORT_MASK |
+ PP_ENABLE_GFX_CG_THRU_SMU |
+ PP_GFXOFF_MASK);
+ hwmgr->pp_table_version = PP_TABLE_V0;
+ hwmgr->od_enabled = false;
+ smu7_init_function_pointers(hwmgr);
+ break;
+ case AMDGPU_FAMILY_CZ:
+ adev->pm.pp_feature &= ~PP_GFXOFF_MASK;
+ hwmgr->od_enabled = false;
+ hwmgr->smumgr_funcs = &smu8_smu_funcs;
+ hwmgr->feature_mask &= ~PP_GFXOFF_MASK;
+ smu8_init_function_pointers(hwmgr);
+ break;
+ case AMDGPU_FAMILY_VI:
+ adev->pm.pp_feature &= ~PP_GFXOFF_MASK;
+ hwmgr->feature_mask &= ~PP_GFXOFF_MASK;
+ switch (hwmgr->chip_id) {
+ case CHIP_TOPAZ:
+ hwmgr->smumgr_funcs = &iceland_smu_funcs;
+ topaz_set_asic_special_caps(hwmgr);
+ hwmgr->feature_mask &= ~ (PP_VBI_TIME_SUPPORT_MASK |
+ PP_ENABLE_GFX_CG_THRU_SMU);
+ hwmgr->pp_table_version = PP_TABLE_V0;
+ hwmgr->od_enabled = false;
+ break;
+ case CHIP_TONGA:
+ hwmgr->smumgr_funcs = &tonga_smu_funcs;
+ tonga_set_asic_special_caps(hwmgr);
+ hwmgr->feature_mask &= ~PP_VBI_TIME_SUPPORT_MASK;
+ break;
+ case CHIP_FIJI:
+ hwmgr->smumgr_funcs = &fiji_smu_funcs;
+ fiji_set_asic_special_caps(hwmgr);
+ hwmgr->feature_mask &= ~ (PP_VBI_TIME_SUPPORT_MASK |
+ PP_ENABLE_GFX_CG_THRU_SMU);
+ break;
+ case CHIP_POLARIS11:
+ case CHIP_POLARIS10:
+ case CHIP_POLARIS12:
+ hwmgr->smumgr_funcs = &polaris10_smu_funcs;
+ polaris_set_asic_special_caps(hwmgr);
+ hwmgr->feature_mask &= ~(PP_UVD_HANDSHAKE_MASK);
+ break;
+ case CHIP_VEGAM:
+ hwmgr->smumgr_funcs = &vegam_smu_funcs;
+ polaris_set_asic_special_caps(hwmgr);
+ hwmgr->feature_mask &= ~(PP_UVD_HANDSHAKE_MASK);
+ break;
+ default:
+ return -EINVAL;
+ }
+ smu7_init_function_pointers(hwmgr);
+ break;
+ case AMDGPU_FAMILY_AI:
+ switch (hwmgr->chip_id) {
+ case CHIP_VEGA10:
+ adev->pm.pp_feature &= ~PP_GFXOFF_MASK;
+ hwmgr->feature_mask &= ~PP_GFXOFF_MASK;
+ hwmgr->smumgr_funcs = &vega10_smu_funcs;
+ vega10_hwmgr_init(hwmgr);
+ break;
+ case CHIP_VEGA12:
+ hwmgr->smumgr_funcs = &vega12_smu_funcs;
+ vega12_hwmgr_init(hwmgr);
+ break;
+ case CHIP_VEGA20:
+ adev->pm.pp_feature &= ~PP_GFXOFF_MASK;
+ hwmgr->feature_mask &= ~PP_GFXOFF_MASK;
+ hwmgr->smumgr_funcs = &vega20_smu_funcs;
+ vega20_hwmgr_init(hwmgr);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case AMDGPU_FAMILY_RV:
+ switch (hwmgr->chip_id) {
+ case CHIP_RAVEN:
+ hwmgr->od_enabled = false;
+ hwmgr->smumgr_funcs = &smu10_smu_funcs;
+ smu10_init_function_pointers(hwmgr);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int hwmgr_sw_init(struct pp_hwmgr *hwmgr)
+{
+ if (!hwmgr|| !hwmgr->smumgr_funcs || !hwmgr->smumgr_funcs->smu_init)
+ return -EINVAL;
+
+ phm_register_irq_handlers(hwmgr);
+ pr_info("hwmgr_sw_init smu backed is %s\n", hwmgr->smumgr_funcs->name);
+
+ return hwmgr->smumgr_funcs->smu_init(hwmgr);
+}
+
+
+int hwmgr_sw_fini(struct pp_hwmgr *hwmgr)
+{
+ if (hwmgr && hwmgr->smumgr_funcs && hwmgr->smumgr_funcs->smu_fini)
+ hwmgr->smumgr_funcs->smu_fini(hwmgr);
+
+ return 0;
+}
+
+int hwmgr_hw_init(struct pp_hwmgr *hwmgr)
+{
+ int ret = 0;
+
+ hwmgr->pp_one_vf = amdgpu_sriov_is_pp_one_vf((struct amdgpu_device *)hwmgr->adev);
+ hwmgr->pm_en = (amdgpu_dpm && (hwmgr->not_vf || hwmgr->pp_one_vf))
+ ? true : false;
+ if (!hwmgr->pm_en)
+ return 0;
+
+ if (!hwmgr->pptable_func ||
+ !hwmgr->pptable_func->pptable_init ||
+ !hwmgr->hwmgr_func->backend_init) {
+ hwmgr->pm_en = false;
+ pr_info("dpm not supported \n");
+ return 0;
+ }
+
+ ret = hwmgr->pptable_func->pptable_init(hwmgr);
+ if (ret)
+ goto err;
+
+ ((struct amdgpu_device *)hwmgr->adev)->pm.no_fan =
+ hwmgr->thermal_controller.fanInfo.bNoFan;
+
+ ret = hwmgr->hwmgr_func->backend_init(hwmgr);
+ if (ret)
+ goto err1;
+ /* make sure dc limits are valid */
+ if ((hwmgr->dyn_state.max_clock_voltage_on_dc.sclk == 0) ||
+ (hwmgr->dyn_state.max_clock_voltage_on_dc.mclk == 0))
+ hwmgr->dyn_state.max_clock_voltage_on_dc =
+ hwmgr->dyn_state.max_clock_voltage_on_ac;
+
+ ret = psm_init_power_state_table(hwmgr);
+ if (ret)
+ goto err2;
+
+ ret = phm_setup_asic(hwmgr);
+ if (ret)
+ goto err2;
+
+ ret = phm_enable_dynamic_state_management(hwmgr);
+ if (ret)
+ goto err2;
+ ret = phm_start_thermal_controller(hwmgr);
+ ret |= psm_set_performance_states(hwmgr);
+ if (ret)
+ goto err2;
+
+ ((struct amdgpu_device *)hwmgr->adev)->pm.dpm_enabled = true;
+
+ return 0;
+err2:
+ if (hwmgr->hwmgr_func->backend_fini)
+ hwmgr->hwmgr_func->backend_fini(hwmgr);
+err1:
+ if (hwmgr->pptable_func->pptable_fini)
+ hwmgr->pptable_func->pptable_fini(hwmgr);
+err:
+ return ret;
+}
+
+int hwmgr_hw_fini(struct pp_hwmgr *hwmgr)
+{
+ if (!hwmgr || !hwmgr->pm_en || !hwmgr->not_vf)
+ return 0;
+
+ phm_stop_thermal_controller(hwmgr);
+ psm_set_boot_states(hwmgr);
+ psm_adjust_power_state_dynamic(hwmgr, true, NULL);
+ phm_disable_dynamic_state_management(hwmgr);
+ phm_disable_clock_power_gatings(hwmgr);
+
+ if (hwmgr->hwmgr_func->backend_fini)
+ hwmgr->hwmgr_func->backend_fini(hwmgr);
+ if (hwmgr->pptable_func->pptable_fini)
+ hwmgr->pptable_func->pptable_fini(hwmgr);
+ return psm_fini_power_state_table(hwmgr);
+}
+
+int hwmgr_suspend(struct pp_hwmgr *hwmgr)
+{
+ int ret = 0;
+
+ if (!hwmgr || !hwmgr->pm_en || !hwmgr->not_vf)
+ return 0;
+
+ phm_disable_smc_firmware_ctf(hwmgr);
+ ret = psm_set_boot_states(hwmgr);
+ if (ret)
+ return ret;
+ ret = psm_adjust_power_state_dynamic(hwmgr, true, NULL);
+ if (ret)
+ return ret;
+ ret = phm_power_down_asic(hwmgr);
+
+ return ret;
+}
+
+int hwmgr_resume(struct pp_hwmgr *hwmgr)
+{
+ int ret = 0;
+
+ if (!hwmgr)
+ return -EINVAL;
+
+ if (!hwmgr->not_vf || !hwmgr->pm_en)
+ return 0;
+
+ ret = phm_setup_asic(hwmgr);
+ if (ret)
+ return ret;
+
+ ret = phm_enable_dynamic_state_management(hwmgr);
+ if (ret)
+ return ret;
+ ret = phm_start_thermal_controller(hwmgr);
+ ret |= psm_set_performance_states(hwmgr);
+ if (ret)
+ return ret;
+
+ ret = psm_adjust_power_state_dynamic(hwmgr, false, NULL);
+
+ return ret;
+}
+
+static enum PP_StateUILabel power_state_convert(enum amd_pm_state_type state)
+{
+ switch (state) {
+ case POWER_STATE_TYPE_BATTERY:
+ return PP_StateUILabel_Battery;
+ case POWER_STATE_TYPE_BALANCED:
+ return PP_StateUILabel_Balanced;
+ case POWER_STATE_TYPE_PERFORMANCE:
+ return PP_StateUILabel_Performance;
+ default:
+ return PP_StateUILabel_None;
+ }
+}
+
+int hwmgr_handle_task(struct pp_hwmgr *hwmgr, enum amd_pp_task task_id,
+ enum amd_pm_state_type *user_state)
+{
+ int ret = 0;
+
+ if (hwmgr == NULL)
+ return -EINVAL;
+
+ switch (task_id) {
+ case AMD_PP_TASK_DISPLAY_CONFIG_CHANGE:
+ if (!hwmgr->not_vf)
+ return ret;
+ ret = phm_pre_display_configuration_changed(hwmgr);
+ if (ret)
+ return ret;
+ ret = phm_set_cpu_power_state(hwmgr);
+ if (ret)
+ return ret;
+ ret = psm_set_performance_states(hwmgr);
+ if (ret)
+ return ret;
+ ret = psm_adjust_power_state_dynamic(hwmgr, false, NULL);
+ break;
+ case AMD_PP_TASK_ENABLE_USER_STATE:
+ {
+ enum PP_StateUILabel requested_ui_label;
+ struct pp_power_state *requested_ps = NULL;
+
+ if (!hwmgr->not_vf)
+ return ret;
+ if (user_state == NULL) {
+ ret = -EINVAL;
+ break;
+ }
+
+ requested_ui_label = power_state_convert(*user_state);
+ ret = psm_set_user_performance_state(hwmgr, requested_ui_label, &requested_ps);
+ if (ret)
+ return ret;
+ ret = psm_adjust_power_state_dynamic(hwmgr, true, requested_ps);
+ break;
+ }
+ case AMD_PP_TASK_COMPLETE_INIT:
+ case AMD_PP_TASK_READJUST_POWER_STATE:
+ ret = psm_adjust_power_state_dynamic(hwmgr, true, NULL);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+void hwmgr_init_default_caps(struct pp_hwmgr *hwmgr)
+{
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_PCIEPerformanceRequest);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_UVDDPM);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_VCEDPM);
+
+#if defined(CONFIG_ACPI)
+ if (amdgpu_acpi_is_pcie_performance_request_supported(hwmgr->adev))
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_PCIEPerformanceRequest);
+#endif
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DynamicPatchPowerState);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EnableSMU7ThermalManagement);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DynamicPowerManagement);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SMC);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DynamicUVDState);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_FanSpeedInTableIsRPM);
+ return;
+}
+
+int hwmgr_set_user_specify_caps(struct pp_hwmgr *hwmgr)
+{
+ if (hwmgr->feature_mask & PP_SCLK_DEEP_SLEEP_MASK)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkDeepSleep);
+ else
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkDeepSleep);
+
+ if (hwmgr->feature_mask & PP_POWER_CONTAINMENT_MASK) {
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PowerContainment);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_CAC);
+ } else {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PowerContainment);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_CAC);
+ }
+
+ if (hwmgr->feature_mask & PP_OVERDRIVE_MASK)
+ hwmgr->od_enabled = true;
+
+ return 0;
+}
+
+int polaris_set_asic_special_caps(struct pp_hwmgr *hwmgr)
+{
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EVV);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SQRamping);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_RegulatorHot);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition);
+
+ if (hwmgr->chip_id != CHIP_POLARIS10)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SPLLShutdownSupport);
+
+ if (hwmgr->chip_id != CHIP_POLARIS11) {
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DBRamping);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TDRamping);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TCPRamping);
+ }
+ return 0;
+}
+
+int fiji_set_asic_special_caps(struct pp_hwmgr *hwmgr)
+{
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EVV);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SQRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DBRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TDRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TCPRamping);
+ return 0;
+}
+
+int tonga_set_asic_special_caps(struct pp_hwmgr *hwmgr)
+{
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EVV);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SQRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DBRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TDRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TCPRamping);
+
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UVDPowerGating);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_VCEPowerGating);
+ return 0;
+}
+
+int topaz_set_asic_special_caps(struct pp_hwmgr *hwmgr)
+{
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EVV);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SQRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DBRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TDRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TCPRamping);
+ return 0;
+}
+
+int ci_set_asic_special_caps(struct pp_hwmgr *hwmgr)
+{
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SQRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DBRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TDRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TCPRamping);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MemorySpreadSpectrumSupport);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EngineSpreadSpectrumSupport);
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr_ppt.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr_ppt.h
new file mode 100644
index 000000000000..c0193e09d58a
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr_ppt.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 PP_HWMGR_PPT_H
+#define PP_HWMGR_PPT_H
+
+#include "hardwaremanager.h"
+#include "smumgr.h"
+#include "atom-types.h"
+
+struct phm_ppt_v1_clock_voltage_dependency_record {
+ uint32_t clk;
+ uint8_t vddInd;
+ uint8_t vddciInd;
+ uint8_t mvddInd;
+ uint16_t vdd_offset;
+ uint16_t vddc;
+ uint16_t vddgfx;
+ uint16_t vddci;
+ uint16_t mvdd;
+ uint8_t phases;
+ uint8_t cks_enable;
+ uint8_t cks_voffset;
+ uint32_t sclk_offset;
+};
+
+typedef struct phm_ppt_v1_clock_voltage_dependency_record phm_ppt_v1_clock_voltage_dependency_record;
+
+struct phm_ppt_v1_clock_voltage_dependency_table {
+ uint32_t count; /* Number of entries. */
+ phm_ppt_v1_clock_voltage_dependency_record entries[1]; /* Dynamically allocate count entries. */
+};
+
+typedef struct phm_ppt_v1_clock_voltage_dependency_table phm_ppt_v1_clock_voltage_dependency_table;
+
+
+/* Multimedia Clock Voltage Dependency records and table */
+struct phm_ppt_v1_mm_clock_voltage_dependency_record {
+ uint32_t dclk; /* UVD D-clock */
+ uint32_t vclk; /* UVD V-clock */
+ uint32_t eclk; /* VCE clock */
+ uint32_t aclk; /* ACP clock */
+ uint32_t samclock; /* SAMU clock */
+ uint8_t vddcInd;
+ uint16_t vddgfx_offset;
+ uint16_t vddc;
+ uint16_t vddgfx;
+ uint8_t phases;
+};
+typedef struct phm_ppt_v1_mm_clock_voltage_dependency_record phm_ppt_v1_mm_clock_voltage_dependency_record;
+
+struct phm_ppt_v1_mm_clock_voltage_dependency_table {
+ uint32_t count; /* Number of entries. */
+ phm_ppt_v1_mm_clock_voltage_dependency_record entries[1]; /* Dynamically allocate count entries. */
+};
+typedef struct phm_ppt_v1_mm_clock_voltage_dependency_table phm_ppt_v1_mm_clock_voltage_dependency_table;
+
+struct phm_ppt_v1_voltage_lookup_record {
+ uint16_t us_calculated;
+ uint16_t us_vdd; /* Base voltage */
+ uint16_t us_cac_low;
+ uint16_t us_cac_mid;
+ uint16_t us_cac_high;
+};
+typedef struct phm_ppt_v1_voltage_lookup_record phm_ppt_v1_voltage_lookup_record;
+
+struct phm_ppt_v1_voltage_lookup_table {
+ uint32_t count;
+ phm_ppt_v1_voltage_lookup_record entries[1]; /* Dynamically allocate count entries. */
+};
+typedef struct phm_ppt_v1_voltage_lookup_table phm_ppt_v1_voltage_lookup_table;
+
+/* PCIE records and Table */
+
+struct phm_ppt_v1_pcie_record {
+ uint8_t gen_speed;
+ uint8_t lane_width;
+ uint16_t usreserved;
+ uint16_t reserved;
+ uint32_t pcie_sclk;
+};
+typedef struct phm_ppt_v1_pcie_record phm_ppt_v1_pcie_record;
+
+struct phm_ppt_v1_pcie_table {
+ uint32_t count; /* Number of entries. */
+ phm_ppt_v1_pcie_record entries[1]; /* Dynamically allocate count entries. */
+};
+typedef struct phm_ppt_v1_pcie_table phm_ppt_v1_pcie_table;
+
+struct phm_ppt_v1_gpio_table {
+ uint8_t vrhot_triggered_sclk_dpm_index; /* SCLK DPM level index to switch to when VRHot is triggered */
+};
+typedef struct phm_ppt_v1_gpio_table phm_ppt_v1_gpio_table;
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/polaris_baco.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/polaris_baco.c
new file mode 100644
index 000000000000..8f8e296f2fe9
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/polaris_baco.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "amdgpu.h"
+#include "polaris_baco.h"
+
+#include "gmc/gmc_8_1_d.h"
+#include "gmc/gmc_8_1_sh_mask.h"
+
+#include "bif/bif_5_0_d.h"
+#include "bif/bif_5_0_sh_mask.h"
+
+#include "dce/dce_11_0_d.h"
+#include "dce/dce_11_0_sh_mask.h"
+
+#include "smu/smu_7_1_3_d.h"
+#include "smu/smu_7_1_3_sh_mask.h"
+
+static const struct baco_cmd_entry gpio_tbl[] =
+{
+ { CMD_WRITE, mmGPIOPAD_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmGPIOPAD_PD_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmGPIOPAD_PU_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmGPIOPAD_MASK, 0, 0, 0, 0xff77ffff },
+ { CMD_WRITE, mmDC_GPIO_DVODATA_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmDC_GPIO_DVODATA_MASK, 0, 0, 0, 0xffffffff },
+ { CMD_WRITE, mmDC_GPIO_GENERIC_EN, 0, 0, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmDC_GPIO_GENERIC_MASK, 0, 0, 0, 0x03333333 },
+ { CMD_WRITE, mmDC_GPIO_SYNCA_EN, 0, 0, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmDC_GPIO_SYNCA_MASK, 0, 0, 0, 0x00001111 }
+};
+
+static const struct baco_cmd_entry enable_fb_req_rej_tbl[] =
+{
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, 0xC0300024 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x1, 0x0, 0, 0x1 },
+ { CMD_WRITE, mmBIF_FB_EN, 0, 0, 0, 0x0 }
+};
+
+static const struct baco_cmd_entry use_bclk_tbl[] =
+{
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_FUNC_CNTL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL__SPLL_BYPASS_EN_MASK, CG_SPLL_FUNC_CNTL__SPLL_BYPASS_EN__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, 0xC0500170 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x4000000, 0x1a, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixGCK_DFS_BYPASS_CNTL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, GCK_DFS_BYPASS_CNTL__BYPASSACLK_MASK, GCK_DFS_BYPASS_CNTL__BYPASSACLK__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixMPLL_BYPASSCLK_SEL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, MPLL_BYPASSCLK_SEL__MPLL_CLKOUT_SEL_MASK, MPLL_BYPASSCLK_SEL__MPLL_CLKOUT_SEL__SHIFT, 0, 0x2 },
+ { CMD_READMODIFYWRITE, mmMPLL_CNTL_MODE, MPLL_CNTL_MODE__MPLL_SW_DIR_CONTROL_MASK, MPLL_CNTL_MODE__MPLL_SW_DIR_CONTROL__SHIFT, 0, 0x1 },
+ { CMD_READMODIFYWRITE, mmMPLL_CNTL_MODE, MPLL_CNTL_MODE__MPLL_MCLK_SEL_MASK, MPLL_CNTL_MODE__MPLL_MCLK_SEL__SHIFT, 0, 0x0 }
+};
+
+static const struct baco_cmd_entry turn_off_plls_tbl[] =
+{
+ { CMD_READMODIFYWRITE, mmDC_GPIO_PAD_STRENGTH_1, DC_GPIO_PAD_STRENGTH_1__GENLK_STRENGTH_SP_MASK, DC_GPIO_PAD_STRENGTH_1__GENLK_STRENGTH_SP__SHIFT, 0, 0x1 },
+ { CMD_DELAY_US, 0, 0, 0, 1, 0x0 },
+ { CMD_READMODIFYWRITE, mmMC_SEQ_DRAM, MC_SEQ_DRAM__RST_CTL_MASK, MC_SEQ_DRAM__RST_CTL__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, 0xC05002B0 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x10, 0x4, 0, 0x1 },
+ { CMD_WAITFOR, mmGCK_SMC_IND_DATA, 0x10, 0, 1, 0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, 0xC050032C },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x10, 0x4, 0, 0x1 },
+ { CMD_WAITFOR, mmGCK_SMC_IND_DATA, 0x10, 0, 1, 0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, 0xC0500080 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x1, 0x0, 0, 0x1 },
+ { CMD_READMODIFYWRITE, 0xda2, 0x40, 0x6, 0, 0x0 },
+ { CMD_DELAY_US, 0, 0, 0, 3, 0x0 },
+ { CMD_READMODIFYWRITE, 0xda2, 0x8, 0x3, 0, 0x0 },
+ { CMD_READMODIFYWRITE, 0xda2, 0x3fff00, 0x8, 0, 0x32 },
+ { CMD_DELAY_US, 0, 0, 0, 3, 0x0 },
+ { CMD_READMODIFYWRITE, mmMPLL_FUNC_CNTL_2, MPLL_FUNC_CNTL_2__ISO_DIS_P_MASK, MPLL_FUNC_CNTL_2__ISO_DIS_P__SHIFT, 0, 0x0 },
+ { CMD_DELAY_US, 0, 0, 0, 5, 0x0 }
+};
+
+static const struct baco_cmd_entry clk_req_b_tbl[] =
+{
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixTHM_CLK_CNTL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, THM_CLK_CNTL__CMON_CLK_SEL_MASK, THM_CLK_CNTL__CMON_CLK_SEL__SHIFT, 0, 0x1 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, THM_CLK_CNTL__TMON_CLK_SEL_MASK, THM_CLK_CNTL__TMON_CLK_SEL__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixMISC_CLK_CTRL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, MISC_CLK_CTRL__DEEP_SLEEP_CLK_SEL_MASK, MISC_CLK_CTRL__DEEP_SLEEP_CLK_SEL__SHIFT, 0, 0x1 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, MISC_CLK_CTRL__ZCLK_SEL_MASK, MISC_CLK_CTRL__ZCLK_SEL__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_CLKPIN_CNTL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_CLKPIN_CNTL__BCLK_AS_XCLK_MASK, CG_CLKPIN_CNTL__BCLK_AS_XCLK__SHIFT, 0, 0x0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_CLKPIN_CNTL_2 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_CLKPIN_CNTL_2__FORCE_BIF_REFCLK_EN_MASK, CG_CLKPIN_CNTL_2__FORCE_BIF_REFCLK_EN__SHIFT, 0, 0x0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixMPLL_BYPASSCLK_SEL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, MPLL_BYPASSCLK_SEL__MPLL_CLKOUT_SEL_MASK, MPLL_BYPASSCLK_SEL__MPLL_CLKOUT_SEL__SHIFT, 0, 0x4 }
+};
+
+static const struct baco_cmd_entry enter_baco_tbl[] =
+{
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_EN_MASK, BACO_CNTL__BACO_EN__SHIFT, 0, 0x01 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_BIF_SCLK_SWITCH_MASK, BACO_CNTL__BACO_BIF_SCLK_SWITCH__SHIFT, 0, 0x01 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_BIF_SCLK_SWITCH_MASK, 0, 5, 0x40000 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_BCLK_OFF_MASK, BACO_CNTL__BACO_BCLK_OFF__SHIFT, 0, 0x01 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_BCLK_OFF_MASK, 0, 5, 0x02 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_ISO_DIS_MASK, BACO_CNTL__BACO_ISO_DIS__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_ISO_DIS_MASK, 0, 5, 0x00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_ANA_ISO_DIS_MASK, BACO_CNTL__BACO_ANA_ISO_DIS__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_ANA_ISO_DIS_MASK, 0, 5, 0x00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_POWER_OFF_MASK, BACO_CNTL__BACO_POWER_OFF__SHIFT, 0, 0x01 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_POWER_OFF_MASK, 0, 5, 0x08 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_MODE_MASK, 0, 0xffffffff, 0x40 }
+};
+
+#define BACO_CNTL__PWRGOOD_MASK BACO_CNTL__PWRGOOD_GPIO_MASK+BACO_CNTL__PWRGOOD_MEM_MASK+BACO_CNTL__PWRGOOD_DVO_MASK
+
+static const struct baco_cmd_entry exit_baco_tbl[] =
+{
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_RESET_EN_MASK, BACO_CNTL__BACO_RESET_EN__SHIFT, 0, 0x01 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_BCLK_OFF_MASK, BACO_CNTL__BACO_BCLK_OFF__SHIFT, 0, 0x00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_POWER_OFF_MASK, BACO_CNTL__BACO_POWER_OFF__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__PWRGOOD_BF_MASK, 0, 0xffffffff, 0x200 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_ISO_DIS_MASK, BACO_CNTL__BACO_ISO_DIS__SHIFT, 0, 0x01 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__PWRGOOD_MASK, 0, 5, 0x1c00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_ANA_ISO_DIS_MASK, BACO_CNTL__BACO_ANA_ISO_DIS__SHIFT, 0, 0x01 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_BIF_SCLK_SWITCH_MASK, BACO_CNTL__BACO_BIF_SCLK_SWITCH__SHIFT, 0, 0x00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_RESET_EN_MASK, BACO_CNTL__BACO_RESET_EN__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__RCU_BIF_CONFIG_DONE_MASK, 0, 5, 0x100 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_EN_MASK, BACO_CNTL__BACO_EN__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_MODE_MASK, 0, 0xffffffff, 0x00 }
+};
+
+static const struct baco_cmd_entry clean_baco_tbl[] =
+{
+ { CMD_WRITE, mmBIOS_SCRATCH_6, 0, 0, 0, 0 },
+ { CMD_WRITE, mmBIOS_SCRATCH_7, 0, 0, 0, 0 }
+};
+
+static const struct baco_cmd_entry use_bclk_tbl_vg[] =
+{
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_FUNC_CNTL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL__SPLL_BYPASS_EN_MASK, CG_SPLL_FUNC_CNTL__SPLL_BYPASS_EN__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, 0xC0500170 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x4000000, 0x1a, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixGCK_DFS_BYPASS_CNTL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, GCK_DFS_BYPASS_CNTL__BYPASSACLK_MASK, GCK_DFS_BYPASS_CNTL__BYPASSACLK__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixMPLL_BYPASSCLK_SEL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, MPLL_BYPASSCLK_SEL__MPLL_CLKOUT_SEL_MASK, MPLL_BYPASSCLK_SEL__MPLL_CLKOUT_SEL__SHIFT, 0, 0x2 }
+};
+
+static const struct baco_cmd_entry turn_off_plls_tbl_vg[] =
+{
+ { CMD_READMODIFYWRITE, mmDC_GPIO_PAD_STRENGTH_1, DC_GPIO_PAD_STRENGTH_1__GENLK_STRENGTH_SP_MASK, DC_GPIO_PAD_STRENGTH_1__GENLK_STRENGTH_SP__SHIFT, 0, 0x1 },
+ { CMD_DELAY_US, 0, 0, 0, 1, 0x0 },
+ { CMD_READMODIFYWRITE, mmMC_SEQ_DRAM, MC_SEQ_DRAM__RST_CTL_MASK, MC_SEQ_DRAM__RST_CTL__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, 0xC05002B0 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x10, 0x4, 0, 0x1 },
+ { CMD_WAITFOR, mmGCK_SMC_IND_DATA, 0x10, 0, 1, 0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, 0xC050032C },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x10, 0x4, 0, 0x1 },
+ { CMD_WAITFOR, mmGCK_SMC_IND_DATA, 0x10, 0, 1, 0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, 0xC0500080 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x1, 0x0, 0, 0x1 },
+ { CMD_DELAY_US, 0, 0, 0, 3, 0x0 },
+ { CMD_DELAY_US, 0, 0, 0, 3, 0x0 },
+ { CMD_DELAY_US, 0, 0, 0, 5, 0x0 }
+};
+
+int polaris_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state)
+{
+ enum BACO_STATE cur_state;
+
+ smu7_baco_get_state(hwmgr, &cur_state);
+
+ if (cur_state == state)
+ /* aisc already in the target state */
+ return 0;
+
+ if (state == BACO_STATE_IN) {
+ baco_program_registers(hwmgr, gpio_tbl, ARRAY_SIZE(gpio_tbl));
+ baco_program_registers(hwmgr, enable_fb_req_rej_tbl,
+ ARRAY_SIZE(enable_fb_req_rej_tbl));
+ if (hwmgr->chip_id == CHIP_VEGAM) {
+ baco_program_registers(hwmgr, use_bclk_tbl_vg, ARRAY_SIZE(use_bclk_tbl_vg));
+ baco_program_registers(hwmgr, turn_off_plls_tbl_vg,
+ ARRAY_SIZE(turn_off_plls_tbl_vg));
+ } else {
+ baco_program_registers(hwmgr, use_bclk_tbl, ARRAY_SIZE(use_bclk_tbl));
+ baco_program_registers(hwmgr, turn_off_plls_tbl,
+ ARRAY_SIZE(turn_off_plls_tbl));
+ }
+ baco_program_registers(hwmgr, clk_req_b_tbl, ARRAY_SIZE(clk_req_b_tbl));
+ if (baco_program_registers(hwmgr, enter_baco_tbl,
+ ARRAY_SIZE(enter_baco_tbl)))
+ return 0;
+
+ } else if (state == BACO_STATE_OUT) {
+ /* HW requires at least 20ms between regulator off and on */
+ msleep(20);
+ /* Execute Hardware BACO exit sequence */
+ if (baco_program_registers(hwmgr, exit_baco_tbl,
+ ARRAY_SIZE(exit_baco_tbl))) {
+ if (baco_program_registers(hwmgr, clean_baco_tbl,
+ ARRAY_SIZE(clean_baco_tbl)))
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/polaris_baco.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/polaris_baco.h
new file mode 100644
index 000000000000..87a5fa0a157a
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/polaris_baco.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 __POLARIS_BACO_H__
+#define __POLARIS_BACO_H__
+#include "smu7_baco.h"
+
+extern int polaris_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pp_overdriver.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pp_overdriver.c
new file mode 100644
index 000000000000..8de384bf9a8f
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pp_overdriver.c
@@ -0,0 +1,1288 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "pp_overdriver.h"
+#include <linux/errno.h>
+
+static const struct phm_fuses_default vega10_fuses_default[] = {
+ { 0x0213EA94DE0E4964, 0x00003C96, 0xFFFFE226, 0x00000656, 0x00002203, 0xFFFFF201, 0x000003FF, 0x00002203, 0xFFFFF201, 0x000003FF },
+ { 0x0213EA94DE0A1884, 0x00003CC5, 0xFFFFE23A, 0x0000064E, 0x00002258, 0xFFFFF1F7, 0x000003FC, 0x00002258, 0xFFFFF1F7, 0x000003FC },
+ { 0x0213EA94DE0E31A4, 0x00003CAF, 0xFFFFE36E, 0x00000602, 0x00001E98, 0xFFFFF569, 0x00000357, 0x00001E98, 0xFFFFF569, 0x00000357 },
+ { 0x0213EA94DE2C1144, 0x0000391A, 0xFFFFE548, 0x000005C9, 0x00001B98, 0xFFFFF707, 0x00000324, 0x00001B98, 0xFFFFF707, 0x00000324 },
+ { 0x0213EA94DE2C18C4, 0x00003821, 0xFFFFE674, 0x00000597, 0x00002196, 0xFFFFF361, 0x000003C0, 0x00002196, 0xFFFFF361, 0x000003C0 },
+ { 0x0213EA94DE263884, 0x000044A2, 0xFFFFDCB7, 0x00000738, 0x0000325C, 0xFFFFE6A7, 0x000005E6, 0x0000325C, 0xFFFFE6A7, 0x000005E6 },
+ { 0x0213EA94DE082924, 0x00004057, 0xFFFFE1CF, 0x0000063C, 0x00002E2E, 0xFFFFEB62, 0x000004FD, 0x00002E2E, 0xFFFFEB62, 0x000004FD },
+ { 0x0213EA94DE284924, 0x00003FD0, 0xFFFFDF0F, 0x000006E5, 0x0000267C, 0xFFFFEE2D, 0x000004AB, 0x0000267C, 0xFFFFEE2D, 0x000004AB },
+ { 0x0213EA94DE280904, 0x00003F13, 0xFFFFE010, 0x000006AD, 0x000020E7, 0xFFFFF266, 0x000003EC, 0x000020E7, 0xFFFFF266, 0x000003EC },
+ { 0x0213EA94DE082044, 0x00004088, 0xFFFFDFAB, 0x000006B6, 0x0000252B, 0xFFFFEFDB, 0x00000458, 0x0000252B, 0xFFFFEFDB, 0x00000458 },
+ { 0x0213EA94DE283884, 0x00003EF6, 0xFFFFE017, 0x000006AA, 0x00001F67, 0xFFFFF369, 0x000003BE, 0x00001F67, 0xFFFFF369, 0x000003BE },
+ { 0x0213EA94DE2C2184, 0x00003CDD, 0xFFFFE2A7, 0x0000063C, 0x000026C6, 0xFFFFEF38, 0x00000478, 0x000026C6, 0xFFFFEF38, 0x00000478 },
+ { 0x0213EA94DE105124, 0x00003FA8, 0xFFFFDF02, 0x000006F0, 0x000027FE, 0xFFFFECF6, 0x000004EA, 0x000027FE, 0xFFFFECF6, 0x000004EA },
+ { 0x0213EA94DE2638C4, 0x00004670, 0xFFFFDC40, 0x00000742, 0x00003A7A, 0xFFFFE1A7, 0x000006B6, 0x00003A7A, 0xFFFFE1A7, 0x000006B6 },
+ { 0x0213EA94DE2C3024, 0x00003CDC, 0xFFFFE18C, 0x00000683, 0x00002A69, 0xFFFFEBE7, 0x00000515, 0x00002A69, 0xFFFFEBE7, 0x00000515 },
+ { 0x0213EA94DE0E38C4, 0x00003CEC, 0xFFFFE38E, 0x00000601, 0x00002752, 0xFFFFEFA7, 0x00000453, 0x00002752, 0xFFFFEFA7, 0x00000453 },
+ { 0x0213EA94DE2C1124, 0x000037D0, 0xFFFFE634, 0x000005A7, 0x00001CD2, 0xFFFFF644, 0x00000348, 0x00001CD2, 0xFFFFF644, 0x00000348 },
+ { 0x0213EA94DE283964, 0x00003DF5, 0xFFFFE0A5, 0x00000698, 0x00001FD5, 0xFFFFF30E, 0x000003D1, 0x00001FD5, 0xFFFFF30E, 0x000003D1 },
+ { 0x0213EA94DE0828C4, 0x00004201, 0xFFFFE03E, 0x00000688, 0x00003206, 0xFFFFE852, 0x0000058A, 0x00003206, 0xFFFFE852, 0x0000058A },
+ { 0x0213EA94DE2C1864, 0x00003BED, 0xFFFFE2F5, 0x00000638, 0x0000270D, 0xFFFFEED0, 0x0000048E, 0x0000270D, 0xFFFFEED0, 0x0000048E },
+ { 0x0213EA94DE0A1904, 0x00003E82, 0xFFFFE1BE, 0x00000654, 0x000025FB, 0xFFFFEFFA, 0x00000448, 0x000025FB, 0xFFFFEFFA, 0x00000448 },
+ { 0x0213EA94DE2C40C4, 0x00003962, 0xFFFFE4B9, 0x000005EF, 0x00002385, 0xFFFFF156, 0x00000423, 0x00002385, 0xFFFFF156, 0x00000423 },
+ { 0x0213EA94DE2C0944, 0x00003D88, 0xFFFFE21A, 0x00000655, 0x0000295A, 0xFFFFED68, 0x000004C4, 0x0000295A, 0xFFFFED68, 0x000004C4 },
+ { 0x0213EA94DE2C1104, 0x00003AA4, 0xFFFFE4A3, 0x000005E0, 0x000022EF, 0xFFFFF250, 0x000003EB, 0x000022EF, 0xFFFFF250, 0x000003EB },
+ { 0x0213EA94DE0E29A4, 0x00003D97, 0xFFFFE30D, 0x0000060D, 0x0000205D, 0xFFFFF45D, 0x00000380, 0x0000205D, 0xFFFFF45D, 0x00000380 },
+ { 0x0213EA94DE2C40A4, 0x000039B6, 0xFFFFE446, 0x00000605, 0x00002325, 0xFFFFF16C, 0x0000041F, 0x00002325, 0xFFFFF16C, 0x0000041F },
+ { 0x0213EA94DE263904, 0x0000457E, 0xFFFFDCF6, 0x00000722, 0x00003972, 0xFFFFE27B, 0x0000068E, 0x00003972, 0xFFFFE27B, 0x0000068E },
+ { 0x0213EA94DE0A1924, 0x00003FB8, 0xFFFFE101, 0x00000670, 0x00002787, 0xFFFFEEF5, 0x00000471, 0x00002787, 0xFFFFEEF5, 0x00000471 },
+ { 0x0213EA94DE0E38A4, 0x00003BB2, 0xFFFFE430, 0x000005EA, 0x000024A5, 0xFFFFF162, 0x00000409, 0x000024A5, 0xFFFFF162, 0x00000409 },
+ { 0x0213EA94DE082144, 0x00003EC5, 0xFFFFE1BD, 0x0000064F, 0x000022F0, 0xFFFFF227, 0x000003E8, 0x000022F0, 0xFFFFF227, 0x000003E8 },
+ { 0x0213EA94DE2C3164, 0x000038A7, 0xFFFFE59F, 0x000005C1, 0x000021CC, 0xFFFFF2DF, 0x000003D9, 0x000021CC, 0xFFFFF2DF, 0x000003D9 },
+ { 0x0213EA94DE324184, 0x00002995, 0xFFFFEF7A, 0x0000044C, 0x00001552, 0xFFFFFB5D, 0x00000292, 0x00001552, 0xFFFFFB5D, 0x00000292 },
+ { 0x0213EA94DE2C4064, 0x00003B26, 0xFFFFE2D3, 0x00000649, 0x000023B4, 0xFFFFF09B, 0x00000449, 0x000023B4, 0xFFFFF09B, 0x00000449 },
+ { 0x0213EA94DE081124, 0x000040D2, 0xFFFFE00A, 0x00000696, 0x000022DA, 0xFFFFF1E9, 0x000003F2, 0x000022DA, 0xFFFFF1E9, 0x000003F2 },
+ { 0x0213EA94DE2C3924, 0x00003C98, 0xFFFFE365, 0x00000618, 0x00002D5D, 0xFFFFEB3A, 0x0000051D, 0x00002D5D, 0xFFFFEB3A, 0x0000051D },
+ { 0x0213EA94DE2C10A4, 0x00003BBD, 0xFFFFE37E, 0x00000617, 0x0000252E, 0xFFFFF06E, 0x00000441, 0x0000252E, 0xFFFFF06E, 0x00000441 },
+ { 0x0213EA94DE262924, 0x00004363, 0xFFFFDF7A, 0x000006A0, 0x000031F5, 0xFFFFE880, 0x0000057B, 0x000031F5, 0xFFFFE880, 0x0000057B },
+ { 0x0213EA94DE0E3844, 0x00003CFC, 0xFFFFE2AF, 0x0000062E, 0x0000212A, 0xFFFFF335, 0x000003BF, 0x0000212A, 0xFFFFF335, 0x000003BF },
+ { 0x0213EA94DE1C4924, 0x0000252D, 0xFFFFF31B, 0x000003C3, 0x00001A1A, 0xFFFFF882, 0x00000325, 0x00001A1A, 0xFFFFF882, 0x00000325 },
+ { 0x0213EA94DE0A29A4, 0x00003FE2, 0xFFFFDFEF, 0x000006AC, 0x000025A2, 0xFFFFEF84, 0x00000462, 0x000025A2, 0xFFFFEF84, 0x00000462 },
+ { 0x0213EA94DE0820E4, 0x000040A5, 0xFFFFE13B, 0x0000065B, 0x00002C13, 0xFFFFEC75, 0x000004D7, 0x00002C13, 0xFFFFEC75, 0x000004D7 },
+ { 0x0213EA94DE0E48A4, 0x00003E42, 0xFFFFE1B3, 0x00000657, 0x0000221D, 0xFFFFF273, 0x000003DE, 0x0000221D, 0xFFFFF273, 0x000003DE },
+ { 0x0213EA94DE0A20E4, 0x00003E7F, 0xFFFFE255, 0x00000638, 0x00002D30, 0xFFFFEB8A, 0x00000503, 0x00002D30, 0xFFFFEB8A, 0x00000503 },
+ { 0x0213EA94DE2C29C4, 0x00003E56, 0xFFFFE16D, 0x00000670, 0x000028DC, 0xFFFFEDA0, 0x000004BA, 0x000028DC, 0xFFFFEDA0, 0x000004BA },
+ { 0x0213EA94DE2630A4, 0x000044AD, 0xFFFFDE24, 0x000006DD, 0x000031AD, 0xFFFFE850, 0x00000585, 0x000031AD, 0xFFFFE850, 0x00000585 },
+ { 0x0213EA94DE2C20E4, 0x00003AF3, 0xFFFFE5B0, 0x000005A6, 0x00002CF6, 0xFFFFEC75, 0x000004DD, 0x00002CF6, 0xFFFFEC75, 0x000004DD },
+ { 0x0213EA94DE0A2084, 0x00003E66, 0xFFFFE19E, 0x0000065B, 0x00002332, 0xFFFFF1B9, 0x000003FD, 0x00002332, 0xFFFFF1B9, 0x000003FD },
+ { 0x0213EA94DE082884, 0x00003FB4, 0xFFFFE0A5, 0x00000686, 0x0000253E, 0xFFFFF02E, 0x00000444, 0x0000253E, 0xFFFFF02E, 0x00000444 },
+ { 0x0213EA94DE2818A4, 0x00003E28, 0xFFFFE14D, 0x0000066E, 0x00001FE2, 0xFFFFF39A, 0x000003B1, 0x00001FE2, 0xFFFFF39A, 0x000003B1 },
+ { 0x0213EA94DE2C0904, 0x000039E6, 0xFFFFE44B, 0x000005FE, 0x0000210C, 0xFFFFF2F4, 0x000003DA, 0x0000210C, 0xFFFFF2F4, 0x000003DA },
+ { 0x0213EA94DE2C5104, 0x00003A4D, 0xFFFFE252, 0x0000067A, 0x000027E2, 0xFFFFECED, 0x000004FA, 0x000027E2, 0xFFFFECED, 0x000004FA },
+ { 0x0213EA94DE0A2964, 0x00004065, 0xFFFFE02F, 0x0000069B, 0x0000299D, 0xFFFFED38, 0x000004C2, 0x0000299D, 0xFFFFED38, 0x000004C2 },
+ { 0x0213EA94DE0E20A4, 0x000039EE, 0xFFFFE603, 0x00000594, 0x0000214F, 0xFFFFF429, 0x0000038E, 0x0000214F, 0xFFFFF429, 0x0000038E },
+ { 0x0213EA94DE0E48E4, 0x00003BD2, 0xFFFFE351, 0x00000618, 0x000020B8, 0xFFFFF377, 0x000003B4, 0x000020B8, 0xFFFFF377, 0x000003B4 },
+ { 0x0213EA94DE0A3124, 0x00003FAA, 0xFFFFE183, 0x0000065E, 0x000032AE, 0xFFFFE7C2, 0x000005A6, 0x000032AE, 0xFFFFE7C2, 0x000005A6 },
+ { 0x0213EA94DE2C2984, 0x00003AFB, 0xFFFFE3E4, 0x00000608, 0x00002293, 0xFFFFF21F, 0x000003FA, 0x00002293, 0xFFFFF21F, 0x000003FA },
+ { 0x0213EA94DE262064, 0x0000448B, 0xFFFFDD5D, 0x0000070D, 0x00002E4E, 0xFFFFE9DF, 0x00000551, 0x00002E4E, 0xFFFFE9DF, 0x00000551 },
+ { 0x0213EA94DE0E2184, 0x00003D46, 0xFFFFE39B, 0x000005F3, 0x0000218E, 0xFFFFF3CD, 0x00000398, 0x0000218E, 0xFFFFF3CD, 0x00000398 },
+ { 0x0213EA94DE0848E4, 0x00003F01, 0xFFFFDFD9, 0x000006BF, 0x000023AF, 0xFFFFF04E, 0x0000044C, 0x000023AF, 0xFFFFF04E, 0x0000044C },
+ { 0x0213EA94DE1029A4, 0x0000403D, 0xFFFFDF6B, 0x000006C9, 0x0000270D, 0xFFFFEE4B, 0x0000049E, 0x0000270D, 0xFFFFEE4B, 0x0000049E },
+ { 0x0213EA94DE0E3964, 0x00003C11, 0xFFFFE35C, 0x00000613, 0x000020F9, 0xFFFFF365, 0x000003B9, 0x000020F9, 0xFFFFF365, 0x000003B9 },
+ { 0x0213EA94DE2C3884, 0x00003B58, 0xFFFFE37D, 0x0000061F, 0x00002698, 0xFFFFEF46, 0x00000478, 0x00002698, 0xFFFFEF46, 0x00000478 },
+ { 0x0213EA94DE2841A4, 0x00003EBC, 0xFFFFDF7A, 0x000006D6, 0x0000212B, 0xFFFFF195, 0x0000041B, 0x0000212B, 0xFFFFF195, 0x0000041B },
+ { 0x0213EA94DE0848C4, 0x00004050, 0xFFFFDEB3, 0x000006FE, 0x00002D6C, 0xFFFFE961, 0x00000582, 0x00002D6C, 0xFFFFE961, 0x00000582 },
+ { 0x0213EA94DE262044, 0x000043F0, 0xFFFFDD9C, 0x00000702, 0x00002B31, 0xFFFFEBEA, 0x000004F7, 0x00002B31, 0xFFFFEBEA, 0x000004F7 },
+ { 0x0213EA94DE100924, 0x00003EFA, 0xFFFFE093, 0x00000696, 0x000026DB, 0xFFFFEEB3, 0x00000489, 0x000026DB, 0xFFFFEEB3, 0x00000489 },
+ { 0x0213EA94DE082064, 0x0000425D, 0xFFFFDE8D, 0x000006E6, 0x00002CA4, 0xFFFFEAD2, 0x00000531, 0x00002CA4, 0xFFFFEAD2, 0x00000531 },
+ { 0x0213EA94DE2639A4, 0x000043B0, 0xFFFFDD03, 0x00000728, 0x00002946, 0xFFFFECA6, 0x000004DE, 0x00002946, 0xFFFFECA6, 0x000004DE },
+ { 0x0213EA94DE282864, 0x00003F6A, 0xFFFFE03A, 0x0000069D, 0x00002208, 0xFFFFF1F8, 0x000003F6, 0x00002208, 0xFFFFF1F8, 0x000003F6 },
+ { 0x0213EA94DE2C2964, 0x00003A94, 0xFFFFE4A7, 0x000005E2, 0x000024D0, 0xFFFFF100, 0x00000426, 0x000024D0, 0xFFFFF100, 0x00000426 },
+ { 0x0213EA94DE2810C4, 0x00003F2F, 0xFFFFE0A3, 0x00000688, 0x00002198, 0xFFFFF271, 0x000003E2, 0x00002198, 0xFFFFF271, 0x000003E2 },
+ { 0x0213EA94DE1048E4, 0x00003EA5, 0xFFFFE032, 0x000006AE, 0x0000227C, 0xFFFFF130, 0x00000426, 0x0000227C, 0xFFFFF130, 0x00000426 },
+ { 0x0213EA94DE264144, 0x0000442F, 0xFFFFDBC4, 0x0000078B, 0x00003CD6, 0xFFFFDE6C, 0x0000076C, 0x00003CD6, 0xFFFFDE6C, 0x0000076C },
+ { 0x0213EA94DE282884, 0x00003DDE, 0xFFFFE174, 0x00000668, 0x00001FF4, 0xFFFFF38F, 0x000003B1, 0x00001FF4, 0xFFFFF38F, 0x000003B1 },
+ { 0x0213EA94DE0A3144, 0x000040B0, 0xFFFFE016, 0x000006A0, 0x00002DBB, 0xFFFFEA7F, 0x00000537, 0x00002DBB, 0xFFFFEA7F, 0x00000537 },
+ { 0x0213EA94DE2C3104, 0x00003429, 0xFFFFEA97, 0x000004DD, 0x000024D5, 0xFFFFF26F, 0x000003DF, 0x000024D5, 0xFFFFF26F, 0x000003DF },
+ { 0x0213EA94DE0E1904, 0x00003AEB, 0xFFFFE590, 0x000005A3, 0x000022CB, 0xFFFFF347, 0x000003B2, 0x000022CB, 0xFFFFF347, 0x000003B2 },
+ { 0x0213EA94DE283904, 0x00003B8E, 0xFFFFE2EF, 0x00000636, 0x00002351, 0xFFFFF143, 0x0000041C, 0x00002351, 0xFFFFF143, 0x0000041C },
+ { 0x0213EA94DE3240C4, 0x00002926, 0xFFFFF0B0, 0x00000410, 0x0000194E, 0xFFFFF94E, 0x000002E9, 0x0000194E, 0xFFFFF94E, 0x000002E9 },
+ { 0x0213EA94DE283184, 0x0000402B, 0xFFFFDF78, 0x000006C2, 0x00002273, 0xFFFFF16C, 0x00000414, 0x00002273, 0xFFFFF16C, 0x00000414 },
+ { 0x0213EA94DE0A10A4, 0x00003D6A, 0xFFFFE1D3, 0x00000659, 0x00002006, 0xFFFFF394, 0x000003B1, 0x00002006, 0xFFFFF394, 0x000003B1 },
+ { 0x0213EA94DE284064, 0x00004042, 0xFFFFDFD8, 0x000006A8, 0x00002135, 0xFFFFF29F, 0x000003D9, 0x00002135, 0xFFFFF29F, 0x000003D9 },
+ { 0x0213EA94DE0820A4, 0x0000405B, 0xFFFFE093, 0x00000682, 0x0000288F, 0xFFFFEE3A, 0x00000491, 0x0000288F, 0xFFFFEE3A, 0x00000491 },
+ { 0x0213EA94DE2C48A4, 0x00003A49, 0xFFFFE30C, 0x00000648, 0x000023F9, 0xFFFFF02D, 0x00000460, 0x000023F9, 0xFFFFF02D, 0x00000460 },
+ { 0x0213EA94DE282964, 0x00003D59, 0xFFFFE1CC, 0x0000065B, 0x00002013, 0xFFFFF37D, 0x000003B6, 0x00002013, 0xFFFFF37D, 0x000003B6 },
+ { 0x0213EA94DE2C3984, 0x000040C1, 0xFFFFDF8C, 0x000006CA, 0x00003271, 0xFFFFE6CA, 0x000005EA, 0x00003271, 0xFFFFE6CA, 0x000005EA },
+ { 0x0213EA94DE2620E4, 0x000042E9, 0xFFFFDFDC, 0x0000068C, 0x00002ED9, 0xFFFFEAAF, 0x0000051B, 0x00002ED9, 0xFFFFEAAF, 0x0000051B },
+ { 0x0213EA94DE083084, 0x000042ED, 0xFFFFDE50, 0x000006F0, 0x00002FCF, 0xFFFFE8BB, 0x0000058C, 0x00002FCF, 0xFFFFE8BB, 0x0000058C },
+ { 0x0213EA94DE0A4104, 0x00003EBD, 0xFFFFE099, 0x00000698, 0x00002709, 0xFFFFEE7B, 0x00000495, 0x00002709, 0xFFFFEE7B, 0x00000495 },
+ { 0x0213EA94DE284904, 0x00003F71, 0xFFFFDF82, 0x000006C9, 0x0000219B, 0xFFFFF1AD, 0x0000040F, 0x0000219B, 0xFFFFF1AD, 0x0000040F },
+ { 0x0213EA94DE2808E4, 0x00003E73, 0xFFFFE080, 0x0000069B, 0x000020E7, 0xFFFFF273, 0x000003E9, 0x000020E7, 0xFFFFF273, 0x000003E9 },
+ { 0x0213EA94DE0E3184, 0x00003E14, 0xFFFFE278, 0x0000062C, 0x00002275, 0xFFFFF2B3, 0x000003CE, 0x00002275, 0xFFFFF2B3, 0x000003CE },
+ { 0x0213EA94DE2C21A4, 0x00003ABB, 0xFFFFE3B9, 0x00000615, 0x00002192, 0xFFFFF28F, 0x000003EB, 0x00002192, 0xFFFFF28F, 0x000003EB },
+ { 0x0213EA94DE283124, 0x00003D53, 0xFFFFE255, 0x00000643, 0x0000275B, 0xFFFFEEED, 0x00000479, 0x0000275B, 0xFFFFEEED, 0x00000479 },
+ { 0x0213EA94DE262864, 0x000043E3, 0xFFFFDDC3, 0x000006FB, 0x00002B6B, 0xFFFFEBD6, 0x000004FA, 0x00002B6B, 0xFFFFEBD6, 0x000004FA },
+ { 0x0213EA94DE0E2144, 0x00003BDE, 0xFFFFE507, 0x000005B4, 0x000022CE, 0xFFFFF358, 0x000003AB, 0x000022CE, 0xFFFFF358, 0x000003AB },
+ { 0x0213EA94DE323164, 0x00002460, 0xFFFFF3B5, 0x000003A2, 0x000014E7, 0xFFFFFC32, 0x0000027C, 0x000014E7, 0xFFFFFC32, 0x0000027C },
+ { 0x0213EA94DE2820C4, 0x00003D20, 0xFFFFE298, 0x0000062F, 0x00002080, 0xFFFFF3AF, 0x000003A8, 0x00002080, 0xFFFFF3AF, 0x000003A8 },
+ { 0x0213EA94DE081904, 0x00003E14, 0xFFFFE221, 0x00000641, 0x000021BB, 0xFFFFF2EA, 0x000003CA, 0x000021BB, 0xFFFFF2EA, 0x000003CA },
+ { 0x0213EA94DE0A40C4, 0x00003DE1, 0xFFFFE14E, 0x00000677, 0x00002468, 0xFFFFF068, 0x00000440, 0x00002468, 0xFFFFF068, 0x00000440 },
+ { 0x0213EA94DE261084, 0x00004372, 0xFFFFDDF8, 0x000006F5, 0x00002B3F, 0xFFFFEBE8, 0x000004F8, 0x00002B3F, 0xFFFFEBE8, 0x000004F8 },
+ { 0x0213EA94DE0A28C4, 0x00003E4F, 0xFFFFE2A3, 0x0000062B, 0x00002F5A, 0xFFFFEA37, 0x0000053B, 0x00002F5A, 0xFFFFEA37, 0x0000053B },
+ { 0x0213EA94DE2850E4, 0x00003E07, 0xFFFFE02F, 0x000006B6, 0x0000216B, 0xFFFFF1A3, 0x00000416, 0x0000216B, 0xFFFFF1A3, 0x00000416 },
+ { 0x0213EA94DE2838A4, 0x00003DAB, 0xFFFFE128, 0x0000067F, 0x0000216F, 0xFFFFF236, 0x000003F3, 0x0000216F, 0xFFFFF236, 0x000003F3 },
+ { 0x0213EA94DE2C2924, 0x0000364B, 0xFFFFE8CB, 0x0000052A, 0x00002568, 0xFFFFF1B2, 0x00000400, 0x00002568, 0xFFFFF1B2, 0x00000400 },
+ { 0x0213EA94DE261064, 0x00004219, 0xFFFFDE87, 0x000006E8, 0x00002C59, 0xFFFFEAEE, 0x00000529, 0x00002C59, 0xFFFFEAEE, 0x00000529 },
+ { 0x0213EA94DE0E1944, 0x000039A8, 0xFFFFE602, 0x00000594, 0x00001D06, 0xFFFFF6F0, 0x00000316, 0x00001D06, 0xFFFFF6F0, 0x00000316 },
+ { 0x0213EA94DE2610E4, 0x00004052, 0xFFFFE01C, 0x00000698, 0x00002310, 0xFFFFF1A1, 0x000003FE, 0x00002310, 0xFFFFF1A1, 0x000003FE },
+ { 0x0213EA94DE0E2824, 0x00003C1C, 0xFFFFE3EB, 0x000005F1, 0x00002289, 0xFFFFF2CF, 0x000003C9, 0x00002289, 0xFFFFF2CF, 0x000003C9 },
+ { 0x0213EA94DE0E5124, 0x00003F19, 0xFFFFE085, 0x0000069E, 0x00002B94, 0xFFFFEB72, 0x0000051D, 0x00002B94, 0xFFFFEB72, 0x0000051D },
+ { 0x0213EA94DE0E41A4, 0x00003C51, 0xFFFFE2AD, 0x00000638, 0x0000206B, 0xFFFFF361, 0x000003BE, 0x0000206B, 0xFFFFF361, 0x000003BE },
+ { 0x0213EA94DE2610C4, 0x000040B9, 0xFFFFDFBB, 0x000006AB, 0x0000241F, 0xFFFFF0CC, 0x00000425, 0x0000241F, 0xFFFFF0CC, 0x00000425 },
+ { 0x0213EA94DE0A2064, 0x00003E62, 0xFFFFE12C, 0x00000678, 0x00002445, 0xFFFFF09E, 0x00000435, 0x00002445, 0xFFFFF09E, 0x00000435 },
+ { 0x0213EA94DE0E1984, 0x00003C97, 0xFFFFE399, 0x000005FB, 0x0000209D, 0xFFFFF41D, 0x0000038F, 0x0000209D, 0xFFFFF41D, 0x0000038F },
+ { 0x0213EA94DE0E3144, 0x00003FF9, 0xFFFFE1E9, 0x0000063E, 0x00002E96, 0xFFFFEAF5, 0x00000516, 0x00002E96, 0xFFFFEAF5, 0x00000516 },
+ { 0x0213EA94DE0A3084, 0x00003F04, 0xFFFFE109, 0x0000067A, 0x000026E1, 0xFFFFEF0B, 0x00000476, 0x000026E1, 0xFFFFEF0B, 0x00000476 },
+ { 0x0213EA94DE101124, 0x00003E3E, 0xFFFFE187, 0x00000660, 0x00002049, 0xFFFFF38D, 0x000003B0, 0x00002049, 0xFFFFF38D, 0x000003B0 },
+ { 0x0213EA94DE282944, 0x00003D58, 0xFFFFE253, 0x0000063D, 0x00002158, 0xFFFFF308, 0x000003C3, 0x00002158, 0xFFFFF308, 0x000003C3 },
+ { 0x0213EA94DE0840C4, 0x00004074, 0xFFFFDF8D, 0x000006C0, 0x00002799, 0xFFFFEE19, 0x000004A5, 0x00002799, 0xFFFFEE19, 0x000004A5 },
+ { 0x0213EA94DE281924, 0x00003DAF, 0xFFFFE1C9, 0x00000659, 0x000020E5, 0xFFFFF313, 0x000003C6, 0x000020E5, 0xFFFFF313, 0x000003C6 },
+ { 0x0213EA94DE0A3964, 0x000041DD, 0xFFFFDDFA, 0x0000071B, 0x0000348D, 0xFFFFE4B4, 0x0000064C, 0x0000348D, 0xFFFFE4B4, 0x0000064C },
+ { 0x0213EA94DE2C2884, 0x00003947, 0xFFFFE5AE, 0x000005B8, 0x000024A6, 0xFFFFF140, 0x0000041D, 0x000024A6, 0xFFFFF140, 0x0000041D },
+ { 0x0213EA94DE101844, 0x00003D35, 0xFFFFE197, 0x0000066E, 0x00002248, 0xFFFFF1BC, 0x00000408, 0x00002248, 0xFFFFF1BC, 0x00000408 },
+ { 0x0213EA94DE0A18E4, 0x00003F4F, 0xFFFFE13E, 0x0000066D, 0x00002AF0, 0xFFFFEC99, 0x000004DB, 0x00002AF0, 0xFFFFEC99, 0x000004DB },
+ { 0x0213EA94DE263944, 0x0000430F, 0xFFFFDDFB, 0x000006FC, 0x00002D4D, 0xFFFFEA55, 0x00000540, 0x00002D4D, 0xFFFFEA55, 0x00000540 },
+ { 0x0213EA94DE0E2944, 0x00003B22, 0xFFFFE543, 0x000005B1, 0x000022E1, 0xFFFFF31B, 0x000003B9, 0x000022E1, 0xFFFFF31B, 0x000003B9 },
+ { 0x0213EA94DE0E2084, 0x00003978, 0xFFFFE611, 0x00000592, 0x00001C36, 0xFFFFF771, 0x00000302, 0x00001C36, 0xFFFFF771, 0x00000302 },
+ { 0x0213EA94DE262164, 0x000044DF, 0xFFFFDDAB, 0x000006F2, 0x00002CEA, 0xFFFFEB47, 0x00000507, 0x00002CEA, 0xFFFFEB47, 0x00000507 },
+ { 0x0213EA94DE0A38C4, 0x00003E9B, 0xFFFFE12C, 0x0000067C, 0x00002B79, 0xFFFFEBD9, 0x00000503, 0x00002B79, 0xFFFFEBD9, 0x00000503 },
+ { 0x0213EA94DE263044, 0x00004464, 0xFFFFDCD3, 0x00000731, 0x00002D14, 0xFFFFEA2D, 0x0000054E, 0x00002D14, 0xFFFFEA2D, 0x0000054E },
+ { 0x0213EA94DE281124, 0x00003FB3, 0xFFFFE052, 0x00000693, 0x000020AC, 0xFFFFF311, 0x000003C6, 0x000020AC, 0xFFFFF311, 0x000003C6 },
+ { 0x0213EA94DE2C1084, 0x00003BDA, 0xFFFFE2FB, 0x00000636, 0x0000261E, 0xFFFFEF72, 0x00000471, 0x0000261E, 0xFFFFEF72, 0x00000471 },
+ { 0x0213EA94DE2C1964, 0x00003D72, 0xFFFFE28A, 0x0000063E, 0x000029D8, 0xFFFFED54, 0x000004C7, 0x000029D8, 0xFFFFED54, 0x000004C7 },
+ { 0x0213EA94DE2C2824, 0x00003E26, 0xFFFFE102, 0x00000694, 0x00002DD1, 0xFFFFE9CA, 0x0000056D, 0x00002DD1, 0xFFFFE9CA, 0x0000056D },
+ { 0x0213EA94DE104124, 0x000041CD, 0xFFFFDE97, 0x000006ED, 0x00002DE5, 0xFFFFE9B9, 0x00000565, 0x00002DE5, 0xFFFFE9B9, 0x00000565 },
+ { 0x0213EA94DE0A2984, 0x00003F30, 0xFFFFE06E, 0x00000698, 0x000024FF, 0xFFFFEFFC, 0x0000044F, 0x000024FF, 0xFFFFEFFC, 0x0000044F },
+ { 0x0213EA94DE2C38C4, 0x0000378B, 0xFFFFE6B4, 0x00000594, 0x000023A7, 0xFFFFF1DC, 0x00000407, 0x000023A7, 0xFFFFF1DC, 0x00000407 },
+ { 0x0213EA94DE0E4164, 0x00003CD7, 0xFFFFE28D, 0x00000636, 0x00002036, 0xFFFFF3B5, 0x000003AA, 0x00002036, 0xFFFFF3B5, 0x000003AA },
+ { 0x0213EA94DE0A3884, 0x00003EF9, 0xFFFFE0AA, 0x0000068D, 0x000024D3, 0xFFFFF02F, 0x00000445, 0x000024D3, 0xFFFFF02F, 0x00000445 },
+ { 0x0213EA94DE283944, 0x00003D08, 0xFFFFE1BB, 0x00000665, 0x00002159, 0xFFFFF26F, 0x000003E6, 0x00002159, 0xFFFFF26F, 0x000003E6 },
+ { 0x0213EA94DE2C20C4, 0x000038A9, 0xFFFFE6CA, 0x00000580, 0x000025D3, 0xFFFFF101, 0x00000421, 0x000025D3, 0xFFFFF101, 0x00000421 },
+ { 0x0213EA94DE0A20A4, 0x00003E45, 0xFFFFE1F8, 0x0000064D, 0x000027E3, 0xFFFFEEBB, 0x0000047F, 0x000027E3, 0xFFFFEEBB, 0x0000047F },
+ { 0x0213EA94DE0E3864, 0x00003F76, 0xFFFFE128, 0x0000066E, 0x0000286B, 0xFFFFEE4C, 0x00000493, 0x0000286B, 0xFFFFEE4C, 0x00000493 },
+ { 0x0213EA94DE264104, 0x0000440D, 0xFFFFDCA2, 0x0000074F, 0x00003817, 0xFFFFE256, 0x000006AF, 0x00003817, 0xFFFFE256, 0x000006AF },
+ { 0x0213EA94DE105104, 0x00003EE1, 0xFFFFDFA7, 0x000006D4, 0x000027EA, 0xFFFFED2B, 0x000004DE, 0x000027EA, 0xFFFFED2B, 0x000004DE },
+ { 0x0213EA94DE2C3864, 0x00003C62, 0xFFFFE285, 0x0000064A, 0x00002520, 0xFFFFF001, 0x0000045C, 0x00002520, 0xFFFFF001, 0x0000045C },
+ { 0x0213EA94DE323964, 0x0000272E, 0xFFFFF17A, 0x000003FA, 0x0000150B, 0xFFFFFBD5, 0x00000284, 0x0000150B, 0xFFFFFBD5, 0x00000284 },
+ { 0x0213EA94DE261924, 0x00004275, 0xFFFFDF69, 0x000006A5, 0x000025AA, 0xFFFFF05C, 0x0000042B, 0x000025AA, 0xFFFFF05C, 0x0000042B },
+ { 0x0213EA94DE0E40E4, 0x00003CAA, 0xFFFFE392, 0x000005FF, 0x000023A8, 0xFFFFF20E, 0x000003E9, 0x000023A8, 0xFFFFF20E, 0x000003E9 },
+ { 0x0213EA94DE2C50C4, 0x00003CF8, 0xFFFFE0FB, 0x000006A6, 0x00002CA7, 0xFFFFE9FF, 0x0000056E, 0x00002CA7, 0xFFFFE9FF, 0x0000056E },
+ { 0x0213EA94DE282124, 0x00003D00, 0xFFFFE296, 0x00000633, 0x000021C1, 0xFFFFF2C8, 0x000003CF, 0x000021C1, 0xFFFFF2C8, 0x000003CF },
+ { 0x0213EA94DE2838E4, 0x00003B46, 0xFFFFE301, 0x00000632, 0x0000204C, 0xFFFFF33B, 0x000003C8, 0x0000204C, 0xFFFFF33B, 0x000003C8 },
+ { 0x0213EA94DE204164, 0x00002026, 0xFFFFF5CE, 0x00000368, 0x00001598, 0xFFFFFB29, 0x000002C3, 0x00001598, 0xFFFFFB29, 0x000002C3 },
+ { 0x0213EA94DE283164, 0x00003DCA, 0xFFFFE178, 0x00000668, 0x00001FDB, 0xFFFFF39D, 0x000003AF, 0x00001FDB, 0xFFFFF39D, 0x000003AF },
+ { 0x0213EA94DE2C48C4, 0x00003A59, 0xFFFFE327, 0x00000642, 0x000024B9, 0xFFFFEFC4, 0x00000471, 0x000024B9, 0xFFFFEFC4, 0x00000471 },
+ { 0x0213EA94DE2C2944, 0x00003C26, 0xFFFFE440, 0x000005EB, 0x00002C0F, 0xFFFFEC88, 0x000004E0, 0x00002C0F, 0xFFFFEC88, 0x000004E0 },
+ { 0x0213EA94DE083884, 0x00004149, 0xFFFFDEB8, 0x000006E7, 0x0000280A, 0xFFFFED89, 0x000004C2, 0x0000280A, 0xFFFFED89, 0x000004C2 },
+ { 0x0213EA94DE0E4124, 0x00003EB4, 0xFFFFE1E5, 0x0000064D, 0x0000299F, 0xFFFFEDB3, 0x000004A9, 0x0000299F, 0xFFFFEDB3, 0x000004A9 },
+ { 0x0213EA94DE2C39A4, 0x00003BBF, 0xFFFFE268, 0x0000065A, 0x00002504, 0xFFFFEFB0, 0x00000470, 0x00002504, 0xFFFFEFB0, 0x00000470 },
+ { 0x0213EA94DE084904, 0x00004203, 0xFFFFDDC6, 0x00000720, 0x0000303B, 0xFFFFE78F, 0x000005D0, 0x0000303B, 0xFFFFE78F, 0x000005D0 },
+ { 0x0213EA94DE0E3984, 0x00003DA3, 0xFFFFE244, 0x0000063E, 0x000021B4, 0xFFFFF2DA, 0x000003CD, 0x000021B4, 0xFFFFF2DA, 0x000003CD },
+ { 0x0213EA94DE0A38E4, 0x00004035, 0xFFFFE065, 0x0000069B, 0x00003323, 0xFFFFE6D6, 0x000005D8, 0x00003323, 0xFFFFE6D6, 0x000005D8 },
+ { 0x0213EA94DE2C1164, 0x00003944, 0xFFFFE4E5, 0x000005E2, 0x00001F3C, 0xFFFFF456, 0x0000039D, 0x00001F3C, 0xFFFFF456, 0x0000039D },
+ { 0x0213EA94DE061904, 0x000032D8, 0xFFFFEAE8, 0x000004E6, 0x00001812, 0xFFFFFA1C, 0x000002BC, 0x00001812, 0xFFFFFA1C, 0x000002BC },
+ { 0x0213F0FD42D22944, 0x000041F6, 0xFFFFE025, 0x0000069A, 0x0000241E, 0xFFFFF1B4, 0x00000402, 0x0000241E, 0xFFFFF1B4, 0x00000402 },
+ { 0x0213F0FE990C30A4, 0x00003300, 0xFFFFEB60, 0x000004C1, 0x00001E15, 0xFFFFF6A6, 0x0000033B, 0x00001E15, 0xFFFFF6A6, 0x0000033B },
+ { 0x0213EA94DE0408A4, 0x000037F0, 0xFFFFE68F, 0x0000059B, 0x00001F8A, 0xFFFFF467, 0x000003A3, 0x00001F8A, 0xFFFFF467, 0x000003A3 },
+ { 0x0213F0FE99182984, 0x000025D8, 0xFFFFF2AA, 0x000003C3, 0x000018A8, 0xFFFFF9BE, 0x000002D2, 0x000018A8, 0xFFFFF9BE, 0x000002D2 },
+ { 0x0213F0FE990620C4, 0x0000364F, 0xFFFFE988, 0x000004FC, 0x00001E51, 0xFFFFF633, 0x0000034F, 0x00001E51, 0xFFFFF633, 0x0000034F },
+ { 0x0213EA94DE061144, 0x00002288, 0xFFFFF483, 0x0000036C, 0x0000280F, 0xFFFFEF39, 0x0000047B, 0x0000280F, 0xFFFFEF39, 0x0000047B },
+ { 0x0213F0FE99082084, 0x00003322, 0xFFFFEA7E, 0x000004ED, 0x00001DAD, 0xFFFFF62B, 0x00000355, 0x00001DAD, 0xFFFFF62B, 0x00000355 },
+ { 0x0213EA94DE0250E4, 0x00002B7B, 0xFFFFEE4F, 0x0000045B, 0x00001AA2, 0xFFFFF710, 0x0000033E, 0x00001AA2, 0xFFFFF710, 0x0000033E },
+ { 0x0213F0FE990420C4, 0x000034CC, 0xFFFFEA79, 0x000004E4, 0x00001B05, 0xFFFFF8B3, 0x000002EC, 0x00001B05, 0xFFFFF8B3, 0x000002EC },
+ { 0x0213F0FD42DC2864, 0x00003837, 0xFFFFE5ED, 0x000005C3, 0x00001ACB, 0xFFFFF7B2, 0x00000314, 0x00001ACB, 0xFFFFF7B2, 0x00000314 },
+ { 0x0213F0FE99044164, 0x0000352D, 0xFFFFE88F, 0x00000548, 0x000021E6, 0xFFFFF3B5, 0x000003AA, 0x000021E6, 0xFFFFF3B5, 0x000003AA },
+ { 0x0213F0FE990A4884, 0x00003300, 0xFFFFE835, 0x0000057B, 0x00001A85, 0xFFFFF715, 0x00000336, 0x00001A85, 0xFFFFF715, 0x00000336 },
+ { 0x0213EA94DE0448A4, 0x000033FA, 0xFFFFE851, 0x00000565, 0x00001A8E, 0xFFFFF727, 0x0000033B, 0x00001A8E, 0xFFFFF727, 0x0000033B },
+ { 0x0213F0FD42DA3924, 0x000039D3, 0xFFFFE5D3, 0x000005B0, 0x00001888, 0xFFFFF978, 0x000002C8, 0x00001888, 0xFFFFF978, 0x000002C8 },
+ { 0x0213F0FE990E4864, 0x00002F6B, 0xFFFFEC53, 0x000004B9, 0x00001C15, 0xFFFFF71B, 0x00000337, 0x00001C15, 0xFFFFF71B, 0x00000337 },
+ { 0x0213F0FE99064144, 0x0000384D, 0xFFFFE737, 0x00000569, 0x00001D2D, 0xFFFFF673, 0x00000343, 0x00001D2D, 0xFFFFF673, 0x00000343 },
+ { 0x0213F0FE990620A4, 0x00003A49, 0xFFFFE70B, 0x0000055F, 0x00001A63, 0xFFFFF8CD, 0x000002E2, 0x00001A63, 0xFFFFF8CD, 0x000002E2 },
+ { 0x0213F0FE99042984, 0x0000311E, 0xFFFFEB97, 0x000004C6, 0x00001EAE, 0xFFFFF5A9, 0x00000367, 0x00001EAE, 0xFFFFF5A9, 0x00000367 },
+ { 0x0213F0FE990E1124, 0x000027D3, 0xFFFFF075, 0x00000417, 0x00002001, 0xFFFFF44A, 0x000003A2, 0x00002001, 0xFFFFF44A, 0x000003A2 },
+ { 0x0213F0FE99064904, 0x00003B72, 0xFFFFE4BD, 0x000005DC, 0x00001D76, 0xFFFFF606, 0x0000035A, 0x00001D76, 0xFFFFF606, 0x0000035A },
+ { 0x0213F0FE99101124, 0x00002E0F, 0xFFFFECA7, 0x000004AE, 0x00001DC6, 0xFFFFF5BF, 0x0000036A, 0x00001DC6, 0xFFFFF5BF, 0x0000036A },
+ { 0x0213F0FE990238A4, 0x000032C7, 0xFFFFEA7A, 0x000004F0, 0x00001A7B, 0xFFFFF827, 0x00000301, 0x00001A7B, 0xFFFFF827, 0x00000301 },
+ { 0x0213EA94DE044884, 0x0000312D, 0xFFFFEA39, 0x00000515, 0x00001948, 0xFFFFF800, 0x00000318, 0x00001948, 0xFFFFF800, 0x00000318 },
+ { 0x0213EA94DE062084, 0x00003611, 0xFFFFE8D7, 0x00000533, 0x00001929, 0xFFFFF965, 0x000002D2, 0x00001929, 0xFFFFF965, 0x000002D2 },
+ { 0x0213F0FE992C30E4, 0x00002FE2, 0xFFFFED89, 0x00000470, 0x00001A3C, 0xFFFFF955, 0x000002D5, 0x00001A3C, 0xFFFFF955, 0x000002D5 },
+ { 0x0213EA94DE0208A4, 0x000035FF, 0xFFFFE884, 0x00000548, 0x0000182A, 0xFFFFF9AB, 0x000002CF, 0x0000182A, 0xFFFFF9AB, 0x000002CF },
+ { 0x0213F0FE990220E4, 0x00003597, 0xFFFFE904, 0x00000528, 0x00001A94, 0xFFFFF840, 0x00000300, 0x00001A94, 0xFFFFF840, 0x00000300 },
+ { 0x0213F0FE99181944, 0x000026CB, 0xFFFFF1FB, 0x000003E4, 0x000017CC, 0xFFFFFA25, 0x000002C8, 0x000017CC, 0xFFFFFA25, 0x000002C8 },
+ { 0x0213EA94DE0608C4, 0x00003274, 0xFFFFEA39, 0x0000050C, 0x00001B20, 0xFFFFF7C1, 0x00000314, 0x00001B20, 0xFFFFF7C1, 0x00000314 },
+ { 0x0213F0FD42D82924, 0x0000280B, 0xFFFFF283, 0x000003B5, 0x000018D0, 0xFFFFF992, 0x000002EC, 0x000018D0, 0xFFFFF992, 0x000002EC },
+ { 0x0213F0FE99062104, 0x000033AB, 0xFFFFEB1B, 0x000004C4, 0x00001FEE, 0xFFFFF53A, 0x00000378, 0x00001FEE, 0xFFFFF53A, 0x00000378 },
+ { 0x0213F0FE990A3964, 0x00002F79, 0xFFFFEB0C, 0x000004FA, 0x00001E57, 0xFFFFF4BF, 0x0000039B, 0x00001E57, 0xFFFFF4BF, 0x0000039B },
+ { 0x0213F0FE990448E4, 0x00003487, 0xFFFFE8F2, 0x00000539, 0x0000185B, 0xFFFFF9AE, 0x000002BA, 0x0000185B, 0xFFFFF9AE, 0x000002BA },
+ { 0x0213F0FE990A18A4, 0x00003500, 0xFFFFE793, 0x0000058A, 0x00001AA2, 0xFFFFF792, 0x0000031D, 0x00001AA2, 0xFFFFF792, 0x0000031D },
+ { 0x0213F0FE99081164, 0x00003943, 0xFFFFE54D, 0x000005D9, 0x00001BC8, 0xFFFFF6E0, 0x00000339, 0x00001BC8, 0xFFFFF6E0, 0x00000339 },
+ { 0x0213EA94DE0430A4, 0x0000306D, 0xFFFFEC5E, 0x000004A5, 0x00001A3A, 0xFFFFF85F, 0x00000304, 0x00001A3A, 0xFFFFF85F, 0x00000304 },
+ { 0x0213F0FD42D83084, 0x00002BA4, 0xFFFFEE8D, 0x0000046A, 0x0000198C, 0xFFFFF88E, 0x00000307, 0x0000198C, 0xFFFFF88E, 0x00000307 },
+ { 0x0213F0FD42D218E4, 0x00003D30, 0xFFFFE2F6, 0x0000062A, 0x000025DC, 0xFFFFF074, 0x00000435, 0x000025DC, 0xFFFFF074, 0x00000435 },
+ { 0x0213F0FD42D83964, 0x00002CD6, 0xFFFFED79, 0x0000049B, 0x000016D0, 0xFFFFFA53, 0x000002BB, 0x000016D0, 0xFFFFFA53, 0x000002BB },
+ { 0x0213F0FE99163164, 0x00002484, 0xFFFFF3BD, 0x000003A0, 0x000015B8, 0xFFFFFB6B, 0x000002A4, 0x000015B8, 0xFFFFFB6B, 0x000002A4 },
+ { 0x0213F0FE990E3944, 0x000038AE, 0xFFFFE6D1, 0x00000587, 0x00001A2A, 0xFFFFF8F1, 0x000002D4, 0x00001A2A, 0xFFFFF8F1, 0x000002D4 },
+ { 0x0213F0FE99044944, 0x000036FD, 0xFFFFE76C, 0x00000576, 0x00001EE4, 0xFFFFF58D, 0x00000361, 0x00001EE4, 0xFFFFF58D, 0x00000361 },
+ { 0x0213F0FD42D830A4, 0x00002BCF, 0xFFFFEF28, 0x00000448, 0x00001B93, 0xFFFFF7BA, 0x00000327, 0x00001B93, 0xFFFFF7BA, 0x00000327 },
+ { 0x0213F0FE99062884, 0x00003834, 0xFFFFE818, 0x0000053B, 0x00001AFE, 0xFFFFF85C, 0x000002F3, 0x00001AFE, 0xFFFFF85C, 0x000002F3 },
+ { 0x0213F0FE993231A4, 0x00002EF7, 0xFFFFEBFC, 0x000004CE, 0x00001897, 0xFFFFF8EF, 0x000002EC, 0x00001897, 0xFFFFF8EF, 0x000002EC },
+ { 0x0213F0FE992C18C4, 0x000035BD, 0xFFFFE8BB, 0x0000053B, 0x00001F22, 0xFFFFF561, 0x00000373, 0x00001F22, 0xFFFFF561, 0x00000373 },
+ { 0x0213F0FE99183984, 0x00002D42, 0xFFFFEE1D, 0x00000478, 0x000016F0, 0xFFFFFAAE, 0x000002B3, 0x000016F0, 0xFFFFFAAE, 0x000002B3 },
+ { 0x0213EA94DE045124, 0x00002F98, 0xFFFFEB3C, 0x000004F0, 0x00001903, 0xFFFFF818, 0x00000319, 0x00001903, 0xFFFFF818, 0x00000319 },
+ { 0x0213F0FD42D42144, 0x00004081, 0xFFFFDF13, 0x000006F3, 0x00002A6D, 0xFFFFEC1B, 0x00000509, 0x00002A6D, 0xFFFFEC1B, 0x00000509 },
+ { 0x0213EA94DE040904, 0x00002D68, 0xFFFFED21, 0x00000498, 0x00001FF6, 0xFFFFF427, 0x000003B0, 0x00001FF6, 0xFFFFF427, 0x000003B0 },
+ { 0x0213F0FE99023884, 0x00003243, 0xFFFFEA5C, 0x000004FD, 0x000020FB, 0xFFFFF39E, 0x000003C0, 0x000020FB, 0xFFFFF39E, 0x000003C0 },
+ { 0x0213F0FD42D848A4, 0x00002F20, 0xFFFFEC19, 0x000004C6, 0x00001748, 0xFFFFF99F, 0x000002DA, 0x00001748, 0xFFFFF99F, 0x000002DA },
+ { 0x0213F0FE99103984, 0x00002D68, 0xFFFFED21, 0x00000498, 0x00001A43, 0xFFFFF843, 0x000002F9, 0x00001A43, 0xFFFFF843, 0x000002F9 },
+ { 0x0213F0FE990220A4, 0x0000396E, 0xFFFFE616, 0x000005A9, 0x00001A51, 0xFFFFF850, 0x000002FA, 0x00001A51, 0xFFFFF850, 0x000002FA },
+ { 0x0213F0FE99043144, 0x0000305C, 0xFFFFED4B, 0x0000046C, 0x00001CF9, 0xFFFFF7BA, 0x00000304, 0x00001CF9, 0xFFFFF7BA, 0x00000304 },
+ { 0x0213F0FD42DA4164, 0x0000343C, 0xFFFFE869, 0x00000559, 0x00001CE2, 0xFFFFF614, 0x00000359, 0x00001CE2, 0xFFFFF614, 0x00000359 },
+ { 0x0213F0FE99183964, 0x00002782, 0xFFFFF1FE, 0x000003D9, 0x000015DC, 0xFFFFFB8B, 0x00000290, 0x000015DC, 0xFFFFFB8B, 0x00000290 },
+ { 0x0213F0FE991818C4, 0x00002B9C, 0xFFFFEF63, 0x00000443, 0x00001369, 0xFFFFFD51, 0x00000244, 0x00001369, 0xFFFFFD51, 0x00000244 },
+ { 0x0213F0FE990A2084, 0x000035F8, 0xFFFFE743, 0x00000592, 0x000018D8, 0xFFFFF8EE, 0x000002E4, 0x000018D8, 0xFFFFF8EE, 0x000002E4 },
+ { 0x0213EA94DE062844, 0x00002B72, 0xFFFFEF1E, 0x0000043C, 0x00002647, 0xFFFFF092, 0x0000043E, 0x00002647, 0xFFFFF092, 0x0000043E },
+ { 0x0213F0FE99102184, 0x00002EC9, 0xFFFFEC5F, 0x000004B8, 0x000018B6, 0xFFFFF936, 0x000002D8, 0x000018B6, 0xFFFFF936, 0x000002D8 },
+ { 0x0213F0FE99064084, 0x000038A7, 0xFFFFE6AC, 0x00000589, 0x00001C42, 0xFFFFF70B, 0x00000329, 0x00001C42, 0xFFFFF70B, 0x00000329 },
+ { 0x0213F0FE993008A4, 0x00002F6B, 0xFFFFEBF6, 0x000004CF, 0x000018AE, 0xFFFFF928, 0x000002E3, 0x000018AE, 0xFFFFF928, 0x000002E3 },
+ { 0x0213F0FD42DA5104, 0x000029CD, 0xFFFFEEE1, 0x00000459, 0x00001AB5, 0xFFFFF76F, 0x00000324, 0x00001AB5, 0xFFFFF76F, 0x00000324 },
+ { 0x0213EA94DE0638C4, 0x00003921, 0xFFFFE71D, 0x00000577, 0x00001646, 0xFFFFFB24, 0x00000293, 0x00001646, 0xFFFFFB24, 0x00000293 },
+ { 0x0213EA94DE044164, 0x00003940, 0xFFFFE521, 0x000005E8, 0x00001947, 0xFFFFF839, 0x0000030D, 0x00001947, 0xFFFFF839, 0x0000030D },
+ { 0x0213F0FD42D24164, 0x00003DCA, 0xFFFFE211, 0x00000659, 0x0000250E, 0xFFFFF072, 0x00000443, 0x0000250E, 0xFFFFF072, 0x00000443 },
+ { 0x0213F0FE990C0904, 0x00002E95, 0xFFFFEC20, 0x000004C9, 0x000015B4, 0xFFFFFAD3, 0x0000029D, 0x000015B4, 0xFFFFFAD3, 0x0000029D },
+ { 0x0213F0FE99041084, 0x00002C11, 0xFFFFEE6E, 0x00000468, 0x00001901, 0xFFFFF924, 0x000002E7, 0x00001901, 0xFFFFF924, 0x000002E7 },
+ { 0x0213EA94DE062104, 0x0000293F, 0xFFFFF158, 0x000003E6, 0x0000183F, 0xFFFFF9F6, 0x000002D2, 0x0000183F, 0xFFFFF9F6, 0x000002D2 },
+ { 0x0213F0FE990E1104, 0x00002A67, 0xFFFFEF34, 0x0000043E, 0x00001C6F, 0xFFFFF6F1, 0x0000032B, 0x00001C6F, 0xFFFFF6F1, 0x0000032B },
+ { 0x0213EA94DE065124, 0x00002F8D, 0xFFFFEB77, 0x000004DA, 0x00001C0D, 0xFFFFF627, 0x00000365, 0x00001C0D, 0xFFFFF627, 0x00000365 },
+ { 0x0213F0FE990C38C4, 0x00003476, 0xFFFFEA5B, 0x000004E7, 0x00001DBF, 0xFFFFF6C7, 0x00000333, 0x00001DBF, 0xFFFFF6C7, 0x00000333 },
+ { 0x0213F0FE990E0944, 0x00003336, 0xFFFFE92F, 0x00000546, 0x00001614, 0xFFFFFAE0, 0x00000296, 0x00001614, 0xFFFFFAE0, 0x00000296 },
+ { 0x0213F0FE99162164, 0x00002513, 0xFFFFF323, 0x000003BC, 0x000016DB, 0xFFFFFA79, 0x000002CD, 0x000016DB, 0xFFFFFA79, 0x000002CD },
+ { 0x0213F0FE990A2944, 0x000035A7, 0xFFFFE78E, 0x00000584, 0x00001B0D, 0xFFFFF77D, 0x0000031F, 0x00001B0D, 0xFFFFF77D, 0x0000031F },
+ { 0x0213F0FE993238E4, 0x00003171, 0xFFFFEB98, 0x000004C6, 0x00001C76, 0xFFFFF71F, 0x0000032F, 0x00001C76, 0xFFFFF71F, 0x0000032F },
+ { 0x0213F0FD42DA1084, 0x00002C52, 0xFFFFED2E, 0x000004A7, 0x00002182, 0xFFFFF2F4, 0x000003E4, 0x00002182, 0xFFFFF2F4, 0x000003E4 },
+ { 0x0213F0FE99102924, 0x000032E1, 0xFFFFEB39, 0x000004D0, 0x00001B55, 0xFFFFF859, 0x000002FA, 0x00001B55, 0xFFFFF859, 0x000002FA },
+ { 0x0213F0FE991848A4, 0x000029B6, 0xFFFFEFF7, 0x00000430, 0x0000151B, 0xFFFFFBC6, 0x0000027F, 0x0000151B, 0xFFFFFBC6, 0x0000027F },
+ { 0x0213F0FD42DA1964, 0x00002FF7, 0xFFFFEB67, 0x000004DA, 0x000020E9, 0xFFFFF363, 0x000003CE, 0x000020E9, 0xFFFFF363, 0x000003CE },
+ { 0x0213F0FD42DA5124, 0x00003CDD, 0xFFFFE2B2, 0x00000649, 0x00001B18, 0xFFFFF739, 0x00000329, 0x00001B18, 0xFFFFF739, 0x00000329 },
+ { 0x0213F0FE990628A4, 0x00003C82, 0xFFFFE5C6, 0x0000058E, 0x00001F3F, 0xFFFFF5AD, 0x00000361, 0x00001F3F, 0xFFFFF5AD, 0x00000361 },
+ { 0x0213F0FD42DC4084, 0x0000319B, 0xFFFFEA15, 0x0000051B, 0x00001CC9, 0xFFFFF62E, 0x00000358, 0x00001CC9, 0xFFFFF62E, 0x00000358 },
+ { 0x0213EA94DE0638E4, 0x000032B6, 0xFFFFEB2B, 0x000004D6, 0x000018E0, 0xFFFFF966, 0x000002DE, 0x000018E0, 0xFFFFF966, 0x000002DE },
+ { 0x0213EA94DE023984, 0x0000300A, 0xFFFFEBA6, 0x000004D1, 0x00001CFD, 0xFFFFF5F6, 0x0000036D, 0x00001CFD, 0xFFFFF5F6, 0x0000036D },
+ { 0x0213F0FD42D82984, 0x000026A9, 0xFFFFF15D, 0x00000400, 0x00001561, 0xFFFFFB1F, 0x000002A0, 0x00001561, 0xFFFFFB1F, 0x000002A0 },
+ { 0x0213F0FE990E5124, 0x00003123, 0xFFFFEAD2, 0x000004FA, 0x000018CB, 0xFFFFF8F5, 0x000002EC, 0x000018CB, 0xFFFFF8F5, 0x000002EC },
+ { 0x0213F0FE991840C4, 0x00003577, 0xFFFFE935, 0x00000533, 0x000016CD, 0xFFFFFB44, 0x00000289, 0x000016CD, 0xFFFFFB44, 0x00000289 },
+ { 0x0213F0FE99282184, 0x00002875, 0xFFFFF170, 0x000003F3, 0x00001567, 0xFFFFFBD5, 0x00000289, 0x00001567, 0xFFFFFBD5, 0x00000289 },
+ { 0x0213F0FE99084084, 0x00003AE2, 0xFFFFE538, 0x000005C1, 0x00001CB4, 0xFFFFF6A3, 0x0000033C, 0x00001CB4, 0xFFFFF6A3, 0x0000033C },
+ { 0x0213F0FE990C38E4, 0x000031DF, 0xFFFFEC2A, 0x000004A3, 0x00001EF0, 0xFFFFF626, 0x00000352, 0x00001EF0, 0xFFFFF626, 0x00000352 },
+ { 0x0213F0FD42D25144, 0x00004A6A, 0xFFFFDB15, 0x00000758, 0x000027F3, 0xFFFFEEEE, 0x00000479, 0x000027F3, 0xFFFFEEEE, 0x00000479 },
+ { 0x0213EA94DE063904, 0x00002BB9, 0xFFFFEF5D, 0x00000433, 0x00001589, 0xFFFFFB57, 0x00000295, 0x00001589, 0xFFFFFB57, 0x00000295 },
+ { 0x0213F0FE99042164, 0x000033A0, 0xFFFFE98F, 0x00000528, 0x00001CB4, 0xFFFFF706, 0x0000032D, 0x00001CB4, 0xFFFFF706, 0x0000032D },
+ { 0x0213F0FE99163064, 0x0000248E, 0xFFFFF380, 0x000003AC, 0x000016EA, 0xFFFFFA6C, 0x000002CE, 0x000016EA, 0xFFFFFA6C, 0x000002CE },
+ { 0x0213F0FE990221A4, 0x00002FE2, 0xFFFFEB2F, 0x000004E9, 0x00001D4E, 0xFFFFF56B, 0x00000380, 0x00001D4E, 0xFFFFF56B, 0x00000380 },
+ { 0x0213F0FE990A2884, 0x00003283, 0xFFFFE9E7, 0x0000051D, 0x00000694, 0xFFFFFD32, 0x000003C3, 0x00000694, 0xFFFFFD32, 0x000003C3 },
+ { 0x0213F0FD42D850C4, 0x00002EE4, 0xFFFFEBFD, 0x000004D3, 0x0000151A, 0xFFFFFAF6, 0x000002A4, 0x0000151A, 0xFFFFFAF6, 0x000002A4 },
+ { 0x0213F0FD42DC18E4, 0x0000302D, 0xFFFFEB7F, 0x000004DA, 0x00001E6D, 0xFFFFF54B, 0x00000380, 0x00001E6D, 0xFFFFF54B, 0x00000380 },
+ { 0x0213F0FD42DA50C4, 0x000033DA, 0xFFFFE7FB, 0x0000057F, 0x00001DED, 0xFFFFF50E, 0x0000038D, 0x00001DED, 0xFFFFF50E, 0x0000038D },
+ { 0x0213F0FE992C4084, 0x000030B5, 0xFFFFEBB8, 0x000004C4, 0x00001C3F, 0xFFFFF726, 0x0000032A, 0x00001C3F, 0xFFFFF726, 0x0000032A },
+ { 0x0213F0FE990831C4, 0x00003BBD, 0xFFFFE55C, 0x000005B8, 0x000019DB, 0xFFFFF8BB, 0x000002EF, 0x000019DB, 0xFFFFF8BB, 0x000002EF },
+ { 0x0213F0FE990E3884, 0x00002964, 0xFFFFF051, 0x0000040E, 0x000025CD, 0xFFFFF11B, 0x0000041F, 0x000025CD, 0xFFFFF11B, 0x0000041F },
+ { 0x0213F0FD42DC4884, 0x000033F5, 0xFFFFE863, 0x00000560, 0x00001BCE, 0xFFFFF689, 0x0000034B, 0x00001BCE, 0xFFFFF689, 0x0000034B },
+ { 0x0213F0FE990A2864, 0x00003294, 0xFFFFE924, 0x00000548, 0x00001D41, 0xFFFFF580, 0x0000037D, 0x00001D41, 0xFFFFF580, 0x0000037D },
+ { 0x0213F0FD42DC39A4, 0x000034FB, 0xFFFFE7FE, 0x0000056D, 0x00001CB1, 0xFFFFF635, 0x00000357, 0x00001CB1, 0xFFFFF635, 0x00000357 },
+ { 0x0213F0FE990A10A4, 0x00002E28, 0xFFFFEBB9, 0x000004E0, 0x00001B20, 0xFFFFF6E3, 0x0000033C, 0x00001B20, 0xFFFFF6E3, 0x0000033C },
+ { 0x0213F0FD42DA1904, 0x00002799, 0xFFFFF0F4, 0x000003FC, 0x00001C9D, 0xFFFFF6A1, 0x00000345, 0x00001C9D, 0xFFFFF6A1, 0x00000345 },
+ { 0x0213F0FE99064104, 0x00003AEA, 0xFFFFE5DB, 0x0000059D, 0x00001B61, 0xFFFFF7F0, 0x00000301, 0x00001B61, 0xFFFFF7F0, 0x00000301 },
+ { 0x0213EA94DE041984, 0x000031F6, 0xFFFFEAB8, 0x000004F3, 0x00001D90, 0xFFFFF622, 0x00000359, 0x00001D90, 0xFFFFF622, 0x00000359 },
+ { 0x0213F0FE990C4064, 0x000031B8, 0xFFFFEA61, 0x0000050F, 0x0000199D, 0xFFFFF87C, 0x000002FD, 0x0000199D, 0xFFFFF87C, 0x000002FD },
+ { 0x0213F0FD42D23144, 0x00004514, 0xFFFFDDFF, 0x000006F6, 0x000022CD, 0xFFFFF29F, 0x000003D9, 0x000022CD, 0xFFFFF29F, 0x000003D9 },
+ { 0x0213EA94DE043164, 0x00002F30, 0xFFFFECB8, 0x000004A0, 0x00001B07, 0xFFFFF7E2, 0x00000313, 0x00001B07, 0xFFFFF7E2, 0x00000313 },
+ { 0x0213F0FD42DC30A4, 0x0000383B, 0xFFFFE702, 0x00000581, 0x00001A08, 0xFFFFF8CA, 0x000002E2, 0x00001A08, 0xFFFFF8CA, 0x000002E2 },
+ { 0x0213F0FE99022164, 0x00002CC5, 0xFFFFEDF8, 0x00000465, 0x00001F47, 0xFFFFF4B2, 0x00000393, 0x00001F47, 0xFFFFF4B2, 0x00000393 },
+ { 0x0213F0FE991621C4, 0x00002304, 0xFFFFF453, 0x00000384, 0x0000170A, 0xFFFFFA3F, 0x000002CE, 0x0000170A, 0xFFFFFA3F, 0x000002CE },
+ { 0x0213F0FE990A5124, 0x0000337E, 0xFFFFE850, 0x0000056E, 0x00001BDD, 0xFFFFF668, 0x00000353, 0x00001BDD, 0xFFFFF668, 0x00000353 },
+ { 0x0213F0FE990E4924, 0x00002E2F, 0xFFFFEC9B, 0x000004AE, 0x00001C4D, 0xFFFFF6D3, 0x00000338, 0x00001C4D, 0xFFFFF6D3, 0x00000338 },
+ { 0x0213EA94DE061124, 0x00002DDD, 0xFFFFEDA4, 0x00000477, 0x00002010, 0xFFFFF4BB, 0x00000390, 0x00002010, 0xFFFFF4BB, 0x00000390 },
+ { 0x0213F0FD42DA48E4, 0x0000290C, 0xFFFFEF61, 0x00000445, 0x00002133, 0xFFFFF324, 0x000003D8, 0x00002133, 0xFFFFF324, 0x000003D8 },
+ { 0x0213F0FE99062924, 0x0000371E, 0xFFFFE8D5, 0x00000524, 0x00001C3A, 0xFFFFF7AE, 0x00000314, 0x00001C3A, 0xFFFFF7AE, 0x00000314 },
+ { 0x0213F0FD42D838E4, 0x00002A58, 0xFFFFF007, 0x00000429, 0x000018A6, 0xFFFFF98F, 0x000002E1, 0x000018A6, 0xFFFFF98F, 0x000002E1 },
+ { 0x0213F0FE99023084, 0x00002FED, 0xFFFFEC48, 0x000004AA, 0x00001E9D, 0xFFFFF584, 0x00000370, 0x00001E9D, 0xFFFFF584, 0x00000370 },
+ { 0x0213F0FE99181884, 0x00002829, 0xFFFFF15F, 0x000003F7, 0x0000157E, 0xFFFFFBD4, 0x00000282, 0x0000157E, 0xFFFFFBD4, 0x00000282 },
+ { 0x0213F0FE99101924, 0x000030CF, 0xFFFFEB8D, 0x000004CE, 0x00001A4C, 0xFFFFF868, 0x000002F7, 0x00001A4C, 0xFFFFF868, 0x000002F7 },
+ { 0x0213F0FD42DA2084, 0x00002C8F, 0xFFFFEDD2, 0x0000047D, 0x00001CCE, 0xFFFFF6A1, 0x00000343, 0x00001CCE, 0xFFFFF6A1, 0x00000343 },
+ { 0x0213F0FE99182164, 0x00002A84, 0xFFFFEFBA, 0x0000043E, 0x000015EF, 0xFFFFFB4B, 0x0000029E, 0x000015EF, 0xFFFFFB4B, 0x0000029E },
+ { 0x0213F0FE990C28A4, 0x000034CA, 0xFFFFEA08, 0x000004FF, 0x00001C19, 0xFFFFF7ED, 0x00000309, 0x00001C19, 0xFFFFF7ED, 0x00000309 },
+ { 0x0213F0FE991639A4, 0x00002187, 0xFFFFF4B0, 0x0000037E, 0x0000154A, 0xFFFFFB0C, 0x000002AE, 0x0000154A, 0xFFFFFB0C, 0x000002AE },
+ { 0x0213F0FD42DA3844, 0x00002F4F, 0xFFFFEB3C, 0x000004F8, 0x0000181F, 0xFFFFF92D, 0x000002DF, 0x0000181F, 0xFFFFF92D, 0x000002DF },
+ { 0x0213F0FE990410E4, 0x0000290C, 0xFFFFF0B1, 0x000003FC, 0x00001DB0, 0xFFFFF636, 0x00000355, 0x00001DB0, 0xFFFFF636, 0x00000355 },
+ { 0x0213F0FE990A1064, 0x000034C1, 0xFFFFE888, 0x0000055A, 0x000019BF, 0xFFFFF881, 0x000002FB, 0x000019BF, 0xFFFFF881, 0x000002FB },
+ { 0x0213F0FD42DC18C4, 0x00003139, 0xFFFFEA98, 0x00000504, 0x000019F2, 0xFFFFF820, 0x0000030B, 0x000019F2, 0xFFFFF820, 0x0000030B },
+ { 0x0213F0FD42D83144, 0x00002CAC, 0xFFFFEEB2, 0x00000458, 0x0000152C, 0xFFFFFBEF, 0x0000027B, 0x0000152C, 0xFFFFFBEF, 0x0000027B },
+ { 0x0213F0FE992C38E4, 0x00003577, 0xFFFFE99C, 0x0000050D, 0x00001E64, 0xFFFFF679, 0x0000033F, 0x00001E64, 0xFFFFF679, 0x0000033F },
+ { 0x0213F0FD42DA4104, 0x0000263A, 0xFFFFF1E4, 0x000003D4, 0x00001F68, 0xFFFFF4ED, 0x00000386, 0x00001F68, 0xFFFFF4ED, 0x00000386 },
+ { 0x0213F0FD42D81984, 0x00002CE9, 0xFFFFED63, 0x00000497, 0x00001810, 0xFFFFF94D, 0x000002E3, 0x00001810, 0xFFFFF94D, 0x000002E3 },
+ { 0x0213EA94DE044104, 0x0000318A, 0xFFFFEAC8, 0x000004F5, 0x0000195C, 0xFFFFF896, 0x000002FB, 0x0000195C, 0xFFFFF896, 0x000002FB },
+ { 0x0213F0FD42D83904, 0x00002C41, 0xFFFFEEC6, 0x0000045D, 0x000017DD, 0xFFFFFA16, 0x000002CB, 0x000017DD, 0xFFFFFA16, 0x000002CB },
+ { 0x0213F0FE990231A4, 0x00002DD4, 0xFFFFEC98, 0x000004AD, 0x00001BD7, 0xFFFFF69F, 0x00000347, 0x00001BD7, 0xFFFFF69F, 0x00000347 },
+ { 0x0213F0FD42DA3944, 0x00003351, 0xFFFFE9B2, 0x0000051A, 0x00001CA1, 0xFFFFF6A4, 0x00000341, 0x00001CA1, 0xFFFFF6A4, 0x00000341 },
+ { 0x0213F0FE99021104, 0x0000322D, 0xFFFFE9BE, 0x00000527, 0x00001CF9, 0xFFFFF5EB, 0x00000366, 0x00001CF9, 0xFFFFF5EB, 0x00000366 },
+ { 0x0213F0FE990C28C4, 0x00003678, 0xFFFFE9A8, 0x00000503, 0x00001AD4, 0xFFFFF8F6, 0x000002E3, 0x00001AD4, 0xFFFFF8F6, 0x000002E3 },
+ { 0x0213F0FE99161924, 0x0000260E, 0xFFFFF2C1, 0x000003CA, 0x00001139, 0xFFFFFE48, 0x00000236, 0x00001139, 0xFFFFFE48, 0x00000236 },
+ { 0x0213F0FE990A2164, 0x000033D3, 0xFFFFE872, 0x00000565, 0x00001B72, 0xFFFFF713, 0x00000332, 0x00001B72, 0xFFFFF713, 0x00000332 },
+ { 0x0213F0FE99323844, 0x0000309B, 0xFFFFEB42, 0x000004E4, 0x00001918, 0xFFFFF8C8, 0x000002F2, 0x00001918, 0xFFFFF8C8, 0x000002F2 },
+ { 0x0213F0FE99182864, 0x000028B8, 0xFFFFF105, 0x00000402, 0x000018BB, 0xFFFFF9BC, 0x000002D3, 0x000018BB, 0xFFFFF9BC, 0x000002D3 },
+ { 0x0213F0FE990A1884, 0x00003123, 0xFFFFE9D1, 0x00000534, 0x00001B19, 0xFFFFF6FE, 0x0000033C, 0x00001B19, 0xFFFFF6FE, 0x0000033C },
+ { 0x0213F0FE99022144, 0x00003216, 0xFFFFEA8E, 0x000004F6, 0x00001F72, 0xFFFFF4CE, 0x0000038B, 0x00001F72, 0xFFFFF4CE, 0x0000038B },
+ { 0x0213F0FE99162964, 0x00002564, 0xFFFFF32D, 0x000003B6, 0x00001685, 0xFFFFFADB, 0x000002BB, 0x00001685, 0xFFFFFADB, 0x000002BB },
+ { 0x0213F0FD42DA2924, 0x00002E60, 0xFFFFED13, 0x00000497, 0x00001CA5, 0xFFFFF6B9, 0x00000346, 0x00001CA5, 0xFFFFF6B9, 0x00000346 },
+ { 0x0213F0FE990E39A4, 0x0000336D, 0xFFFFE934, 0x0000053B, 0x00001B3E, 0xFFFFF763, 0x00000327, 0x00001B3E, 0xFFFFF763, 0x00000327 },
+ { 0x0213F0FE99101084, 0x0000274A, 0xFFFFF119, 0x000003FA, 0x00001D75, 0xFFFFF5CD, 0x0000036F, 0x00001D75, 0xFFFFF5CD, 0x0000036F },
+ { 0x0213F0FD42DA2164, 0x0000366B, 0xFFFFE70A, 0x0000059A, 0x00001ED8, 0xFFFFF501, 0x00000389, 0x00001ED8, 0xFFFFF501, 0x00000389 },
+ { 0x0213F0FE99223964, 0x00003164, 0xFFFFEAB4, 0x000004FA, 0x00001C52, 0xFFFFF6E0, 0x00000336, 0x00001C52, 0xFFFFF6E0, 0x00000336 },
+ { 0x0213F0FD42D23064, 0x00004224, 0xFFFFDF7F, 0x000006C1, 0x00002A52, 0xFFFFED5E, 0x000004BB, 0x00002A52, 0xFFFFED5E, 0x000004BB },
+ { 0x0213F0FE99102864, 0x000030E3, 0xFFFFEB07, 0x000004ED, 0x00001FD3, 0xFFFFF46D, 0x000003A1, 0x00001FD3, 0xFFFFF46D, 0x000003A1 },
+ { 0x0213F0FD42D82884, 0x00002AEB, 0xFFFFEF1B, 0x00000454, 0x00001829, 0xFFFFF995, 0x000002DD, 0x00001829, 0xFFFFF995, 0x000002DD },
+ { 0x0213F0FD42DC50E4, 0x0000346B, 0xFFFFE7A2, 0x0000058B, 0x000020C5, 0xFFFFF2E8, 0x000003EC, 0x000020C5, 0xFFFFF2E8, 0x000003EC },
+ { 0x0213F0FD42DC4164, 0x000039CF, 0xFFFFE5D7, 0x000005A9, 0x00001D66, 0xFFFFF5D6, 0x00000366, 0x00001D66, 0xFFFFF5D6, 0x00000366 },
+ { 0x0213F0FE990418E4, 0x000034AC, 0xFFFFE9AE, 0x00000515, 0x00001A28, 0xFFFFF904, 0x000002DC, 0x00001A28, 0xFFFFF904, 0x000002DC },
+ { 0x0213F0FD42DC2084, 0x00002D68, 0xFFFFED21, 0x00000498, 0x00001C6F, 0xFFFFF686, 0x0000034C, 0x00001C6F, 0xFFFFF686, 0x0000034C },
+ { 0x0213F0FE990820C4, 0x0000328B, 0xFFFFEBA1, 0x000004B4, 0x00001DA3, 0xFFFFF683, 0x00000349, 0x00001DA3, 0xFFFFF683, 0x00000349 },
+ { 0x0213F0FE991828C4, 0x000027DC, 0xFFFFF295, 0x000003BF, 0x000019C1, 0xFFFFF98E, 0x000002E8, 0x000019C1, 0xFFFFF98E, 0x000002E8 },
+ { 0x0213F0FE99184084, 0x00002756, 0xFFFFF1D7, 0x000003DF, 0x000015D9, 0xFFFFFB51, 0x00000298, 0x000015D9, 0xFFFFFB51, 0x00000298 },
+ { 0x0213F0FE99083884, 0x00003526, 0xFFFFE907, 0x00000526, 0x000017AB, 0xFFFFFA12, 0x000002AB, 0x000017AB, 0xFFFFFA12, 0x000002AB },
+ { 0x0213F0FD42DA18E4, 0x0000351B, 0xFFFFE8B7, 0x00000540, 0x00001A86, 0xFFFFF821, 0x00000303, 0x00001A86, 0xFFFFF821, 0x00000303 },
+ { 0x0213F0FE99164144, 0x000024B2, 0xFFFFF34E, 0x000003B1, 0x000018E2, 0xFFFFF926, 0x000002FC, 0x000018E2, 0xFFFFF926, 0x000002FC },
+ { 0x0213F0FD42D828A4, 0x00002F36, 0xFFFFED5D, 0x00000486, 0x0000157A, 0xFFFFFB85, 0x00000293, 0x0000157A, 0xFFFFFB85, 0x00000293 },
+ { 0x0213F0FD42DC50C4, 0x00003A6E, 0xFFFFE456, 0x000005FD, 0x00001F68, 0xFFFFF3D1, 0x000003C3, 0x00001F68, 0xFFFFF3D1, 0x000003C3 },
+ { 0x0213F0FE990A31A4, 0x00002BC3, 0xFFFFED2D, 0x000004A7, 0x00001C3F, 0xFFFFF609, 0x00000364, 0x00001C3F, 0xFFFFF609, 0x00000364 },
+ { 0x0213F0FE990E2084, 0x000032E1, 0xFFFFEA83, 0x000004F6, 0x00001B37, 0xFFFFF842, 0x000002F5, 0x00001B37, 0xFFFFF842, 0x000002F5 },
+ { 0x0213F0FD42D83184, 0x000028E3, 0xFFFFF07F, 0x00000412, 0x00001676, 0xFFFFFA68, 0x000002BE, 0x00001676, 0xFFFFFA68, 0x000002BE },
+ { 0x0213F0FD42D21104, 0x0000444C, 0xFFFFDDAD, 0x00000712, 0x00002634, 0xFFFFEF89, 0x0000046C, 0x00002634, 0xFFFFEF89, 0x0000046C },
+ { 0x0213F0FE990418C4, 0x00003121, 0xFFFFEBBB, 0x000004C6, 0x00001C98, 0xFFFFF72B, 0x0000032D, 0x00001C98, 0xFFFFF72B, 0x0000032D },
+ { 0x0213F0FD42D840A4, 0x00002C31, 0xFFFFEDC4, 0x00000490, 0x0000162D, 0xFFFFFA8E, 0x000002B4, 0x0000162D, 0xFFFFFA8E, 0x000002B4 },
+ { 0x0213F0FD42DA18C4, 0x00002749, 0xFFFFF112, 0x000003FC, 0x00001C85, 0xFFFFF6B8, 0x00000342, 0x00001C85, 0xFFFFF6B8, 0x00000342 },
+ { 0x0213F0FE99044104, 0x00003159, 0xFFFFEB99, 0x000004C2, 0x00001BD0, 0xFFFFF7CA, 0x00000307, 0x00001BD0, 0xFFFFF7CA, 0x00000307 },
+ { 0x0213F0FE99164164, 0x00002610, 0xFFFFF1FD, 0x000003EC, 0x000016BE, 0xFFFFFA53, 0x000002CB, 0x000016BE, 0xFFFFFA53, 0x000002CB },
+ { 0x0213F0FE99023184, 0x000037B5, 0xFFFFE63D, 0x000005B5, 0x00002285, 0xFFFFF25D, 0x000003F7, 0x00002285, 0xFFFFF25D, 0x000003F7 },
+ { 0x0213F0FE990A28A4, 0x00002FEE, 0xFFFFEB47, 0x000004EF, 0x00001CBE, 0xFFFFF64E, 0x00000358, 0x00001CBE, 0xFFFFF64E, 0x00000358 },
+ { 0x0213F0FE99105104, 0x00002E90, 0xFFFFEC48, 0x000004C0, 0x00001A47, 0xFFFFF7D1, 0x0000031A, 0x00001A47, 0xFFFFF7D1, 0x0000031A },
+ { 0x0213F0FD42DA4084, 0x000034AB, 0xFFFFE84A, 0x00000559, 0x00001A72, 0xFFFFF79A, 0x0000031C, 0x00001A72, 0xFFFFF79A, 0x0000031C },
+ { 0x0213F0FE99183884, 0x00002F7B, 0xFFFFECFC, 0x0000049C, 0x00001814, 0xFFFFFA22, 0x000002C2, 0x00001814, 0xFFFFFA22, 0x000002C2 },
+ { 0x0213F0FE99021964, 0x00003618, 0xFFFFE709, 0x00000596, 0x00001EBF, 0xFFFFF482, 0x000003A5, 0x00001EBF, 0xFFFFF482, 0x000003A5 },
+ { 0x0213EA94DE024904, 0x0000341B, 0xFFFFE8B2, 0x0000054F, 0x00001D26, 0xFFFFF578, 0x00000388, 0x00001D26, 0xFFFFF578, 0x00000388 },
+ { 0x0213F0FE99102144, 0x000030F6, 0xFFFFEB89, 0x000004CD, 0x000019C0, 0xFFFFF8CC, 0x000002E6, 0x000019C0, 0xFFFFF8CC, 0x000002E6 },
+ { 0x0213F0FE992841A4, 0x00002B76, 0xFFFFEF6C, 0x00000444, 0x00001563, 0xFFFFFBBE, 0x0000028D, 0x00001563, 0xFFFFFBBE, 0x0000028D },
+ { 0x0213F0FD42D81864, 0x00002BA2, 0xFFFFEE31, 0x0000047F, 0x00001A3D, 0xFFFFF7F3, 0x00000320, 0x00001A3D, 0xFFFFF7F3, 0x00000320 },
+ { 0x0213F0FE992C48E4, 0x00003545, 0xFFFFE87A, 0x0000054A, 0x00001B5A, 0xFFFFF7B0, 0x0000030C, 0x00001B5A, 0xFFFFF7B0, 0x0000030C },
+ { 0x0213EA94DE042944, 0x00003879, 0xFFFFE73F, 0x00000578, 0x00001649, 0xFFFFFB57, 0x00000283, 0x00001649, 0xFFFFFB57, 0x00000283 },
+ { 0x0213F0FD42D840C4, 0x00002772, 0xFFFFF0F1, 0x00000410, 0x0000142F, 0xFFFFFBCF, 0x00000287, 0x0000142F, 0xFFFFFBCF, 0x00000287 },
+ { 0x0213F0FD42DA3184, 0x00003228, 0xFFFFE98E, 0x00000535, 0x00001F48, 0xFFFFF495, 0x00000399, 0x00001F48, 0xFFFFF495, 0x00000399 },
+ { 0x0213F0FE990E40E4, 0x00002887, 0xFFFFF119, 0x000003E8, 0x000021AA, 0xFFFFF3F5, 0x000003A5, 0x000021AA, 0xFFFFF3F5, 0x000003A5 },
+ { 0x0213F0FD42DA28A4, 0x0000301F, 0xFFFFEBB2, 0x000004D2, 0x00001C02, 0xFFFFF736, 0x0000032B, 0x00001C02, 0xFFFFF736, 0x0000032B },
+ { 0x0213F0FE991820A4, 0x00002E13, 0xFFFFEE3F, 0x00000468, 0x000016AC, 0xFFFFFB32, 0x0000029E, 0x000016AC, 0xFFFFFB32, 0x0000029E },
+ { 0x0213F0FE99044924, 0x00003478, 0xFFFFE8F9, 0x00000538, 0x00001DAB, 0xFFFFF645, 0x00000345, 0x00001DAB, 0xFFFFF645, 0x00000345 },
+ { 0x0213F0FE990608C4, 0x000030C6, 0xFFFFEB6C, 0x000004D4, 0x0000184A, 0xFFFFF934, 0x000002E1, 0x0000184A, 0xFFFFF934, 0x000002E1 },
+ { 0x0213F0FE990A2044, 0x00002F1B, 0xFFFFEBD3, 0x000004D3, 0x000019E7, 0xFFFFF813, 0x0000030D, 0x000019E7, 0xFFFFF813, 0x0000030D },
+ { 0x0213F0FE99023904, 0x00003214, 0xFFFFEAE9, 0x000004E0, 0x0000178F, 0xFFFFFA1C, 0x000002B1, 0x0000178F, 0xFFFFFA1C, 0x000002B1 },
+ { 0x0213F0FD42DC3144, 0x0000399C, 0xFFFFE738, 0x0000055E, 0x00001EA1, 0xFFFFF5E7, 0x0000035A, 0x00001EA1, 0xFFFFF5E7, 0x0000035A },
+ { 0x0213F0FE990650C4, 0x00003A01, 0xFFFFE5B2, 0x000005B6, 0x00001D95, 0xFFFFF5D2, 0x0000036A, 0x00001D95, 0xFFFFF5D2, 0x0000036A },
+ { 0x0213F0FE99043884, 0x0000310D, 0xFFFFEB78, 0x000004D0, 0x00001C06, 0xFFFFF76E, 0x0000031A, 0x00001C06, 0xFFFFF76E, 0x0000031A },
+ { 0x0213F0FE99063864, 0x00003CD1, 0xFFFFE42F, 0x000005EB, 0x00001933, 0xFFFFF91F, 0x000002D4, 0x00001933, 0xFFFFF91F, 0x000002D4 },
+ { 0x0213F0FD42DA3164, 0x00003119, 0xFFFFEB1B, 0x000004E1, 0x00001FC7, 0xFFFFF46A, 0x000003A2, 0x00001FC7, 0xFFFFF46A, 0x000003A2 },
+ { 0x0213EA94DE0648A4, 0x0000390D, 0xFFFFE566, 0x000005D8, 0x00001EC6, 0xFFFFF4DC, 0x00000391, 0x00001EC6, 0xFFFFF4DC, 0x00000391 },
+ { 0x0213F0FD42DA10C4, 0x00003446, 0xFFFFE858, 0x00000561, 0x00001FDB, 0xFFFFF3FF, 0x000003B9, 0x00001FDB, 0xFFFFF3FF, 0x000003B9 },
+ { 0x0213F0FE99044904, 0x000032BA, 0xFFFFEA07, 0x00000511, 0x00001B25, 0xFFFFF7C9, 0x0000030D, 0x00001B25, 0xFFFFF7C9, 0x0000030D },
+ { 0x0213F0FE990E1864, 0x00002CCF, 0xFFFFEDE5, 0x00000478, 0x00001BC8, 0xFFFFF761, 0x00000326, 0x00001BC8, 0xFFFFF761, 0x00000326 },
+ { 0x0213F0FE99062984, 0x0000400E, 0xFFFFE1CB, 0x00000652, 0x00001AF8, 0xFFFFF7B9, 0x00000312, 0x00001AF8, 0xFFFFF7B9, 0x00000312 },
+ { 0x0213F0FE990408E4, 0x00002F24, 0xFFFFEC2A, 0x000004C7, 0x00001B94, 0xFFFFF748, 0x00000333, 0x00001B94, 0xFFFFF748, 0x00000333 },
+ { 0x0213F0FD42D21924, 0x00003FDA, 0xFFFFE1C1, 0x0000064B, 0x00002427, 0xFFFFF180, 0x0000040C, 0x00002427, 0xFFFFF180, 0x0000040C },
+ { 0x0213F0FE990A18C4, 0x00002F6B, 0xFFFFEBA7, 0x000004DD, 0x00001C25, 0xFFFFF6C1, 0x00000344, 0x00001C25, 0xFFFFF6C1, 0x00000344 },
+ { 0x0213F0FE99182104, 0x00002A53, 0xFFFFF0EE, 0x00000402, 0x000017C6, 0xFFFFFAA0, 0x000002BF, 0x000017C6, 0xFFFFFAA0, 0x000002BF },
+ { 0x0213F0FE99105144, 0x000031F4, 0xFFFFEA34, 0x00000517, 0x000016FF, 0xFFFFFA4E, 0x000002AC, 0x000016FF, 0xFFFFFA4E, 0x000002AC },
+ { 0x0213F0FE99322144, 0x00002E24, 0xFFFFED46, 0x00000489, 0x00001712, 0xFFFFFA5D, 0x000002AC, 0x00001712, 0xFFFFFA5D, 0x000002AC },
+ { 0x0213F0FE99182824, 0x000028CD, 0xFFFFF0E3, 0x0000040E, 0x00001606, 0xFFFFFB37, 0x000002A4, 0x00001606, 0xFFFFFB37, 0x000002A4 },
+ { 0x0213F0FE990220C4, 0x00003184, 0xFFFFEB88, 0x000004C3, 0x000018DA, 0xFFFFF939, 0x000002DB, 0x000018DA, 0xFFFFF939, 0x000002DB },
+ { 0x0213F0FE99162124, 0x0000239B, 0xFFFFF470, 0x00000386, 0x00001714, 0xFFFFFA9F, 0x000002C8, 0x00001714, 0xFFFFFA9F, 0x000002C8 },
+ { 0x0213F0FD42DC38E4, 0x00003641, 0xFFFFE92B, 0x00000515, 0x00001BE2, 0xFFFFF795, 0x0000031B, 0x00001BE2, 0xFFFFF795, 0x0000031B },
+ { 0x0213F0FE992C1144, 0x00003278, 0xFFFFEA17, 0x00000510, 0x00001B71, 0xFFFFF778, 0x0000031D, 0x00001B71, 0xFFFFF778, 0x0000031D },
+ { 0x0213F0FE99062844, 0x000035B9, 0xFFFFE8DA, 0x0000052D, 0x00001A6A, 0xFFFFF83B, 0x000002FF, 0x00001A6A, 0xFFFFF83B, 0x000002FF },
+ { 0x0213F0FE990E18C4, 0x00002E5E, 0xFFFFED32, 0x0000048B, 0x00001E7D, 0xFFFFF60E, 0x0000034E, 0x00001E7D, 0xFFFFF60E, 0x0000034E },
+ { 0x0213F0FE991019A4, 0x00003178, 0xFFFFEA52, 0x00000513, 0x00001AD0, 0xFFFFF793, 0x0000031F, 0x00001AD0, 0xFFFFF793, 0x0000031F },
+ { 0x0213F0FD42D44104, 0x00003A2C, 0xFFFFE346, 0x00000641, 0x000023D0, 0xFFFFF0CE, 0x00000433, 0x000023D0, 0xFFFFF0CE, 0x00000433 },
+ { 0x0213F0FD42D818C4, 0x000028FD, 0xFFFFF02A, 0x0000042B, 0x0000152B, 0xFFFFFB90, 0x00000289, 0x0000152B, 0xFFFFFB90, 0x00000289 },
+ { 0x0213F0FE990E3084, 0x000030DE, 0xFFFFEBDF, 0x000004BE, 0x00001CDC, 0xFFFFF747, 0x0000031C, 0x00001CDC, 0xFFFFF747, 0x0000031C },
+ { 0x0213F0FE99021944, 0x000036CB, 0xFFFFE6EE, 0x00000596, 0x00002096, 0xFFFFF3C2, 0x000003BB, 0x00002096, 0xFFFFF3C2, 0x000003BB },
+ { 0x0213F0FE990C48C4, 0x00003172, 0xFFFFEAC1, 0x000004F4, 0x00001C87, 0xFFFFF6CD, 0x00000337, 0x00001C87, 0xFFFFF6CD, 0x00000337 },
+ { 0x0213F0FD42D24864, 0x00004A18, 0xFFFFDB34, 0x00000758, 0x0000213C, 0xFFFFF3A2, 0x000003AC, 0x0000213C, 0xFFFFF3A2, 0x000003AC },
+ { 0x0213F0FE99022104, 0x000031F3, 0xFFFFEB73, 0x000004C6, 0x00001B23, 0xFFFFF7CB, 0x0000031A, 0x00001B23, 0xFFFFF7CB, 0x0000031A },
+ { 0x0213F0FE990A2924, 0x000031C0, 0xFFFFEABA, 0x000004F7, 0x00001A5A, 0xFFFFF845, 0x000002FF, 0x00001A5A, 0xFFFFF845, 0x000002FF },
+ { 0x0213F0FE99104944, 0x00003B77, 0xFFFFE3B3, 0x00000623, 0x00001BCA, 0xFFFFF6F8, 0x00000333, 0x00001BCA, 0xFFFFF6F8, 0x00000333 },
+ { 0x0213F0FE990A3944, 0x000035AF, 0xFFFFE76D, 0x00000588, 0x00001C16, 0xFFFFF6AB, 0x00000341, 0x00001C16, 0xFFFFF6AB, 0x00000341 },
+ { 0x0213EA94DE0438C4, 0x000032AD, 0xFFFFEA8E, 0x000004F8, 0x00001A3A, 0xFFFFF832, 0x0000030E, 0x00001A3A, 0xFFFFF832, 0x0000030E },
+ { 0x0213F0FE99104884, 0x00002E92, 0xFFFFEBD2, 0x000004DA, 0x00001E04, 0xFFFFF51E, 0x0000038A, 0x00001E04, 0xFFFFF51E, 0x0000038A },
+ { 0x0213F0FD42D440A4, 0x00003E57, 0xFFFFE0F7, 0x0000068F, 0x000021F1, 0xFFFFF1C6, 0x00000411, 0x000021F1, 0xFFFFF1C6, 0x00000411 },
+ { 0x0213F0FE990821A4, 0x00003598, 0xFFFFE8BB, 0x00000535, 0x00001B62, 0xFFFFF764, 0x00000326, 0x00001B62, 0xFFFFF764, 0x00000326 },
+ { 0x0213F0FE990A3884, 0x00002B15, 0xFFFFEDEC, 0x00000487, 0x00001E8B, 0xFFFFF4AB, 0x0000039F, 0x00001E8B, 0xFFFFF4AB, 0x0000039F },
+ { 0x0213EA94DE060904, 0x0000267E, 0xFFFFF1A7, 0x000003E1, 0x000021C1, 0xFFFFF2E9, 0x000003EA, 0x000021C1, 0xFFFFF2E9, 0x000003EA },
+ { 0x0213EA94DE0239A4, 0x00002ED7, 0xFFFFEC88, 0x000004A6, 0x00001DEC, 0xFFFFF57C, 0x00000378, 0x00001DEC, 0xFFFFF57C, 0x00000378 },
+ { 0x0213EA94DE0441A4, 0x00003365, 0xFFFFE946, 0x00000536, 0x000019E9, 0xFFFFF7E0, 0x0000031D, 0x000019E9, 0xFFFFF7E0, 0x0000031D },
+ { 0x0213F0FE991818E4, 0x000029A4, 0xFFFFF0FD, 0x000003FE, 0x0000163F, 0xFFFFFB68, 0x00000299, 0x0000163F, 0xFFFFFB68, 0x00000299 },
+ { 0x0213EA94DE021904, 0x0000348D, 0xFFFFE9F7, 0x00000509, 0x000017A0, 0xFFFFFA59, 0x000002B6, 0x000017A0, 0xFFFFFA59, 0x000002B6 },
+ { 0x0213F0FE990610C4, 0x00003144, 0xFFFFEB23, 0x000004D9, 0x00001C9B, 0xFFFFF664, 0x00000351, 0x00001C9B, 0xFFFFF664, 0x00000351 },
+ { 0x0213EA94DE0620E4, 0x00002E95, 0xFFFFEE1A, 0x00000463, 0x00001707, 0xFFFFFAB7, 0x000002B3, 0x00001707, 0xFFFFFAB7, 0x000002B3 },
+ { 0x0213F0FD42D41864, 0x0000489C, 0xFFFFDA43, 0x000007AC, 0x00002866, 0xFFFFED6B, 0x000004D0, 0x00002866, 0xFFFFED6B, 0x000004D0 },
+ { 0x0213F0FE99161844, 0x00002895, 0xFFFFF10A, 0x0000040A, 0x000013E9, 0xFFFFFC9F, 0x0000026E, 0x000013E9, 0xFFFFFC9F, 0x0000026E },
+ { 0x0213F0FE99061964, 0x000033A0, 0xFFFFE9B1, 0x00000510, 0x00001D96, 0xFFFFF5AE, 0x0000036F, 0x00001D96, 0xFFFFF5AE, 0x0000036F },
+ { 0x0213F0FE99083984, 0x0000327C, 0xFFFFEAEA, 0x000004DD, 0x00001D45, 0xFFFFF649, 0x00000356, 0x00001D45, 0xFFFFF649, 0x00000356 },
+ { 0x0213EA94DE0248A4, 0x000031DF, 0xFFFFE9AB, 0x0000052F, 0x000019C8, 0xFFFFF7B7, 0x00000321, 0x000019C8, 0xFFFFF7B7, 0x00000321 },
+ { 0x0213F0FE991640A4, 0x00002BCC, 0xFFFFEEF4, 0x0000045C, 0x000015CD, 0xFFFFFB58, 0x0000029E, 0x000015CD, 0xFFFFFB58, 0x0000029E },
+ { 0x0213F0FE990638E4, 0x00003534, 0xFFFFEA10, 0x000004EB, 0x00001BB6, 0xFFFFF7B9, 0x00000314, 0x00001BB6, 0xFFFFF7B9, 0x00000314 },
+ { 0x0213F0FE99041984, 0x00002F4F, 0xFFFFEC35, 0x000004B9, 0x0000205D, 0xFFFFF47F, 0x00000392, 0x0000205D, 0xFFFFF47F, 0x00000392 },
+ { 0x0213F0FE990C20A4, 0x00003295, 0xFFFFEB1C, 0x000004D6, 0x000019C1, 0xFFFFF931, 0x000002D5, 0x000019C1, 0xFFFFF931, 0x000002D5 },
+ { 0x0213F0FE99024144, 0x00003557, 0xFFFFE7F7, 0x00000568, 0x00002342, 0xFFFFF1F9, 0x00000405, 0x00002342, 0xFFFFF1F9, 0x00000405 },
+ { 0x0213F0FE990450C4, 0x00003487, 0xFFFFE872, 0x0000055D, 0x000019D7, 0xFFFFF823, 0x0000030C, 0x000019D7, 0xFFFFF823, 0x0000030C },
+ { 0x0213F0FE992C3944, 0x0000378F, 0xFFFFE7A6, 0x00000566, 0x00001875, 0xFFFFFA04, 0x000002AF, 0x00001875, 0xFFFFFA04, 0x000002AF },
+ { 0x0213EA94DE0230E4, 0x00002A67, 0xFFFFF157, 0x000003DD, 0x000017BD, 0xFFFFFA53, 0x000002D1, 0x000017BD, 0xFFFFFA53, 0x000002D1 },
+ { 0x0213F0FD42D220E4, 0x000030B5, 0xFFFFEB32, 0x000004D9, 0x00002129, 0xFFFFF38A, 0x000003BB, 0x00002129, 0xFFFFF38A, 0x000003BB },
+ { 0x0213F0FE990610A4, 0x00003786, 0xFFFFE703, 0x00000584, 0x00001D63, 0xFFFFF5DC, 0x00000367, 0x00001D63, 0xFFFFF5DC, 0x00000367 },
+ { 0x0213F0FD42DA20C4, 0x0000346A, 0xFFFFE93E, 0x0000052C, 0x00001B27, 0xFFFFF79D, 0x0000031F, 0x00001B27, 0xFFFFF79D, 0x0000031F },
+ { 0x0213F0FE990E3024, 0x0000294E, 0xFFFFF0A5, 0x00000409, 0x00001928, 0xFFFFF93B, 0x000002E6, 0x00001928, 0xFFFFF93B, 0x000002E6 },
+ { 0x0213F0FD42D410C4, 0x00003E09, 0xFFFFE0FF, 0x00000694, 0x000025A0, 0xFFFFEF0F, 0x0000048F, 0x000025A0, 0xFFFFEF0F, 0x0000048F },
+ { 0x0213F0FE990A2964, 0x00003197, 0xFFFFEA06, 0x00000520, 0x00001B42, 0xFFFFF73B, 0x0000032A, 0x00001B42, 0xFFFFF73B, 0x0000032A },
+ { 0x0213F0FE99161864, 0x000022CB, 0xFFFFF3FC, 0x000003A3, 0x00001449, 0xFFFFFBD0, 0x00000297, 0x00001449, 0xFFFFFBD0, 0x00000297 },
+ { 0x0213F0FD42D82944, 0x00002A79, 0xFFFFEFD2, 0x00000433, 0x00001585, 0xFFFFFB92, 0x0000028E, 0x00001585, 0xFFFFFB92, 0x0000028E },
+ { 0x0213F0FE990C4184, 0x00003249, 0xFFFFEA92, 0x000004F4, 0x000019CB, 0xFFFFF8CF, 0x000002E1, 0x000019CB, 0xFFFFF8CF, 0x000002E1 },
+ { 0x0213EA94DE0218A4, 0x00002CEA, 0xFFFFEE46, 0x00000463, 0x00001A5E, 0xFFFFF83C, 0x0000030D, 0x00001A5E, 0xFFFFF83C, 0x0000030D },
+ { 0x0213F0FD42DC5144, 0x00003AE2, 0xFFFFE422, 0x00000600, 0x00001C65, 0xFFFFF62F, 0x0000034B, 0x00001C65, 0xFFFFF62F, 0x0000034B },
+ { 0x0213F0FE99181184, 0x000026A0, 0xFFFFF1C2, 0x000003F8, 0x000010E5, 0xFFFFFE56, 0x0000022A, 0x000010E5, 0xFFFFFE56, 0x0000022A },
+ { 0x0213F0FE992829A4, 0x00002A7B, 0xFFFFF063, 0x00000417, 0x000016FC, 0xFFFFFAD7, 0x000002B1, 0x000016FC, 0xFFFFFAD7, 0x000002B1 },
+ { 0x0213F0FE993210C4, 0x00003092, 0xFFFFEAB9, 0x00000507, 0x00001AE3, 0xFFFFF783, 0x00000323, 0x00001AE3, 0xFFFFF783, 0x00000323 },
+ { 0x0213F0FE990438E4, 0x00003265, 0xFFFFEBE8, 0x000004AA, 0x00001D65, 0xFFFFF73F, 0x00000321, 0x00001D65, 0xFFFFF73F, 0x00000321 },
+ { 0x0213EA94DE023084, 0x00002F14, 0xFFFFECC2, 0x000004A4, 0x00001A8D, 0xFFFFF7F3, 0x0000031D, 0x00001A8D, 0xFFFFF7F3, 0x0000031D },
+ { 0x0213F0FD42DC10E4, 0x000035FB, 0xFFFFE6D3, 0x000005AC, 0x00001B19, 0xFFFFF712, 0x00000338, 0x00001B19, 0xFFFFF712, 0x00000338 },
+ { 0x0213F0FD42DA2124, 0x00003519, 0xFFFFE8CC, 0x0000053A, 0x00001A0F, 0xFFFFF86E, 0x000002F5, 0x00001A0F, 0xFFFFF86E, 0x000002F5 },
+ { 0x0213F0FE992C2144, 0x0000364C, 0xFFFFE879, 0x00000541, 0x00001A42, 0xFFFFF8BA, 0x000002E2, 0x00001A42, 0xFFFFF8BA, 0x000002E2 },
+ { 0x0213EA94DE0218C4, 0x000029BA, 0xFFFFF09A, 0x00000408, 0x00001986, 0xFFFFF8D9, 0x000002FE, 0x00001986, 0xFFFFF8D9, 0x000002FE },
+ { 0x0213F0FD42DA38E4, 0x00003507, 0xFFFFE961, 0x00000518, 0x00001B79, 0xFFFFF775, 0x00000325, 0x00001B79, 0xFFFFF775, 0x00000325 },
+ { 0x0213F0FD42DC3184, 0x00003AD5, 0xFFFFE415, 0x00000613, 0x00001CB4, 0xFFFFF66D, 0x00000348, 0x00001CB4, 0xFFFFF66D, 0x00000348 },
+ { 0x0213F0FE991640E4, 0x000023D1, 0xFFFFF42B, 0x0000038F, 0x00001546, 0xFFFFFBA0, 0x0000029F, 0x00001546, 0xFFFFFBA0, 0x0000029F },
+ { 0x0213F0FE990A1924, 0x0000399E, 0xFFFFE518, 0x000005E7, 0x00001990, 0xFFFFF871, 0x000002FB, 0x00001990, 0xFFFFF871, 0x000002FB },
+ { 0x0213F0FD42D82964, 0x00002EDE, 0xFFFFEC93, 0x000004B8, 0x0000152C, 0xFFFFFBB3, 0x0000027E, 0x0000152C, 0xFFFFFBB3, 0x0000027E },
+ { 0x0213EA94DE042964, 0x00003140, 0xFFFFEBC9, 0x000004BB, 0x000016BE, 0xFFFFFB0A, 0x00000288, 0x000016BE, 0xFFFFFB0A, 0x00000288 },
+ { 0x0213F0FE99064064, 0x000030F6, 0xFFFFEB89, 0x000004CD, 0x0000185D, 0xFFFFF95A, 0x000002D9, 0x0000185D, 0xFFFFF95A, 0x000002D9 },
+ { 0x0213F0FE99023844, 0x0000389C, 0xFFFFE65A, 0x000005A2, 0x0000195D, 0xFFFFF8C8, 0x000002E8, 0x0000195D, 0xFFFFF8C8, 0x000002E8 },
+ { 0x0213F0FE99042104, 0x0000362B, 0xFFFFE9EC, 0x000004F6, 0x00001605, 0xFFFFFC1C, 0x00000263, 0x00001605, 0xFFFFFC1C, 0x00000263 },
+ { 0x0213F0FE992A1964, 0x00002946, 0xFFFFF04F, 0x00000426, 0x000015BA, 0xFFFFFB2F, 0x000002A3, 0x000015BA, 0xFFFFFB2F, 0x000002A3 },
+ { 0x0213F0FE99082184, 0x0000368E, 0xFFFFE837, 0x0000054A, 0x000017D7, 0xFFFFF9EB, 0x000002BA, 0x000017D7, 0xFFFFF9EB, 0x000002BA },
+ { 0x0213F0FD42DA2844, 0x00002E74, 0xFFFFEBE8, 0x000004DA, 0x00001DD6, 0xFFFFF57E, 0x00000379, 0x00001DD6, 0xFFFFF57E, 0x00000379 },
+ { 0x0213F0FE99041944, 0x0000322D, 0xFFFFEAA8, 0x000004F5, 0x00001B55, 0xFFFFF7DD, 0x0000030B, 0x00001B55, 0xFFFFF7DD, 0x0000030B },
+ { 0x0213F0FE99181904, 0x00002A29, 0xFFFFF07B, 0x00000416, 0x00001671, 0xFFFFFB3E, 0x0000029F, 0x00001671, 0xFFFFFB3E, 0x0000029F },
+ { 0x0213F0FD42DA2104, 0x000030F6, 0xFFFFEB89, 0x000004CD, 0x00001815, 0xFFFFF9AE, 0x000002C9, 0x00001815, 0xFFFFF9AE, 0x000002C9 },
+ { 0x0213F0FE990E10E4, 0x0000265F, 0xFFFFF1CB, 0x000003D5, 0x00001ED2, 0xFFFFF539, 0x0000037A, 0x00001ED2, 0xFFFFF539, 0x0000037A },
+ { 0x0213F0FE99162184, 0x000027A8, 0xFFFFF10D, 0x00000413, 0x000014B5, 0xFFFFFBA1, 0x00000299, 0x000014B5, 0xFFFFFBA1, 0x00000299 },
+ { 0x0213F0FE99043064, 0x00002CEE, 0xFFFFEDF6, 0x00000476, 0x00001A99, 0xFFFFF83E, 0x00000305, 0x00001A99, 0xFFFFF83E, 0x00000305 },
+ { 0x0213F0FE990640C4, 0x0000346C, 0xFFFFEA17, 0x000004EF, 0x00001D38, 0xFFFFF69F, 0x0000033D, 0x00001D38, 0xFFFFF69F, 0x0000033D },
+ { 0x0213F0FD42DA2944, 0x00002DBB, 0xFFFFED35, 0x00000490, 0x000018C1, 0xFFFFF930, 0x000002DA, 0x000018C1, 0xFFFFF930, 0x000002DA },
+ { 0x0213F0FE99042924, 0x000038DF, 0xFFFFE8A7, 0x0000051E, 0x00001B59, 0xFFFFF915, 0x000002D3, 0x00001B59, 0xFFFFF915, 0x000002D3 },
+ { 0x0213F0FE99080944, 0x00003384, 0xFFFFE979, 0x00000524, 0x00001AF3, 0xFFFFF74C, 0x0000032F, 0x00001AF3, 0xFFFFF74C, 0x0000032F },
+ { 0x0213F0FE99181864, 0x0000258B, 0xFFFFF2AE, 0x000003CB, 0x0000190C, 0xFFFFF93E, 0x000002EF, 0x0000190C, 0xFFFFF93E, 0x000002EF },
+ { 0x0213F0FE99103884, 0x000034F1, 0xFFFFE84B, 0x0000055E, 0x00001CB8, 0xFFFFF670, 0x0000034A, 0x00001CB8, 0xFFFFF670, 0x0000034A },
+ { 0x0213F0FE990C2104, 0x000030FB, 0xFFFFECD2, 0x00000488, 0x00001BF4, 0xFFFFF821, 0x00000302, 0x00001BF4, 0xFFFFF821, 0x00000302 },
+ { 0x0213F0FE99063044, 0x000036A6, 0xFFFFE815, 0x00000556, 0x000018FD, 0xFFFFF925, 0x000002DF, 0x000018FD, 0xFFFFF925, 0x000002DF },
+ { 0x0213EA94DE023044, 0x0000302A, 0xFFFFEB79, 0x000004E0, 0x00001C11, 0xFFFFF694, 0x00000358, 0x00001C11, 0xFFFFF694, 0x00000358 },
+ { 0x0213F0FE99181124, 0x00002555, 0xFFFFF2C4, 0x000003CB, 0x000017E3, 0xFFFFFA1F, 0x000002CB, 0x000017E3, 0xFFFFFA1F, 0x000002CB },
+ { 0x0213F0FE990A3164, 0x000032A3, 0xFFFFE933, 0x00000544, 0x000019D3, 0xFFFFF81A, 0x00000306, 0x000019D3, 0xFFFFF81A, 0x00000306 },
+ { 0x0213F0FD42D85104, 0x00002B91, 0xFFFFED81, 0x000004A9, 0x0000158B, 0xFFFFFAB9, 0x000002AC, 0x0000158B, 0xFFFFFAB9, 0x000002AC },
+ { 0x0213F0FE990E20C4, 0x00003537, 0xFFFFE912, 0x0000052C, 0x00001C8A, 0xFFFFF754, 0x0000031B, 0x00001C8A, 0xFFFFF754, 0x0000031B },
+ { 0x0213EA94DE063184, 0x000032E1, 0xFFFFEA5A, 0x000004F9, 0x000017B4, 0xFFFFF9D9, 0x000002C2, 0x000017B4, 0xFFFFF9D9, 0x000002C2 },
+ { 0x0213F0FD42D210C4, 0x00003B76, 0xFFFFE330, 0x00000636, 0x000026FB, 0xFFFFEF06, 0x00000481, 0x000026FB, 0xFFFFEF06, 0x00000481 },
+ { 0x0213F0FE99042144, 0x0000320C, 0xFFFFEB84, 0x000004C3, 0x00001A3A, 0xFFFFF8E9, 0x000002DF, 0x00001A3A, 0xFFFFF8E9, 0x000002DF },
+ { 0x0213F0FE99023984, 0x0000317D, 0xFFFFEA1F, 0x00000515, 0x00002100, 0xFFFFF31B, 0x000003DD, 0x00002100, 0xFFFFF31B, 0x000003DD },
+ { 0x0213F0FD42D43164, 0x00003DCB, 0xFFFFE0B4, 0x000006B4, 0x00002160, 0xFFFFF269, 0x000003F0, 0x00002160, 0xFFFFF269, 0x000003F0 },
+ { 0x0213F0FE991618C4, 0x00002737, 0xFFFFF218, 0x000003E1, 0x000015B5, 0xFFFFFB8F, 0x0000029C, 0x000015B5, 0xFFFFFB8F, 0x0000029C },
+ { 0x0213EA94DE023184, 0x0000318F, 0xFFFFEB3F, 0x000004D8, 0x00001938, 0xFFFFF8E9, 0x000002EB, 0x00001938, 0xFFFFF8E9, 0x000002EB },
+ { 0x0213F0FE991048C4, 0x000031BD, 0xFFFFE9DE, 0x00000527, 0x000018A7, 0xFFFFF8CA, 0x000002ED, 0x000018A7, 0xFFFFF8CA, 0x000002ED },
+ { 0x0213F0FD42DA3884, 0x00002F77, 0xFFFFEC2F, 0x000004B4, 0x00001D25, 0xFFFFF61B, 0x0000035D, 0x00001D25, 0xFFFFF61B, 0x0000035D },
+ { 0x0213F0FE990E4904, 0x00002CCA, 0xFFFFEDB3, 0x0000047C, 0x00001FBD, 0xFFFFF4A7, 0x00000391, 0x00001FBD, 0xFFFFF4A7, 0x00000391 },
+ { 0x0213F0FD42D438A4, 0x00003FF6, 0xFFFFE058, 0x000006A2, 0x000024CD, 0xFFFFF026, 0x00000452, 0x000024CD, 0xFFFFF026, 0x00000452 },
+ { 0x0213F0FE990A38E4, 0x00003161, 0xFFFFEAC8, 0x000004F3, 0x00001BB6, 0xFFFFF72A, 0x0000032B, 0x00001BB6, 0xFFFFF72A, 0x0000032B },
+ { 0x0213F0FD42D838A4, 0x00002EA0, 0xFFFFECA6, 0x000004B7, 0x000018C2, 0xFFFFF94E, 0x000002E1, 0x000018C2, 0xFFFFF94E, 0x000002E1 },
+ { 0x0213F0FE99182184, 0x00002F62, 0xFFFFEC9E, 0x000004B8, 0x00001531, 0xFFFFFBCD, 0x00000285, 0x00001531, 0xFFFFFBCD, 0x00000285 },
+ { 0x0213F0FE990440A4, 0x00003013, 0xFFFFEBD6, 0x000004C2, 0x00001B01, 0xFFFFF802, 0x000002FF, 0x00001B01, 0xFFFFF802, 0x000002FF },
+ { 0x0213F0FE99183064, 0x00002972, 0xFFFFF08D, 0x00000417, 0x00001A32, 0xFFFFF8A4, 0x00000305, 0x00001A32, 0xFFFFF8A4, 0x00000305 },
+ { 0x0213F0FD42D820E4, 0x00002E95, 0xFFFFED94, 0x00000487, 0x00001529, 0xFFFFFC26, 0x00000271, 0x00001529, 0xFFFFFC26, 0x00000271 },
+ { 0x0213F0FE990A1084, 0x00002D6A, 0xFFFFEC79, 0x000004C1, 0x00001AE2, 0xFFFFF725, 0x00000337, 0x00001AE2, 0xFFFFF725, 0x00000337 },
+ { 0x0213F0FE99021884, 0x000036B4, 0xFFFFE704, 0x00000591, 0x00001E7E, 0xFFFFF51C, 0x00000383, 0x00001E7E, 0xFFFFF51C, 0x00000383 },
+ { 0x0213F0FE99041844, 0x00002A6F, 0xFFFFEF70, 0x00000443, 0x00001BAA, 0xFFFFF752, 0x00000336, 0x00001BAA, 0xFFFFF752, 0x00000336 },
+ { 0x0213F0FE99183944, 0x00002C66, 0xFFFFEF5F, 0x0000043A, 0x000019F7, 0xFFFFF931, 0x000002EC, 0x000019F7, 0xFFFFF931, 0x000002EC },
+ { 0x0213EA94DE0631C4, 0x00003852, 0xFFFFE6AB, 0x00000590, 0x000019C1, 0xFFFFF8B1, 0x000002E5, 0x000019C1, 0xFFFFF8B1, 0x000002E5 },
+ { 0x0213F0FD42DA3124, 0x00003521, 0xFFFFE932, 0x00000523, 0x000018A9, 0xFFFFF96B, 0x000002D0, 0x000018A9, 0xFFFFF96B, 0x000002D0 },
+ { 0x0213F0FE99062164, 0x000031B9, 0xFFFFEB36, 0x000004D0, 0x00001D65, 0xFFFFF612, 0x0000035D, 0x00001D65, 0xFFFFF612, 0x0000035D },
+ { 0x0213F0FD42D41064, 0x00003ED0, 0xFFFFE135, 0x00000679, 0x00002351, 0xFFFFF0FE, 0x00000433, 0x00002351, 0xFFFFF0FE, 0x00000433 },
+ { 0x0213F0FE990A20E4, 0x000033ED, 0xFFFFE91A, 0x00000541, 0x00001C93, 0xFFFFF6A0, 0x0000034A, 0x00001C93, 0xFFFFF6A0, 0x0000034A },
+ { 0x0213EA94DE021844, 0x0000356F, 0xFFFFE8F7, 0x00000530, 0x000016BF, 0xFFFFFA85, 0x000002AB, 0x000016BF, 0xFFFFFA85, 0x000002AB },
+ { 0x0213F0FE991840E4, 0x00002304, 0xFFFFF4F3, 0x00000364, 0x000017CC, 0xFFFFFA41, 0x000002CA, 0x000017CC, 0xFFFFFA41, 0x000002CA },
+ { 0x0213F0FE99161164, 0x00002887, 0xFFFFEFD7, 0x00000450, 0x00001474, 0xFFFFFB94, 0x00000299, 0x00001474, 0xFFFFFB94, 0x00000299 },
+ { 0x0213F0FE99063064, 0x00003D0B, 0xFFFFE416, 0x000005EF, 0x00001C7E, 0xFFFFF71D, 0x00000325, 0x00001C7E, 0xFFFFF71D, 0x00000325 },
+ { 0x0213F0FE990810E4, 0x00003185, 0xFFFFEAFA, 0x000004E4, 0x00001A12, 0xFFFFF83C, 0x00000303, 0x00001A12, 0xFFFFF83C, 0x00000303 },
+ { 0x0213F0FE990A1944, 0x00003032, 0xFFFFEAE6, 0x000004FC, 0x00001B2A, 0xFFFFF73F, 0x0000032B, 0x00001B2A, 0xFFFFF73F, 0x0000032B },
+ { 0x0213F0FD42D838C4, 0x00002691, 0xFFFFF22D, 0x000003D6, 0x00001700, 0xFFFFFA6E, 0x000002C0, 0x00001700, 0xFFFFFA6E, 0x000002C0 },
+ { 0x0213F0FE990218A4, 0x00002B2F, 0xFFFFEEC4, 0x0000044B, 0x0000215F, 0xFFFFF33F, 0x000003D2, 0x0000215F, 0xFFFFF33F, 0x000003D2 },
+ { 0x0213F0FE990A4184, 0x000034AA, 0xFFFFE706, 0x000005B1, 0x00001B28, 0xFFFFF6B5, 0x00000349, 0x00001B28, 0xFFFFF6B5, 0x00000349 },
+ { 0x0213F0FD42DA2964, 0x0000307E, 0xFFFFEB38, 0x000004E6, 0x00001A22, 0xFFFFF83F, 0x00000300, 0x00001A22, 0xFFFFF83F, 0x00000300 },
+ { 0x0213F0FE990618A4, 0x000038D6, 0xFFFFE6D8, 0x0000057C, 0x00001B24, 0xFFFFF7E4, 0x00000307, 0x00001B24, 0xFFFFF7E4, 0x00000307 },
+ { 0x0213F0FE99183044, 0x00002757, 0xFFFFF1E8, 0x000003DD, 0x000017F5, 0xFFFFFA15, 0x000002C8, 0x000017F5, 0xFFFFFA15, 0x000002C8 },
+ { 0x0213F0FE99083184, 0x000031FC, 0xFFFFEB3E, 0x000004CE, 0x00001B4C, 0xFFFFF7AD, 0x00000319, 0x00001B4C, 0xFFFFF7AD, 0x00000319 },
+ { 0x0213F0FE99301864, 0x00002933, 0xFFFFF073, 0x0000040E, 0x00001C3C, 0xFFFFF701, 0x0000033C, 0x00001C3C, 0xFFFFF701, 0x0000033C },
+ { 0x0213F0FD42D218A4, 0x000040BB, 0xFFFFE066, 0x0000069A, 0x0000257F, 0xFFFFF08A, 0x00000435, 0x0000257F, 0xFFFFF08A, 0x00000435 },
+ { 0x0213F0FE991010A4, 0x0000305B, 0xFFFFEB9B, 0x000004CB, 0x00001996, 0xFFFFF846, 0x00000308, 0x00001996, 0xFFFFF846, 0x00000308 },
+ { 0x0213F0FE99064884, 0x000039C0, 0xFFFFE5D3, 0x000005B0, 0x00001A8D, 0xFFFFF7DA, 0x00000313, 0x00001A8D, 0xFFFFF7DA, 0x00000313 },
+ { 0x0213EA94DE0210A4, 0x00002E23, 0xFFFFED3F, 0x0000048F, 0x0000189D, 0xFFFFF94C, 0x000002DE, 0x0000189D, 0xFFFFF94C, 0x000002DE },
+ { 0x0213EA94DE021984, 0x0000332B, 0xFFFFE9F1, 0x00000516, 0x000018E6, 0xFFFFF8FE, 0x000002EC, 0x000018E6, 0xFFFFF8FE, 0x000002EC },
+ { 0x0213F0FE990838C4, 0x000034A0, 0xFFFFEA44, 0x000004E4, 0x00001ECD, 0xFFFFF5B4, 0x00000364, 0x00001ECD, 0xFFFFF5B4, 0x00000364 },
+ { 0x0213F0FD42D24104, 0x0000448C, 0xFFFFDF34, 0x000006A8, 0x0000231C, 0xFFFFF286, 0x000003D9, 0x0000231C, 0xFFFFF286, 0x000003D9 },
+ { 0x0213EA94DE062144, 0x00002D8C, 0xFFFFEE65, 0x00000456, 0x000018B1, 0xFFFFF9C8, 0x000002C8, 0x000018B1, 0xFFFFF9C8, 0x000002C8 },
+ { 0x0213F0FE99061904, 0x00003527, 0xFFFFE9BF, 0x000004FD, 0x00001D23, 0xFFFFF69F, 0x00000342, 0x00001D23, 0xFFFFF69F, 0x00000342 },
+ { 0x0213F0FD42DC38A4, 0x00002C51, 0xFFFFEDC3, 0x00000483, 0x00001BE0, 0xFFFFF720, 0x0000032D, 0x00001BE0, 0xFFFFF720, 0x0000032D },
+ { 0x0213F0FE990A3044, 0x00002C6C, 0xFFFFECEB, 0x000004B7, 0x00001C86, 0xFFFFF5E7, 0x00000371, 0x00001C86, 0xFFFFF5E7, 0x00000371 },
+ { 0x0213F0FE99045144, 0x000037CF, 0xFFFFE6BE, 0x00000599, 0x000018CD, 0xFFFFF967, 0x000002C7, 0x000018CD, 0xFFFFF967, 0x000002C7 },
+ { 0x0213F0FE99103164, 0x00002E6F, 0xFFFFED1D, 0x0000048E, 0x00001ADC, 0xFFFFF7F4, 0x0000030E, 0x00001ADC, 0xFFFFF7F4, 0x0000030E },
+ { 0x0213F0FD42D42984, 0x00003FF3, 0xFFFFDF13, 0x000006F9, 0x000025BF, 0xFFFFEEEE, 0x00000497, 0x000025BF, 0xFFFFEEEE, 0x00000497 },
+ { 0x0213F0FD42DC5104, 0x00004135, 0xFFFFDF97, 0x000006CC, 0x00001D52, 0xFFFFF541, 0x00000383, 0x00001D52, 0xFFFFF541, 0x00000383 },
+ { 0x0213F0FD42DC20E4, 0x00002EA9, 0xFFFFEDDB, 0x0000045F, 0x0000197C, 0xFFFFF8E1, 0x000002F0, 0x0000197C, 0xFFFFF8E1, 0x000002F0 },
+ { 0x0213EA94DE043084, 0x0000345C, 0xFFFFE922, 0x00000532, 0x00001922, 0xFFFFF8C7, 0x000002F1, 0x00001922, 0xFFFFF8C7, 0x000002F1 },
+ { 0x0213F0FE99064124, 0x000035C4, 0xFFFFE8FE, 0x00000521, 0x00001C87, 0xFFFFF6F3, 0x00000330, 0x00001C87, 0xFFFFF6F3, 0x00000330 },
+ { 0x0213F0FD42D83164, 0x00002888, 0xFFFFF08A, 0x0000041E, 0x0000150F, 0xFFFFFB87, 0x00000291, 0x0000150F, 0xFFFFFB87, 0x00000291 },
+ { 0x0213F0FE990A1124, 0x000035E9, 0xFFFFE657, 0x000005CC, 0x00001BD6, 0xFFFFF664, 0x00000355, 0x00001BD6, 0xFFFFF664, 0x00000355 },
+ { 0x0213F0FE991648E4, 0x00002F94, 0xFFFFEBD0, 0x000004E5, 0x00001333, 0xFFFFFCA7, 0x00000266, 0x00001333, 0xFFFFFCA7, 0x00000266 },
+ { 0x0213F0FE99181964, 0x000029E7, 0xFFFFF009, 0x00000433, 0x0000144A, 0xFFFFFC37, 0x0000027D, 0x0000144A, 0xFFFFFC37, 0x0000027D },
+ { 0x0213F0FE992C1944, 0x00003418, 0xFFFFE979, 0x00000521, 0x00001D33, 0xFFFFF66B, 0x0000034A, 0x00001D33, 0xFFFFF66B, 0x0000034A },
+ { 0x0213EA94DE0440E4, 0x00003656, 0xFFFFE79D, 0x0000057A, 0x000017C2, 0xFFFFF992, 0x000002D4, 0x000017C2, 0xFFFFF992, 0x000002D4 },
+ { 0x0213F0FE990C40C4, 0x00002EB2, 0xFFFFECFE, 0x00000493, 0x00001F2A, 0xFFFFF543, 0x0000037B, 0x00001F2A, 0xFFFFF543, 0x0000037B },
+ { 0x0213F0FE99021124, 0x00002FC1, 0xFFFFEB3F, 0x000004E8, 0x00001CD0, 0xFFFFF5F7, 0x00000364, 0x00001CD0, 0xFFFFF5F7, 0x00000364 },
+ { 0x0213F0FE990C1124, 0x0000307B, 0xFFFFEB66, 0x000004DE, 0x00001953, 0xFFFFF8ED, 0x000002E4, 0x00001953, 0xFFFFF8ED, 0x000002E4 },
+ { 0x0213F0FD42DA1884, 0x00002CAA, 0xFFFFED07, 0x000004AC, 0x0000251C, 0xFFFFF086, 0x0000044D, 0x0000251C, 0xFFFFF086, 0x0000044D },
+ { 0x0213EA94DE043944, 0x00002C94, 0xFFFFEE5F, 0x0000045B, 0x000018D7, 0xFFFFF900, 0x000002EB, 0x000018D7, 0xFFFFF900, 0x000002EB },
+ { 0x0213F0FE99021864, 0x000031F1, 0xFFFFE9BE, 0x0000052E, 0x00001DDF, 0xFFFFF558, 0x00000380, 0x00001DDF, 0xFFFFF558, 0x00000380 },
+ { 0x0213F0FE990E50C4, 0x00002603, 0xFFFFF1E9, 0x000003DA, 0x00001B37, 0xFFFFF75A, 0x0000032F, 0x00001B37, 0xFFFFF75A, 0x0000032F },
+ { 0x0213F0FD42DA3044, 0x00003992, 0xFFFFE4F9, 0x000005EB, 0x00001775, 0xFFFFF9B8, 0x000002C2, 0x00001775, 0xFFFFF9B8, 0x000002C2 },
+ { 0x0213F0FE99184964, 0x000029DA, 0xFFFFF052, 0x0000041F, 0x000016E2, 0xFFFFFA99, 0x000002BB, 0x000016E2, 0xFFFFFA99, 0x000002BB },
+ { 0x0213F0FE99101064, 0x00002FF2, 0xFFFFEB8F, 0x000004DF, 0x00001AF6, 0xFFFFF7A1, 0x00000321, 0x00001AF6, 0xFFFFF7A1, 0x00000321 },
+ { 0x0213F0FE991608E4, 0x00002590, 0xFFFFF222, 0x000003EE, 0x0000130B, 0xFFFFFCC9, 0x00000268, 0x0000130B, 0xFFFFFCC9, 0x00000268 },
+ { 0x0213F0FE99024064, 0x000038A2, 0xFFFFE65F, 0x000005A2, 0x000018B1, 0xFFFFF917, 0x000002E1, 0x000018B1, 0xFFFFF917, 0x000002E1 },
+ { 0x0213F0FD42DC48E4, 0x000035FD, 0xFFFFE73C, 0x0000058D, 0x00001BB3, 0xFFFFF6E1, 0x00000337, 0x00001BB3, 0xFFFFF6E1, 0x00000337 },
+ { 0x0213F0FE991038C4, 0x00002AB7, 0xFFFFEF98, 0x00000429, 0x00001F35, 0xFFFFF539, 0x0000037C, 0x00001F35, 0xFFFFF539, 0x0000037C },
+ { 0x0213F0FE990A0944, 0x000034BA, 0xFFFFE73D, 0x000005A6, 0x000018A6, 0xFFFFF888, 0x000002FB, 0x000018A6, 0xFFFFF888, 0x000002FB },
+ { 0x0213F0FE99063844, 0x000032EA, 0xFFFFEA78, 0x000004F4, 0x00001AB6, 0xFFFFF812, 0x00000308, 0x00001AB6, 0xFFFFF812, 0x00000308 },
+ { 0x0213F0FE990C3044, 0x00002BE9, 0xFFFFEE9A, 0x00000457, 0x00001942, 0xFFFFF8D2, 0x000002F2, 0x00001942, 0xFFFFF8D2, 0x000002F2 },
+ { 0x0213F0FE99105124, 0x00002FAB, 0xFFFFEB76, 0x000004E1, 0x00001DCA, 0xFFFFF57D, 0x00000378, 0x00001DCA, 0xFFFFF57D, 0x00000378 },
+ { 0x0213F0FE992E2844, 0x0000330A, 0xFFFFE9E1, 0x0000051B, 0x00001CC4, 0xFFFFF6DF, 0x00000335, 0x00001CC4, 0xFFFFF6DF, 0x00000335 },
+ { 0x0213F0FE991828A4, 0x000027D8, 0xFFFFF276, 0x000003BF, 0x0000178A, 0xFFFFFABF, 0x000002B5, 0x0000178A, 0xFFFFFABF, 0x000002B5 },
+ { 0x0213F0FD42DC3864, 0x0000340A, 0xFFFFE86D, 0x00000562, 0x00001B85, 0xFFFFF719, 0x0000032F, 0x00001B85, 0xFFFFF719, 0x0000032F },
+ { 0x0213EA94DE063084, 0x00003879, 0xFFFFE73F, 0x00000578, 0x0000161C, 0xFFFFFB6B, 0x00000281, 0x0000161C, 0xFFFFFB6B, 0x00000281 },
+ { 0x0213F0FE99184064, 0x00002879, 0xFFFFF0F8, 0x0000040A, 0x00001749, 0xFFFFFA37, 0x000002CC, 0x00001749, 0xFFFFFA37, 0x000002CC },
+ { 0x0213F0FE99043964, 0x00002C3A, 0xFFFFEEA0, 0x0000044F, 0x00001D57, 0xFFFFF6C2, 0x00000332, 0x00001D57, 0xFFFFF6C2, 0x00000332 },
+ { 0x0213EA94DE021964, 0x000035BB, 0xFFFFE90D, 0x0000052A, 0x000017D9, 0xFFFFF9F5, 0x000002C3, 0x000017D9, 0xFFFFF9F5, 0x000002C3 },
+ { 0x0213EA94DE041124, 0x000031F1, 0xFFFFEAD4, 0x000004ED, 0x00001F10, 0xFFFFF539, 0x0000037D, 0x00001F10, 0xFFFFF539, 0x0000037D },
+ { 0x0213F0FE99102824, 0x00002A1A, 0xFFFFEFAD, 0x00000430, 0x00001D47, 0xFFFFF62F, 0x0000035E, 0x00001D47, 0xFFFFF62F, 0x0000035E },
+ { 0x0213F0FE99164924, 0x00002AF0, 0xFFFFEEDC, 0x00000465, 0x0000145F, 0xFFFFFBEB, 0x00000281, 0x0000145F, 0xFFFFFBEB, 0x00000281 },
+ { 0x0213F0FE99183164, 0x00002657, 0xFFFFF2E0, 0x000003B6, 0x00001664, 0xFFFFFB37, 0x000002A2, 0x00001664, 0xFFFFFB37, 0x000002A2 },
+ { 0x0213F0FD42D03864, 0x00003183, 0xFFFFE9F1, 0x0000052B, 0x00002020, 0xFFFFF3CE, 0x000003C1, 0x00002020, 0xFFFFF3CE, 0x000003C1 },
+ { 0x0213F0FD42C628E4, 0x00003240, 0xFFFFEB65, 0x000004C7, 0x00002425, 0xFFFFF245, 0x000003F3, 0x00002425, 0xFFFFF245, 0x000003F3 },
+ { 0x0213EA94DE321104, 0x000023D0, 0xFFFFF400, 0x00000397, 0x00001345, 0xFFFFFD6B, 0x00000241, 0x00001345, 0xFFFFFD6B, 0x00000241 },
+ { 0x0213F0FD42CE38A4, 0x00003440, 0xFFFFE872, 0x0000055B, 0x00002247, 0xFFFFF296, 0x000003E8, 0x00002247, 0xFFFFF296, 0x000003E8 },
+ { 0x0213F0FD42D04904, 0x00003275, 0xFFFFE970, 0x00000538, 0x00001F94, 0xFFFFF429, 0x000003AD, 0x00001F94, 0xFFFFF429, 0x000003AD },
+ { 0x0213F0FD42C640A4, 0x00003918, 0xFFFFE5DA, 0x000005B6, 0x000024FC, 0xFFFFF106, 0x00000426, 0x000024FC, 0xFFFFF106, 0x00000426 },
+ { 0x0213EA94DE062044, 0x0000334B, 0xFFFFEA39, 0x000004FD, 0x00001983, 0xFFFFF8F6, 0x000002E2, 0x00001983, 0xFFFFF8F6, 0x000002E2 },
+ { 0x0213F0FD42C64984, 0x00003B59, 0xFFFFE4D0, 0x000005DA, 0x00002605, 0xFFFFF090, 0x00000439, 0x00002605, 0xFFFFF090, 0x00000439 },
+ { 0x0213F0FD42D03124, 0x00003251, 0xFFFFEA46, 0x00000511, 0x00002781, 0xFFFFEF84, 0x00000470, 0x00002781, 0xFFFFEF84, 0x00000470 },
+ { 0x0213F0FD42CA3164, 0x00003304, 0xFFFFE926, 0x00000542, 0x00001EE9, 0xFFFFF4E4, 0x0000038B, 0x00001EE9, 0xFFFFF4E4, 0x0000038B },
+ { 0x0213F0FD42CC38C4, 0x00002F4C, 0xFFFFEC0C, 0x000004C4, 0x00001E49, 0xFFFFF578, 0x00000374, 0x00001E49, 0xFFFFF578, 0x00000374 },
+ { 0x0213EA94DE1C2164, 0x00002034, 0xFFFFF692, 0x0000034C, 0x000014B8, 0xFFFFFC5B, 0x00000294, 0x000014B8, 0xFFFFFC5B, 0x00000294 },
+ { 0x0213F0FD42CE4924, 0x0000385F, 0xFFFFE513, 0x000005F3, 0x000024E7, 0xFFFFF053, 0x00000450, 0x000024E7, 0xFFFFF053, 0x00000450 },
+ { 0x0213EA94DE1C40E4, 0x00001D70, 0xFFFFF821, 0x0000030F, 0x00001541, 0xFFFFFBB4, 0x000002B0, 0x00001541, 0xFFFFFBB4, 0x000002B0 },
+ { 0x0213F0FD42D02084, 0x000034EB, 0xFFFFE7FF, 0x00000575, 0x000019B4, 0xFFFFF836, 0x00000308, 0x000019B4, 0xFFFFF836, 0x00000308 },
+ { 0x0213F0FD42D050E4, 0x000037C9, 0xFFFFE5D4, 0x000005CD, 0x000026A1, 0xFFFFEF0C, 0x00000491, 0x000026A1, 0xFFFFEF0C, 0x00000491 },
+ { 0x0213EA94DE121944, 0x00002918, 0xFFFFF148, 0x000003E9, 0x00001A49, 0xFFFFF94C, 0x000002CF, 0x00001A49, 0xFFFFF94C, 0x000002CF },
+ { 0x0213F0FD42CA4064, 0x00002F90, 0xFFFFEAB5, 0x00000514, 0x00001707, 0xFFFFF9C7, 0x000002C4, 0x00001707, 0xFFFFF9C7, 0x000002C4 },
+ { 0x0213EA94DE062064, 0x0000327E, 0xFFFFEA99, 0x000004F4, 0x0000194F, 0xFFFFF929, 0x000002DC, 0x0000194F, 0xFFFFF929, 0x000002DC },
+ { 0x0213F0FD42C64084, 0x0000326F, 0xFFFFE9CF, 0x00000519, 0x00002240, 0xFFFFF299, 0x000003E7, 0x00002240, 0xFFFFF299, 0x000003E7 },
+ { 0x0213EA94DE321124, 0x000022FB, 0xFFFFF4C6, 0x00000371, 0x00001506, 0xFFFFFC73, 0x00000265, 0x00001506, 0xFFFFFC73, 0x00000265 },
+ { 0x0213F0FD42CA3924, 0x00003AD6, 0xFFFFE470, 0x000005FE, 0x00001F03, 0xFFFFF4F3, 0x00000387, 0x00001F03, 0xFFFFF4F3, 0x00000387 },
+ { 0x0213EA94DE201124, 0x00001F11, 0xFFFFF756, 0x00000332, 0x00001666, 0xFFFFFB8A, 0x000002B2, 0x00001666, 0xFFFFFB8A, 0x000002B2 },
+ { 0x0213EA94DE0238A4, 0x00002A5F, 0xFFFFEFA7, 0x00000430, 0x00001943, 0xFFFFF8C6, 0x000002F7, 0x00001943, 0xFFFFF8C6, 0x000002F7 },
+ { 0x0213EA94DE1650E4, 0x0000235E, 0xFFFFF3B4, 0x000003B3, 0x00001489, 0xFFFFFBCF, 0x0000029B, 0x00001489, 0xFFFFFBCF, 0x0000029B },
+ { 0x0213F0FD42CC38A4, 0x00003570, 0xFFFFE780, 0x0000058D, 0x00001B1D, 0xFFFFF767, 0x00000325, 0x00001B1D, 0xFFFFF767, 0x00000325 },
+ { 0x0213EA94DE042064, 0x00003678, 0xFFFFE7C3, 0x00000569, 0x00001831, 0xFFFFF98E, 0x000002C8, 0x00001831, 0xFFFFF98E, 0x000002C8 },
+ { 0x0213EA94DE201864, 0x000020B9, 0xFFFFF625, 0x0000035A, 0x000015C5, 0xFFFFFB8A, 0x000002B5, 0x000015C5, 0xFFFFFB8A, 0x000002B5 },
+ { 0x0213F0FD42C63184, 0x00003985, 0xFFFFE529, 0x000005DD, 0x00002165, 0xFFFFF351, 0x000003C5, 0x00002165, 0xFFFFF351, 0x000003C5 },
+ { 0x0213F0FD42D02064, 0x0000322A, 0xFFFFE99D, 0x00000535, 0x000019A1, 0xFFFFF844, 0x00000305, 0x000019A1, 0xFFFFF844, 0x00000305 },
+ { 0x0213F0FD42D05104, 0x000033ED, 0xFFFFE834, 0x00000571, 0x00002094, 0xFFFFF33A, 0x000003DB, 0x00002094, 0xFFFFF33A, 0x000003DB },
+ { 0x0213EA94DE2040C4, 0x00001D10, 0xFFFFF84D, 0x0000030B, 0x00001659, 0xFFFFFB0A, 0x000002CB, 0x00001659, 0xFFFFFB0A, 0x000002CB },
+ { 0x0213EA94DE1C1124, 0x0000210F, 0xFFFFF644, 0x00000355, 0x00001A4A, 0xFFFFF90F, 0x00000310, 0x00001A4A, 0xFFFFF90F, 0x00000310 },
+ { 0x0213EA94DE164164, 0x00001CA8, 0xFFFFF813, 0x00000316, 0x00001440, 0xFFFFFC1C, 0x0000029D, 0x00001440, 0xFFFFFC1C, 0x0000029D },
+ { 0x0213EA94DE3210C4, 0x00002864, 0xFFFFF15A, 0x000003FA, 0x0000137F, 0xFFFFFD43, 0x00000248, 0x0000137F, 0xFFFFFD43, 0x00000248 },
+ { 0x0213F0FD42D04184, 0x00002CDB, 0xFFFFECFD, 0x000004A7, 0x00002472, 0xFFFFF0E1, 0x00000437, 0x00002472, 0xFFFFF0E1, 0x00000437 },
+ { 0x0213F0FD42CC5104, 0x00003348, 0xFFFFE8CA, 0x00000554, 0x00001E91, 0xFFFFF4D4, 0x00000392, 0x00001E91, 0xFFFFF4D4, 0x00000392 },
+ { 0x0213F0FD42C64944, 0x00003989, 0xFFFFE4BB, 0x000005F8, 0x00001ACB, 0xFFFFF780, 0x00000319, 0x00001ACB, 0xFFFFF780, 0x00000319 },
+ { 0x0213F0FD42CA2104, 0x00003238, 0xFFFFEA09, 0x0000051E, 0x00001F08, 0xFFFFF4F4, 0x0000038C, 0x00001F08, 0xFFFFF4F4, 0x0000038C },
+ { 0x0213EA94DE120904, 0x00002453, 0xFFFFF3B0, 0x0000038D, 0x00001AED, 0xFFFFF8A2, 0x000002EA, 0x00001AED, 0xFFFFF8A2, 0x000002EA },
+ { 0x0213EA94DE1C3024, 0x00002459, 0xFFFFF409, 0x000003A8, 0x000017B5, 0xFFFFFA53, 0x000002E1, 0x000017B5, 0xFFFFFA53, 0x000002E1 },
+ { 0x0213EA94DE021184, 0x0000310D, 0xFFFFEB78, 0x000004D0, 0x00001DC9, 0xFFFFF5D5, 0x00000368, 0x00001DC9, 0xFFFFF5D5, 0x00000368 },
+ { 0x0213EA94DE023104, 0x000031BF, 0xFFFFECA3, 0x00000498, 0x00001DC9, 0xFFFFF717, 0x00000336, 0x00001DC9, 0xFFFFF717, 0x00000336 },
+ { 0x0213F0FD42CE2104, 0x00003896, 0xFFFFE5DD, 0x000005C5, 0x000023E2, 0xFFFFF1A1, 0x00000416, 0x000023E2, 0xFFFFF1A1, 0x00000416 },
+ { 0x0213EA94DE323904, 0x000023CB, 0xFFFFF4C8, 0x00000372, 0x00001C33, 0xFFFFF7D5, 0x0000032A, 0x00001C33, 0xFFFFF7D5, 0x0000032A },
+ { 0x0213F0FD42D020C4, 0x00002F6B, 0xFFFFEBF0, 0x000004CE, 0x00001C89, 0xFFFFF689, 0x0000034D, 0x00001C89, 0xFFFFF689, 0x0000034D },
+ { 0x0213F0FD42CE3904, 0x00003E72, 0xFFFFE211, 0x0000065D, 0x0000218D, 0xFFFFF309, 0x000003DC, 0x0000218D, 0xFFFFF309, 0x000003DC },
+ { 0x0213EA94DE022084, 0x00002612, 0xFFFFF2C3, 0x000003AD, 0x000019F7, 0xFFFFF891, 0x000002FE, 0x000019F7, 0xFFFFF891, 0x000002FE },
+ { 0x0213EA94DE164184, 0x0000205D, 0xFFFFF59F, 0x00000372, 0x000012E6, 0xFFFFFD0A, 0x00000270, 0x000012E6, 0xFFFFFD0A, 0x00000270 },
+ { 0x0213F0FD42CA2124, 0x00002ECB, 0xFFFFEC47, 0x000004BD, 0x00001936, 0xFFFFF8D9, 0x000002E4, 0x00001936, 0xFFFFF8D9, 0x000002E4 },
+ { 0x0213EA94DE064904, 0x00002BDB, 0xFFFFEE6D, 0x00000458, 0x00001852, 0xFFFFF943, 0x000002D9, 0x00001852, 0xFFFFF943, 0x000002D9 },
+ { 0x0213EA94DE124904, 0x00003387, 0xFFFFE958, 0x00000534, 0x00001932, 0xFFFFF8FA, 0x000002E4, 0x00001932, 0xFFFFF8FA, 0x000002E4 },
+ { 0x0213EA94DE0208C4, 0x00002E3C, 0xFFFFED26, 0x00000495, 0x00001858, 0xFFFFF990, 0x000002D1, 0x00001858, 0xFFFFF990, 0x000002D1 },
+ { 0x0213EA94DE022964, 0x000033B8, 0xFFFFEA5C, 0x000004F9, 0x00001BD1, 0xFFFFF76A, 0x0000032E, 0x00001BD1, 0xFFFFF76A, 0x0000032E },
+ { 0x0213EA94DE062984, 0x00002BCE, 0xFFFFEEE9, 0x00000443, 0x00001982, 0xFFFFF90D, 0x000002DF, 0x00001982, 0xFFFFF90D, 0x000002DF },
+ { 0x0213F0FD42D048E4, 0x00003495, 0xFFFFE7D9, 0x0000057B, 0x00001D2A, 0xFFFFF5A5, 0x00000372, 0x00001D2A, 0xFFFFF5A5, 0x00000372 },
+ { 0x0213F0FD42CA38E4, 0x000034B1, 0xFFFFE88D, 0x00000556, 0x00002014, 0xFFFFF43A, 0x000003AA, 0x00002014, 0xFFFFF43A, 0x000003AA },
+ { 0x0213F0FD42CC3124, 0x00002F96, 0xFFFFEC84, 0x000004AD, 0x000024A2, 0xFFFFF1CE, 0x0000040A, 0x000024A2, 0xFFFFF1CE, 0x0000040A },
+ { 0x0213EA94DE161064, 0x0000203B, 0xFFFFF640, 0x00000359, 0x000014EC, 0xFFFFFC14, 0x0000029C, 0x000014EC, 0xFFFFFC14, 0x0000029C },
+ { 0x0213F0FD42D02984, 0x000034E2, 0xFFFFE7B8, 0x00000582, 0x00001938, 0xFFFFF872, 0x000002FA, 0x00001938, 0xFFFFF872, 0x000002FA },
+ { 0x0213EA94DE063124, 0x00002AC7, 0xFFFFF0C1, 0x000003F5, 0x00002268, 0xFFFFF39C, 0x000003C9, 0x00002268, 0xFFFFF39C, 0x000003C9 },
+ { 0x0213F0FD42C63144, 0x000036F6, 0xFFFFE77F, 0x00000571, 0x000027D9, 0xFFFFEF6F, 0x00000461, 0x000027D9, 0xFFFFEF6F, 0x00000461 },
+ { 0x0213EA94DE123124, 0x00002BAB, 0xFFFFF018, 0x00000419, 0x00002126, 0xFFFFF4E2, 0x0000038F, 0x00002126, 0xFFFFF4E2, 0x0000038F },
+ { 0x0213EA94DE323924, 0x000028C4, 0xFFFFF161, 0x000003F8, 0x0000180C, 0xFFFFFA4B, 0x000002C8, 0x0000180C, 0xFFFFFA4B, 0x000002C8 },
+ { 0x0213F0FD42CA2864, 0x00002F48, 0xFFFFEB62, 0x000004EE, 0x00001912, 0xFFFFF8C8, 0x000002EA, 0x00001912, 0xFFFFF8C8, 0x000002EA },
+ { 0x0213F0FD42CE2864, 0x000032DF, 0xFFFFE911, 0x00000545, 0x00001F06, 0xFFFFF485, 0x0000039C, 0x00001F06, 0xFFFFF485, 0x0000039C },
+ { 0x0213F0FD42D04144, 0x000035B8, 0xFFFFE74F, 0x00000590, 0x00001FD7, 0xFFFFF410, 0x000003AF, 0x00001FD7, 0xFFFFF410, 0x000003AF },
+ { 0x0213F0FD42D050C4, 0x00003608, 0xFFFFE6D7, 0x000005A9, 0x000024A6, 0xFFFFF075, 0x00000450, 0x000024A6, 0xFFFFF075, 0x00000450 },
+ { 0x0213F0FD42CA3884, 0x000030AB, 0xFFFFEAED, 0x000004F5, 0x000019EE, 0xFFFFF84E, 0x000002FC, 0x000019EE, 0xFFFFF84E, 0x000002FC },
+ { 0x0213EA94DE0620C4, 0x000030C6, 0xFFFFEC92, 0x0000049E, 0x000019BB, 0xFFFFF8F1, 0x000002F3, 0x000019BB, 0xFFFFF8F1, 0x000002F3 },
+ { 0x0213F0FD42C630A4, 0x00003B27, 0xFFFFE544, 0x000005C1, 0x00002697, 0xFFFFF072, 0x00000438, 0x00002697, 0xFFFFF072, 0x00000438 },
+ { 0x0213EA94DE1248E4, 0x00002F23, 0xFFFFEC48, 0x000004B9, 0x0000199A, 0xFFFFF8CF, 0x000002E9, 0x0000199A, 0xFFFFF8CF, 0x000002E9 },
+ { 0x0213EA94DE0629A4, 0x00002BD7, 0xFFFFEEAC, 0x00000450, 0x00001991, 0xFFFFF8F4, 0x000002E2, 0x00001991, 0xFFFFF8F4, 0x000002E2 },
+ { 0x0213EA94DE022024, 0x00003210, 0xFFFFEB24, 0x000004DE, 0x00001BDF, 0xFFFFF744, 0x00000333, 0x00001BDF, 0xFFFFF744, 0x00000333 },
+ { 0x0213EA94DE244144, 0x00002DDC, 0xFFFFED0D, 0x000004AC, 0x000019D0, 0xFFFFF869, 0x0000030F, 0x000019D0, 0xFFFFF869, 0x0000030F },
+ { 0x0213EA94DE203964, 0x000023E6, 0xFFFFF40C, 0x000003A9, 0x000014EB, 0xFFFFFBC4, 0x000002AF, 0x000014EB, 0xFFFFFBC4, 0x000002AF },
+ { 0x0213F0FD42CA29A4, 0x000030CE, 0xFFFFE9A5, 0x0000053C, 0x00001C45, 0xFFFFF60E, 0x0000035D, 0x00001C45, 0xFFFFF60E, 0x0000035D },
+ { 0x0213EA94DE161084, 0x00001E89, 0xFFFFF73A, 0x00000337, 0x0000157C, 0xFFFFFBC0, 0x000002AA, 0x0000157C, 0xFFFFFBC0, 0x000002AA },
+ { 0x0213F0FD42D04124, 0x000036C6, 0xFFFFE6CF, 0x000005A1, 0x00002457, 0xFFFFF11D, 0x0000042D, 0x00002457, 0xFFFFF11D, 0x0000042D },
+ { 0x0213EA94DE321944, 0x00002815, 0xFFFFF19A, 0x000003F2, 0x000016D2, 0xFFFFFB40, 0x00000299, 0x000016D2, 0xFFFFFB40, 0x00000299 },
+ { 0x0213EA94DE1C19A4, 0x00001FE2, 0xFFFFF660, 0x00000354, 0x000015A7, 0xFFFFFB47, 0x000002C1, 0x000015A7, 0xFFFFFB47, 0x000002C1 },
+ { 0x0213EA94DE161964, 0x00002114, 0xFFFFF634, 0x00000356, 0x000016C1, 0xFFFFFB43, 0x000002B8, 0x000016C1, 0xFFFFFB43, 0x000002B8 },
+ { 0x0213F0FD42CC28C4, 0x000028E3, 0xFFFFF075, 0x00000414, 0x0000203C, 0xFFFFF438, 0x000003B3, 0x0000203C, 0xFFFFF438, 0x000003B3 },
+ { 0x0213EA94DE1C3924, 0x00001EEB, 0xFFFFF7BB, 0x0000031A, 0x00001580, 0xFFFFFBD7, 0x000002AD, 0x00001580, 0xFFFFFBD7, 0x000002AD },
+ { 0x0213EA94DE2408C4, 0x00002BB2, 0xFFFFEE72, 0x00000470, 0x0000192C, 0xFFFFF91E, 0x000002E7, 0x0000192C, 0xFFFFF91E, 0x000002E7 },
+ { 0x0213EA94DE0650E4, 0x00003A3D, 0xFFFFE49D, 0x000005F5, 0x00001A3B, 0xFFFFF7B1, 0x00000320, 0x00001A3B, 0xFFFFF7B1, 0x00000320 },
+ { 0x0213F0FD42CE3164, 0x00002E93, 0xFFFFEC5A, 0x000004B4, 0x000025EB, 0xFFFFF03C, 0x0000044A, 0x000025EB, 0xFFFFF03C, 0x0000044A },
+ { 0x0213F0FD42CA20C4, 0x0000331F, 0xFFFFE97A, 0x00000531, 0x00001A06, 0xFFFFF850, 0x000002FD, 0x00001A06, 0xFFFFF850, 0x000002FD },
+ { 0x0213F0FD42C63964, 0x00003937, 0xFFFFE5A0, 0x000005C7, 0x0000235E, 0xFFFFF234, 0x000003F2, 0x0000235E, 0xFFFFF234, 0x000003F2 },
+ { 0x0213EA94DE1E3924, 0x00001DD0, 0xFFFFF80E, 0x00000319, 0x000015C7, 0xFFFFFB91, 0x000002BC, 0x000015C7, 0xFFFFFB91, 0x000002BC },
+ { 0x0213F0FD42D03964, 0x00003328, 0xFFFFE905, 0x0000054A, 0x00002054, 0xFFFFF3BF, 0x000003C0, 0x00002054, 0xFFFFF3BF, 0x000003C0 },
+ { 0x0213F0FD42CC1104, 0x00002FE5, 0xFFFFEA65, 0x00000520, 0x0000188B, 0xFFFFF8A7, 0x000002F5, 0x0000188B, 0xFFFFF8A7, 0x000002F5 },
+ { 0x0213F0FD42CA38A4, 0x00002ED3, 0xFFFFEC51, 0x000004B9, 0x00001888, 0xFFFFF96A, 0x000002CA, 0x00001888, 0xFFFFF96A, 0x000002CA },
+ { 0x0213F0FD42D03084, 0x00002FCC, 0xFFFFEB60, 0x000004EA, 0x00001F8D, 0xFFFFF436, 0x000003B4, 0x00001F8D, 0xFFFFF436, 0x000003B4 },
+ { 0x0213F0FD42CE4084, 0x0000329F, 0xFFFFE8F7, 0x0000054F, 0x000023DB, 0xFFFFF0EE, 0x0000043A, 0x000023DB, 0xFFFFF0EE, 0x0000043A },
+ { 0x0213EA94DE0438A4, 0x000030B5, 0xFFFFEBB8, 0x000004C4, 0x00001AFD, 0xFFFFF781, 0x00000329, 0x00001AFD, 0xFFFFF781, 0x00000329 },
+ { 0x0213EA94DE1E19A4, 0x00001BBF, 0xFFFFF8E2, 0x000002F7, 0x00001722, 0xFFFFFA85, 0x000002DB, 0x00001722, 0xFFFFFA85, 0x000002DB },
+ { 0x0213EA94DE022044, 0x000030E4, 0xFFFFEBE6, 0x000004BB, 0x00001C80, 0xFFFFF6E1, 0x0000033E, 0x00001C80, 0xFFFFF6E1, 0x0000033E },
+ { 0x0213EA94DE122944, 0x000030E2, 0xFFFFECD0, 0x00000492, 0x00001CE0, 0xFFFFF753, 0x0000032F, 0x00001CE0, 0xFFFFF753, 0x0000032F },
+ { 0x0213EA94DE322864, 0x00002513, 0xFFFFF323, 0x000003BC, 0x00001965, 0xFFFFF93C, 0x000002F0, 0x00001965, 0xFFFFF93C, 0x000002F0 },
+ { 0x0213EA94DE1610A4, 0x00002147, 0xFFFFF585, 0x0000037A, 0x000014CC, 0xFFFFFC3B, 0x00000296, 0x000014CC, 0xFFFFFC3B, 0x00000296 },
+ { 0x0213EA94DE322124, 0x00002507, 0xFFFFF432, 0x0000038A, 0x00001890, 0xFFFFFA61, 0x000002C6, 0x00001890, 0xFFFFFA61, 0x000002C6 },
+ { 0x0213EA94DE0638A4, 0x0000339B, 0xFFFFEA7D, 0x000004F0, 0x0000191E, 0xFFFFF944, 0x000002DF, 0x0000191E, 0xFFFFF944, 0x000002DF },
+ { 0x0213F0FD42CC28A4, 0x00002842, 0xFFFFF043, 0x00000427, 0x00001988, 0xFFFFF892, 0x000002F7, 0x00001988, 0xFFFFF892, 0x000002F7 },
+ { 0x0213F0FD42C618A4, 0x0000389D, 0xFFFFE5D8, 0x000005BF, 0x00001EE1, 0xFFFFF4EF, 0x00000387, 0x00001EE1, 0xFFFFF4EF, 0x00000387 },
+ { 0x0213F0FD42CE3184, 0x0000396D, 0xFFFFE4D7, 0x000005F2, 0x000020DA, 0xFFFFF34E, 0x000003CD, 0x000020DA, 0xFFFFF34E, 0x000003CD },
+ { 0x0213F0FD42CA3104, 0x0000355F, 0xFFFFE85A, 0x0000055F, 0x0000281F, 0xFFFFEF28, 0x0000047D, 0x0000281F, 0xFFFFEF28, 0x0000047D },
+ { 0x0213EA94DE1C50E4, 0x00002284, 0xFFFFF46E, 0x00000399, 0x00001498, 0xFFFFFBE3, 0x0000029C, 0x00001498, 0xFFFFFBE3, 0x0000029C },
+ { 0x0213EA94DE023944, 0x000031B6, 0xFFFFEB42, 0x000004D9, 0x00001F54, 0xFFFFF4D2, 0x00000399, 0x00001F54, 0xFFFFF4D2, 0x00000399 },
+ { 0x0213F0FD42C63064, 0x000035CE, 0xFFFFE79D, 0x00000578, 0x00001C78, 0xFFFFF68C, 0x00000344, 0x00001C78, 0xFFFFF68C, 0x00000344 },
+ { 0x0213EA94DE1E4964, 0x00001C0A, 0xFFFFF81B, 0x00000318, 0x00001492, 0xFFFFFBCC, 0x000002A5, 0x00001492, 0xFFFFFBCC, 0x000002A5 },
+ { 0x0213EA94DE022184, 0x00003492, 0xFFFFE95C, 0x00000526, 0x00001A97, 0xFFFFF81B, 0x0000030B, 0x00001A97, 0xFFFFF81B, 0x0000030B },
+ { 0x0213EA94DE163164, 0x00001E89, 0xFFFFF7D0, 0x0000031A, 0x000017A5, 0xFFFFFA99, 0x000002D9, 0x000017A5, 0xFFFFFA99, 0x000002D9 },
+ { 0x0213F0FD42CA48C4, 0x00002DCC, 0xFFFFEBE0, 0x000004DE, 0x000019BA, 0xFFFFF7F5, 0x0000030D, 0x000019BA, 0xFFFFF7F5, 0x0000030D },
+ { 0x0213EA94DE042984, 0x000030EF, 0xFFFFEBC1, 0x000004C0, 0x00001AA9, 0xFFFFF814, 0x0000030A, 0x00001AA9, 0xFFFFF814, 0x0000030A },
+ { 0x0213EA94DE245124, 0x00002EA3, 0xFFFFEBF6, 0x000004D8, 0x00001DCF, 0xFFFFF521, 0x00000399, 0x00001DCF, 0xFFFFF521, 0x00000399 },
+ { 0x0213EA94DE324164, 0x00002B5F, 0xFFFFEEA1, 0x0000046C, 0x000017EB, 0xFFFFF9C9, 0x000002D4, 0x000017EB, 0xFFFFF9C9, 0x000002D4 },
+ { 0x0213EA94DE024104, 0x00002C63, 0xFFFFEE82, 0x00000455, 0x00002268, 0xFFFFF29D, 0x000003F6, 0x00002268, 0xFFFFF29D, 0x000003F6 },
+ { 0x0213EA94DE121904, 0x00002B1A, 0xFFFFF016, 0x0000041C, 0x000019AA, 0xFFFFF988, 0x000002D2, 0x000019AA, 0xFFFFF988, 0x000002D2 },
+ { 0x0213F0FD42CA2964, 0x0000332F, 0xFFFFE934, 0x0000053B, 0x00001E47, 0xFFFFF566, 0x00000374, 0x00001E47, 0xFFFFF566, 0x00000374 },
+ { 0x0213F0FD42CA48E4, 0x00002995, 0xFFFFEEC1, 0x00000465, 0x0000178F, 0xFFFFF995, 0x000002C5, 0x0000178F, 0xFFFFF995, 0x000002C5 },
+ { 0x0213EA94DE201884, 0x00001C2E, 0xFFFFF932, 0x000002E9, 0x000015C2, 0xFFFFFBC5, 0x000002AD, 0x000015C2, 0xFFFFFBC5, 0x000002AD },
+ { 0x0213F0FD42C640E4, 0x00003B08, 0xFFFFE4E8, 0x000005D8, 0x0000209D, 0xFFFFF444, 0x00000398, 0x0000209D, 0xFFFFF444, 0x00000398 },
+ { 0x0213EA94DE0450E4, 0x00002F1F, 0xFFFFEB74, 0x000004EB, 0x00001F4C, 0xFFFFF3D4, 0x000003CE, 0x00001F4C, 0xFFFFF3D4, 0x000003CE },
+ { 0x0213EA94DE043884, 0x00003415, 0xFFFFE89F, 0x00000553, 0x0000186B, 0xFFFFF8E1, 0x000002EF, 0x0000186B, 0xFFFFF8E1, 0x000002EF },
+ { 0x0213F0FD42CC10C4, 0x00003441, 0xFFFFE779, 0x0000059D, 0x000019EA, 0xFFFFF7B2, 0x0000031F, 0x000019EA, 0xFFFFF7B2, 0x0000031F },
+ { 0x0213EA94DE164064, 0x00002174, 0xFFFFF546, 0x00000378, 0x00001456, 0xFFFFFC5F, 0x00000284, 0x00001456, 0xFFFFFC5F, 0x00000284 },
+ { 0x0213F0FD42CE40C4, 0x00003788, 0xFFFFE61E, 0x000005BF, 0x00001DF4, 0xFFFFF562, 0x00000374, 0x00001DF4, 0xFFFFF562, 0x00000374 },
+ { 0x0213EA94DE1E1844, 0x00001C41, 0xFFFFF8C1, 0x000002FC, 0x0000171E, 0xFFFFFA93, 0x000002DE, 0x0000171E, 0xFFFFFA93, 0x000002DE },
+ { 0x0213F0FD42CA3864, 0x00002B15, 0xFFFFEDEC, 0x00000487, 0x000017E4, 0xFFFFF934, 0x000002DF, 0x000017E4, 0xFFFFF934, 0x000002DF },
+ { 0x0213F0FD42CC3144, 0x0000327A, 0xFFFFEA71, 0x000004FF, 0x00001D96, 0xFFFFF63B, 0x00000351, 0x00001D96, 0xFFFFF63B, 0x00000351 },
+ { 0x0213EA94DE1E4064, 0x000023C6, 0xFFFFF3E5, 0x000003B6, 0x000014DE, 0xFFFFFC29, 0x00000294, 0x000014DE, 0xFFFFFC29, 0x00000294 },
+ { 0x0213EA94DE164944, 0x00001F96, 0xFFFFF5FA, 0x00000364, 0x00001397, 0xFFFFFC9D, 0x0000027D, 0x00001397, 0xFFFFFC9D, 0x0000027D },
+ { 0x0213EA94DE063144, 0x00002B51, 0xFFFFEFB5, 0x00000420, 0x00001ACA, 0xFFFFF824, 0x0000030D, 0x00001ACA, 0xFFFFF824, 0x0000030D },
+ { 0x0213EA94DE1E4944, 0x000020DB, 0xFFFFF55B, 0x0000037C, 0x0000153D, 0xFFFFFB5F, 0x000002BA, 0x0000153D, 0xFFFFFB5F, 0x000002BA },
+ { 0x0213EA94DE0221A4, 0x000030BB, 0xFFFFEBDA, 0x000004BC, 0x00001B0E, 0xFFFFF7A8, 0x0000031E, 0x00001B0E, 0xFFFFF7A8, 0x0000031E },
+ { 0x0213F0FD42C62904, 0x000033C4, 0xFFFFEA41, 0x000004FA, 0x000022C6, 0xFFFFF363, 0x000003BC, 0x000022C6, 0xFFFFF363, 0x000003BC },
+ { 0x0213EA94DE240924, 0x00002D47, 0xFFFFEE01, 0x00000477, 0x000021CD, 0xFFFFF36E, 0x000003D6, 0x000021CD, 0xFFFFF36E, 0x000003D6 },
+ { 0x0213EA94DE1E31A4, 0x00001E7B, 0xFFFFF733, 0x00000339, 0x00001668, 0xFFFFFB29, 0x000002BF, 0x00001668, 0xFFFFFB29, 0x000002BF },
+ { 0x0213F0FD42CA2984, 0x00002F7E, 0xFFFFEAFF, 0x000004FC, 0x000018D4, 0xFFFFF8BE, 0x000002E8, 0x000018D4, 0xFFFFF8BE, 0x000002E8 },
+ { 0x0213EA94DE3238A4, 0x00002635, 0xFFFFF2E1, 0x000003BC, 0x000017A4, 0xFFFFFA67, 0x000002C3, 0x000017A4, 0xFFFFFA67, 0x000002C3 },
+ { 0x0213EA94DE1230A4, 0x000026CA, 0xFFFFF2C1, 0x000003B2, 0x00001C3E, 0xFFFFF7AE, 0x0000031F, 0x00001C3E, 0xFFFFF7AE, 0x0000031F },
+ { 0x0213EA94DE1C1064, 0x00002550, 0xFFFFF380, 0x000003B5, 0x000019F5, 0xFFFFF8E7, 0x00000313, 0x000019F5, 0xFFFFF8E7, 0x00000313 },
+ { 0x0213F0FD42CA4904, 0x00002FBC, 0xFFFFEAF8, 0x000004FA, 0x000018CC, 0xFFFFF8C6, 0x000002E8, 0x000018CC, 0xFFFFF8C6, 0x000002E8 },
+ { 0x0213F0FD42D018E4, 0x00002FCC, 0xFFFFEB60, 0x000004EA, 0x00001EFF, 0xFFFFF4DA, 0x0000038F, 0x00001EFF, 0xFFFFF4DA, 0x0000038F },
+ { 0x0213EA94DE164084, 0x000023E6, 0xFFFFF413, 0x000003A1, 0x00001544, 0xFFFFFC16, 0x0000028B, 0x00001544, 0xFFFFFC16, 0x0000028B },
+ { 0x0213F0FD42CE3024, 0x00003251, 0xFFFFEAA2, 0x000004F5, 0x000025B0, 0xFFFFF0DF, 0x00000431, 0x000025B0, 0xFFFFF0DF, 0x00000431 },
+ { 0x0213F0FD42D03984, 0x00002F6F, 0xFFFFEB67, 0x000004E6, 0x00002275, 0xFFFFF249, 0x000003FB, 0x00002275, 0xFFFFF249, 0x000003FB },
+ { 0x0213EA94DE322964, 0x00002597, 0xFFFFF34A, 0x000003B1, 0x00001BCC, 0xFFFFF822, 0x0000031A, 0x00001BCC, 0xFFFFF822, 0x0000031A },
+ { 0x0213F0FD42C63864, 0x00003B1D, 0xFFFFE40E, 0x0000060D, 0x00001F61, 0xFFFFF470, 0x0000039F, 0x00001F61, 0xFFFFF470, 0x0000039F },
+ { 0x0213F0FD42C64144, 0x0000379F, 0xFFFFE6DB, 0x0000058C, 0x00002460, 0xFFFFF170, 0x00000415, 0x00002460, 0xFFFFF170, 0x00000415 },
+ { 0x0213EA94DE165144, 0x00002442, 0xFFFFF2FB, 0x000003D9, 0x00001414, 0xFFFFFBDC, 0x000002A2, 0x00001414, 0xFFFFFBDC, 0x000002A2 },
+ { 0x0213EA94DE0240C4, 0x00003270, 0xFFFFEA0D, 0x0000051C, 0x00001AFD, 0xFFFFF783, 0x00000328, 0x00001AFD, 0xFFFFF783, 0x00000328 },
+ { 0x0213EA94DE161104, 0x00001B23, 0xFFFFF94B, 0x000002EB, 0x000015F1, 0xFFFFFB82, 0x000002B4, 0x000015F1, 0xFFFFFB82, 0x000002B4 },
+ { 0x0213EA94DE323844, 0x000026AE, 0xFFFFF21A, 0x000003DB, 0x00001827, 0xFFFFFA10, 0x000002C8, 0x00001827, 0xFFFFFA10, 0x000002C8 },
+ { 0x0213F0FD42CA4884, 0x00002DCF, 0xFFFFEBD8, 0x000004DB, 0x00001A75, 0xFFFFF719, 0x0000033A, 0x00001A75, 0xFFFFF719, 0x0000033A },
+ { 0x0213F0FD42CE40E4, 0x00003983, 0xFFFFE500, 0x000005EA, 0x000022A6, 0xFFFFF25F, 0x000003F1, 0x000022A6, 0xFFFFF25F, 0x000003F1 },
+ { 0x0213EA94DE1218C4, 0x00002AD5, 0xFFFFF07A, 0x00000406, 0x000019FB, 0xFFFFF961, 0x000002D8, 0x000019FB, 0xFFFFF961, 0x000002D8 },
+ { 0x0213F0FD42CA39A4, 0x00002A43, 0xFFFFEE43, 0x00000474, 0x00001D65, 0xFFFFF538, 0x00000387, 0x00001D65, 0xFFFFF538, 0x00000387 },
+ { 0x0213F0FD42C62084, 0x0000311E, 0xFFFFEAF8, 0x000004E8, 0x00001959, 0xFFFFF8E4, 0x000002DC, 0x00001959, 0xFFFFF8E4, 0x000002DC },
+ { 0x0213F0FD42D031A4, 0x0000339A, 0xFFFFE8A7, 0x00000559, 0x00001A04, 0xFFFFF7E5, 0x00000311, 0x00001A04, 0xFFFFF7E5, 0x00000311 },
+ { 0x0213EA94DE204144, 0x000021B3, 0xFFFFF50F, 0x00000389, 0x00001470, 0xFFFFFBF7, 0x000002A5, 0x00001470, 0xFFFFFBF7, 0x000002A5 },
+ { 0x0213EA94DE021884, 0x00003417, 0xFFFFE9A6, 0x0000051D, 0x000018A4, 0xFFFFF984, 0x000002CF, 0x000018A4, 0xFFFFF984, 0x000002CF },
+ { 0x0213EA94DE202984, 0x00001FED, 0xFFFFF6A2, 0x00000347, 0x00001639, 0xFFFFFB59, 0x000002BB, 0x00001639, 0xFFFFFB59, 0x000002BB },
+ { 0x0213EA94DE1218A4, 0x000032D2, 0xFFFFEB18, 0x000004DC, 0x00001A01, 0xFFFFF95E, 0x000002CF, 0x00001A01, 0xFFFFF95E, 0x000002CF },
+ { 0x0213F0FD42D04084, 0x00003147, 0xFFFFEA3B, 0x00000518, 0x0000241D, 0xFFFFF11C, 0x00000431, 0x0000241D, 0xFFFFF11C, 0x00000431 },
+ { 0x0213EA94DE1C0904, 0x00001D44, 0xFFFFF7E7, 0x0000031A, 0x0000153F, 0xFFFFFBBC, 0x000002A9, 0x0000153F, 0xFFFFFBBC, 0x000002A9 },
+ { 0x0213F0FD42CC4104, 0x00003690, 0xFFFFE6E3, 0x000005A4, 0x000018DE, 0xFFFFF908, 0x000002DD, 0x000018DE, 0xFFFFF908, 0x000002DD },
+ { 0x0213F0FD42CC2184, 0x00003561, 0xFFFFE6F8, 0x000005AB, 0x000018B5, 0xFFFFF8A0, 0x000002F3, 0x000018B5, 0xFFFFF8A0, 0x000002F3 },
+ { 0x0213EA94DE323124, 0x000028F4, 0xFFFFF23A, 0x000003CE, 0x00001BC6, 0xFFFFF881, 0x00000311, 0x00001BC6, 0xFFFFF881, 0x00000311 },
+ { 0x0213F0FD42D03184, 0x000035D7, 0xFFFFE71C, 0x0000059B, 0x00001D49, 0xFFFFF5C8, 0x00000368, 0x00001D49, 0xFFFFF5C8, 0x00000368 },
+ { 0x0213F0FD42CE18A4, 0x0000397E, 0xFFFFE4CB, 0x000005F4, 0x00001989, 0xFFFFF844, 0x000002FD, 0x00001989, 0xFFFFF844, 0x000002FD },
+ { 0x0213F0FD42C62064, 0x00003BAB, 0xFFFFE332, 0x0000063F, 0x00001A69, 0xFFFFF7B9, 0x00000312, 0x00001A69, 0xFFFFF7B9, 0x00000312 },
+ { 0x0213F0FD42D03064, 0x00002F26, 0xFFFFEB82, 0x000004E8, 0x00001D7D, 0xFFFFF590, 0x00000379, 0x00001D7D, 0xFFFFF590, 0x00000379 },
+ { 0x0213EA94DE0631A4, 0x00002FDC, 0xFFFFEBE0, 0x000004C3, 0x00001940, 0xFFFFF8CC, 0x000002EE, 0x00001940, 0xFFFFF8CC, 0x000002EE },
+ { 0x0213EA94DE1C08E4, 0x000021B2, 0xFFFFF558, 0x00000379, 0x00001643, 0xFFFFFB1C, 0x000002C3, 0x00001643, 0xFFFFFB1C, 0x000002C3 },
+ { 0x0213EA94DE321904, 0x00002897, 0xFFFFF181, 0x000003F7, 0x00001990, 0xFFFFF994, 0x000002E2, 0x00001990, 0xFFFFF994, 0x000002E2 },
+ { 0x0213EA94DE1E0924, 0x00001D19, 0xFFFFF829, 0x0000031A, 0x00001558, 0xFFFFFBCA, 0x000002AF, 0x00001558, 0xFFFFFBCA, 0x000002AF },
+ { 0x0213EA94DE043144, 0x00003311, 0xFFFFEAD9, 0x000004E1, 0x00001BDC, 0xFFFFF79E, 0x0000031D, 0x00001BDC, 0xFFFFF79E, 0x0000031D },
+ { 0x0213EA94DE1E29C4, 0x00001E54, 0xFFFFF740, 0x00000333, 0x000016A1, 0xFFFFFAF0, 0x000002C4, 0x000016A1, 0xFFFFFAF0, 0x000002C4 },
+ { 0x0213F0FD42CE3964, 0x00003266, 0xFFFFE9A8, 0x00000527, 0x00002307, 0xFFFFF219, 0x000003FC, 0x00002307, 0xFFFFF219, 0x000003FC },
+ { 0x0213EA94DE321144, 0x00001D1F, 0xFFFFF82B, 0x000002F0, 0x000013F0, 0xFFFFFD0B, 0x0000024E, 0x000013F0, 0xFFFFFD0B, 0x0000024E },
+ { 0x0213F0FD42C648A4, 0x0000312E, 0xFFFFEA67, 0x00000502, 0x0000222A, 0xFFFFF253, 0x000003F9, 0x0000222A, 0xFFFFF253, 0x000003F9 },
+ { 0x0213F0FD42CA4124, 0x000032B2, 0xFFFFE9AD, 0x00000523, 0x00001E97, 0xFFFFF527, 0x0000037F, 0x00001E97, 0xFFFFF527, 0x0000037F },
+ { 0x0213EA94DE1640E4, 0x00001F6A, 0xFFFFF6FC, 0x00000338, 0x0000164B, 0xFFFFFB2C, 0x000002C2, 0x0000164B, 0xFFFFFB2C, 0x000002C2 },
+ { 0x0213EA94DE0228C4, 0x00002603, 0xFFFFF386, 0x00000392, 0x00001EE0, 0xFFFFF601, 0x00000369, 0x00001EE0, 0xFFFFF601, 0x00000369 },
+ { 0x0213EA94DE201164, 0x00001D0C, 0xFFFFF803, 0x00000317, 0x00001345, 0xFFFFFD52, 0x00000260, 0x00001345, 0xFFFFFD52, 0x00000260 },
+ { 0x0213F0FD42CC1884, 0x0000327A, 0xFFFFE8E5, 0x0000055C, 0x00001680, 0xFFFFFA2D, 0x000002B2, 0x00001680, 0xFFFFFA2D, 0x000002B2 },
+ { 0x0213F0FD42CA3964, 0x000032B8, 0xFFFFE91A, 0x0000054A, 0x00001BAB, 0xFFFFF6EC, 0x00000338, 0x00001BAB, 0xFFFFF6EC, 0x00000338 },
+ { 0x0213F0FD42CC3044, 0x00002F79, 0xFFFFEB63, 0x000004EF, 0x000017BB, 0xFFFFF9B1, 0x000002CA, 0x000017BB, 0xFFFFF9B1, 0x000002CA },
+ { 0x0213EA94DE0438E4, 0x00002AE5, 0xFFFFEFCB, 0x0000041D, 0x0000214A, 0xFFFFF3A7, 0x000003C7, 0x0000214A, 0xFFFFF3A7, 0x000003C7 },
+ { 0x0213EA94DE322064, 0x0000212C, 0xFFFFF5BC, 0x0000034F, 0x000017ED, 0xFFFFFA4C, 0x000002C1, 0x000017ED, 0xFFFFFA4C, 0x000002C1 },
+ { 0x0213EA94DE121124, 0x00002BE7, 0xFFFFEF40, 0x0000043C, 0x00001AE2, 0xFFFFF8CF, 0x000002E3, 0x00001AE2, 0xFFFFF8CF, 0x000002E3 },
+ { 0x0213F0FD42D05144, 0x000032DC, 0xFFFFE90F, 0x00000549, 0x00002A2D, 0xFFFFECC9, 0x000004ED, 0x00002A2D, 0xFFFFECC9, 0x000004ED },
+ { 0x0213EA94DE1618A4, 0x00001DE3, 0xFFFFF80D, 0x00000319, 0x000016FA, 0xFFFFFB42, 0x000002BC, 0x000016FA, 0xFFFFFB42, 0x000002BC },
+ { 0x0213EA94DE1E2844, 0x00001F1B, 0xFFFFF6DE, 0x00000346, 0x00001502, 0xFFFFFC23, 0x00000298, 0x00001502, 0xFFFFFC23, 0x00000298 },
+ { 0x0213EA94DE061864, 0x00003203, 0xFFFFEA87, 0x000004FE, 0x0000194E, 0xFFFFF8E3, 0x000002EC, 0x0000194E, 0xFFFFF8E3, 0x000002EC },
+ { 0x0213F0FD42D02144, 0x0000337A, 0xFFFFE8DD, 0x00000551, 0x00001E3C, 0xFFFFF534, 0x00000385, 0x00001E3C, 0xFFFFF534, 0x00000385 },
+ { 0x0213F0FD42CA4864, 0x000036F6, 0xFFFFE62A, 0x000005C5, 0x000023C0, 0xFFFFF117, 0x00000435, 0x000023C0, 0xFFFFF117, 0x00000435 },
+ { 0x0213F0FD42CC2144, 0x00003125, 0xFFFFEA4E, 0x0000051A, 0x00001E6C, 0xFFFFF503, 0x0000038E, 0x00001E6C, 0xFFFFF503, 0x0000038E },
+ { 0x0213EA94DE1C08A4, 0x00001CD4, 0xFFFFF82D, 0x0000030E, 0x0000156D, 0xFFFFFB64, 0x000002B8, 0x0000156D, 0xFFFFFB64, 0x000002B8 },
+ { 0x0213EA94DE0240A4, 0x00002F14, 0xFFFFEC46, 0x000004B8, 0x000017F1, 0xFFFFF977, 0x000002D2, 0x000017F1, 0xFFFFF977, 0x000002D2 },
+ { 0x0213EA94DE0640A4, 0x000031F1, 0xFFFFEAD4, 0x000004ED, 0x0000184C, 0xFFFFF983, 0x000002D4, 0x0000184C, 0xFFFFF983, 0x000002D4 },
+ { 0x0213F0FD42D04984, 0x00002EA9, 0xFFFFEBD7, 0x000004D5, 0x0000288D, 0xFFFFEDDB, 0x000004C0, 0x0000288D, 0xFFFFEDDB, 0x000004C0 },
+ { 0x0213F0FD42CA3984, 0x0000335F, 0xFFFFE82C, 0x00000579, 0x00001DBF, 0xFFFFF512, 0x0000038C, 0x00001DBF, 0xFFFFF512, 0x0000038C },
+ { 0x0213EA94DE201184, 0x0000224F, 0xFFFFF4B5, 0x00000391, 0x0000138C, 0xFFFFFCC3, 0x0000027A, 0x0000138C, 0xFFFFFCC3, 0x0000027A },
+ { 0x0213EA94DE1240A4, 0x0000320D, 0xFFFFEACD, 0x000004F5, 0x00001976, 0xFFFFF913, 0x000002E2, 0x00001976, 0xFFFFF913, 0x000002E2 },
+ { 0x0213EA94DE202104, 0x00001BEB, 0xFFFFF99C, 0x000002E4, 0x000016A4, 0xFFFFFB77, 0x000002C3, 0x000016A4, 0xFFFFFB77, 0x000002C3 },
+ { 0x0213EA94DE063044, 0x0000396E, 0xFFFFE616, 0x000005A9, 0x000018F4, 0xFFFFF91A, 0x000002E3, 0x000018F4, 0xFFFFF91A, 0x000002E3 },
+ { 0x0213EA94DE022864, 0x00003251, 0xFFFFEA8E, 0x000004FA, 0x000018EF, 0xFFFFF910, 0x000002E4, 0x000018EF, 0xFFFFF910, 0x000002E4 },
+ { 0x0213EA94DE1C1924, 0x00001DAF, 0xFFFFF857, 0x0000030D, 0x00001915, 0xFFFFF9D8, 0x000002F7, 0x00001915, 0xFFFFF9D8, 0x000002F7 },
+ { 0x0213EA94DE2041A4, 0x000025B6, 0xFFFFF26B, 0x000003E5, 0x00001531, 0xFFFFFB68, 0x000002AF, 0x00001531, 0xFFFFFB68, 0x000002AF },
+ { 0x0213EA94DE061884, 0x00002B2E, 0xFFFFEF2E, 0x00000440, 0x00001968, 0xFFFFF91A, 0x000002DF, 0x00001968, 0xFFFFF91A, 0x000002DF },
+ { 0x0213EA94DE1C2064, 0x00002305, 0xFFFFF528, 0x00000377, 0x000018A4, 0xFFFFF9EB, 0x000002F0, 0x000018A4, 0xFFFFF9EB, 0x000002F0 },
+ { 0x0213F0FD42CA40C4, 0x000032A1, 0xFFFFE992, 0x0000052E, 0x00001A55, 0xFFFFF826, 0x000002FE, 0x00001A55, 0xFFFFF826, 0x000002FE },
+ { 0x0213EA94DE042184, 0x00002CCD, 0xFFFFEE35, 0x00000462, 0x00001B09, 0xFFFFF7E6, 0x0000030F, 0x00001B09, 0xFFFFF7E6, 0x0000030F },
+ { 0x0213EA94DE323084, 0x00002602, 0xFFFFF2CF, 0x000003C5, 0x000016EE, 0xFFFFFAD4, 0x000002B4, 0x000016EE, 0xFFFFFAD4, 0x000002B4 },
+ { 0x0213F0FD42D01964, 0x00003370, 0xFFFFE891, 0x00000560, 0x000017F0, 0xFFFFF930, 0x000002DF, 0x000017F0, 0xFFFFF930, 0x000002DF },
+ { 0x0213F0FD42CA1884, 0x00002EDC, 0xFFFFEB6D, 0x000004EC, 0x000016E6, 0xFFFFF9ED, 0x000002BC, 0x000016E6, 0xFFFFF9ED, 0x000002BC },
+ { 0x0213EA94DE1228C4, 0x00002A05, 0xFFFFF13D, 0x000003F0, 0x00002065, 0xFFFFF57B, 0x00000378, 0x00002065, 0xFFFFF57B, 0x00000378 },
+ { 0x0213F0FD42CE2044, 0x00002F8A, 0xFFFFEB6E, 0x000004E4, 0x00001E3E, 0xFFFFF50E, 0x0000038D, 0x00001E3E, 0xFFFFF50E, 0x0000038D },
+ { 0x0213F0FD42CA3044, 0x00002BB5, 0xFFFFED6A, 0x000004A1, 0x000017BF, 0xFFFFF937, 0x000002E5, 0x000017BF, 0xFFFFF937, 0x000002E5 },
+ { 0x0213EA94DE201964, 0x0000202C, 0xFFFFF6CE, 0x0000033F, 0x000015EE, 0xFFFFFB83, 0x000002B9, 0x000015EE, 0xFFFFFB83, 0x000002B9 },
+ { 0x0213EA94DE022884, 0x00002C0C, 0xFFFFEF10, 0x0000043F, 0x00001A73, 0xFFFFF83E, 0x0000030C, 0x00001A73, 0xFFFFF83E, 0x0000030C },
+ { 0x0213EA94DE324104, 0x0000234F, 0xFFFFF460, 0x00000385, 0x000018C3, 0xFFFFF9A5, 0x000002DD, 0x000018C3, 0xFFFFF9A5, 0x000002DD },
+ { 0x0213F0FD42CE1904, 0x00003679, 0xFFFFE704, 0x00000595, 0x00002177, 0xFFFFF31A, 0x000003D7, 0x00002177, 0xFFFFF31A, 0x000003D7 },
+ { 0x0213F0FD42CA2924, 0x00003008, 0xFFFFEBB8, 0x000004D5, 0x000024FF, 0xFFFFF112, 0x00000430, 0x000024FF, 0xFFFFF112, 0x00000430 },
+ { 0x0213F0FD42C641A4, 0x00003848, 0xFFFFE6A3, 0x00000594, 0x00002958, 0xFFFFEE37, 0x000004A0, 0x00002958, 0xFFFFEE37, 0x000004A0 },
+ { 0x0213F0FD42CC1924, 0x00002FDF, 0xFFFFEB08, 0x000004FD, 0x00001D77, 0xFFFFF58B, 0x0000037A, 0x00001D77, 0xFFFFF58B, 0x0000037A },
+ { 0x0213EA94DE063064, 0x00002EC8, 0xFFFFED41, 0x00000481, 0x00001949, 0xFFFFF91C, 0x000002DF, 0x00001949, 0xFFFFF91C, 0x000002DF },
+ { 0x0213F0FD42D041A4, 0x000037C1, 0xFFFFE5BA, 0x000005D7, 0x0000252C, 0xFFFFF023, 0x00000460, 0x0000252C, 0xFFFFF023, 0x00000460 },
+ { 0x0213F0FD42CE2944, 0x00003716, 0xFFFFE70C, 0x0000058A, 0x000028CC, 0xFFFFEE57, 0x0000049D, 0x000028CC, 0xFFFFEE57, 0x0000049D },
+ { 0x0213F0FD42CA40E4, 0x000033D1, 0xFFFFE8E8, 0x00000547, 0x00001AB1, 0xFFFFF7E5, 0x00000309, 0x00001AB1, 0xFFFFF7E5, 0x00000309 },
+ { 0x0213F0FD42CC2944, 0x00002D72, 0xFFFFED65, 0x0000048E, 0x00001E0D, 0xFFFFF5A7, 0x00000370, 0x00001E0D, 0xFFFFF5A7, 0x00000370 },
+ { 0x0213EA94DE1C39A4, 0x00002292, 0xFFFFF49F, 0x00000393, 0x000017F4, 0xFFFFF9CD, 0x000002F5, 0x000017F4, 0xFFFFF9CD, 0x000002F5 },
+ { 0x0213EA94DE243044, 0x000026EE, 0xFFFFF18C, 0x000003F7, 0x000018A7, 0xFFFFF95A, 0x000002E5, 0x000018A7, 0xFFFFF95A, 0x000002E5 },
+ { 0x0213EA94DE042164, 0x00002F62, 0xFFFFEC9B, 0x000004A4, 0x0000194E, 0xFFFFF932, 0x000002D9, 0x0000194E, 0xFFFFF932, 0x000002D9 },
+ { 0x0213EA94DE1E3984, 0x00001CE8, 0xFFFFF7FA, 0x0000031C, 0x000014CE, 0xFFFFFBD4, 0x000002AB, 0x000014CE, 0xFFFFFBD4, 0x000002AB },
+ { 0x0213EA94DE1210E4, 0x00002E5A, 0xFFFFEDAB, 0x0000047C, 0x00001A82, 0xFFFFF8F7, 0x000002DE, 0x00001A82, 0xFFFFF8F7, 0x000002DE },
+ { 0x0213F0FD42CC30E4, 0x00003057, 0xFFFFEC34, 0x000004B9, 0x00002296, 0xFFFFF342, 0x000003D0, 0x00002296, 0xFFFFF342, 0x000003D0 },
+ { 0x0213EA94DE0418A4, 0x00002B0F, 0xFFFFEF58, 0x00000434, 0x00001BFD, 0xFFFFF721, 0x00000330, 0x00001BFD, 0xFFFFF721, 0x00000330 },
+ { 0x0213EA94DE2010A4, 0x00001F01, 0xFFFFF751, 0x0000032F, 0x00001502, 0xFFFFFC3E, 0x00000296, 0x00001502, 0xFFFFFC3E, 0x00000296 },
+ { 0x0213F0FD42CA3064, 0x00002FF4, 0xFFFFEAE2, 0x00000503, 0x00001B36, 0xFFFFF736, 0x00000330, 0x00001B36, 0xFFFFF736, 0x00000330 },
+ { 0x0213F0FD42CE2064, 0x00003762, 0xFFFFE5AB, 0x000005DE, 0x000018CB, 0xFFFFF896, 0x000002F4, 0x000018CB, 0xFFFFF896, 0x000002F4 },
+ { 0x0213F0FD42CC2064, 0x00002890, 0xFFFFEF92, 0x00000445, 0x0000191D, 0xFFFFF86F, 0x00000302, 0x0000191D, 0xFFFFF86F, 0x00000302 },
+ { 0x0213EA94DE043064, 0x00002F76, 0xFFFFEC0E, 0x000004BF, 0x00001F7D, 0xFFFFF41A, 0x000003C0, 0x00001F7D, 0xFFFFF41A, 0x000003C0 },
+ { 0x0213EA94DE1E08A4, 0x00001D55, 0xFFFFF7F8, 0x0000031E, 0x000015DF, 0xFFFFFB79, 0x000002B7, 0x000015DF, 0xFFFFFB79, 0x000002B7 },
+ { 0x0213EA94DE204924, 0x00001FE9, 0xFFFFF64A, 0x00000353, 0x000019E8, 0xFFFFF882, 0x0000032A, 0x000019E8, 0xFFFFF882, 0x0000032A },
+ { 0x0213EA94DE063964, 0x000030B5, 0xFFFFEBB8, 0x000004C4, 0x00001857, 0xFFFFF968, 0x000002D8, 0x00001857, 0xFFFFF968, 0x000002D8 },
+ { 0x0213F0FD42CA28C4, 0x00003398, 0xFFFFE9A3, 0x00000524, 0x00001FF9, 0xFFFFF458, 0x000003AD, 0x00001FF9, 0xFFFFF458, 0x000003AD },
+ { 0x0213F0FD42CE2964, 0x00003897, 0xFFFFE5BD, 0x000005C8, 0x00002519, 0xFFFFF0BA, 0x00000438, 0x00002519, 0xFFFFF0BA, 0x00000438 },
+ { 0x0213F0FD42D04064, 0x00003234, 0xFFFFE9B1, 0x00000530, 0x000022CC, 0xFFFFF20E, 0x00000409, 0x000022CC, 0xFFFFF20E, 0x00000409 },
+ { 0x0213EA94DE205104, 0x00001FD2, 0xFFFFF641, 0x00000354, 0x000017C9, 0xFFFFF9C0, 0x000002FB, 0x000017C9, 0xFFFFF9C0, 0x000002FB },
+ { 0x0213F0FD42CE48E4, 0x00003234, 0xFFFFE946, 0x0000053D, 0x00002267, 0xFFFFF1F5, 0x0000040D, 0x00002267, 0xFFFFF1F5, 0x0000040D },
+ { 0x0213EA94DE2029A4, 0x00002330, 0xFFFFF474, 0x00000399, 0x00001490, 0xFFFFFC67, 0x00000288, 0x00001490, 0xFFFFFC67, 0x00000288 },
+ { 0x0213F0FD42D03924, 0x000032A3, 0xFFFFE9EB, 0x0000051B, 0x0000234D, 0xFFFFF23C, 0x000003F7, 0x0000234D, 0xFFFFF23C, 0x000003F7 },
+ { 0x0213EA94DE200904, 0x0000217E, 0xFFFFF53A, 0x00000384, 0x00001511, 0xFFFFFBF5, 0x0000029E, 0x00001511, 0xFFFFFBF5, 0x0000029E },
+ { 0x0213F0FD42CE50E4, 0x0000384F, 0xFFFFE562, 0x000005E2, 0x0000295A, 0xFFFFED53, 0x000004D3, 0x0000295A, 0xFFFFED53, 0x000004D3 },
+ { 0x0213F0FD42D05124, 0x00003315, 0xFFFFE8D1, 0x00000552, 0x000025D1, 0xFFFFEFAF, 0x00000471, 0x000025D1, 0xFFFFEFAF, 0x00000471 },
+ { 0x0213F0FD42C64924, 0x00004183, 0xFFFFDF61, 0x000006DA, 0x0000193C, 0xFFFFF88F, 0x000002EC, 0x0000193C, 0xFFFFF88F, 0x000002EC },
+ { 0x0213EA94DE242164, 0x00002DFC, 0xFFFFEDF2, 0x0000047A, 0x00001755, 0xFFFFFAC2, 0x000002AC, 0x00001755, 0xFFFFFAC2, 0x000002AC },
+ { 0x0213F0FD42CA31A4, 0x000033FE, 0xFFFFE774, 0x0000059F, 0x00001E70, 0xFFFFF492, 0x000003A0, 0x00001E70, 0xFFFFF492, 0x000003A0 },
+ { 0x0213F0FD42C629A4, 0x000040D7, 0xFFFFDFB8, 0x000006CE, 0x00001AC8, 0xFFFFF773, 0x0000031D, 0x00001AC8, 0xFFFFF773, 0x0000031D },
+ { 0x0213EA94DE1E1164, 0x00001D02, 0xFFFFF803, 0x00000322, 0x000015FE, 0xFFFFFB71, 0x000002BB, 0x000015FE, 0xFFFFFB71, 0x000002BB },
+ { 0x0213F0FD42D02884, 0x00002EB0, 0xFFFFEC31, 0x000004C4, 0x00001B3C, 0xFFFFF73B, 0x00000330, 0x00001B3C, 0xFFFFF73B, 0x00000330 },
+ { 0x0213F0FD42CA4984, 0x00002D9F, 0xFFFFECBF, 0x000004A8, 0x000022B0, 0xFFFFF23C, 0x000003F9, 0x000022B0, 0xFFFFF23C, 0x000003F9 },
+ { 0x0213F0FD42CC18E4, 0x00002C6A, 0xFFFFEDAC, 0x00000488, 0x00002419, 0xFFFFF159, 0x00000427, 0x00002419, 0xFFFFF159, 0x00000427 },
+ { 0x0213EA94DE1210A4, 0x00002991, 0xFFFFF06C, 0x0000040E, 0x00001AA9, 0xFFFFF8D0, 0x000002E1, 0x00001AA9, 0xFFFFF8D0, 0x000002E1 },
+ { 0x0213EA94DE123904, 0x00002F8E, 0xFFFFED1B, 0x00000493, 0x00001DE4, 0xFFFFF69C, 0x00000347, 0x00001DE4, 0xFFFFF69C, 0x00000347 },
+ { 0x0213EA94DE204184, 0x00002136, 0xFFFFF540, 0x0000037C, 0x000014FF, 0xFFFFFB83, 0x000002B2, 0x000014FF, 0xFFFFFB83, 0x000002B2 },
+ { 0x0213EA94DE0618E4, 0x0000354C, 0xFFFFE97D, 0x0000051A, 0x00001906, 0xFFFFF965, 0x000002DD, 0x00001906, 0xFFFFF965, 0x000002DD },
+ { 0x0213F0FD42C620C4, 0x0000348B, 0xFFFFE94D, 0x0000051F, 0x0000285B, 0xFFFFEF1A, 0x00000473, 0x0000285B, 0xFFFFEF1A, 0x00000473 },
+ { 0x0213EA94DE3218A4, 0x000026E6, 0xFFFFF24E, 0x000003D6, 0x0000141F, 0xFFFFFCCE, 0x00000260, 0x0000141F, 0xFFFFFCCE, 0x00000260 },
+ { 0x0213F0FD42C64164, 0x00003CED, 0xFFFFE2A5, 0x0000064E, 0x00002060, 0xFFFFF3E0, 0x000003B0, 0x00002060, 0xFFFFF3E0, 0x000003B0 },
+ { 0x0213EA94DE021084, 0x000029D4, 0xFFFFEFF7, 0x00000426, 0x00001976, 0xFFFFF8E1, 0x000002EE, 0x00001976, 0xFFFFF8E1, 0x000002EE },
+ { 0x0213F0FD42CA40A4, 0x00003767, 0xFFFFE601, 0x000005CC, 0x00001D22, 0xFFFFF5F4, 0x00000361, 0x00001D22, 0xFFFFF5F4, 0x00000361 },
+ { 0x0213F0FD42C650C4, 0x00003CE8, 0xFFFFE2E8, 0x00000637, 0x0000232C, 0xFFFFF1E7, 0x00000405, 0x0000232C, 0xFFFFF1E7, 0x00000405 },
+ { 0x0213EA94DE201064, 0x000023A8, 0xFFFFF4CD, 0x00000386, 0x00001944, 0xFFFFF983, 0x00000300, 0x00001944, 0xFFFFF983, 0x00000300 },
+ { 0x0213F0FD42CC30A4, 0x00003451, 0xFFFFE8B9, 0x00000551, 0x00001AD7, 0xFFFFF7BF, 0x00000318, 0x00001AD7, 0xFFFFF7BF, 0x00000318 },
+ { 0x0213F0FD42CE2984, 0x0000381B, 0xFFFFE5A0, 0x000005D0, 0x00001E0F, 0xFFFFF521, 0x00000382, 0x00001E0F, 0xFFFFF521, 0x00000382 },
+ { 0x0213EA94DE2038C4, 0x000023A4, 0xFFFFF4A6, 0x00000394, 0x0000171F, 0xFFFFFABB, 0x000002D9, 0x0000171F, 0xFFFFFABB, 0x000002D9 },
+ { 0x0213F0FD42C620A4, 0x00003C2B, 0xFFFFE447, 0x000005F0, 0x0000207F, 0xFFFFF44E, 0x0000039A, 0x0000207F, 0xFFFFF44E, 0x0000039A },
+ { 0x0213F0FD42CC3984, 0x00002F07, 0xFFFFEB70, 0x000004E9, 0x00001765, 0xFFFFF9A5, 0x000002C6, 0x00001765, 0xFFFFF9A5, 0x000002C6 },
+ { 0x0213F0FD42C62984, 0x00003A01, 0xFFFFE4E0, 0x000005E7, 0x0000227A, 0xFFFFF292, 0x000003E5, 0x0000227A, 0xFFFFF292, 0x000003E5 },
+ { 0x0213F0FD42CE20A4, 0x0000376E, 0xFFFFE686, 0x000005A6, 0x00001FCF, 0xFFFFF43B, 0x000003A8, 0x00001FCF, 0xFFFFF43B, 0x000003A8 },
+ { 0x0213F0FFEF5A4984, 0x0000485F, 0xFFFFDCC1, 0x00000713, 0x00002CF8, 0xFFFFEC45, 0x000004DA, 0x00002CF8, 0xFFFFEC45, 0x000004DA },
+ { 0x0213F0FFEF5C3184, 0x0000331C, 0xFFFFE8FF, 0x00000541, 0x00002366, 0xFFFFF19D, 0x00000411, 0x00002366, 0xFFFFF19D, 0x00000411 },
+ { 0x0213F0FFEF643864, 0x00003CF3, 0xFFFFE15A, 0x00000694, 0x00002FB3, 0xFFFFE827, 0x000005B9, 0x00002FB3, 0xFFFFE827, 0x000005B9 },
+ { 0x0213EA94DE321104, 0x000023F3, 0xFFFFF3EA, 0x0000039A, 0x00001345, 0xFFFFFD6B, 0x00000241, 0x00001345, 0xFFFFFD6B, 0x00000241 },
+ { 0x0213F0FFEF5C28A4, 0x000038C0, 0xFFFFE58A, 0x000005CC, 0x000023CA, 0xFFFFF1AA, 0x00000408, 0x000023CA, 0xFFFFF1AA, 0x00000408 },
+ { 0x0213F0FFEF662944, 0x00004976, 0xFFFFDD6A, 0x000006D7, 0x000033C6, 0xFFFFE8EB, 0x0000054D, 0x000033C6, 0xFFFFE8EB, 0x0000054D },
+ { 0x0213F0FFEF644904, 0x00004049, 0xFFFFDF6D, 0x000006D8, 0x00003129, 0xFFFFE716, 0x000005E9, 0x00003129, 0xFFFFE716, 0x000005E9 },
+ { 0x0213F0FFEF661164, 0x000046C2, 0xFFFFDCEB, 0x0000071C, 0x00002E6D, 0xFFFFEA8F, 0x0000052E, 0x00002E6D, 0xFFFFEA8F, 0x0000052E },
+ { 0x0213F0FFEF6238A4, 0x00004080, 0xFFFFE1E1, 0x0000063A, 0x0000396D, 0xFFFFE40A, 0x0000062C, 0x0000396D, 0xFFFFE40A, 0x0000062C },
+ { 0x0213F0FFEF5E2124, 0x00003DE0, 0xFFFFE358, 0x0000060C, 0x00002AA2, 0xFFFFEDBF, 0x000004A0, 0x00002AA2, 0xFFFFEDBF, 0x000004A0 },
+ { 0x0213F0FFEF5E3144, 0x00003FC0, 0xFFFFE2A1, 0x0000061A, 0x000027D8, 0xFFFFEFEC, 0x0000043A, 0x000027D8, 0xFFFFEFEC, 0x0000043A },
+ { 0x0213F0FFEF661924, 0x00003FBF, 0xFFFFE2F5, 0x00000603, 0x000032D7, 0xFFFFE900, 0x00000552, 0x000032D7, 0xFFFFE900, 0x00000552 },
+ { 0x0213F0FFEF5C10E4, 0x000035EE, 0xFFFFE6CA, 0x000005A2, 0x0000247C, 0xFFFFF088, 0x00000446, 0x0000247C, 0xFFFFF088, 0x00000446 },
+ { 0x0213F0FFEF643884, 0x000039C8, 0xFFFFE3AE, 0x0000062A, 0x000028AF, 0xFFFFED24, 0x000004DF, 0x000028AF, 0xFFFFED24, 0x000004DF },
+ { 0x0213F0FFEF5C2884, 0x00003BDE, 0xFFFFE33B, 0x00000632, 0x00001B6C, 0xFFFFF720, 0x00000326, 0x00001B6C, 0xFFFFF720, 0x00000326 },
+ { 0x0213F0FFEF7210A4, 0x00003818, 0xFFFFE57D, 0x000005D4, 0x000020EF, 0xFFFFF327, 0x000003CE, 0x000020EF, 0xFFFFF327, 0x000003CE },
+ { 0x0213F0FFEF5E19A4, 0x000038DA, 0xFFFFE561, 0x000005D3, 0x0000297D, 0xFFFFED6D, 0x000004C5, 0x0000297D, 0xFFFFED6D, 0x000004C5 },
+ { 0x0213F0FFEF684884, 0x000027AC, 0xFFFFF0CE, 0x00000417, 0x00001F5F, 0xFFFFF484, 0x000003B2, 0x00001F5F, 0xFFFFF484, 0x000003B2 },
+ { 0x0213F0FFEF6648A4, 0x00003F02, 0xFFFFE222, 0x00000643, 0x000026D4, 0xFFFFF000, 0x00000443, 0x000026D4, 0xFFFFF000, 0x00000443 },
+ { 0x0213F0FFEF624164, 0x00004303, 0xFFFFDFE3, 0x00000690, 0x0000312C, 0xFFFFE912, 0x00000561, 0x0000312C, 0xFFFFE912, 0x00000561 },
+ { 0x0213F0FFEF600904, 0x000039E5, 0xFFFFE31F, 0x00000657, 0x00001D23, 0xFFFFF51F, 0x00000386, 0x00001D23, 0xFFFFF51F, 0x00000386 },
+ { 0x0213F0FFEF661144, 0x000041FA, 0xFFFFE01B, 0x00000697, 0x00002767, 0xFFFFEF90, 0x00000455, 0x00002767, 0xFFFFEF90, 0x00000455 },
+ { 0x0213F0FFEF6830A4, 0x00002888, 0xFFFFF11C, 0x00000403, 0x00001864, 0xFFFFF9D8, 0x000002D3, 0x00001864, 0xFFFFF9D8, 0x000002D3 },
+ { 0x0213EA94DE201864, 0x0000215C, 0xFFFFF5B6, 0x0000036D, 0x000015C5, 0xFFFFFB8A, 0x000002B5, 0x000015C5, 0xFFFFFB8A, 0x000002B5 },
+ { 0x0213F0FFEF683984, 0x00002FAF, 0xFFFFEC27, 0x000004CA, 0x00002184, 0xFFFFF39C, 0x000003CD, 0x00002184, 0xFFFFF39C, 0x000003CD },
+ { 0x0213F0FFEF5E10C4, 0x00004ACE, 0xFFFFD9A3, 0x000007BC, 0x00001A5D, 0xFFFFF7F6, 0x000002FC, 0x00001A5D, 0xFFFFF7F6, 0x000002FC },
+ { 0x0213F0FFEF5A3044, 0x00003763, 0xFFFFE797, 0x0000055F, 0x000029B5, 0xFFFFEEA1, 0x00000474, 0x000029B5, 0xFFFFEEA1, 0x00000474 },
+ { 0x0213F0FFEF5E3164, 0x00003832, 0xFFFFE6F9, 0x00000575, 0x00002C99, 0xFFFFEC42, 0x000004E3, 0x00002C99, 0xFFFFEC42, 0x000004E3 },
+ { 0x0213F0FFEF604164, 0x000041C9, 0xFFFFDE33, 0x0000071E, 0x0000199D, 0xFFFFF808, 0x000002F9, 0x0000199D, 0xFFFFF808, 0x000002F9 },
+ { 0x0213F0FFEF641164, 0x0000474A, 0xFFFFD96E, 0x00000802, 0x00002A30, 0xFFFFEB57, 0x0000053F, 0x00002A30, 0xFFFFEB57, 0x0000053F },
+ { 0x0213F0FFEF5C31C4, 0x0000312F, 0xFFFFEA6A, 0x00000508, 0x000029D3, 0xFFFFED38, 0x000004D3, 0x000029D3, 0xFFFFED38, 0x000004D3 },
+ { 0x0213F0FFEF7210C4, 0x00003BD6, 0xFFFFE2E7, 0x00000644, 0x00002093, 0xFFFFF37B, 0x000003BD, 0x00002093, 0xFFFFF37B, 0x000003BD },
+ { 0x0213F0FFEF6840E4, 0x00002F94, 0xFFFFECD4, 0x000004A3, 0x00002196, 0xFFFFF40B, 0x000003B5, 0x00002196, 0xFFFFF40B, 0x000003B5 },
+ { 0x0213F0FFEF5E1944, 0x0000369B, 0xFFFFE762, 0x00000571, 0x00002726, 0xFFFFEF99, 0x00000459, 0x00002726, 0xFFFFEF99, 0x00000459 },
+ { 0x0213F0FFEF642064, 0x00003F57, 0xFFFFDF47, 0x000006F4, 0x00002E5F, 0xFFFFE8AE, 0x000005AB, 0x00002E5F, 0xFFFFE8AE, 0x000005AB },
+ { 0x0213EA94DE0A40C4, 0x00004313, 0xFFFFDD81, 0x0000072D, 0x00002468, 0xFFFFF068, 0x00000440, 0x00002468, 0xFFFFF068, 0x00000440 },
+ { 0x0213F0FFEF683044, 0x00002A35, 0xFFFFEFA8, 0x00000441, 0x00001F3F, 0xFFFFF4F3, 0x000003A0, 0x00001F3F, 0xFFFFF4F3, 0x000003A0 },
+ { 0x0213F0FFEF6630A4, 0x00003E33, 0xFFFFE4B0, 0x000005AF, 0x00002802, 0xFFFFF092, 0x00000412, 0x00002802, 0xFFFFF092, 0x00000412 },
+ { 0x0213EA94DE323904, 0x00002815, 0xFFFFF20E, 0x000003DD, 0x00001C33, 0xFFFFF7D5, 0x0000032A, 0x00001C33, 0xFFFFF7D5, 0x0000032A },
+ { 0x0213F0FFEF5A2184, 0x00003CC2, 0xFFFFE43E, 0x000005DE, 0x00002C16, 0xFFFFECED, 0x000004BA, 0x00002C16, 0xFFFFECED, 0x000004BA },
+ { 0x0213F0FFEF5C4084, 0x00003CFA, 0xFFFFE1EE, 0x00000673, 0x00001F7D, 0xFFFFF402, 0x000003AE, 0x00001F7D, 0xFFFFF402, 0x000003AE },
+ { 0x0213F0FFEF622104, 0x0000486E, 0xFFFFDD43, 0x000006EE, 0x000036F0, 0xFFFFE609, 0x000005D5, 0x000036F0, 0xFFFFE609, 0x000005D5 },
+ { 0x0213F0FFEF5C4964, 0x000039FE, 0xFFFFE41F, 0x00000613, 0x0000266C, 0xFFFFEF35, 0x0000047D, 0x0000266C, 0xFFFFEF35, 0x0000047D },
+ { 0x0213EA94DE123124, 0x00002EA4, 0xFFFFEE3B, 0x00000462, 0x00002126, 0xFFFFF4E2, 0x0000038F, 0x00002126, 0xFFFFF4E2, 0x0000038F },
+ { 0x0213F0FFEF683944, 0x00002D2E, 0xFFFFEE7B, 0x00000462, 0x0000229D, 0xFFFFF363, 0x000003D4, 0x0000229D, 0xFFFFF363, 0x000003D4 },
+ { 0x0213F0FFEF5E2844, 0x0000375C, 0xFFFFE695, 0x0000059D, 0x00002319, 0xFFFFF237, 0x000003EE, 0x00002319, 0xFFFFF237, 0x000003EE },
+ { 0x0213F0FFEF7250C4, 0x00004522, 0xFFFFDC71, 0x0000075E, 0x0000247E, 0xFFFFF0A0, 0x0000043C, 0x0000247E, 0xFFFFF0A0, 0x0000043C },
+ { 0x0213EA94DE1248E4, 0x00002E58, 0xFFFFECB9, 0x000004A9, 0x0000199A, 0xFFFFF8CF, 0x000002E9, 0x0000199A, 0xFFFFF8CF, 0x000002E9 },
+ { 0x0213F0FFEF6438E4, 0x00003791, 0xFFFFE5FE, 0x000005B6, 0x000029F5, 0xFFFFED0D, 0x000004CD, 0x000029F5, 0xFFFFED0D, 0x000004CD },
+ { 0x0213EA94DE244144, 0x00002E9E, 0xFFFFEC8D, 0x000004C1, 0x000019D0, 0xFFFFF869, 0x0000030F, 0x000019D0, 0xFFFFF869, 0x0000030F },
+ { 0x0213EA94DE203964, 0x0000237C, 0xFFFFF435, 0x000003A6, 0x000014EB, 0xFFFFFBC4, 0x000002AF, 0x000014EB, 0xFFFFFBC4, 0x000002AF },
+ { 0x0213F0FFEF662924, 0x00003FE5, 0xFFFFE4A2, 0x000005A0, 0x00003416, 0xFFFFE995, 0x00000523, 0x00003416, 0xFFFFE995, 0x00000523 },
+ { 0x0213F0FFEF5C0924, 0x00002B27, 0xFFFFED51, 0x000004A5, 0x000025D1, 0xFFFFEF18, 0x00000492, 0x000025D1, 0xFFFFEF18, 0x00000492 },
+ { 0x0213F0FFEF684904, 0x00002D77, 0xFFFFED79, 0x00000494, 0x00002196, 0xFFFFF352, 0x000003DE, 0x00002196, 0xFFFFF352, 0x000003DE },
+ { 0x0213F0FFEF5C20C4, 0x00003750, 0xFFFFE6AC, 0x00000596, 0x00002524, 0xFFFFF0B5, 0x00000431, 0x00002524, 0xFFFFF0B5, 0x00000431 },
+ { 0x0213EA94DE122944, 0x00002896, 0xFFFFF1BB, 0x000003D9, 0x00001CE0, 0xFFFFF753, 0x0000032F, 0x00001CE0, 0xFFFFF753, 0x0000032F },
+ { 0x0213F0FFEF641984, 0x00003CA7, 0xFFFFE0F7, 0x000006B1, 0x00002CB8, 0xFFFFE9AB, 0x00000587, 0x00002CB8, 0xFFFFE9AB, 0x00000587 },
+ { 0x0213EA94DE322864, 0x00002513, 0xFFFFF323, 0x000003BC, 0x00001965, 0xFFFFF93C, 0x000002F0, 0x00001965, 0xFFFFF93C, 0x000002F0 },
+ { 0x0213F0FFEF662164, 0x00003914, 0xFFFFE683, 0x00000586, 0x00003120, 0xFFFFE9A6, 0x00000543, 0x00003120, 0xFFFFE9A6, 0x00000543 },
+ { 0x0213F0FFEF643904, 0x000040D0, 0xFFFFE007, 0x000006AC, 0x00002B9E, 0xFFFFEBF5, 0x000004FB, 0x00002B9E, 0xFFFFEBF5, 0x000004FB },
+ { 0x0213F0FFEF5A4884, 0x00004412, 0xFFFFDF5F, 0x000006A9, 0x00002A9E, 0xFFFFEDCE, 0x00000498, 0x00002A9E, 0xFFFFEDCE, 0x00000498 },
+ { 0x0213F0FFEF624884, 0x000042A6, 0xFFFFDFEF, 0x00000696, 0x00002E65, 0xFFFFEAAE, 0x00000529, 0x00002E65, 0xFFFFEAAE, 0x00000529 },
+ { 0x0213EA94DE322124, 0x000022E8, 0xFFFFF565, 0x0000035F, 0x00001890, 0xFFFFFA61, 0x000002C6, 0x00001890, 0xFFFFFA61, 0x000002C6 },
+ { 0x0213F0FFEF6239A4, 0x00004637, 0xFFFFDDD8, 0x000006E9, 0x0000349D, 0xFFFFE6C8, 0x000005C7, 0x0000349D, 0xFFFFE6C8, 0x000005C7 },
+ { 0x0213EA94DE263904, 0x00004686, 0xFFFFDC58, 0x0000073D, 0x00003972, 0xFFFFE27B, 0x0000068E, 0x00003972, 0xFFFFE27B, 0x0000068E },
+ { 0x0213F0FFEF6808E4, 0x00002B35, 0xFFFFEE9C, 0x0000046C, 0x00001F5B, 0xFFFFF4A3, 0x000003A9, 0x00001F5B, 0xFFFFF4A3, 0x000003A9 },
+ { 0x0213F0FFEF724144, 0x00003AC9, 0xFFFFE3B2, 0x0000061B, 0x000023A1, 0xFFFFF170, 0x0000040F, 0x000023A1, 0xFFFFF170, 0x0000040F },
+ { 0x0213F0FFEF5E1884, 0x00003C50, 0xFFFFE37E, 0x00000617, 0x0000218F, 0xFFFFF339, 0x000003C4, 0x0000218F, 0xFFFFF339, 0x000003C4 },
+ { 0x0213F0FFEF663044, 0x00003793, 0xFFFFE761, 0x0000055D, 0x000029C7, 0xFFFFEE03, 0x00000496, 0x000029C7, 0xFFFFEE03, 0x00000496 },
+ { 0x0213F0FFEF6438A4, 0x000040B5, 0xFFFFDF78, 0x000006DA, 0x00002DED, 0xFFFFEA20, 0x00000551, 0x00002DED, 0xFFFFEA20, 0x00000551 },
+ { 0x0213F0FFEF601144, 0x000039D6, 0xFFFFE37D, 0x0000063C, 0x00001AED, 0xFFFFF6E2, 0x00000331, 0x00001AED, 0xFFFFF6E2, 0x00000331 },
+ { 0x0213F0FFEF662144, 0x0000431F, 0xFFFFE09B, 0x0000066A, 0x00002BDF, 0xFFFFED93, 0x00000496, 0x00002BDF, 0xFFFFED93, 0x00000496 },
+ { 0x0213F0FFEF623864, 0x00004887, 0xFFFFDC65, 0x00000721, 0x00003669, 0xFFFFE5C4, 0x000005E9, 0x00003669, 0xFFFFE5C4, 0x000005E9 },
+ { 0x0213F0FFEF640924, 0x00004120, 0xFFFFDDAE, 0x00000748, 0x0000303B, 0xFFFFE70D, 0x000005FC, 0x0000303B, 0xFFFFE70D, 0x000005FC },
+ { 0x0213F0FFEF5E28A4, 0x0000415D, 0xFFFFE0BE, 0x0000067B, 0x00002FA7, 0xFFFFEA28, 0x00000538, 0x00002FA7, 0xFFFFEA28, 0x00000538 },
+ { 0x0213F0FFEF681904, 0x00002B12, 0xFFFFEFF9, 0x00000428, 0x00001DDA, 0xFFFFF693, 0x00000356, 0x00001DDA, 0xFFFFF693, 0x00000356 },
+ { 0x0213F0FFEF5E3184, 0x00003ED3, 0xFFFFE28D, 0x0000062D, 0x00002B00, 0xFFFFED4E, 0x000004B3, 0x00002B00, 0xFFFFED4E, 0x000004B3 },
+ { 0x0213F0FFEF6250A4, 0x00004218, 0xFFFFE039, 0x0000068F, 0x00002F84, 0xFFFFEA0C, 0x00000541, 0x00002F84, 0xFFFFEA0C, 0x00000541 },
+ { 0x0213F0FFEF5A3844, 0x00003FF5, 0xFFFFE2A3, 0x00000617, 0x00003017, 0xFFFFEA7A, 0x00000520, 0x00003017, 0xFFFFEA7A, 0x00000520 },
+ { 0x0213F0FFEF5A08A4, 0x00004304, 0xFFFFDFCC, 0x0000069E, 0x00002E0C, 0xFFFFEB51, 0x00000505, 0x00002E0C, 0xFFFFEB51, 0x00000505 },
+ { 0x0213F0FFEF641944, 0x00003D3A, 0xFFFFE17F, 0x00000687, 0x0000284C, 0xFFFFED83, 0x000004CD, 0x0000284C, 0xFFFFED83, 0x000004CD },
+ { 0x0213F0FFEF5E40A4, 0x000042F5, 0xFFFFDF76, 0x000006B2, 0x000027B6, 0xFFFFEF72, 0x00000455, 0x000027B6, 0xFFFFEF72, 0x00000455 },
+ { 0x0213F0FFEF5C38C4, 0x00004267, 0xFFFFDF29, 0x000006D5, 0x0000298F, 0xFFFFEDBD, 0x000004AC, 0x0000298F, 0xFFFFEDBD, 0x000004AC },
+ { 0x0213EA94DE240924, 0x0000303E, 0xFFFFEC00, 0x000004CB, 0x000021CD, 0xFFFFF36E, 0x000003D6, 0x000021CD, 0xFFFFF36E, 0x000003D6 },
+ { 0x0213F0FFEF5E28C4, 0x00003127, 0xFFFFEBDB, 0x000004A6, 0x00002E95, 0xFFFFEB78, 0x000004F3, 0x00002E95, 0xFFFFEB78, 0x000004F3 },
+ { 0x0213EA94DE1C1064, 0x00002655, 0xFFFFF2D9, 0x000003CF, 0x000019F5, 0xFFFFF8E7, 0x00000313, 0x000019F5, 0xFFFFF8E7, 0x00000313 },
+ { 0x0213EA94DE164084, 0x00002372, 0xFFFFF449, 0x0000039B, 0x00001544, 0xFFFFFC16, 0x0000028B, 0x00001544, 0xFFFFFC16, 0x0000028B },
+ { 0x0213F0FFEF6628C4, 0x0000348E, 0xFFFFEB20, 0x000004B2, 0x00002BE8, 0xFFFFEE80, 0x00000467, 0x00002BE8, 0xFFFFEE80, 0x00000467 },
+ { 0x0213F0FFEF5E1104, 0x00004092, 0xFFFFE073, 0x0000069B, 0x00002061, 0xFFFFF403, 0x000003A0, 0x00002061, 0xFFFFF403, 0x000003A0 },
+ { 0x0213F0FFEF7220E4, 0x000039D1, 0xFFFFE55D, 0x000005CC, 0x000025CB, 0xFFFFF0C0, 0x00000428, 0x000025CB, 0xFFFFF0C0, 0x00000428 },
+ { 0x0213F0FFEF5E4884, 0x000042AA, 0xFFFFDF68, 0x000006C2, 0x0000290B, 0xFFFFEE78, 0x00000485, 0x0000290B, 0xFFFFEE78, 0x00000485 },
+ { 0x0213F0FFEF7218C4, 0x0000356F, 0xFFFFE7AC, 0x0000056E, 0x00001BE8, 0xFFFFF6E3, 0x0000032A, 0x00001BE8, 0xFFFFF6E3, 0x0000032A },
+ { 0x0213F0FFEF5E1144, 0x00003525, 0xFFFFE7FF, 0x0000055D, 0x0000242C, 0xFFFFF12E, 0x0000041D, 0x0000242C, 0xFFFFF12E, 0x0000041D },
+ { 0x0213F0FFEF5C48C4, 0x00003360, 0xFFFFE895, 0x00000550, 0x00002175, 0xFFFFF29E, 0x000003E9, 0x00002175, 0xFFFFF29E, 0x000003E9 },
+ { 0x0213F0FFEF6440A4, 0x00003C94, 0xFFFFE1C4, 0x0000067E, 0x00002E28, 0xFFFFE964, 0x0000057F, 0x00002E28, 0xFFFFE964, 0x0000057F },
+ { 0x0213F0FFEF724124, 0x0000431C, 0xFFFFDE4B, 0x000006FF, 0x00002270, 0xFFFFF268, 0x000003E5, 0x00002270, 0xFFFFF268, 0x000003E5 },
+ { 0x0213EA94DE1218C4, 0x00002B67, 0xFFFFF01D, 0x00000414, 0x000019FB, 0xFFFFF961, 0x000002D8, 0x000019FB, 0xFFFFF961, 0x000002D8 },
+ { 0x0213F0FFEF5E3984, 0x0000400B, 0xFFFFE13D, 0x0000066F, 0x000024F3, 0xFFFFF125, 0x00000417, 0x000024F3, 0xFFFFF125, 0x00000417 },
+ { 0x0213F0FFEF5A20A4, 0x00004460, 0xFFFFE00E, 0x0000067B, 0x000023DF, 0xFFFFF2E6, 0x000003BB, 0x000023DF, 0xFFFFF2E6, 0x000003BB },
+ { 0x0213F0FFEF641864, 0x00003AFB, 0xFFFFE2C5, 0x00000650, 0x00002D46, 0xFFFFE9C4, 0x00000571, 0x00002D46, 0xFFFFE9C4, 0x00000571 },
+ { 0x0213F0FFEF622924, 0x00005482, 0xFFFFD5BC, 0x0000081A, 0x00003250, 0xFFFFE961, 0x00000541, 0x00003250, 0xFFFFE961, 0x00000541 },
+ { 0x0213F0FFEF5C2944, 0x00003D27, 0xFFFFE2FA, 0x00000632, 0x00002A4D, 0xFFFFED6A, 0x000004BB, 0x00002A4D, 0xFFFFED6A, 0x000004BB },
+ { 0x0213F0FFEF6018A4, 0x00003E03, 0xFFFFE142, 0x00000690, 0x00001E08, 0xFFFFF555, 0x0000036C, 0x00001E08, 0xFFFFF555, 0x0000036C },
+ { 0x0213F0FFEF5C2064, 0x000031B5, 0xFFFFE97D, 0x00000535, 0x0000232E, 0xFFFFF166, 0x00000422, 0x0000232E, 0xFFFFF166, 0x00000422 },
+ { 0x0213F0FFEF5E18E4, 0x00003753, 0xFFFFE724, 0x00000575, 0x0000281A, 0xFFFFEF1A, 0x0000046B, 0x0000281A, 0xFFFFEF1A, 0x0000046B },
+ { 0x0213EA94DE204144, 0x00002071, 0xFFFFF5C9, 0x0000036F, 0x00001470, 0xFFFFFBF7, 0x000002A5, 0x00001470, 0xFFFFFBF7, 0x000002A5 },
+ { 0x0213F0FFEF683144, 0x00002799, 0xFFFFF223, 0x000003CF, 0x00001CD3, 0xFFFFF74A, 0x00000333, 0x00001CD3, 0xFFFFF74A, 0x00000333 },
+ { 0x0213F0FFEF6610C4, 0x000040DF, 0xFFFFE11C, 0x00000664, 0x000031D4, 0xFFFFE8BC, 0x0000056F, 0x000031D4, 0xFFFFE8BC, 0x0000056F },
+ { 0x0213F0FFEF6440C4, 0x00003A4D, 0xFFFFE3A6, 0x00000627, 0x00002871, 0xFFFFEDA0, 0x000004C0, 0x00002871, 0xFFFFEDA0, 0x000004C0 },
+ { 0x0213F0FFEF681984, 0x00002AF9, 0xFFFFEED7, 0x00000464, 0x0000219B, 0xFFFFF368, 0x000003D6, 0x0000219B, 0xFFFFF368, 0x000003D6 },
+ { 0x0213EA94DE323124, 0x000026D5, 0xFFFFF36C, 0x000003A3, 0x00001BC6, 0xFFFFF881, 0x00000311, 0x00001BC6, 0xFFFFF881, 0x00000311 },
+ { 0x0213F0FFEF5E2044, 0x0000325D, 0xFFFFEA07, 0x0000050B, 0x000026D1, 0xFFFFEFB3, 0x0000045A, 0x000026D1, 0xFFFFEFB3, 0x0000045A },
+ { 0x0213F0FFEF682864, 0x00002F75, 0xFFFFEC64, 0x000004BE, 0x00001EEB, 0xFFFFF559, 0x00000386, 0x00001EEB, 0xFFFFF559, 0x00000386 },
+ { 0x0213F0FFEF5A38A4, 0x00003C2F, 0xFFFFE541, 0x000005A3, 0x000025B6, 0xFFFFF16F, 0x000003FA, 0x000025B6, 0xFFFFF16F, 0x000003FA },
+ { 0x0213F0FFEF684924, 0x00002BC2, 0xFFFFEE89, 0x0000046A, 0x00001D04, 0xFFFFF651, 0x00000361, 0x00001D04, 0xFFFFF651, 0x00000361 },
+ { 0x0213F0FFEF6829A4, 0x00002DD0, 0xFFFFED40, 0x0000049F, 0x00001C8C, 0xFFFFF6B3, 0x00000353, 0x00001C8C, 0xFFFFF6B3, 0x00000353 },
+ { 0x0213EA94DE1C08E4, 0x000021ED, 0xFFFFF530, 0x00000380, 0x00001643, 0xFFFFFB1C, 0x000002C3, 0x00001643, 0xFFFFFB1C, 0x000002C3 },
+ { 0x0213EA94DE321904, 0x000028C7, 0xFFFFF160, 0x000003FD, 0x00001990, 0xFFFFF994, 0x000002E2, 0x00001990, 0xFFFFF994, 0x000002E2 },
+ { 0x0213F0FFEF6610A4, 0x0000431C, 0xFFFFDF9D, 0x000006A3, 0x000034A6, 0xFFFFE6B0, 0x000005C9, 0x000034A6, 0xFFFFE6B0, 0x000005C9 },
+ { 0x0213EA94DE2630A4, 0x00004115, 0xFFFFE0D6, 0x00000667, 0x000031AD, 0xFFFFE850, 0x00000585, 0x000031AD, 0xFFFFE850, 0x00000585 },
+ { 0x0213F0FFEF643924, 0x0000424A, 0xFFFFDEEC, 0x000006E1, 0x0000346A, 0xFFFFE5EA, 0x00000602, 0x0000346A, 0xFFFFE5EA, 0x00000602 },
+ { 0x0213F0FFEF661984, 0x00004990, 0xFFFFDAFA, 0x00000771, 0x00002A9C, 0xFFFFED37, 0x000004BC, 0x00002A9C, 0xFFFFED37, 0x000004BC },
+ { 0x0213F0FFEF6428A4, 0x00003858, 0xFFFFE568, 0x000005D2, 0x00003030, 0xFFFFE8B0, 0x0000058E, 0x00003030, 0xFFFFE8B0, 0x0000058E },
+ { 0x0213F0FFEF684164, 0x00001EDC, 0xFFFFF6CD, 0x00000322, 0x00001FCA, 0xFFFFF4BD, 0x0000039E, 0x00001FCA, 0xFFFFF4BD, 0x0000039E },
+ { 0x0213F0FFEF662124, 0x00004C88, 0xFFFFDBA3, 0x0000071B, 0x000030C4, 0xFFFFEAFD, 0x000004F7, 0x000030C4, 0xFFFFEAFD, 0x000004F7 },
+ { 0x0213F0FFEF680904, 0x00002B9A, 0xFFFFEE41, 0x0000047D, 0x00002131, 0xFFFFF344, 0x000003E5, 0x00002131, 0xFFFFF344, 0x000003E5 },
+ { 0x0213F0FFEF623984, 0x00003E4B, 0xFFFFE33C, 0x000005FA, 0x00003877, 0xFFFFE437, 0x0000062E, 0x00003877, 0xFFFFE437, 0x0000062E },
+ { 0x0213EA94DE322064, 0x00002376, 0xFFFFF444, 0x0000038A, 0x000017ED, 0xFFFFFA4C, 0x000002C1, 0x000017ED, 0xFFFFFA4C, 0x000002C1 },
+ { 0x0213F0FFEF661084, 0x00004517, 0xFFFFDDF4, 0x000006F2, 0x000030DC, 0xFFFFE8EF, 0x00000571, 0x000030DC, 0xFFFFE8EF, 0x00000571 },
+ { 0x0213F0FFEF681944, 0x0000270C, 0xFFFFF1F3, 0x000003DF, 0x0000207B, 0xFFFFF474, 0x000003AD, 0x0000207B, 0xFFFFF474, 0x000003AD },
+ { 0x0213F0FFEF645144, 0x00004086, 0xFFFFDF39, 0x000006E3, 0x00002A24, 0xFFFFEC2B, 0x000004FF, 0x00002A24, 0xFFFFEC2B, 0x000004FF },
+ { 0x0213F0FFEF5C3124, 0x00003BDE, 0xFFFFE45E, 0x000005EB, 0x00002CD5, 0xFFFFEC45, 0x000004DD, 0x00002CD5, 0xFFFFEC45, 0x000004DD },
+ { 0x0213F0FFEF7230E4, 0x00003803, 0xFFFFE714, 0x00000579, 0x0000288A, 0xFFFFEF21, 0x0000046B, 0x0000288A, 0xFFFFEF21, 0x0000046B },
+ { 0x0213F0FFEF601104, 0x00003F50, 0xFFFFE002, 0x000006CD, 0x00001AD4, 0xFFFFF72E, 0x0000031F, 0x00001AD4, 0xFFFFF72E, 0x0000031F },
+ { 0x0213F0FFEF6820E4, 0x00002968, 0xFFFFF100, 0x00000402, 0x00001FB5, 0xFFFFF57C, 0x0000037F, 0x00001FB5, 0xFFFFF57C, 0x0000037F },
+ { 0x0213F0FFEF662104, 0x00004283, 0xFFFFE2A7, 0x000005F5, 0x00003165, 0xFFFFEB0C, 0x000004EC, 0x00003165, 0xFFFFEB0C, 0x000004EC },
+ { 0x0213F0FFEF6431A4, 0x00004253, 0xFFFFDDA8, 0x00000732, 0x00002E5C, 0xFFFFE90A, 0x00000593, 0x00002E5C, 0xFFFFE90A, 0x00000593 },
+ { 0x0213F0FFEF5C50A4, 0x00003551, 0xFFFFE756, 0x0000058D, 0x000029A7, 0xFFFFED0C, 0x000004DE, 0x000029A7, 0xFFFFED0C, 0x000004DE },
+ { 0x0213F0FFEF6428C4, 0x00003728, 0xFFFFE604, 0x000005C4, 0x00002832, 0xFFFFEE64, 0x00000493, 0x00002832, 0xFFFFEE64, 0x00000493 },
+ { 0x0213F0FFEF623964, 0x00004796, 0xFFFFDCC8, 0x00000715, 0x000032AB, 0xFFFFE848, 0x0000057C, 0x000032AB, 0xFFFFE848, 0x0000057C },
+ { 0x0213F0FFEF6210C4, 0x000049DF, 0xFFFFDB24, 0x0000075F, 0x00003076, 0xFFFFE967, 0x0000055C, 0x00003076, 0xFFFFE967, 0x0000055C },
+ { 0x0213F0FFEF721104, 0x00003F13, 0xFFFFE099, 0x000006A8, 0x00002279, 0xFFFFF226, 0x000003F3, 0x00002279, 0xFFFFF226, 0x000003F3 },
+ { 0x0213F0FFEF6430A4, 0x00003E03, 0xFFFFE19F, 0x00000674, 0x00002D66, 0xFFFFEAA7, 0x00000537, 0x00002D66, 0xFFFFEAA7, 0x00000537 },
+ { 0x0213F0FFEF5C4104, 0x000037DA, 0xFFFFE63F, 0x000005A7, 0x00002543, 0xFFFFF0A0, 0x00000431, 0x00002543, 0xFFFFF0A0, 0x00000431 },
+ { 0x0213F0FFEF624944, 0x00003D82, 0xFFFFE3F5, 0x000005D9, 0x0000332F, 0xFFFFE834, 0x00000577, 0x0000332F, 0xFFFFE834, 0x00000577 },
+ { 0x0213EA94DE1228C4, 0x00002915, 0xFFFFF1E0, 0x000003D4, 0x00002065, 0xFFFFF57B, 0x00000378, 0x00002065, 0xFFFFF57B, 0x00000378 },
+ { 0x0213F0FFEF5E4904, 0x000036FC, 0xFFFFE72D, 0x00000577, 0x00002811, 0xFFFFEF30, 0x00000464, 0x00002811, 0xFFFFEF30, 0x00000464 },
+ { 0x0213F0FFEF623184, 0x00004767, 0xFFFFDD30, 0x000006FD, 0x00003703, 0xFFFFE564, 0x000005F8, 0x00003703, 0xFFFFE564, 0x000005F8 },
+ { 0x0213F0FFEF603184, 0x00003094, 0xFFFFEAA9, 0x000004F5, 0x000022E7, 0xFFFFF200, 0x000003FB, 0x000022E7, 0xFFFFF200, 0x000003FB },
+ { 0x0213F0FFEF641144, 0x00003EF0, 0xFFFFDF83, 0x000006ED, 0x00002A27, 0xFFFFEB7C, 0x00000537, 0x00002A27, 0xFFFFEB7C, 0x00000537 },
+ { 0x0213F0FFEF681124, 0x0000243C, 0xFFFFF358, 0x000003AC, 0x00001DC4, 0xFFFFF5E9, 0x00000372, 0x00001DC4, 0xFFFFF5E9, 0x00000372 },
+ { 0x0213F0FFEF722144, 0x0000284B, 0xFFFFF036, 0x0000040F, 0x00001FCD, 0xFFFFF445, 0x00000395, 0x00001FCD, 0xFFFFF445, 0x00000395 },
+ { 0x0213F0FFEF6840C4, 0x00002611, 0xFFFFF285, 0x000003C7, 0x00001CFE, 0xFFFFF6A0, 0x00000355, 0x00001CFE, 0xFFFFF6A0, 0x00000355 },
+ { 0x0213EA94DE1C39A4, 0x00002292, 0xFFFFF49F, 0x00000393, 0x000017F4, 0xFFFFF9CD, 0x000002F5, 0x000017F4, 0xFFFFF9CD, 0x000002F5 },
+ { 0x0213F0FFEF5E38A4, 0x000037F3, 0xFFFFE68D, 0x00000590, 0x00002443, 0xFFFFF1AD, 0x000003FA, 0x00002443, 0xFFFFF1AD, 0x000003FA },
+ { 0x0213F0FFEF682144, 0x00002C01, 0xFFFFEF3F, 0x00000444, 0x0000210A, 0xFFFFF475, 0x000003A7, 0x0000210A, 0xFFFFF475, 0x000003A7 },
+ { 0x0213EA94DE1210E4, 0x00002C0E, 0xFFFFEF0F, 0x00000446, 0x00001A82, 0xFFFFF8F7, 0x000002DE, 0x00001A82, 0xFFFFF8F7, 0x000002DE },
+ { 0x0213F0FFEF5E20C4, 0x00003FA6, 0xFFFFE20A, 0x0000063F, 0x00002E29, 0xFFFFEB21, 0x00000510, 0x00002E29, 0xFFFFEB21, 0x00000510 },
+ { 0x0213F0FFEF5C2164, 0x00003BCD, 0xFFFFE31B, 0x0000063C, 0x000019AF, 0xFFFFF83D, 0x000002F8, 0x000019AF, 0xFFFFF83D, 0x000002F8 },
+ { 0x0213F0FFEF664164, 0x000044C8, 0xFFFFDF08, 0x000006B0, 0x00002E2E, 0xFFFFEB62, 0x000004FD, 0x00002E2E, 0xFFFFEB62, 0x000004FD },
+ { 0x0213F0FFEF5C1884, 0x00003790, 0xFFFFE571, 0x000005E3, 0x00002042, 0xFFFFF35D, 0x000003CF, 0x00002042, 0xFFFFF35D, 0x000003CF },
+ { 0x0213F0FFEF6050E4, 0x000038AC, 0xFFFFE46C, 0x00000609, 0x0000215E, 0xFFFFF22D, 0x00000403, 0x0000215E, 0xFFFFF22D, 0x00000403 },
+ { 0x0213F0FFEF5E29A4, 0x00003A1E, 0xFFFFE536, 0x000005C9, 0x000024F3, 0xFFFFF11A, 0x0000041B, 0x000024F3, 0xFFFFF11A, 0x0000041B },
+ { 0x0213F0FFEF6650E4, 0x0000431A, 0xFFFFDF1B, 0x000006C5, 0x00002F34, 0xFFFFEA02, 0x00000545, 0x00002F34, 0xFFFFEA02, 0x00000545 },
+ { 0x0213F0FFEF641904, 0x000042DC, 0xFFFFDE28, 0x0000070C, 0x00003B53, 0xFFFFE0EA, 0x000006E2, 0x00003B53, 0xFFFFE0EA, 0x000006E2 },
+ { 0x0213F0FFEF683164, 0x0000264B, 0xFFFFF29A, 0x000003C4, 0x000021D0, 0xFFFFF3CE, 0x000003C4, 0x000021D0, 0xFFFFF3CE, 0x000003C4 },
+ { 0x0213F0FFEF5A4064, 0x00004225, 0xFFFFE0E8, 0x00000665, 0x00002B53, 0xFFFFED89, 0x0000049F, 0x00002B53, 0xFFFFED89, 0x0000049F },
+ { 0x0213EA94DE204924, 0x00001FCC, 0xFFFFF63F, 0x00000358, 0x000019E8, 0xFFFFF882, 0x0000032A, 0x000019E8, 0xFFFFF882, 0x0000032A },
+ { 0x0213F0FFEF6240A4, 0x000045E0, 0xFFFFDDD0, 0x000006ED, 0x00003193, 0xFFFFE8BD, 0x00000572, 0x00003193, 0xFFFFE8BD, 0x00000572 },
+ { 0x0213F0FFEF683924, 0x000024FC, 0xFFFFF366, 0x000003A6, 0x00001FE8, 0xFFFFF509, 0x00000394, 0x00001FE8, 0xFFFFF509, 0x00000394 },
+ { 0x0213F0FFEF5C4884, 0x0000378F, 0xFFFFE54B, 0x000005F1, 0x00001C9B, 0xFFFFF5C7, 0x00000368, 0x00001C9B, 0xFFFFF5C7, 0x00000368 },
+ { 0x0213F0FFEF6418A4, 0x00003CF3, 0xFFFFE15A, 0x00000694, 0x00002CDD, 0xFFFFEA44, 0x00000557, 0x00002CDD, 0xFFFFEA44, 0x00000557 },
+ { 0x0213EA94DE200904, 0x000021EC, 0xFFFFF4F4, 0x0000038F, 0x00001511, 0xFFFFFBF5, 0x0000029E, 0x00001511, 0xFFFFFBF5, 0x0000029E },
+ { 0x0213F0FFEF6010A4, 0x00003C8A, 0xFFFFE1C1, 0x00000685, 0x000019C7, 0xFFFFF7E2, 0x00000301, 0x000019C7, 0xFFFFF7E2, 0x00000301 },
+ { 0x0213F0FFEF5E2064, 0x00003908, 0xFFFFE5C7, 0x000005B3, 0x00002793, 0xFFFFEF46, 0x00000465, 0x00002793, 0xFFFFEF46, 0x00000465 },
+ { 0x0213F0FFEF605104, 0x000040A3, 0xFFFFDE61, 0x00000725, 0x00002077, 0xFFFFF2CE, 0x000003E8, 0x00002077, 0xFFFFF2CE, 0x000003E8 },
+ { 0x0213F0FFEF664144, 0x00003DCA, 0xFFFFE34D, 0x00000608, 0x00002D66, 0xFFFFEBDF, 0x000004E8, 0x00002D66, 0xFFFFEBDF, 0x000004E8 },
+ { 0x0213F0FFEF5E50C4, 0x00003085, 0xFFFFEB70, 0x000004C8, 0x000029B1, 0xFFFFEDD9, 0x000004A5, 0x000029B1, 0xFFFFEDD9, 0x000004A5 },
+ { 0x0213EA94DE083884, 0x00004C73, 0xFFFFD676, 0x0000086C, 0x0000280A, 0xFFFFED89, 0x000004C2, 0x0000280A, 0xFFFFED89, 0x000004C2 },
+ { 0x0213EA94DE242164, 0x00002CE5, 0xFFFFEE8C, 0x00000466, 0x00001755, 0xFFFFFAC2, 0x000002AC, 0x00001755, 0xFFFFFAC2, 0x000002AC },
+ { 0x0213F0FFEF621124, 0x0000489F, 0xFFFFDBF1, 0x0000073E, 0x0000332D, 0xFFFFE786, 0x000005AD, 0x0000332D, 0xFFFFE786, 0x000005AD },
+ { 0x0213F0FFEF602864, 0x00003D09, 0xFFFFE193, 0x00000689, 0x00001E82, 0xFFFFF4C0, 0x00000386, 0x00001E82, 0xFFFFF4C0, 0x00000386 },
+ { 0x0213F0FFEF644104, 0x00003E4C, 0xFFFFE131, 0x00000689, 0x00002F4E, 0xFFFFE925, 0x0000057B, 0x00002F4E, 0xFFFFE925, 0x0000057B },
+ { 0x0213F0FFEF5A4084, 0x00003B31, 0xFFFFE53F, 0x000005B3, 0x0000248A, 0xFFFFF211, 0x000003DF, 0x0000248A, 0xFFFFF211, 0x000003DF },
+ { 0x0213F0FFEF644124, 0x000038DD, 0xFFFFE54A, 0x000005C9, 0x00002B6D, 0xFFFFEBDF, 0x00000502, 0x00002B6D, 0xFFFFEBDF, 0x00000502 },
+ { 0x0213F0FFEF684064, 0x00002698, 0xFFFFF1A8, 0x000003F2, 0x00002163, 0xFFFFF34B, 0x000003E3, 0x00002163, 0xFFFFF34B, 0x000003E3 },
+ { 0x0213EA94DE201064, 0x000023A8, 0xFFFFF4CD, 0x00000386, 0x00001944, 0xFFFFF983, 0x00000300, 0x00001944, 0xFFFFF983, 0x00000300 },
+ { 0x0213F0FFEF6418C4, 0x00003EAF, 0xFFFFE0C3, 0x000006A0, 0x000030AB, 0xFFFFE829, 0x000005A6, 0x000030AB, 0xFFFFE829, 0x000005A6 },
+ { 0x0213F0FFEF684944, 0x00002E89, 0xFFFFECA6, 0x000004B6, 0x00001FA0, 0xFFFFF4A8, 0x000003A3, 0x00001FA0, 0xFFFFF4A8, 0x000003A3 },
+ { 0x0213F0FFEF6828A4, 0x000028A4, 0xFFFFF112, 0x00000402, 0x00001F7C, 0xFFFFF545, 0x0000038A, 0x00001F7C, 0xFFFFF545, 0x0000038A },
+ { 0x0213F0FFEF6650A4, 0x00004135, 0xFFFFDFA2, 0x000006C5, 0x0000324C, 0xFFFFE7AA, 0x000005AF, 0x0000324C, 0xFFFFE7AA, 0x000005AF },
+ { 0x0213EA94DE2038C4, 0x00002012, 0xFFFFF693, 0x00000352, 0x0000171F, 0xFFFFFABB, 0x000002D9, 0x0000171F, 0xFFFFFABB, 0x000002D9 },
+ { 0x0213F0FFEF643084, 0x00003D7C, 0xFFFFE1BC, 0x00000671, 0x00002A45, 0xFFFFEC84, 0x000004EC, 0x00002A45, 0xFFFFEC84, 0x000004EC },
+ { 0x0213F0FFEF723064, 0x00004172, 0xFFFFDF58, 0x000006DA, 0x00002504, 0xFFFFF0A6, 0x00000431, 0x00002504, 0xFFFFF0A6, 0x00000431 },
+ { 0x0213F0FE99281944, 0x000029C7, 0xFFFFF087, 0x00000414, 0x00001DCB, 0xFFFFF675, 0x0000035F, 0x00001DCB, 0xFFFFF675, 0x0000035F },
+ { 0x0213F0FE992A29A4, 0x000027F0, 0xFFFFF05A, 0x00000432, 0x00001707, 0xFFFFFA0E, 0x000002D1, 0x00001707, 0xFFFFFA0E, 0x000002D1 },
+ { 0x0213F0FE99222144, 0x00003279, 0xFFFFE9F7, 0x00000511, 0x00001B5E, 0xFFFFF787, 0x00000317, 0x00001B5E, 0xFFFFF787, 0x00000317 },
+ { 0x0213F0FE99322184, 0x000030A5, 0xFFFFEABC, 0x000004FF, 0x000019D1, 0xFFFFF83C, 0x00000304, 0x000019D1, 0xFFFFF83C, 0x00000304 },
+ { 0x0213F0FE99282844, 0x0000283B, 0xFFFFF122, 0x00000402, 0x000019C2, 0xFFFFF8E9, 0x000002FB, 0x000019C2, 0xFFFFF8E9, 0x000002FB },
+ { 0x0213F0FE992C2084, 0x00003376, 0xFFFFE9E1, 0x00000510, 0x000021A7, 0xFFFFF39F, 0x000003BF, 0x000021A7, 0xFFFFF39F, 0x000003BF },
+ { 0x0213F0FE993218C4, 0x000031D2, 0xFFFFEA9C, 0x000004FC, 0x00001F66, 0xFFFFF4E4, 0x00000390, 0x00001F66, 0xFFFFF4E4, 0x00000390 },
+ { 0x0213F0FE991A3864, 0x00003006, 0xFFFFEB18, 0x000004F2, 0x000019B3, 0xFFFFF84E, 0x00000301, 0x000019B3, 0xFFFFF84E, 0x00000301 },
+ { 0x0213F0FE993039A4, 0x0000364F, 0xFFFFE81F, 0x00000556, 0x00002AC9, 0xFFFFED87, 0x000004BD, 0x00002AC9, 0xFFFFED87, 0x000004BD },
+ { 0x0213F0FE992E3844, 0x00003043, 0xFFFFEBAE, 0x000004CD, 0x00001B0C, 0xFFFFF7ED, 0x0000030C, 0x00001B0C, 0xFFFFF7ED, 0x0000030C },
+ { 0x0213F0FE993048A4, 0x000037CE, 0xFFFFE69E, 0x00000596, 0x0000276B, 0xFFFFEF65, 0x0000046E, 0x0000276B, 0xFFFFEF65, 0x0000046E },
+ { 0x0213F0FE992C3104, 0x00003063, 0xFFFFED5E, 0x0000046F, 0x000024AE, 0xFFFFF2C4, 0x000003D8, 0x000024AE, 0xFFFFF2C4, 0x000003D8 },
+ { 0x0213F0FE992E08A4, 0x00002F5D, 0xFFFFEBDC, 0x000004D3, 0x00001EDB, 0xFFFFF50F, 0x0000038E, 0x00001EDB, 0xFFFFF50F, 0x0000038E },
+ { 0x0213F0FE992E48A4, 0x00003148, 0xFFFFEA9A, 0x000004FB, 0x0000192D, 0xFFFFF8E9, 0x000002DF, 0x0000192D, 0xFFFFF8E9, 0x000002DF },
+ { 0x0213F0FE992C2064, 0x00003682, 0xFFFFE7E4, 0x0000055C, 0x0000250E, 0xFFFFF150, 0x0000041A, 0x0000250E, 0xFFFFF150, 0x0000041A },
+ { 0x0213F0FE992A2084, 0x0000284E, 0xFFFFF15A, 0x000003F8, 0x00001CE2, 0xFFFFF6F9, 0x0000034F, 0x00001CE2, 0xFFFFF6F9, 0x0000034F },
+ { 0x0213F0FE993018A4, 0x00003171, 0xFFFFEAE9, 0x000004ED, 0x00001F40, 0xFFFFF513, 0x00000384, 0x00001F40, 0xFFFFF513, 0x00000384 },
+ { 0x0213F0FE99323044, 0x000031BD, 0xFFFFEA64, 0x0000050A, 0x00001EFD, 0xFFFFF4F7, 0x00000390, 0x00001EFD, 0xFFFFF4F7, 0x00000390 },
+ { 0x0213F0FE992E50E4, 0x00003050, 0xFFFFEB29, 0x000004EA, 0x000019B3, 0xFFFFF878, 0x000002F9, 0x000019B3, 0xFFFFF878, 0x000002F9 },
+ { 0x0213F0FE992C1904, 0x00003400, 0xFFFFE9A0, 0x0000051A, 0x00002460, 0xFFFFF1DA, 0x00000409, 0x00002460, 0xFFFFF1DA, 0x00000409 },
+ { 0x0213F0FE992C4884, 0x000034A1, 0xFFFFE86F, 0x00000558, 0x0000255D, 0xFFFFF09E, 0x00000443, 0x0000255D, 0xFFFFF09E, 0x00000443 },
+ { 0x0213F0FE992E48E4, 0x00003103, 0xFFFFEAD7, 0x000004F0, 0x00001896, 0xFFFFF95A, 0x000002CC, 0x00001896, 0xFFFFF95A, 0x000002CC },
+ { 0x0213F0FE993018E4, 0x00003120, 0xFFFFEB9E, 0x000004CB, 0x000021E8, 0xFFFFF3A2, 0x000003C1, 0x000021E8, 0xFFFFF3A2, 0x000003C1 },
+ { 0x0213F0FE991C50E4, 0x00003558, 0xFFFFE812, 0x00000565, 0x0000256E, 0xFFFFF097, 0x00000447, 0x0000256E, 0xFFFFF097, 0x00000447 },
+ { 0x0213F0FE991A2844, 0x00002DA8, 0xFFFFECA8, 0x000004B7, 0x0000180B, 0xFFFFF96D, 0x000002D8, 0x0000180B, 0xFFFFF96D, 0x000002D8 },
+ { 0x0213F0FE992E3064, 0x00003232, 0xFFFFEA66, 0x000004FF, 0x00001DDE, 0xFFFFF5FE, 0x0000035A, 0x00001DDE, 0xFFFFF5FE, 0x0000035A },
+ { 0x0213F0FE993050E4, 0x000034D2, 0xFFFFE89F, 0x00000548, 0x0000246C, 0xFFFFF17F, 0x00000418, 0x0000246C, 0xFFFFF17F, 0x00000418 },
+ { 0x0213F0FE99304904, 0x000033EC, 0xFFFFE954, 0x0000052A, 0x00002323, 0xFFFFF279, 0x000003EE, 0x00002323, 0xFFFFF279, 0x000003EE },
+ { 0x0213F0FE99303884, 0x000033AA, 0xFFFFE955, 0x0000052D, 0x0000229F, 0xFFFFF2B2, 0x000003E7, 0x0000229F, 0xFFFFF2B2, 0x000003E7 },
+ { 0x0213F0FE99324964, 0x00003258, 0xFFFFE9AA, 0x0000052A, 0x00001D5F, 0xFFFFF5D1, 0x0000036B, 0x00001D5F, 0xFFFFF5D1, 0x0000036B },
+ { 0x0213F0FE993029A4, 0x0000323A, 0xFFFFEA5F, 0x00000504, 0x00002108, 0xFFFFF3D5, 0x000003BA, 0x00002108, 0xFFFFF3D5, 0x000003BA },
+ { 0x0213F0FE992C2184, 0x00003216, 0xFFFFEA6B, 0x000004FF, 0x00001D6E, 0xFFFFF640, 0x00000350, 0x00001D6E, 0xFFFFF640, 0x00000350 },
+ { 0x0213F0FE993210E4, 0x000030C5, 0xFFFFEAC4, 0x000004FC, 0x00001924, 0xFFFFF8C2, 0x000002EE, 0x00001924, 0xFFFFF8C2, 0x000002EE },
+ { 0x0213F0FE99305104, 0x000032BB, 0xFFFFE9F1, 0x00000515, 0x00002211, 0xFFFFF31B, 0x000003D5, 0x00002211, 0xFFFFF31B, 0x000003D5 },
+ { 0x0213F0FE993048C4, 0x0000352C, 0xFFFFE85B, 0x00000553, 0x00002410, 0xFFFFF1B4, 0x0000040F, 0x00002410, 0xFFFFF1B4, 0x0000040F },
+ { 0x0213F0FE992238C4, 0x000036A0, 0xFFFFE7E8, 0x0000055D, 0x00002901, 0xFFFFEEB8, 0x00000489, 0x00002901, 0xFFFFEEB8, 0x00000489 },
+ { 0x0213F0FE992C3044, 0x00003340, 0xFFFFE9D9, 0x00000516, 0x00002332, 0xFFFFF27A, 0x000003F0, 0x00002332, 0xFFFFF27A, 0x000003F0 },
+ { 0x0213F0FE991A38A4, 0x00003564, 0xFFFFE86D, 0x0000054E, 0x00002613, 0xFFFFF07C, 0x00000444, 0x00002613, 0xFFFFF07C, 0x00000444 },
+ { 0x0213F0FE99280904, 0x00002AD1, 0xFFFFEF0B, 0x0000045C, 0x00001DEA, 0xFFFFF5C8, 0x00000381, 0x00001DEA, 0xFFFFF5C8, 0x00000381 },
+ { 0x0213F0FE992220E4, 0x000035B0, 0xFFFFE846, 0x00000555, 0x000027BE, 0xFFFFEF5D, 0x00000474, 0x000027BE, 0xFFFFEF5D, 0x00000474 },
+ { 0x0213F0FE992238A4, 0x000032C4, 0xFFFFEA48, 0x00000502, 0x000022C6, 0xFFFFF2DF, 0x000003DE, 0x000022C6, 0xFFFFF2DF, 0x000003DE },
+ { 0x0213F0FE993008C4, 0x00003036, 0xFFFFEB0D, 0x000004F9, 0x00001FE8, 0xFFFFF41A, 0x000003BC, 0x00001FE8, 0xFFFFF41A, 0x000003BC },
+ { 0x0213F0FE991A0904, 0x000030F8, 0xFFFFEA13, 0x00000524, 0x00001B6A, 0xFFFFF6C9, 0x0000034A, 0x00001B6A, 0xFFFFF6C9, 0x0000034A },
+ { 0x0213F0FE993010A4, 0x00002EE2, 0xFFFFEC0C, 0x000004CB, 0x00001A39, 0xFFFFF814, 0x0000030F, 0x00001A39, 0xFFFFF814, 0x0000030F },
+ { 0x0213F0FE991C3184, 0x00003457, 0xFFFFE924, 0x0000052A, 0x00001E9D, 0xFFFFF59C, 0x00000363, 0x00001E9D, 0xFFFFF59C, 0x00000363 },
+ { 0x0213F0FE99322844, 0x000030BF, 0xFFFFEB18, 0x000004ED, 0x00001D37, 0xFFFFF636, 0x0000035C, 0x00001D37, 0xFFFFF636, 0x0000035C },
+ { 0x0213F0FE992E4084, 0x000031AF, 0xFFFFEA75, 0x000004FE, 0x000019F2, 0xFFFFF87A, 0x000002F0, 0x000019F2, 0xFFFFF87A, 0x000002F0 },
+ { 0x0213F0FE99302884, 0x00003642, 0xFFFFE85B, 0x00000547, 0x00002975, 0xFFFFEE98, 0x0000048B, 0x00002975, 0xFFFFEE98, 0x0000048B },
+ { 0x0213F0FE992E2884, 0x00002E8B, 0xFFFFED1E, 0x0000048E, 0x000019C1, 0xFFFFF917, 0x000002D6, 0x000019C1, 0xFFFFF917, 0x000002D6 },
+ { 0x0213F0FE993241A4, 0x000033D9, 0xFFFFE8E1, 0x00000548, 0x0000224B, 0xFFFFF298, 0x000003F4, 0x0000224B, 0xFFFFF298, 0x000003F4 },
+ { 0x0213F0FE992E28C4, 0x000032BC, 0xFFFFEB0F, 0x000004D6, 0x00002488, 0xFFFFF240, 0x000003F2, 0x00002488, 0xFFFFF240, 0x000003F2 },
+ { 0x0213F0FE99304944, 0x000035FD, 0xFFFFE838, 0x00000553, 0x00002762, 0xFFFFEFBC, 0x00000460, 0x00002762, 0xFFFFEFBC, 0x00000460 },
+ { 0x0213F0FE992818A4, 0x0000268B, 0xFFFFF263, 0x000003D1, 0x00001914, 0xFFFFF977, 0x000002E8, 0x00001914, 0xFFFFF977, 0x000002E8 },
+ { 0x0213F0FE992C3184, 0x0000330B, 0xFFFFEA1E, 0x00000505, 0x000020B1, 0xFFFFF44D, 0x0000039E, 0x000020B1, 0xFFFFF44D, 0x0000039E },
+ { 0x0213F0FE99222084, 0x0000326E, 0xFFFFEA26, 0x00000508, 0x00001C17, 0xFFFFF722, 0x00000328, 0x00001C17, 0xFFFFF722, 0x00000328 },
+ { 0x0213F0FE992A31A4, 0x00002A3F, 0xFFFFEEE8, 0x0000046D, 0x00001B2B, 0xFFFFF737, 0x0000034D, 0x00001B2B, 0xFFFFF737, 0x0000034D },
+ { 0x0213F0FE992C4064, 0x00003732, 0xFFFFE765, 0x00000574, 0x00002A6D, 0xFFFFEDA8, 0x000004B7, 0x00002A6D, 0xFFFFEDA8, 0x000004B7 },
+ { 0x0213F0FE99300924, 0x000034D3, 0xFFFFE827, 0x00000569, 0x000027AA, 0xFFFFEEE7, 0x00000492, 0x000027AA, 0xFFFFEEE7, 0x00000492 },
+ { 0x0213F0FE992E40C4, 0x00003306, 0xFFFFEA39, 0x000004FC, 0x00001DCC, 0xFFFFF655, 0x00000344, 0x00001DCC, 0xFFFFF655, 0x00000344 },
+ { 0x0213F0FE99282044, 0x00002A48, 0xFFFFEFCA, 0x00000439, 0x00001DED, 0xFFFFF60D, 0x00000375, 0x00001DED, 0xFFFFF60D, 0x00000375 },
+ { 0x0213F0FE993038C4, 0x000033A3, 0xFFFFEA36, 0x000004F9, 0x0000247C, 0xFFFFF21F, 0x000003F4, 0x0000247C, 0xFFFFF21F, 0x000003F4 },
+ { 0x0213F0FE992C3164, 0x0000311B, 0xFFFFEB76, 0x000004D1, 0x00001EB1, 0xFFFFF5B6, 0x00000366, 0x00001EB1, 0xFFFFF5B6, 0x00000366 },
+ { 0x0213F0FE99324164, 0x00003307, 0xFFFFE97F, 0x0000052A, 0x00001E76, 0xFFFFF54D, 0x0000037C, 0x00001E76, 0xFFFFF54D, 0x0000037C },
+ { 0x0213F0FE991C2144, 0x0000344B, 0xFFFFE9C5, 0x00000509, 0x000020D6, 0xFFFFF486, 0x0000038F, 0x000020D6, 0xFFFFF486, 0x0000038F },
+ { 0x0213F0FE992C3144, 0x000034B9, 0xFFFFEA0B, 0x000004F7, 0x000027B3, 0xFFFFF057, 0x0000043A, 0x000027B3, 0xFFFFF057, 0x0000043A },
+ { 0x0213F0FE99301964, 0x00003360, 0xFFFFE984, 0x00000527, 0x00002238, 0xFFFFF2EE, 0x000003E0, 0x00002238, 0xFFFFF2EE, 0x000003E0 },
+ { 0x0213F0FE99302124, 0x0000315C, 0xFFFFEC05, 0x000004B1, 0x000023C8, 0xFFFFF2CC, 0x000003DE, 0x000023C8, 0xFFFFF2CC, 0x000003DE },
+ { 0x0213F0FE992C2864, 0x0000389B, 0xFFFFE6D5, 0x00000582, 0x00002C6C, 0xFFFFEC92, 0x000004DE, 0x00002C6C, 0xFFFFEC92, 0x000004DE },
+ { 0x0213F0FE992E1124, 0x00003058, 0xFFFFEB30, 0x000004E6, 0x000019B5, 0xFFFFF88B, 0x000002F1, 0x000019B5, 0xFFFFF88B, 0x000002F1 },
+ { 0x0213F0FE992E0904, 0x00002F69, 0xFFFFEB4A, 0x000004F1, 0x00001B82, 0xFFFFF6EC, 0x00000341, 0x00001B82, 0xFFFFF6EC, 0x00000341 },
+ { 0x0213F0FE991A18E4, 0x000031EB, 0xFFFFEA64, 0x00000508, 0x00002059, 0xFFFFF427, 0x000003B0, 0x00002059, 0xFFFFF427, 0x000003B0 },
+ { 0x0213F0FE99224124, 0x000033E2, 0xFFFFE94D, 0x0000052A, 0x000020BF, 0xFFFFF40B, 0x000003AB, 0x000020BF, 0xFFFFF40B, 0x000003AB },
+ { 0x0213F0FE99283184, 0x00002AF9, 0xFFFFEFE9, 0x00000427, 0x00001F72, 0xFFFFF57A, 0x00000383, 0x00001F72, 0xFFFFF57A, 0x00000383 },
+ { 0x0213F0FE992C2824, 0x00003282, 0xFFFFEA88, 0x000004FA, 0x00002561, 0xFFFFF126, 0x0000042B, 0x00002561, 0xFFFFF126, 0x0000042B },
+ { 0x0213F0FE993010E4, 0x0000308A, 0xFFFFEB5D, 0x000004E0, 0x00001E83, 0xFFFFF577, 0x00000378, 0x00001E83, 0xFFFFF577, 0x00000378 },
+ { 0x0213F0FE99324884, 0x0000336E, 0xFFFFE8C8, 0x00000553, 0x0000217C, 0xFFFFF2E1, 0x000003EB, 0x0000217C, 0xFFFFF2E1, 0x000003EB },
+ { 0x0213F0FE991A2164, 0x000034A9, 0xFFFFE838, 0x00000561, 0x000020CE, 0xFFFFF38A, 0x000003C7, 0x000020CE, 0xFFFFF38A, 0x000003C7 },
+ { 0x0213F0FE99222184, 0x00003152, 0xFFFFE9EB, 0x00000522, 0x00001755, 0xFFFFF9A9, 0x000002C6, 0x00001755, 0xFFFFF9A9, 0x000002C6 },
+ { 0x0213F0FE99281884, 0x0000286E, 0xFFFFF136, 0x000003FD, 0x00001BAB, 0xFFFFF7C3, 0x0000032C, 0x00001BAB, 0xFFFFF7C3, 0x0000032C },
+ { 0x0213F0FE99300944, 0x0000316B, 0xFFFFEA02, 0x00000528, 0x00002247, 0xFFFFF24E, 0x00000408, 0x00002247, 0xFFFFF24E, 0x00000408 },
+ { 0x0213F0FE992C08E4, 0x000034CF, 0xFFFFE83D, 0x00000562, 0x00002458, 0xFFFFF130, 0x00000430, 0x00002458, 0xFFFFF130, 0x00000430 },
+ { 0x0213F0FE992C2984, 0x00003352, 0xFFFFE9D1, 0x00000515, 0x0000212A, 0xFFFFF3DC, 0x000003B4, 0x0000212A, 0xFFFFF3DC, 0x000003B4 },
+ { 0x0213F0FE992840A4, 0x00002946, 0xFFFFF09B, 0x00000415, 0x00001DC9, 0xFFFFF650, 0x00000366, 0x00001DC9, 0xFFFFF650, 0x00000366 },
+ { 0x0213F0FE99301124, 0x00003080, 0xFFFFEB47, 0x000004E1, 0x00001BD5, 0xFFFFF73B, 0x00000329, 0x00001BD5, 0xFFFFF73B, 0x00000329 },
+ { 0x0213F0FE991A1884, 0x00002FBD, 0xFFFFEB7B, 0x000004DD, 0x000017FC, 0xFFFFF99E, 0x000002C7, 0x000017FC, 0xFFFFF99E, 0x000002C7 },
+ { 0x0213F0FE99281124, 0x00002A28, 0xFFFFF032, 0x0000041F, 0x00001B19, 0xFFFFF83A, 0x00000312, 0x00001B19, 0xFFFFF83A, 0x00000312 },
+ { 0x0213F0FE992240C4, 0x00003420, 0xFFFFE936, 0x00000530, 0x000023C2, 0xFFFFF203, 0x00000406, 0x000023C2, 0xFFFFF203, 0x00000406 },
+ { 0x0213F0FE99301144, 0x00002F7C, 0xFFFFEBBA, 0x000004D1, 0x0000185D, 0xFFFFF975, 0x000002CA, 0x0000185D, 0xFFFFF975, 0x000002CA },
+ { 0x0213F0FE992E2044, 0x00002C51, 0xFFFFEE3B, 0x0000046F, 0x000019AA, 0xFFFFF8DD, 0x000002ED, 0x000019AA, 0xFFFFF8DD, 0x000002ED },
+ { 0x0213F0FE991A4144, 0x000033D6, 0xFFFFE8F2, 0x0000053D, 0x00001D73, 0xFFFFF5FB, 0x0000035B, 0x00001D73, 0xFFFFF5FB, 0x0000035B },
+ { 0x0213F0FE99323084, 0x000031D9, 0xFFFFEAF7, 0x000004E4, 0x00001EBD, 0xFFFFF5A6, 0x00000368, 0x00001EBD, 0xFFFFF5A6, 0x00000368 },
+ { 0x0213F0FE991A20A4, 0x00003386, 0xFFFFE9CE, 0x00000515, 0x00002422, 0xFFFFF1F3, 0x00000405, 0x00002422, 0xFFFFF1F3, 0x00000405 },
+ { 0x0213F0FE992C50E4, 0x000032FB, 0xFFFFE9BC, 0x00000520, 0x00002301, 0xFFFFF267, 0x000003F7, 0x00002301, 0xFFFFF267, 0x000003F7 },
+ { 0x0213F0FE99322924, 0x000032C2, 0xFFFFEAC0, 0x000004EA, 0x0000250F, 0xFFFFF1A2, 0x00000413, 0x0000250F, 0xFFFFF1A2, 0x00000413 },
+ { 0x0213F0FE991C2944, 0x00003722, 0xFFFFE8A6, 0x00000527, 0x000026E4, 0xFFFFF0F5, 0x0000041C, 0x000026E4, 0xFFFFF0F5, 0x0000041C },
+ { 0x0213F0FE992C48C4, 0x000035A4, 0xFFFFE822, 0x00000558, 0x000022F2, 0xFFFFF288, 0x000003E8, 0x000022F2, 0xFFFFF288, 0x000003E8 },
+ { 0x0213F0FE99280924, 0x00002CD1, 0xFFFFEDC6, 0x0000048C, 0x00001EAF, 0xFFFFF53D, 0x00000396, 0x00001EAF, 0xFFFFF53D, 0x00000396 },
+ { 0x0213F0FE99301164, 0x00003156, 0xFFFFEA60, 0x0000050B, 0x00001BBC, 0xFFFFF704, 0x00000335, 0x00001BBC, 0xFFFFF704, 0x00000335 },
+ { 0x0213F0FE992C5104, 0x000034A1, 0xFFFFE8C0, 0x00000544, 0x00002528, 0xFFFFF105, 0x0000042C, 0x00002528, 0xFFFFF105, 0x0000042C },
+ { 0x0213F0FE99323064, 0x000032CE, 0xFFFFE9D3, 0x00000520, 0x000021FF, 0xFFFFF2FD, 0x000003E4, 0x000021FF, 0xFFFFF2FD, 0x000003E4 },
+ { 0x0213F0FE991A50A4, 0x000034A0, 0xFFFFE823, 0x0000056D, 0x0000256F, 0xFFFFF047, 0x0000045A, 0x0000256F, 0xFFFFF047, 0x0000045A },
+ { 0x0213F0FE99303944, 0x00003109, 0xFFFFEBD6, 0x000004BF, 0x000022D4, 0xFFFFF32D, 0x000003D0, 0x000022D4, 0xFFFFF32D, 0x000003D0 },
+ { 0x0213F0FE992C1164, 0x000030B7, 0xFFFFEAF0, 0x000004F3, 0x00001AEC, 0xFFFFF7A9, 0x0000031B, 0x00001AEC, 0xFFFFF7A9, 0x0000031B },
+ { 0x0213F0FE992C39A4, 0x00003078, 0xFFFFEBA4, 0x000004CF, 0x00001E7A, 0xFFFFF5AF, 0x0000036B, 0x00001E7A, 0xFFFFF5AF, 0x0000036B },
+ { 0x0213F0FE99304124, 0x00003442, 0xFFFFE998, 0x00000518, 0x000025EA, 0xFFFFF0F3, 0x0000042B, 0x000025EA, 0xFFFFF0F3, 0x0000042B },
+ { 0x0213F0FE993021A4, 0x000031CB, 0xFFFFEA80, 0x00000501, 0x000020A3, 0xFFFFF403, 0x000003B2, 0x000020A3, 0xFFFFF403, 0x000003B2 },
+ { 0x0213F0FE992A2984, 0x00002947, 0xFFFFF018, 0x00000433, 0x00001BA5, 0xFFFFF75C, 0x00000340, 0x00001BA5, 0xFFFFF75C, 0x00000340 },
+ { 0x0213F0FE992C3984, 0x000033F9, 0xFFFFE99D, 0x00000518, 0x00002231, 0xFFFFF358, 0x000003C5, 0x00002231, 0xFFFFF358, 0x000003C5 },
+ { 0x0213F0FE99321124, 0x00003131, 0xFFFFEA45, 0x00000513, 0x00001973, 0xFFFFF85E, 0x00000301, 0x00001973, 0xFFFFF85E, 0x00000301 },
+ { 0x0213F0FE991C29A4, 0x00003571, 0xFFFFE8AC, 0x00000539, 0x00002049, 0xFFFFF49C, 0x0000038D, 0x00002049, 0xFFFFF49C, 0x0000038D },
+ { 0x0213F0FE992E3864, 0x0000309E, 0xFFFFEB1D, 0x000004E8, 0x000019ED, 0xFFFFF86E, 0x000002F8, 0x000019ED, 0xFFFFF86E, 0x000002F8 },
+ { 0x0213F0FE99302984, 0x00003091, 0xFFFFEB9B, 0x000004CC, 0x00001D2C, 0xFFFFF6A2, 0x0000033D, 0x00001D2C, 0xFFFFF6A2, 0x0000033D },
+ { 0x0213F0FE993008E4, 0x00003069, 0xFFFFEAFD, 0x000004F8, 0x00001E82, 0xFFFFF51C, 0x0000038D, 0x00001E82, 0xFFFFF51C, 0x0000038D },
+ { 0x0213F0FE992210A4, 0x00003459, 0xFFFFE7F2, 0x00000572, 0x00001DA7, 0xFFFFF552, 0x0000037F, 0x00001DA7, 0xFFFFF552, 0x0000037F },
+ { 0x0213F0FE99321104, 0x0000304B, 0xFFFFEAFB, 0x000004F4, 0x0000191E, 0xFFFFF8BD, 0x000002EE, 0x0000191E, 0xFFFFF8BD, 0x000002EE },
+ { 0x0213F0FE993020C4, 0x0000346E, 0xFFFFEA07, 0x000004FD, 0x00002767, 0xFFFFF058, 0x00000440, 0x00002767, 0xFFFFF058, 0x00000440 },
+ { 0x0213F0FE992E3084, 0x000030B5, 0xFFFFEBC1, 0x000004C1, 0x00001B3C, 0xFFFFF818, 0x000002FD, 0x00001B3C, 0xFFFFF818, 0x000002FD },
+ { 0x0213F0FE99300904, 0x0000321F, 0xFFFFE9EA, 0x00000524, 0x00002380, 0xFFFFF1C2, 0x0000041A, 0x00002380, 0xFFFFF1C2, 0x0000041A },
+ { 0x0213F0FE992E3044, 0x000030DF, 0xFFFFEB37, 0x000004E2, 0x00001E3C, 0xFFFFF5BB, 0x00000369, 0x00001E3C, 0xFFFFF5BB, 0x00000369 },
+ { 0x0213F0FE992848A4, 0x000027E0, 0xFFFFF0E2, 0x00000416, 0x00001A6A, 0xFFFFF820, 0x00000321, 0x00001A6A, 0xFFFFF820, 0x00000321 },
+ { 0x0213F0FE991A1084, 0x00002FA1, 0xFFFFEB63, 0x000004E7, 0x0000196B, 0xFFFFF880, 0x000002FB, 0x0000196B, 0xFFFFF880, 0x000002FB },
+ { 0x0213F0FE991C1084, 0x0000310C, 0xFFFFEAAF, 0x000004FC, 0x000019EF, 0xFFFFF850, 0x000002FD, 0x000019EF, 0xFFFFF850, 0x000002FD },
+ { 0x0213F0FE99323904, 0x0000334A, 0xFFFFEA07, 0x0000050B, 0x00002380, 0xFFFFF26F, 0x000003F0, 0x00002380, 0xFFFFF26F, 0x000003F0 },
+ { 0x0213F0FE99302944, 0x00002FF9, 0xFFFFECDC, 0x00000492, 0x00002297, 0xFFFFF394, 0x000003BF, 0x00002297, 0xFFFFF394, 0x000003BF },
+ { 0x0213F0FE992C2164, 0x0000354B, 0xFFFFE894, 0x00000546, 0x000024C4, 0xFFFFF16C, 0x0000041B, 0x000024C4, 0xFFFFF16C, 0x0000041B },
+ { 0x0213F0FE99220924, 0x00003245, 0xFFFFE92F, 0x00000544, 0x00001829, 0xFFFFF8F1, 0x000002EA, 0x00001829, 0xFFFFF8F1, 0x000002EA },
+ { 0x0213F0FE992E4884, 0x0000302F, 0xFFFFEB51, 0x000004E3, 0x0000199F, 0xFFFFF894, 0x000002F4, 0x0000199F, 0xFFFFF894, 0x000002F4 },
+ { 0x0213F0FE992E18C4, 0x00002F54, 0xFFFFEC86, 0x000004A6, 0x00001A6F, 0xFFFFF891, 0x000002EC, 0x00001A6F, 0xFFFFF891, 0x000002EC },
+ { 0x0213F0FE99284164, 0x00002908, 0xFFFFF0D8, 0x0000040A, 0x00001C9B, 0xFFFFF729, 0x00000342, 0x00001C9B, 0xFFFFF729, 0x00000342 },
+ { 0x0213F0FE99302964, 0x000031D9, 0xFFFFEB40, 0x000004D7, 0x000023F5, 0xFFFFF259, 0x000003F4, 0x000023F5, 0xFFFFF259, 0x000003F4 },
+ { 0x0213F0FE993048E4, 0x000034C8, 0xFFFFE8C6, 0x0000053F, 0x00002313, 0xFFFFF280, 0x000003EC, 0x00002313, 0xFFFFF280, 0x000003EC },
+ { 0x0213F0FE993050C4, 0x000037D1, 0xFFFFE6A1, 0x0000059C, 0x00002C6A, 0xFFFFEBFF, 0x00000504, 0x00002C6A, 0xFFFFEBFF, 0x00000504 },
+ { 0x0213F0FE99321964, 0x000030E9, 0xFFFFEA6B, 0x0000050F, 0x00001A2D, 0xFFFFF7DF, 0x00000316, 0x00001A2D, 0xFFFFF7DF, 0x00000316 },
+ { 0x0213F0FE99302084, 0x0000323D, 0xFFFFEA95, 0x000004F4, 0x00001ED2, 0xFFFFF584, 0x0000036C, 0x00001ED2, 0xFFFFF584, 0x0000036C },
+ { 0x0213F0FE992C3024, 0x000033D6, 0xFFFFE9DB, 0x00000510, 0x000027A7, 0xFFFFEFC7, 0x0000045E, 0x000027A7, 0xFFFFEFC7, 0x0000045E },
+ { 0x0213F0FE991C3164, 0x00003444, 0xFFFFE98A, 0x00000517, 0x000020FD, 0xFFFFF43F, 0x0000039D, 0x000020FD, 0xFFFFF43F, 0x0000039D },
+ { 0x0213F0FE992808E4, 0x00002987, 0xFFFFEFA1, 0x0000044B, 0x00001B06, 0xFFFFF788, 0x0000033C, 0x00001B06, 0xFFFFF788, 0x0000033C },
+ { 0x0213F0FE992C28E4, 0x0000311D, 0xFFFFED20, 0x00000474, 0x000025DA, 0xFFFFF223, 0x000003F0, 0x000025DA, 0xFFFFF223, 0x000003F0 },
+ { 0x0213F0FE992C1124, 0x000032A2, 0xFFFFEA0A, 0x0000050D, 0x00001D48, 0xFFFFF659, 0x0000034A, 0x00001D48, 0xFFFFF659, 0x0000034A },
+ { 0x0213F0FE992208E4, 0x00003110, 0xFFFFE9EA, 0x00000529, 0x00001786, 0xFFFFF958, 0x000002DB, 0x00001786, 0xFFFFF958, 0x000002DB },
+ { 0x0213F0FE992821A4, 0x000027F2, 0xFFFFF174, 0x000003F7, 0x00001C7A, 0xFFFFF72A, 0x00000348, 0x00001C7A, 0xFFFFF72A, 0x00000348 },
+ { 0x0213F0FE991C10E4, 0x000031DB, 0xFFFFEA7D, 0x000004FB, 0x000019C4, 0xFFFFF8B1, 0x000002E6, 0x000019C4, 0xFFFFF8B1, 0x000002E6 },
+ { 0x0213F0FE992C1104, 0x00003158, 0xFFFFEAAC, 0x000004FA, 0x00001BC1, 0xFFFFF737, 0x0000032B, 0x00001BC1, 0xFFFFF737, 0x0000032B },
+ { 0x0213F0FE993010C4, 0x00002F36, 0xFFFFEBF9, 0x000004CA, 0x00001A2A, 0xFFFFF83F, 0x00000303, 0x00001A2A, 0xFFFFF83F, 0x00000303 },
+ { 0x0213F0FE993238A4, 0x000032B4, 0xFFFFEA72, 0x000004FA, 0x000021FF, 0xFFFFF378, 0x000003C5, 0x000021FF, 0xFFFFF378, 0x000003C5 },
+ { 0x0213F0FE99303164, 0x00003262, 0xFFFFEAFA, 0x000004DF, 0x00002441, 0xFFFFF237, 0x000003F6, 0x00002441, 0xFFFFF237, 0x000003F6 },
+ { 0x0213F0FE99303924, 0x0000336A, 0xFFFFEAFB, 0x000004D1, 0x00002746, 0xFFFFF0B8, 0x0000042B, 0x00002746, 0xFFFFF0B8, 0x0000042B },
+ { 0x0213F0FE991A4084, 0x000032E5, 0xFFFFE923, 0x00000541, 0x00001DF0, 0xFFFFF552, 0x00000380, 0x00001DF0, 0xFFFFF552, 0x00000380 },
+ { 0x0213F0FE99304064, 0x000035D1, 0xFFFFE80B, 0x0000055F, 0x00002780, 0xFFFFEF74, 0x0000046F, 0x00002780, 0xFFFFEF74, 0x0000046F },
+ { 0x0213F0FE993028A4, 0x000033EC, 0xFFFFEA48, 0x000004F4, 0x0000269F, 0xFFFFF0D8, 0x0000042A, 0x0000269F, 0xFFFFF0D8, 0x0000042A },
+ { 0x0213F0FE99323884, 0x000030C4, 0xFFFFEB39, 0x000004E2, 0x00001B44, 0xFFFFF7AA, 0x00000318, 0x00001B44, 0xFFFFF7AA, 0x00000318 },
+ { 0x0213F0FE99281144, 0x00002926, 0xFFFFF0AF, 0x0000040E, 0x0000194E, 0xFFFFF959, 0x000002E2, 0x0000194E, 0xFFFFF959, 0x000002E2 },
+ { 0x0213F0FE992C10C4, 0x00003141, 0xFFFFEAAF, 0x000004F6, 0x00001864, 0xFFFFF97C, 0x000002C6, 0x00001864, 0xFFFFF97C, 0x000002C6 },
+ { 0x0213F0FE99301064, 0x000030B2, 0xFFFFEB7C, 0x000004DB, 0x000022CE, 0xFFFFF2B5, 0x000003F0, 0x000022CE, 0xFFFFF2B5, 0x000003F0 },
+ { 0x0213F0FE99301944, 0x0000318C, 0xFFFFEAC7, 0x000004F6, 0x00002113, 0xFFFFF3CA, 0x000003BD, 0x00002113, 0xFFFFF3CA, 0x000003BD },
+ { 0x0213F0FE992E1104, 0x00002FD2, 0xFFFFEB8F, 0x000004D9, 0x00001996, 0xFFFFF89F, 0x000002F1, 0x00001996, 0xFFFFF89F, 0x000002F1 },
+ { 0x0213F0FE991A28A4, 0x0000310D, 0xFFFFEB25, 0x000004E7, 0x00001F67, 0xFFFFF4EF, 0x0000038E, 0x00001F67, 0xFFFFF4EF, 0x0000038E },
+ { 0x0213F0FE992A4964, 0x00002BBC, 0xFFFFEE68, 0x00000477, 0x00002050, 0xFFFFF41D, 0x000003C8, 0x00002050, 0xFFFFF41D, 0x000003C8 },
+ { 0x0213F0FE99302104, 0x00003096, 0xFFFFECED, 0x00000486, 0x000024C9, 0xFFFFF278, 0x000003E7, 0x000024C9, 0xFFFFF278, 0x000003E7 },
+ { 0x0213F0FE992C10A4, 0x00003401, 0xFFFFE8F1, 0x0000053C, 0x00001E75, 0xFFFFF55C, 0x00000376, 0x00001E75, 0xFFFFF55C, 0x00000376 },
+ { 0x0213F0FE99302844, 0x0000319E, 0xFFFFEAB1, 0x000004F8, 0x00001EA3, 0xFFFFF567, 0x00000378, 0x00001EA3, 0xFFFFF567, 0x00000378 },
+ { 0x0213F0FE99322964, 0x000030FD, 0xFFFFEB4C, 0x000004DB, 0x00001CA6, 0xFFFFF6E8, 0x00000335, 0x00001CA6, 0xFFFFF6E8, 0x00000335 },
+ { 0x0213F0FE992E40A4, 0x000030D6, 0xFFFFEB1A, 0x000004E4, 0x00001A0D, 0xFFFFF87D, 0x000002EF, 0x00001A0D, 0xFFFFF87D, 0x000002EF },
+ { 0x0213F0FE992C2124, 0x0000324B, 0xFFFFEB17, 0x000004D9, 0x00002225, 0xFFFFF3A8, 0x000003BA, 0x00002225, 0xFFFFF3A8, 0x000003BA },
+ { 0x0213F0FE99284084, 0x00002A00, 0xFFFFF02E, 0x00000424, 0x00001E21, 0xFFFFF61D, 0x0000036C, 0x00001E21, 0xFFFFF61D, 0x0000036C },
+ { 0x0213F0FE992A48A4, 0x000029CF, 0xFFFFEF53, 0x00000457, 0x00001B11, 0xFFFFF772, 0x0000033D, 0x00001B11, 0xFFFFF772, 0x0000033D },
+ { 0x0213F0FE991A30A4, 0x000032A1, 0xFFFFEA63, 0x000004FB, 0x00001F83, 0xFFFFF516, 0x0000037E, 0x00001F83, 0xFFFFF516, 0x0000037E },
+ { 0x0213F0FE992E20C4, 0x0000305C, 0xFFFFEC14, 0x000004B5, 0x00001D0B, 0xFFFFF6ED, 0x00000332, 0x00001D0B, 0xFFFFF6ED, 0x00000332 },
+ { 0x0213F0FE992C1064, 0x00003467, 0xFFFFE8D5, 0x00000543, 0x0000243F, 0xFFFFF190, 0x00000418, 0x0000243F, 0xFFFFF190, 0x00000418 },
+ { 0x0213F0FE992A2064, 0x00002796, 0xFFFFF133, 0x00000409, 0x00001903, 0xFFFFF91C, 0x000002FC, 0x00001903, 0xFFFFF91C, 0x000002FC },
+ { 0x0213F0FE99302164, 0x000031F6, 0xFFFFEAB7, 0x000004F5, 0x000022B9, 0xFFFFF2D0, 0x000003E6, 0x000022B9, 0xFFFFF2D0, 0x000003E6 },
+ { 0x0213F0FE992E5104, 0x00003196, 0xFFFFEA76, 0x00000503, 0x00001CC5, 0xFFFFF67D, 0x0000034A, 0x00001CC5, 0xFFFFF67D, 0x0000034A },
+ { 0x0213F0FE99321144, 0x00002F9E, 0xFFFFEAD9, 0x00000505, 0x000017C1, 0xFFFFF93D, 0x000002DF, 0x000017C1, 0xFFFFF93D, 0x000002DF },
+ { 0x0213F0FE992E2124, 0x00002FBC, 0xFFFFEC75, 0x000004A8, 0x00001D6D, 0xFFFFF6AC, 0x0000033D, 0x00001D6D, 0xFFFFF6AC, 0x0000033D },
+ { 0x0213F0FE992C38A4, 0x00003541, 0xFFFFE921, 0x00000524, 0x00002662, 0xFFFFF0CB, 0x0000042B, 0x00002662, 0xFFFFF0CB, 0x0000042B },
+ { 0x0213F0FE992A21A4, 0x00002953, 0xFFFFEF76, 0x00000459, 0x00001C05, 0xFFFFF6A0, 0x00000368, 0x00001C05, 0xFFFFF6A0, 0x00000368 },
+ { 0x0213F0FE992C4924, 0x000034BC, 0xFFFFE8DD, 0x00000536, 0x0000210E, 0xFFFFF3F4, 0x000003A8, 0x0000210E, 0xFFFFF3F4, 0x000003A8 },
+ { 0x0213F0FE992C29A4, 0x000034BE, 0xFFFFE916, 0x0000052F, 0x000024A1, 0xFFFFF1A6, 0x00000410, 0x000024A1, 0xFFFFF1A6, 0x00000410 },
+ { 0x0213F0FE99304964, 0x000037B5, 0xFFFFE7A9, 0x0000055B, 0x000028A1, 0xFFFFEF51, 0x00000467, 0x000028A1, 0xFFFFEF51, 0x00000467 },
+ { 0x0213F0FE99301104, 0x00002FC5, 0xFFFFEBBE, 0x000004D1, 0x00001BA5, 0xFFFFF757, 0x00000328, 0x00001BA5, 0xFFFFF757, 0x00000328 },
+ { 0x0213F0FE993040A4, 0x000033CB, 0xFFFFE944, 0x0000052B, 0x00001FBE, 0xFFFFF4B1, 0x0000038C, 0x00001FBE, 0xFFFFF4B1, 0x0000038C },
+ { 0x0213F0FE99301844, 0x000030AE, 0xFFFFEBA0, 0x000004D3, 0x00002268, 0xFFFFF316, 0x000003DD, 0x00002268, 0xFFFFF316, 0x000003DD },
+ { 0x0213F0FE992C20A4, 0x00002F90, 0xFFFFEC5A, 0x000004B0, 0x00001C3A, 0xFFFFF752, 0x00000323, 0x00001C3A, 0xFFFFF752, 0x00000323 },
+ { 0x0213F0FE992E38E4, 0x00003113, 0xFFFFEB91, 0x000004C8, 0x00001E3C, 0xFFFFF623, 0x0000034E, 0x00001E3C, 0xFFFFF623, 0x0000034E },
+ { 0x0213F0FE99323984, 0x0000330B, 0xFFFFE94B, 0x00000539, 0x000020E7, 0xFFFFF37E, 0x000003CD, 0x000020E7, 0xFFFFF37E, 0x000003CD },
+ { 0x0213F0FE992E2864, 0x000031D1, 0xFFFFEACB, 0x000004ED, 0x00001E82, 0xFFFFF5B2, 0x00000365, 0x00001E82, 0xFFFFF5B2, 0x00000365 },
+ { 0x0213F0FE992A3984, 0x00002CD5, 0xFFFFEDC1, 0x0000048D, 0x000020F8, 0xFFFFF3C1, 0x000003D1, 0x000020F8, 0xFFFFF3C1, 0x000003D1 },
+ { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }
+};
+
+int pp_override_get_default_fuse_value(uint64_t key,
+ struct phm_fuses_default *result)
+{
+ const struct phm_fuses_default *list = vega10_fuses_default;
+ uint32_t i;
+
+ for (i = 0; list[i].key != 0; i++) {
+ if (key == list[i].key) {
+ result->key = list[i].key;
+ result->VFT2_m1 = list[i].VFT2_m1;
+ result->VFT2_m2 = list[i].VFT2_m2;
+ result->VFT2_b = list[i].VFT2_b;
+ result->VFT1_m1 = list[i].VFT1_m1;
+ result->VFT1_m2 = list[i].VFT1_m2;
+ result->VFT1_b = list[i].VFT1_b;
+ result->VFT0_m1 = list[i].VFT0_m1;
+ result->VFT0_m2 = list[i].VFT0_m2;
+ result->VFT0_b = list[i].VFT0_b;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pp_overdriver.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pp_overdriver.h
new file mode 100644
index 000000000000..4112a9398163
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pp_overdriver.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _PP_OVERDRIVER_H_
+#define _PP_OVERDRIVER_H_
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+struct phm_fuses_default {
+ uint64_t key;
+ uint32_t VFT2_m1;
+ uint32_t VFT2_m2;
+ uint32_t VFT2_b;
+ uint32_t VFT1_m1;
+ uint32_t VFT1_m2;
+ uint32_t VFT1_b;
+ uint32_t VFT0_m1;
+ uint32_t VFT0_m2;
+ uint32_t VFT0_b;
+};
+
+extern int pp_override_get_default_fuse_value(uint64_t key,
+ struct phm_fuses_default *result);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pp_psm.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pp_psm.c
new file mode 100644
index 000000000000..31a32a79cfc2
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pp_psm.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include "pp_psm.h"
+
+int psm_init_power_state_table(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ unsigned int i;
+ unsigned int table_entries;
+ struct pp_power_state *state;
+ int size;
+
+ if (hwmgr->hwmgr_func->get_num_of_pp_table_entries == NULL)
+ return 0;
+
+ if (hwmgr->hwmgr_func->get_power_state_size == NULL)
+ return 0;
+
+ hwmgr->num_ps = table_entries = hwmgr->hwmgr_func->get_num_of_pp_table_entries(hwmgr);
+
+ hwmgr->ps_size = size = hwmgr->hwmgr_func->get_power_state_size(hwmgr) +
+ sizeof(struct pp_power_state);
+
+ if (table_entries == 0 || size == 0) {
+ pr_warn("Please check whether power state management is supported on this asic\n");
+ return 0;
+ }
+
+ hwmgr->ps = kcalloc(table_entries, size, GFP_KERNEL);
+ if (hwmgr->ps == NULL)
+ return -ENOMEM;
+
+ hwmgr->request_ps = kzalloc(size, GFP_KERNEL);
+ if (hwmgr->request_ps == NULL) {
+ kfree(hwmgr->ps);
+ hwmgr->ps = NULL;
+ return -ENOMEM;
+ }
+
+ hwmgr->current_ps = kzalloc(size, GFP_KERNEL);
+ if (hwmgr->current_ps == NULL) {
+ kfree(hwmgr->request_ps);
+ kfree(hwmgr->ps);
+ hwmgr->request_ps = NULL;
+ hwmgr->ps = NULL;
+ return -ENOMEM;
+ }
+
+ state = hwmgr->ps;
+
+ for (i = 0; i < table_entries; i++) {
+ result = hwmgr->hwmgr_func->get_pp_table_entry(hwmgr, i, state);
+
+ if (state->classification.flags & PP_StateClassificationFlag_Boot) {
+ hwmgr->boot_ps = state;
+ memcpy(hwmgr->current_ps, state, size);
+ memcpy(hwmgr->request_ps, state, size);
+ }
+
+ state->id = i + 1; /* assigned unique num for every power state id */
+
+ if (state->classification.flags & PP_StateClassificationFlag_Uvd)
+ hwmgr->uvd_ps = state;
+ state = (struct pp_power_state *)((unsigned long)state + size);
+ }
+
+ return 0;
+}
+
+int psm_fini_power_state_table(struct pp_hwmgr *hwmgr)
+{
+ if (hwmgr == NULL)
+ return -EINVAL;
+
+ if (!hwmgr->ps)
+ return 0;
+
+ kfree(hwmgr->current_ps);
+ kfree(hwmgr->request_ps);
+ kfree(hwmgr->ps);
+ hwmgr->request_ps = NULL;
+ hwmgr->ps = NULL;
+ hwmgr->current_ps = NULL;
+ return 0;
+}
+
+static int psm_get_ui_state(struct pp_hwmgr *hwmgr,
+ enum PP_StateUILabel ui_label,
+ unsigned long *state_id)
+{
+ struct pp_power_state *state;
+ int table_entries;
+ int i;
+
+ table_entries = hwmgr->num_ps;
+ state = hwmgr->ps;
+
+ for (i = 0; i < table_entries; i++) {
+ if (state->classification.ui_label & ui_label) {
+ *state_id = state->id;
+ return 0;
+ }
+ state = (struct pp_power_state *)((unsigned long)state + hwmgr->ps_size);
+ }
+ return -EINVAL;
+}
+
+static int psm_get_state_by_classification(struct pp_hwmgr *hwmgr,
+ enum PP_StateClassificationFlag flag,
+ unsigned long *state_id)
+{
+ struct pp_power_state *state;
+ int table_entries;
+ int i;
+
+ table_entries = hwmgr->num_ps;
+ state = hwmgr->ps;
+
+ for (i = 0; i < table_entries; i++) {
+ if (state->classification.flags & flag) {
+ *state_id = state->id;
+ return 0;
+ }
+ state = (struct pp_power_state *)((unsigned long)state + hwmgr->ps_size);
+ }
+ return -EINVAL;
+}
+
+static int psm_set_states(struct pp_hwmgr *hwmgr, unsigned long state_id)
+{
+ struct pp_power_state *state;
+ int table_entries;
+ int i;
+
+ table_entries = hwmgr->num_ps;
+
+ state = hwmgr->ps;
+
+ for (i = 0; i < table_entries; i++) {
+ if (state->id == state_id) {
+ memcpy(hwmgr->request_ps, state, hwmgr->ps_size);
+ return 0;
+ }
+ state = (struct pp_power_state *)((unsigned long)state + hwmgr->ps_size);
+ }
+ return -EINVAL;
+}
+
+int psm_set_boot_states(struct pp_hwmgr *hwmgr)
+{
+ unsigned long state_id;
+ int ret = -EINVAL;
+
+ if (!hwmgr->ps)
+ return 0;
+
+ if (!psm_get_state_by_classification(hwmgr, PP_StateClassificationFlag_Boot,
+ &state_id))
+ ret = psm_set_states(hwmgr, state_id);
+
+ return ret;
+}
+
+int psm_set_performance_states(struct pp_hwmgr *hwmgr)
+{
+ unsigned long state_id;
+ int ret = -EINVAL;
+
+ if (!hwmgr->ps)
+ return 0;
+
+ if (!psm_get_ui_state(hwmgr, PP_StateUILabel_Performance,
+ &state_id))
+ ret = psm_set_states(hwmgr, state_id);
+
+ return ret;
+}
+
+int psm_set_user_performance_state(struct pp_hwmgr *hwmgr,
+ enum PP_StateUILabel label_id,
+ struct pp_power_state **state)
+{
+ int table_entries;
+ int i;
+
+ if (!hwmgr->ps)
+ return 0;
+
+ table_entries = hwmgr->num_ps;
+ *state = hwmgr->ps;
+
+restart_search:
+ for (i = 0; i < table_entries; i++) {
+ if ((*state)->classification.ui_label & label_id)
+ return 0;
+ *state = (struct pp_power_state *)((uintptr_t)*state + hwmgr->ps_size);
+ }
+
+ switch (label_id) {
+ case PP_StateUILabel_Battery:
+ case PP_StateUILabel_Balanced:
+ label_id = PP_StateUILabel_Performance;
+ goto restart_search;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static void power_state_management(struct pp_hwmgr *hwmgr,
+ struct pp_power_state *new_ps)
+{
+ struct pp_power_state *pcurrent;
+ struct pp_power_state *requested;
+ bool equal;
+
+ if (new_ps != NULL)
+ requested = new_ps;
+ else
+ requested = hwmgr->request_ps;
+
+ pcurrent = hwmgr->current_ps;
+
+ phm_apply_state_adjust_rules(hwmgr, requested, pcurrent);
+ if (pcurrent == NULL || (0 != phm_check_states_equal(hwmgr,
+ &pcurrent->hardware, &requested->hardware, &equal)))
+ equal = false;
+
+ if (!equal || phm_check_smc_update_required_for_display_configuration(hwmgr)) {
+ phm_set_power_state(hwmgr, &pcurrent->hardware, &requested->hardware);
+ memcpy(hwmgr->current_ps, hwmgr->request_ps, hwmgr->ps_size);
+ }
+}
+
+int psm_adjust_power_state_dynamic(struct pp_hwmgr *hwmgr, bool skip_display_settings,
+ struct pp_power_state *new_ps)
+{
+ uint32_t index;
+ long workload;
+
+ if (hwmgr->not_vf) {
+ if (!skip_display_settings)
+ phm_display_configuration_changed(hwmgr);
+
+ if (hwmgr->ps)
+ power_state_management(hwmgr, new_ps);
+ else
+ /*
+ * for vega12/vega20 which does not support power state manager
+ * DAL clock limits should also be honoured
+ */
+ phm_apply_clock_adjust_rules(hwmgr);
+
+ if (!skip_display_settings)
+ phm_notify_smc_display_config_after_ps_adjustment(hwmgr);
+ }
+
+ if (!phm_force_dpm_levels(hwmgr, hwmgr->request_dpm_level))
+ hwmgr->dpm_level = hwmgr->request_dpm_level;
+
+ if (hwmgr->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL) {
+ index = fls(hwmgr->workload_mask);
+ index = index > 0 && index <= Workload_Policy_Max ? index - 1 : 0;
+ workload = hwmgr->workload_setting[index];
+
+ if (hwmgr->power_profile_mode != workload && hwmgr->hwmgr_func->set_power_profile_mode)
+ hwmgr->hwmgr_func->set_power_profile_mode(hwmgr, &workload, 0);
+ }
+
+ return 0;
+}
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pp_psm.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pp_psm.h
new file mode 100644
index 000000000000..b62d55f1f289
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pp_psm.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 PP_PSM_H
+#define PP_PSM_H
+
+#include "hwmgr.h"
+
+int psm_init_power_state_table(struct pp_hwmgr *hwmgr);
+int psm_fini_power_state_table(struct pp_hwmgr *hwmgr);
+int psm_set_boot_states(struct pp_hwmgr *hwmgr);
+int psm_set_performance_states(struct pp_hwmgr *hwmgr);
+int psm_set_user_performance_state(struct pp_hwmgr *hwmgr,
+ enum PP_StateUILabel label_id,
+ struct pp_power_state **state);
+int psm_adjust_power_state_dynamic(struct pp_hwmgr *hwmgr,
+ bool skip_display_settings,
+ struct pp_power_state *new_ps);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c
new file mode 100644
index 000000000000..01dc46dc9c8a
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c
@@ -0,0 +1,1562 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "pp_debug.h"
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "atom.h"
+#include "ppatomctrl.h"
+#include "atombios.h"
+#include "cgs_common.h"
+#include "ppevvmath.h"
+
+#define MEM_ID_MASK 0xff000000
+#define MEM_ID_SHIFT 24
+#define CLOCK_RANGE_MASK 0x00ffffff
+#define CLOCK_RANGE_SHIFT 0
+#define LOW_NIBBLE_MASK 0xf
+#define DATA_EQU_PREV 0
+#define DATA_FROM_TABLE 4
+
+union voltage_object_info {
+ struct _ATOM_VOLTAGE_OBJECT_INFO v1;
+ struct _ATOM_VOLTAGE_OBJECT_INFO_V2 v2;
+ struct _ATOM_VOLTAGE_OBJECT_INFO_V3_1 v3;
+};
+
+static int atomctrl_retrieve_ac_timing(
+ uint8_t index,
+ ATOM_INIT_REG_BLOCK *reg_block,
+ pp_atomctrl_mc_reg_table *table)
+{
+ uint32_t i, j;
+ uint8_t tmem_id;
+ ATOM_MEMORY_SETTING_DATA_BLOCK *reg_data = (ATOM_MEMORY_SETTING_DATA_BLOCK *)
+ ((uint8_t *)reg_block + (2 * sizeof(uint16_t)) + le16_to_cpu(reg_block->usRegIndexTblSize));
+
+ uint8_t num_ranges = 0;
+
+ while (*(uint32_t *)reg_data != END_OF_REG_DATA_BLOCK &&
+ num_ranges < VBIOS_MAX_AC_TIMING_ENTRIES) {
+ tmem_id = (uint8_t)((*(uint32_t *)reg_data & MEM_ID_MASK) >> MEM_ID_SHIFT);
+
+ if (index == tmem_id) {
+ table->mc_reg_table_entry[num_ranges].mclk_max =
+ (uint32_t)((*(uint32_t *)reg_data & CLOCK_RANGE_MASK) >>
+ CLOCK_RANGE_SHIFT);
+
+ for (i = 0, j = 1; i < table->last; i++) {
+ if ((table->mc_reg_address[i].uc_pre_reg_data &
+ LOW_NIBBLE_MASK) == DATA_FROM_TABLE) {
+ table->mc_reg_table_entry[num_ranges].mc_data[i] =
+ (uint32_t)*((uint32_t *)reg_data + j);
+ j++;
+ } else if ((table->mc_reg_address[i].uc_pre_reg_data &
+ LOW_NIBBLE_MASK) == DATA_EQU_PREV) {
+ table->mc_reg_table_entry[num_ranges].mc_data[i] =
+ table->mc_reg_table_entry[num_ranges].mc_data[i-1];
+ }
+ }
+ num_ranges++;
+ }
+
+ reg_data = (ATOM_MEMORY_SETTING_DATA_BLOCK *)
+ ((uint8_t *)reg_data + le16_to_cpu(reg_block->usRegDataBlkSize)) ;
+ }
+
+ PP_ASSERT_WITH_CODE((*(uint32_t *)reg_data == END_OF_REG_DATA_BLOCK),
+ "Invalid VramInfo table.", return -1);
+ table->num_entries = num_ranges;
+
+ return 0;
+}
+
+/**
+ * Get memory clock AC timing registers index from VBIOS table
+ * VBIOS set end of memory clock AC timing registers by ucPreRegDataLength bit6 = 1
+ * @param reg_block the address ATOM_INIT_REG_BLOCK
+ * @param table the address of MCRegTable
+ * @return 0
+ */
+static int atomctrl_set_mc_reg_address_table(
+ ATOM_INIT_REG_BLOCK *reg_block,
+ pp_atomctrl_mc_reg_table *table)
+{
+ uint8_t i = 0;
+ uint8_t num_entries = (uint8_t)((le16_to_cpu(reg_block->usRegIndexTblSize))
+ / sizeof(ATOM_INIT_REG_INDEX_FORMAT));
+ ATOM_INIT_REG_INDEX_FORMAT *format = &reg_block->asRegIndexBuf[0];
+
+ num_entries--; /* subtract 1 data end mark entry */
+
+ PP_ASSERT_WITH_CODE((num_entries <= VBIOS_MC_REGISTER_ARRAY_SIZE),
+ "Invalid VramInfo table.", return -1);
+
+ /* ucPreRegDataLength bit6 = 1 is the end of memory clock AC timing registers */
+ while ((!(format->ucPreRegDataLength & ACCESS_PLACEHOLDER)) &&
+ (i < num_entries)) {
+ table->mc_reg_address[i].s1 =
+ (uint16_t)(le16_to_cpu(format->usRegIndex));
+ table->mc_reg_address[i].uc_pre_reg_data =
+ format->ucPreRegDataLength;
+
+ i++;
+ format = (ATOM_INIT_REG_INDEX_FORMAT *)
+ ((uint8_t *)format + sizeof(ATOM_INIT_REG_INDEX_FORMAT));
+ }
+
+ table->last = i;
+ return 0;
+}
+
+int atomctrl_initialize_mc_reg_table(
+ struct pp_hwmgr *hwmgr,
+ uint8_t module_index,
+ pp_atomctrl_mc_reg_table *table)
+{
+ ATOM_VRAM_INFO_HEADER_V2_1 *vram_info;
+ ATOM_INIT_REG_BLOCK *reg_block;
+ int result = 0;
+ u8 frev, crev;
+ u16 size;
+
+ vram_info = (ATOM_VRAM_INFO_HEADER_V2_1 *)
+ smu_atom_get_data_table(hwmgr->adev,
+ GetIndexIntoMasterTable(DATA, VRAM_Info), &size, &frev, &crev);
+
+ if (module_index >= vram_info->ucNumOfVRAMModule) {
+ pr_err("Invalid VramInfo table.");
+ result = -1;
+ } else if (vram_info->sHeader.ucTableFormatRevision < 2) {
+ pr_err("Invalid VramInfo table.");
+ result = -1;
+ }
+
+ if (0 == result) {
+ reg_block = (ATOM_INIT_REG_BLOCK *)
+ ((uint8_t *)vram_info + le16_to_cpu(vram_info->usMemClkPatchTblOffset));
+ result = atomctrl_set_mc_reg_address_table(reg_block, table);
+ }
+
+ if (0 == result) {
+ result = atomctrl_retrieve_ac_timing(module_index,
+ reg_block, table);
+ }
+
+ return result;
+}
+
+/**
+ * Set DRAM timings based on engine clock and memory clock.
+ */
+int atomctrl_set_engine_dram_timings_rv770(
+ struct pp_hwmgr *hwmgr,
+ uint32_t engine_clock,
+ uint32_t memory_clock)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ SET_ENGINE_CLOCK_PS_ALLOCATION engine_clock_parameters;
+
+ /* They are both in 10KHz Units. */
+ engine_clock_parameters.ulTargetEngineClock =
+ cpu_to_le32((engine_clock & SET_CLOCK_FREQ_MASK) |
+ ((COMPUTE_ENGINE_PLL_PARAM << 24)));
+
+ /* in 10 khz units.*/
+ engine_clock_parameters.sReserved.ulClock =
+ cpu_to_le32(memory_clock & SET_CLOCK_FREQ_MASK);
+
+ return amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, DynamicMemorySettings),
+ (uint32_t *)&engine_clock_parameters);
+}
+
+/**
+ * Private Function to get the PowerPlay Table Address.
+ * WARNING: The tabled returned by this function is in
+ * dynamically allocated memory.
+ * The caller has to release if by calling kfree.
+ */
+static ATOM_VOLTAGE_OBJECT_INFO *get_voltage_info_table(void *device)
+{
+ int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
+ u8 frev, crev;
+ u16 size;
+ union voltage_object_info *voltage_info;
+
+ voltage_info = (union voltage_object_info *)
+ smu_atom_get_data_table(device, index,
+ &size, &frev, &crev);
+
+ if (voltage_info != NULL)
+ return (ATOM_VOLTAGE_OBJECT_INFO *) &(voltage_info->v3);
+ else
+ return NULL;
+}
+
+static const ATOM_VOLTAGE_OBJECT_V3 *atomctrl_lookup_voltage_type_v3(
+ const ATOM_VOLTAGE_OBJECT_INFO_V3_1 * voltage_object_info_table,
+ uint8_t voltage_type, uint8_t voltage_mode)
+{
+ unsigned int size = le16_to_cpu(voltage_object_info_table->sHeader.usStructureSize);
+ unsigned int offset = offsetof(ATOM_VOLTAGE_OBJECT_INFO_V3_1, asVoltageObj[0]);
+ uint8_t *start = (uint8_t *)voltage_object_info_table;
+
+ while (offset < size) {
+ const ATOM_VOLTAGE_OBJECT_V3 *voltage_object =
+ (const ATOM_VOLTAGE_OBJECT_V3 *)(start + offset);
+
+ if (voltage_type == voltage_object->asGpioVoltageObj.sHeader.ucVoltageType &&
+ voltage_mode == voltage_object->asGpioVoltageObj.sHeader.ucVoltageMode)
+ return voltage_object;
+
+ offset += le16_to_cpu(voltage_object->asGpioVoltageObj.sHeader.usSize);
+ }
+
+ return NULL;
+}
+
+/** atomctrl_get_memory_pll_dividers_si().
+ *
+ * @param hwmgr input parameter: pointer to HwMgr
+ * @param clock_value input parameter: memory clock
+ * @param dividers output parameter: memory PLL dividers
+ * @param strobe_mode input parameter: 1 for strobe mode, 0 for performance mode
+ */
+int atomctrl_get_memory_pll_dividers_si(
+ struct pp_hwmgr *hwmgr,
+ uint32_t clock_value,
+ pp_atomctrl_memory_clock_param *mpll_param,
+ bool strobe_mode)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ COMPUTE_MEMORY_CLOCK_PARAM_PARAMETERS_V2_1 mpll_parameters;
+ int result;
+
+ mpll_parameters.ulClock = cpu_to_le32(clock_value);
+ mpll_parameters.ucInputFlag = (uint8_t)((strobe_mode) ? 1 : 0);
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, ComputeMemoryClockParam),
+ (uint32_t *)&mpll_parameters);
+
+ if (0 == result) {
+ mpll_param->mpll_fb_divider.clk_frac =
+ le16_to_cpu(mpll_parameters.ulFbDiv.usFbDivFrac);
+ mpll_param->mpll_fb_divider.cl_kf =
+ le16_to_cpu(mpll_parameters.ulFbDiv.usFbDiv);
+ mpll_param->mpll_post_divider =
+ (uint32_t)mpll_parameters.ucPostDiv;
+ mpll_param->vco_mode =
+ (uint32_t)(mpll_parameters.ucPllCntlFlag &
+ MPLL_CNTL_FLAG_VCO_MODE_MASK);
+ mpll_param->yclk_sel =
+ (uint32_t)((mpll_parameters.ucPllCntlFlag &
+ MPLL_CNTL_FLAG_BYPASS_DQ_PLL) ? 1 : 0);
+ mpll_param->qdr =
+ (uint32_t)((mpll_parameters.ucPllCntlFlag &
+ MPLL_CNTL_FLAG_QDR_ENABLE) ? 1 : 0);
+ mpll_param->half_rate =
+ (uint32_t)((mpll_parameters.ucPllCntlFlag &
+ MPLL_CNTL_FLAG_AD_HALF_RATE) ? 1 : 0);
+ mpll_param->dll_speed =
+ (uint32_t)(mpll_parameters.ucDllSpeed);
+ mpll_param->bw_ctrl =
+ (uint32_t)(mpll_parameters.ucBWCntl);
+ }
+
+ return result;
+}
+
+/** atomctrl_get_memory_pll_dividers_vi().
+ *
+ * @param hwmgr input parameter: pointer to HwMgr
+ * @param clock_value input parameter: memory clock
+ * @param dividers output parameter: memory PLL dividers
+ */
+int atomctrl_get_memory_pll_dividers_vi(struct pp_hwmgr *hwmgr,
+ uint32_t clock_value, pp_atomctrl_memory_clock_param *mpll_param)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ COMPUTE_MEMORY_CLOCK_PARAM_PARAMETERS_V2_2 mpll_parameters;
+ int result;
+
+ mpll_parameters.ulClock.ulClock = cpu_to_le32(clock_value);
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, ComputeMemoryClockParam),
+ (uint32_t *)&mpll_parameters);
+
+ if (!result)
+ mpll_param->mpll_post_divider =
+ (uint32_t)mpll_parameters.ulClock.ucPostDiv;
+
+ return result;
+}
+
+int atomctrl_get_memory_pll_dividers_ai(struct pp_hwmgr *hwmgr,
+ uint32_t clock_value,
+ pp_atomctrl_memory_clock_param_ai *mpll_param)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ COMPUTE_MEMORY_CLOCK_PARAM_PARAMETERS_V2_3 mpll_parameters = {{0}, 0, 0};
+ int result;
+
+ mpll_parameters.ulClock.ulClock = cpu_to_le32(clock_value);
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, ComputeMemoryClockParam),
+ (uint32_t *)&mpll_parameters);
+
+ /* VEGAM's mpll takes sometime to finish computing */
+ udelay(10);
+
+ if (!result) {
+ mpll_param->ulMclk_fcw_int =
+ le16_to_cpu(mpll_parameters.usMclk_fcw_int);
+ mpll_param->ulMclk_fcw_frac =
+ le16_to_cpu(mpll_parameters.usMclk_fcw_frac);
+ mpll_param->ulClock =
+ le32_to_cpu(mpll_parameters.ulClock.ulClock);
+ mpll_param->ulPostDiv = mpll_parameters.ulClock.ucPostDiv;
+ }
+
+ return result;
+}
+
+int atomctrl_get_engine_pll_dividers_kong(struct pp_hwmgr *hwmgr,
+ uint32_t clock_value,
+ pp_atomctrl_clock_dividers_kong *dividers)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V4 pll_parameters;
+ int result;
+
+ pll_parameters.ulClock = cpu_to_le32(clock_value);
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, ComputeMemoryEnginePLL),
+ (uint32_t *)&pll_parameters);
+
+ if (0 == result) {
+ dividers->pll_post_divider = pll_parameters.ucPostDiv;
+ dividers->real_clock = le32_to_cpu(pll_parameters.ulClock);
+ }
+
+ return result;
+}
+
+int atomctrl_get_engine_pll_dividers_vi(
+ struct pp_hwmgr *hwmgr,
+ uint32_t clock_value,
+ pp_atomctrl_clock_dividers_vi *dividers)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ COMPUTE_GPU_CLOCK_OUTPUT_PARAMETERS_V1_6 pll_patameters;
+ int result;
+
+ pll_patameters.ulClock.ulClock = cpu_to_le32(clock_value);
+ pll_patameters.ulClock.ucPostDiv = COMPUTE_GPUCLK_INPUT_FLAG_SCLK;
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, ComputeMemoryEnginePLL),
+ (uint32_t *)&pll_patameters);
+
+ if (0 == result) {
+ dividers->pll_post_divider =
+ pll_patameters.ulClock.ucPostDiv;
+ dividers->real_clock =
+ le32_to_cpu(pll_patameters.ulClock.ulClock);
+
+ dividers->ul_fb_div.ul_fb_div_frac =
+ le16_to_cpu(pll_patameters.ulFbDiv.usFbDivFrac);
+ dividers->ul_fb_div.ul_fb_div =
+ le16_to_cpu(pll_patameters.ulFbDiv.usFbDiv);
+
+ dividers->uc_pll_ref_div =
+ pll_patameters.ucPllRefDiv;
+ dividers->uc_pll_post_div =
+ pll_patameters.ucPllPostDiv;
+ dividers->uc_pll_cntl_flag =
+ pll_patameters.ucPllCntlFlag;
+ }
+
+ return result;
+}
+
+int atomctrl_get_engine_pll_dividers_ai(struct pp_hwmgr *hwmgr,
+ uint32_t clock_value,
+ pp_atomctrl_clock_dividers_ai *dividers)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ COMPUTE_GPU_CLOCK_OUTPUT_PARAMETERS_V1_7 pll_patameters;
+ int result;
+
+ pll_patameters.ulClock.ulClock = cpu_to_le32(clock_value);
+ pll_patameters.ulClock.ucPostDiv = COMPUTE_GPUCLK_INPUT_FLAG_SCLK;
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, ComputeMemoryEnginePLL),
+ (uint32_t *)&pll_patameters);
+
+ if (0 == result) {
+ dividers->usSclk_fcw_frac = le16_to_cpu(pll_patameters.usSclk_fcw_frac);
+ dividers->usSclk_fcw_int = le16_to_cpu(pll_patameters.usSclk_fcw_int);
+ dividers->ucSclkPostDiv = pll_patameters.ucSclkPostDiv;
+ dividers->ucSclkVcoMode = pll_patameters.ucSclkVcoMode;
+ dividers->ucSclkPllRange = pll_patameters.ucSclkPllRange;
+ dividers->ucSscEnable = pll_patameters.ucSscEnable;
+ dividers->usSsc_fcw1_frac = le16_to_cpu(pll_patameters.usSsc_fcw1_frac);
+ dividers->usSsc_fcw1_int = le16_to_cpu(pll_patameters.usSsc_fcw1_int);
+ dividers->usPcc_fcw_int = le16_to_cpu(pll_patameters.usPcc_fcw_int);
+ dividers->usSsc_fcw_slew_frac = le16_to_cpu(pll_patameters.usSsc_fcw_slew_frac);
+ dividers->usPcc_fcw_slew_frac = le16_to_cpu(pll_patameters.usPcc_fcw_slew_frac);
+ }
+ return result;
+}
+
+int atomctrl_get_dfs_pll_dividers_vi(
+ struct pp_hwmgr *hwmgr,
+ uint32_t clock_value,
+ pp_atomctrl_clock_dividers_vi *dividers)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ COMPUTE_GPU_CLOCK_OUTPUT_PARAMETERS_V1_6 pll_patameters;
+ int result;
+
+ pll_patameters.ulClock.ulClock = cpu_to_le32(clock_value);
+ pll_patameters.ulClock.ucPostDiv =
+ COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK;
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, ComputeMemoryEnginePLL),
+ (uint32_t *)&pll_patameters);
+
+ if (0 == result) {
+ dividers->pll_post_divider =
+ pll_patameters.ulClock.ucPostDiv;
+ dividers->real_clock =
+ le32_to_cpu(pll_patameters.ulClock.ulClock);
+
+ dividers->ul_fb_div.ul_fb_div_frac =
+ le16_to_cpu(pll_patameters.ulFbDiv.usFbDivFrac);
+ dividers->ul_fb_div.ul_fb_div =
+ le16_to_cpu(pll_patameters.ulFbDiv.usFbDiv);
+
+ dividers->uc_pll_ref_div =
+ pll_patameters.ucPllRefDiv;
+ dividers->uc_pll_post_div =
+ pll_patameters.ucPllPostDiv;
+ dividers->uc_pll_cntl_flag =
+ pll_patameters.ucPllCntlFlag;
+ }
+
+ return result;
+}
+
+/**
+ * Get the reference clock in 10KHz
+ */
+uint32_t atomctrl_get_reference_clock(struct pp_hwmgr *hwmgr)
+{
+ ATOM_FIRMWARE_INFO *fw_info;
+ u8 frev, crev;
+ u16 size;
+ uint32_t clock;
+
+ fw_info = (ATOM_FIRMWARE_INFO *)
+ smu_atom_get_data_table(hwmgr->adev,
+ GetIndexIntoMasterTable(DATA, FirmwareInfo),
+ &size, &frev, &crev);
+
+ if (fw_info == NULL)
+ clock = 2700;
+ else
+ clock = (uint32_t)(le16_to_cpu(fw_info->usReferenceClock));
+
+ return clock;
+}
+
+/**
+ * Returns true if the given voltage type is controlled by GPIO pins.
+ * voltage_type is one of SET_VOLTAGE_TYPE_ASIC_VDDC,
+ * SET_VOLTAGE_TYPE_ASIC_MVDDC, SET_VOLTAGE_TYPE_ASIC_MVDDQ.
+ * voltage_mode is one of ATOM_SET_VOLTAGE, ATOM_SET_VOLTAGE_PHASE
+ */
+bool atomctrl_is_voltage_controlled_by_gpio_v3(
+ struct pp_hwmgr *hwmgr,
+ uint8_t voltage_type,
+ uint8_t voltage_mode)
+{
+ ATOM_VOLTAGE_OBJECT_INFO_V3_1 *voltage_info =
+ (ATOM_VOLTAGE_OBJECT_INFO_V3_1 *)get_voltage_info_table(hwmgr->adev);
+ bool ret;
+
+ PP_ASSERT_WITH_CODE((NULL != voltage_info),
+ "Could not find Voltage Table in BIOS.", return false;);
+
+ ret = (NULL != atomctrl_lookup_voltage_type_v3
+ (voltage_info, voltage_type, voltage_mode)) ? true : false;
+
+ return ret;
+}
+
+int atomctrl_get_voltage_table_v3(
+ struct pp_hwmgr *hwmgr,
+ uint8_t voltage_type,
+ uint8_t voltage_mode,
+ pp_atomctrl_voltage_table *voltage_table)
+{
+ ATOM_VOLTAGE_OBJECT_INFO_V3_1 *voltage_info =
+ (ATOM_VOLTAGE_OBJECT_INFO_V3_1 *)get_voltage_info_table(hwmgr->adev);
+ const ATOM_VOLTAGE_OBJECT_V3 *voltage_object;
+ unsigned int i;
+
+ PP_ASSERT_WITH_CODE((NULL != voltage_info),
+ "Could not find Voltage Table in BIOS.", return -1;);
+
+ voltage_object = atomctrl_lookup_voltage_type_v3
+ (voltage_info, voltage_type, voltage_mode);
+
+ if (voltage_object == NULL)
+ return -1;
+
+ PP_ASSERT_WITH_CODE(
+ (voltage_object->asGpioVoltageObj.ucGpioEntryNum <=
+ PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES),
+ "Too many voltage entries!",
+ return -1;
+ );
+
+ for (i = 0; i < voltage_object->asGpioVoltageObj.ucGpioEntryNum; i++) {
+ voltage_table->entries[i].value =
+ le16_to_cpu(voltage_object->asGpioVoltageObj.asVolGpioLut[i].usVoltageValue);
+ voltage_table->entries[i].smio_low =
+ le32_to_cpu(voltage_object->asGpioVoltageObj.asVolGpioLut[i].ulVoltageId);
+ }
+
+ voltage_table->mask_low =
+ le32_to_cpu(voltage_object->asGpioVoltageObj.ulGpioMaskVal);
+ voltage_table->count =
+ voltage_object->asGpioVoltageObj.ucGpioEntryNum;
+ voltage_table->phase_delay =
+ voltage_object->asGpioVoltageObj.ucPhaseDelay;
+
+ return 0;
+}
+
+static bool atomctrl_lookup_gpio_pin(
+ ATOM_GPIO_PIN_LUT * gpio_lookup_table,
+ const uint32_t pinId,
+ pp_atomctrl_gpio_pin_assignment *gpio_pin_assignment)
+{
+ unsigned int size = le16_to_cpu(gpio_lookup_table->sHeader.usStructureSize);
+ unsigned int offset = offsetof(ATOM_GPIO_PIN_LUT, asGPIO_Pin[0]);
+ uint8_t *start = (uint8_t *)gpio_lookup_table;
+
+ while (offset < size) {
+ const ATOM_GPIO_PIN_ASSIGNMENT *pin_assignment =
+ (const ATOM_GPIO_PIN_ASSIGNMENT *)(start + offset);
+
+ if (pinId == pin_assignment->ucGPIO_ID) {
+ gpio_pin_assignment->uc_gpio_pin_bit_shift =
+ pin_assignment->ucGpioPinBitShift;
+ gpio_pin_assignment->us_gpio_pin_aindex =
+ le16_to_cpu(pin_assignment->usGpioPin_AIndex);
+ return true;
+ }
+
+ offset += offsetof(ATOM_GPIO_PIN_ASSIGNMENT, ucGPIO_ID) + 1;
+ }
+
+ return false;
+}
+
+/**
+ * Private Function to get the PowerPlay Table Address.
+ * WARNING: The tabled returned by this function is in
+ * dynamically allocated memory.
+ * The caller has to release if by calling kfree.
+ */
+static ATOM_GPIO_PIN_LUT *get_gpio_lookup_table(void *device)
+{
+ u8 frev, crev;
+ u16 size;
+ void *table_address;
+
+ table_address = (ATOM_GPIO_PIN_LUT *)
+ smu_atom_get_data_table(device,
+ GetIndexIntoMasterTable(DATA, GPIO_Pin_LUT),
+ &size, &frev, &crev);
+
+ PP_ASSERT_WITH_CODE((NULL != table_address),
+ "Error retrieving BIOS Table Address!", return NULL;);
+
+ return (ATOM_GPIO_PIN_LUT *)table_address;
+}
+
+/**
+ * Returns 1 if the given pin id find in lookup table.
+ */
+bool atomctrl_get_pp_assign_pin(
+ struct pp_hwmgr *hwmgr,
+ const uint32_t pinId,
+ pp_atomctrl_gpio_pin_assignment *gpio_pin_assignment)
+{
+ bool bRet = false;
+ ATOM_GPIO_PIN_LUT *gpio_lookup_table =
+ get_gpio_lookup_table(hwmgr->adev);
+
+ PP_ASSERT_WITH_CODE((NULL != gpio_lookup_table),
+ "Could not find GPIO lookup Table in BIOS.", return false);
+
+ bRet = atomctrl_lookup_gpio_pin(gpio_lookup_table, pinId,
+ gpio_pin_assignment);
+
+ return bRet;
+}
+
+int atomctrl_calculate_voltage_evv_on_sclk(
+ struct pp_hwmgr *hwmgr,
+ uint8_t voltage_type,
+ uint32_t sclk,
+ uint16_t virtual_voltage_Id,
+ uint16_t *voltage,
+ uint16_t dpm_level,
+ bool debug)
+{
+ ATOM_ASIC_PROFILING_INFO_V3_4 *getASICProfilingInfo;
+ struct amdgpu_device *adev = hwmgr->adev;
+ EFUSE_LINEAR_FUNC_PARAM sRO_fuse;
+ EFUSE_LINEAR_FUNC_PARAM sCACm_fuse;
+ EFUSE_LINEAR_FUNC_PARAM sCACb_fuse;
+ EFUSE_LOGISTIC_FUNC_PARAM sKt_Beta_fuse;
+ EFUSE_LOGISTIC_FUNC_PARAM sKv_m_fuse;
+ EFUSE_LOGISTIC_FUNC_PARAM sKv_b_fuse;
+ EFUSE_INPUT_PARAMETER sInput_FuseValues;
+ READ_EFUSE_VALUE_PARAMETER sOutput_FuseValues;
+
+ uint32_t ul_RO_fused, ul_CACb_fused, ul_CACm_fused, ul_Kt_Beta_fused, ul_Kv_m_fused, ul_Kv_b_fused;
+ fInt fSM_A0, fSM_A1, fSM_A2, fSM_A3, fSM_A4, fSM_A5, fSM_A6, fSM_A7;
+ fInt fMargin_RO_a, fMargin_RO_b, fMargin_RO_c, fMargin_fixed, fMargin_FMAX_mean, fMargin_Plat_mean, fMargin_FMAX_sigma, fMargin_Plat_sigma, fMargin_DC_sigma;
+ fInt fLkg_FT, repeat;
+ fInt fMicro_FMAX, fMicro_CR, fSigma_FMAX, fSigma_CR, fSigma_DC, fDC_SCLK, fSquared_Sigma_DC, fSquared_Sigma_CR, fSquared_Sigma_FMAX;
+ fInt fRLL_LoadLine, fPowerDPMx, fDerateTDP, fVDDC_base, fA_Term, fC_Term, fB_Term, fRO_DC_margin;
+ fInt fRO_fused, fCACm_fused, fCACb_fused, fKv_m_fused, fKv_b_fused, fKt_Beta_fused, fFT_Lkg_V0NORM;
+ fInt fSclk_margin, fSclk, fEVV_V;
+ fInt fV_min, fV_max, fT_prod, fLKG_Factor, fT_FT, fV_FT, fV_x, fTDP_Power, fTDP_Power_right, fTDP_Power_left, fTDP_Current, fV_NL;
+ uint32_t ul_FT_Lkg_V0NORM;
+ fInt fLn_MaxDivMin, fMin, fAverage, fRange;
+ fInt fRoots[2];
+ fInt fStepSize = GetScaledFraction(625, 100000);
+
+ int result;
+
+ getASICProfilingInfo = (ATOM_ASIC_PROFILING_INFO_V3_4 *)
+ smu_atom_get_data_table(hwmgr->adev,
+ GetIndexIntoMasterTable(DATA, ASIC_ProfilingInfo),
+ NULL, NULL, NULL);
+
+ if (!getASICProfilingInfo)
+ return -1;
+
+ if (getASICProfilingInfo->asHeader.ucTableFormatRevision < 3 ||
+ (getASICProfilingInfo->asHeader.ucTableFormatRevision == 3 &&
+ getASICProfilingInfo->asHeader.ucTableContentRevision < 4))
+ return -1;
+
+ /*-----------------------------------------------------------
+ *GETTING MULTI-STEP PARAMETERS RELATED TO CURRENT DPM LEVEL
+ *-----------------------------------------------------------
+ */
+ fRLL_LoadLine = Divide(getASICProfilingInfo->ulLoadLineSlop, 1000);
+
+ switch (dpm_level) {
+ case 1:
+ fPowerDPMx = Convert_ULONG_ToFraction(le16_to_cpu(getASICProfilingInfo->usPowerDpm1));
+ fDerateTDP = GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulTdpDerateDPM1), 1000);
+ break;
+ case 2:
+ fPowerDPMx = Convert_ULONG_ToFraction(le16_to_cpu(getASICProfilingInfo->usPowerDpm2));
+ fDerateTDP = GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulTdpDerateDPM2), 1000);
+ break;
+ case 3:
+ fPowerDPMx = Convert_ULONG_ToFraction(le16_to_cpu(getASICProfilingInfo->usPowerDpm3));
+ fDerateTDP = GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulTdpDerateDPM3), 1000);
+ break;
+ case 4:
+ fPowerDPMx = Convert_ULONG_ToFraction(le16_to_cpu(getASICProfilingInfo->usPowerDpm4));
+ fDerateTDP = GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulTdpDerateDPM4), 1000);
+ break;
+ case 5:
+ fPowerDPMx = Convert_ULONG_ToFraction(le16_to_cpu(getASICProfilingInfo->usPowerDpm5));
+ fDerateTDP = GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulTdpDerateDPM5), 1000);
+ break;
+ case 6:
+ fPowerDPMx = Convert_ULONG_ToFraction(le16_to_cpu(getASICProfilingInfo->usPowerDpm6));
+ fDerateTDP = GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulTdpDerateDPM6), 1000);
+ break;
+ case 7:
+ fPowerDPMx = Convert_ULONG_ToFraction(le16_to_cpu(getASICProfilingInfo->usPowerDpm7));
+ fDerateTDP = GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulTdpDerateDPM7), 1000);
+ break;
+ default:
+ pr_err("DPM Level not supported\n");
+ fPowerDPMx = Convert_ULONG_ToFraction(1);
+ fDerateTDP = GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulTdpDerateDPM0), 1000);
+ }
+
+ /*-------------------------
+ * DECODING FUSE VALUES
+ * ------------------------
+ */
+ /*Decode RO_Fused*/
+ sRO_fuse = getASICProfilingInfo->sRoFuse;
+
+ sInput_FuseValues.usEfuseIndex = sRO_fuse.usEfuseIndex;
+ sInput_FuseValues.ucBitShift = sRO_fuse.ucEfuseBitLSB;
+ sInput_FuseValues.ucBitLength = sRO_fuse.ucEfuseLength;
+
+ sOutput_FuseValues.sEfuse = sInput_FuseValues;
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, ReadEfuseValue),
+ (uint32_t *)&sOutput_FuseValues);
+
+ if (result)
+ return result;
+
+ /* Finally, the actual fuse value */
+ ul_RO_fused = le32_to_cpu(sOutput_FuseValues.ulEfuseValue);
+ fMin = GetScaledFraction(le32_to_cpu(sRO_fuse.ulEfuseMin), 1);
+ fRange = GetScaledFraction(le32_to_cpu(sRO_fuse.ulEfuseEncodeRange), 1);
+ fRO_fused = fDecodeLinearFuse(ul_RO_fused, fMin, fRange, sRO_fuse.ucEfuseLength);
+
+ sCACm_fuse = getASICProfilingInfo->sCACm;
+
+ sInput_FuseValues.usEfuseIndex = sCACm_fuse.usEfuseIndex;
+ sInput_FuseValues.ucBitShift = sCACm_fuse.ucEfuseBitLSB;
+ sInput_FuseValues.ucBitLength = sCACm_fuse.ucEfuseLength;
+
+ sOutput_FuseValues.sEfuse = sInput_FuseValues;
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, ReadEfuseValue),
+ (uint32_t *)&sOutput_FuseValues);
+
+ if (result)
+ return result;
+
+ ul_CACm_fused = le32_to_cpu(sOutput_FuseValues.ulEfuseValue);
+ fMin = GetScaledFraction(le32_to_cpu(sCACm_fuse.ulEfuseMin), 1000);
+ fRange = GetScaledFraction(le32_to_cpu(sCACm_fuse.ulEfuseEncodeRange), 1000);
+
+ fCACm_fused = fDecodeLinearFuse(ul_CACm_fused, fMin, fRange, sCACm_fuse.ucEfuseLength);
+
+ sCACb_fuse = getASICProfilingInfo->sCACb;
+
+ sInput_FuseValues.usEfuseIndex = sCACb_fuse.usEfuseIndex;
+ sInput_FuseValues.ucBitShift = sCACb_fuse.ucEfuseBitLSB;
+ sInput_FuseValues.ucBitLength = sCACb_fuse.ucEfuseLength;
+ sOutput_FuseValues.sEfuse = sInput_FuseValues;
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, ReadEfuseValue),
+ (uint32_t *)&sOutput_FuseValues);
+
+ if (result)
+ return result;
+
+ ul_CACb_fused = le32_to_cpu(sOutput_FuseValues.ulEfuseValue);
+ fMin = GetScaledFraction(le32_to_cpu(sCACb_fuse.ulEfuseMin), 1000);
+ fRange = GetScaledFraction(le32_to_cpu(sCACb_fuse.ulEfuseEncodeRange), 1000);
+
+ fCACb_fused = fDecodeLinearFuse(ul_CACb_fused, fMin, fRange, sCACb_fuse.ucEfuseLength);
+
+ sKt_Beta_fuse = getASICProfilingInfo->sKt_b;
+
+ sInput_FuseValues.usEfuseIndex = sKt_Beta_fuse.usEfuseIndex;
+ sInput_FuseValues.ucBitShift = sKt_Beta_fuse.ucEfuseBitLSB;
+ sInput_FuseValues.ucBitLength = sKt_Beta_fuse.ucEfuseLength;
+
+ sOutput_FuseValues.sEfuse = sInput_FuseValues;
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, ReadEfuseValue),
+ (uint32_t *)&sOutput_FuseValues);
+
+ if (result)
+ return result;
+
+ ul_Kt_Beta_fused = le32_to_cpu(sOutput_FuseValues.ulEfuseValue);
+ fAverage = GetScaledFraction(le32_to_cpu(sKt_Beta_fuse.ulEfuseEncodeAverage), 1000);
+ fRange = GetScaledFraction(le32_to_cpu(sKt_Beta_fuse.ulEfuseEncodeRange), 1000);
+
+ fKt_Beta_fused = fDecodeLogisticFuse(ul_Kt_Beta_fused,
+ fAverage, fRange, sKt_Beta_fuse.ucEfuseLength);
+
+ sKv_m_fuse = getASICProfilingInfo->sKv_m;
+
+ sInput_FuseValues.usEfuseIndex = sKv_m_fuse.usEfuseIndex;
+ sInput_FuseValues.ucBitShift = sKv_m_fuse.ucEfuseBitLSB;
+ sInput_FuseValues.ucBitLength = sKv_m_fuse.ucEfuseLength;
+
+ sOutput_FuseValues.sEfuse = sInput_FuseValues;
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, ReadEfuseValue),
+ (uint32_t *)&sOutput_FuseValues);
+ if (result)
+ return result;
+
+ ul_Kv_m_fused = le32_to_cpu(sOutput_FuseValues.ulEfuseValue);
+ fAverage = GetScaledFraction(le32_to_cpu(sKv_m_fuse.ulEfuseEncodeAverage), 1000);
+ fRange = GetScaledFraction((le32_to_cpu(sKv_m_fuse.ulEfuseEncodeRange) & 0x7fffffff), 1000);
+ fRange = fMultiply(fRange, ConvertToFraction(-1));
+
+ fKv_m_fused = fDecodeLogisticFuse(ul_Kv_m_fused,
+ fAverage, fRange, sKv_m_fuse.ucEfuseLength);
+
+ sKv_b_fuse = getASICProfilingInfo->sKv_b;
+
+ sInput_FuseValues.usEfuseIndex = sKv_b_fuse.usEfuseIndex;
+ sInput_FuseValues.ucBitShift = sKv_b_fuse.ucEfuseBitLSB;
+ sInput_FuseValues.ucBitLength = sKv_b_fuse.ucEfuseLength;
+ sOutput_FuseValues.sEfuse = sInput_FuseValues;
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, ReadEfuseValue),
+ (uint32_t *)&sOutput_FuseValues);
+
+ if (result)
+ return result;
+
+ ul_Kv_b_fused = le32_to_cpu(sOutput_FuseValues.ulEfuseValue);
+ fAverage = GetScaledFraction(le32_to_cpu(sKv_b_fuse.ulEfuseEncodeAverage), 1000);
+ fRange = GetScaledFraction(le32_to_cpu(sKv_b_fuse.ulEfuseEncodeRange), 1000);
+
+ fKv_b_fused = fDecodeLogisticFuse(ul_Kv_b_fused,
+ fAverage, fRange, sKv_b_fuse.ucEfuseLength);
+
+ /* Decoding the Leakage - No special struct container */
+ /*
+ * usLkgEuseIndex=56
+ * ucLkgEfuseBitLSB=6
+ * ucLkgEfuseLength=10
+ * ulLkgEncodeLn_MaxDivMin=69077
+ * ulLkgEncodeMax=1000000
+ * ulLkgEncodeMin=1000
+ * ulEfuseLogisticAlpha=13
+ */
+
+ sInput_FuseValues.usEfuseIndex = getASICProfilingInfo->usLkgEuseIndex;
+ sInput_FuseValues.ucBitShift = getASICProfilingInfo->ucLkgEfuseBitLSB;
+ sInput_FuseValues.ucBitLength = getASICProfilingInfo->ucLkgEfuseLength;
+
+ sOutput_FuseValues.sEfuse = sInput_FuseValues;
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, ReadEfuseValue),
+ (uint32_t *)&sOutput_FuseValues);
+
+ if (result)
+ return result;
+
+ ul_FT_Lkg_V0NORM = le32_to_cpu(sOutput_FuseValues.ulEfuseValue);
+ fLn_MaxDivMin = GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulLkgEncodeLn_MaxDivMin), 10000);
+ fMin = GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulLkgEncodeMin), 10000);
+
+ fFT_Lkg_V0NORM = fDecodeLeakageID(ul_FT_Lkg_V0NORM,
+ fLn_MaxDivMin, fMin, getASICProfilingInfo->ucLkgEfuseLength);
+ fLkg_FT = fFT_Lkg_V0NORM;
+
+ /*-------------------------------------------
+ * PART 2 - Grabbing all required values
+ *-------------------------------------------
+ */
+ fSM_A0 = fMultiply(GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulSM_A0), 1000000),
+ ConvertToFraction(uPow(-1, getASICProfilingInfo->ucSM_A0_sign)));
+ fSM_A1 = fMultiply(GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulSM_A1), 1000000),
+ ConvertToFraction(uPow(-1, getASICProfilingInfo->ucSM_A1_sign)));
+ fSM_A2 = fMultiply(GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulSM_A2), 100000),
+ ConvertToFraction(uPow(-1, getASICProfilingInfo->ucSM_A2_sign)));
+ fSM_A3 = fMultiply(GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulSM_A3), 1000000),
+ ConvertToFraction(uPow(-1, getASICProfilingInfo->ucSM_A3_sign)));
+ fSM_A4 = fMultiply(GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulSM_A4), 1000000),
+ ConvertToFraction(uPow(-1, getASICProfilingInfo->ucSM_A4_sign)));
+ fSM_A5 = fMultiply(GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulSM_A5), 1000),
+ ConvertToFraction(uPow(-1, getASICProfilingInfo->ucSM_A5_sign)));
+ fSM_A6 = fMultiply(GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulSM_A6), 1000),
+ ConvertToFraction(uPow(-1, getASICProfilingInfo->ucSM_A6_sign)));
+ fSM_A7 = fMultiply(GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulSM_A7), 1000),
+ ConvertToFraction(uPow(-1, getASICProfilingInfo->ucSM_A7_sign)));
+
+ fMargin_RO_a = ConvertToFraction(le32_to_cpu(getASICProfilingInfo->ulMargin_RO_a));
+ fMargin_RO_b = ConvertToFraction(le32_to_cpu(getASICProfilingInfo->ulMargin_RO_b));
+ fMargin_RO_c = ConvertToFraction(le32_to_cpu(getASICProfilingInfo->ulMargin_RO_c));
+
+ fMargin_fixed = ConvertToFraction(le32_to_cpu(getASICProfilingInfo->ulMargin_fixed));
+
+ fMargin_FMAX_mean = GetScaledFraction(
+ le32_to_cpu(getASICProfilingInfo->ulMargin_Fmax_mean), 10000);
+ fMargin_Plat_mean = GetScaledFraction(
+ le32_to_cpu(getASICProfilingInfo->ulMargin_plat_mean), 10000);
+ fMargin_FMAX_sigma = GetScaledFraction(
+ le32_to_cpu(getASICProfilingInfo->ulMargin_Fmax_sigma), 10000);
+ fMargin_Plat_sigma = GetScaledFraction(
+ le32_to_cpu(getASICProfilingInfo->ulMargin_plat_sigma), 10000);
+
+ fMargin_DC_sigma = GetScaledFraction(
+ le32_to_cpu(getASICProfilingInfo->ulMargin_DC_sigma), 100);
+ fMargin_DC_sigma = fDivide(fMargin_DC_sigma, ConvertToFraction(1000));
+
+ fCACm_fused = fDivide(fCACm_fused, ConvertToFraction(100));
+ fCACb_fused = fDivide(fCACb_fused, ConvertToFraction(100));
+ fKt_Beta_fused = fDivide(fKt_Beta_fused, ConvertToFraction(100));
+ fKv_m_fused = fNegate(fDivide(fKv_m_fused, ConvertToFraction(100)));
+ fKv_b_fused = fDivide(fKv_b_fused, ConvertToFraction(10));
+
+ fSclk = GetScaledFraction(sclk, 100);
+
+ fV_max = fDivide(GetScaledFraction(
+ le32_to_cpu(getASICProfilingInfo->ulMaxVddc), 1000), ConvertToFraction(4));
+ fT_prod = GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulBoardCoreTemp), 10);
+ fLKG_Factor = GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulEvvLkgFactor), 100);
+ fT_FT = GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulLeakageTemp), 10);
+ fV_FT = fDivide(GetScaledFraction(
+ le32_to_cpu(getASICProfilingInfo->ulLeakageVoltage), 1000), ConvertToFraction(4));
+ fV_min = fDivide(GetScaledFraction(
+ le32_to_cpu(getASICProfilingInfo->ulMinVddc), 1000), ConvertToFraction(4));
+
+ /*-----------------------
+ * PART 3
+ *-----------------------
+ */
+
+ fA_Term = fAdd(fMargin_RO_a, fAdd(fMultiply(fSM_A4, fSclk), fSM_A5));
+ fB_Term = fAdd(fAdd(fMultiply(fSM_A2, fSclk), fSM_A6), fMargin_RO_b);
+ fC_Term = fAdd(fMargin_RO_c,
+ fAdd(fMultiply(fSM_A0, fLkg_FT),
+ fAdd(fMultiply(fSM_A1, fMultiply(fLkg_FT, fSclk)),
+ fAdd(fMultiply(fSM_A3, fSclk),
+ fSubtract(fSM_A7, fRO_fused)))));
+
+ fVDDC_base = fSubtract(fRO_fused,
+ fSubtract(fMargin_RO_c,
+ fSubtract(fSM_A3, fMultiply(fSM_A1, fSclk))));
+ fVDDC_base = fDivide(fVDDC_base, fAdd(fMultiply(fSM_A0, fSclk), fSM_A2));
+
+ repeat = fSubtract(fVDDC_base,
+ fDivide(fMargin_DC_sigma, ConvertToFraction(1000)));
+
+ fRO_DC_margin = fAdd(fMultiply(fMargin_RO_a,
+ fGetSquare(repeat)),
+ fAdd(fMultiply(fMargin_RO_b, repeat),
+ fMargin_RO_c));
+
+ fDC_SCLK = fSubtract(fRO_fused,
+ fSubtract(fRO_DC_margin,
+ fSubtract(fSM_A3,
+ fMultiply(fSM_A2, repeat))));
+ fDC_SCLK = fDivide(fDC_SCLK, fAdd(fMultiply(fSM_A0, repeat), fSM_A1));
+
+ fSigma_DC = fSubtract(fSclk, fDC_SCLK);
+
+ fMicro_FMAX = fMultiply(fSclk, fMargin_FMAX_mean);
+ fMicro_CR = fMultiply(fSclk, fMargin_Plat_mean);
+ fSigma_FMAX = fMultiply(fSclk, fMargin_FMAX_sigma);
+ fSigma_CR = fMultiply(fSclk, fMargin_Plat_sigma);
+
+ fSquared_Sigma_DC = fGetSquare(fSigma_DC);
+ fSquared_Sigma_CR = fGetSquare(fSigma_CR);
+ fSquared_Sigma_FMAX = fGetSquare(fSigma_FMAX);
+
+ fSclk_margin = fAdd(fMicro_FMAX,
+ fAdd(fMicro_CR,
+ fAdd(fMargin_fixed,
+ fSqrt(fAdd(fSquared_Sigma_FMAX,
+ fAdd(fSquared_Sigma_DC, fSquared_Sigma_CR))))));
+ /*
+ fA_Term = fSM_A4 * (fSclk + fSclk_margin) + fSM_A5;
+ fB_Term = fSM_A2 * (fSclk + fSclk_margin) + fSM_A6;
+ fC_Term = fRO_DC_margin + fSM_A0 * fLkg_FT + fSM_A1 * fLkg_FT * (fSclk + fSclk_margin) + fSM_A3 * (fSclk + fSclk_margin) + fSM_A7 - fRO_fused;
+ */
+
+ fA_Term = fAdd(fMultiply(fSM_A4, fAdd(fSclk, fSclk_margin)), fSM_A5);
+ fB_Term = fAdd(fMultiply(fSM_A2, fAdd(fSclk, fSclk_margin)), fSM_A6);
+ fC_Term = fAdd(fRO_DC_margin,
+ fAdd(fMultiply(fSM_A0, fLkg_FT),
+ fAdd(fMultiply(fMultiply(fSM_A1, fLkg_FT),
+ fAdd(fSclk, fSclk_margin)),
+ fAdd(fMultiply(fSM_A3,
+ fAdd(fSclk, fSclk_margin)),
+ fSubtract(fSM_A7, fRO_fused)))));
+
+ SolveQuadracticEqn(fA_Term, fB_Term, fC_Term, fRoots);
+
+ if (GreaterThan(fRoots[0], fRoots[1]))
+ fEVV_V = fRoots[1];
+ else
+ fEVV_V = fRoots[0];
+
+ if (GreaterThan(fV_min, fEVV_V))
+ fEVV_V = fV_min;
+ else if (GreaterThan(fEVV_V, fV_max))
+ fEVV_V = fSubtract(fV_max, fStepSize);
+
+ fEVV_V = fRoundUpByStepSize(fEVV_V, fStepSize, 0);
+
+ /*-----------------
+ * PART 4
+ *-----------------
+ */
+
+ fV_x = fV_min;
+
+ while (GreaterThan(fAdd(fV_max, fStepSize), fV_x)) {
+ fTDP_Power_left = fMultiply(fMultiply(fMultiply(fAdd(
+ fMultiply(fCACm_fused, fV_x), fCACb_fused), fSclk),
+ fGetSquare(fV_x)), fDerateTDP);
+
+ fTDP_Power_right = fMultiply(fFT_Lkg_V0NORM, fMultiply(fLKG_Factor,
+ fMultiply(fExponential(fMultiply(fAdd(fMultiply(fKv_m_fused,
+ fT_prod), fKv_b_fused), fV_x)), fV_x)));
+ fTDP_Power_right = fMultiply(fTDP_Power_right, fExponential(fMultiply(
+ fKt_Beta_fused, fT_prod)));
+ fTDP_Power_right = fDivide(fTDP_Power_right, fExponential(fMultiply(
+ fAdd(fMultiply(fKv_m_fused, fT_prod), fKv_b_fused), fV_FT)));
+ fTDP_Power_right = fDivide(fTDP_Power_right, fExponential(fMultiply(
+ fKt_Beta_fused, fT_FT)));
+
+ fTDP_Power = fAdd(fTDP_Power_left, fTDP_Power_right);
+
+ fTDP_Current = fDivide(fTDP_Power, fV_x);
+
+ fV_NL = fAdd(fV_x, fDivide(fMultiply(fTDP_Current, fRLL_LoadLine),
+ ConvertToFraction(10)));
+
+ fV_NL = fRoundUpByStepSize(fV_NL, fStepSize, 0);
+
+ if (GreaterThan(fV_max, fV_NL) &&
+ (GreaterThan(fV_NL, fEVV_V) ||
+ Equal(fV_NL, fEVV_V))) {
+ fV_NL = fMultiply(fV_NL, ConvertToFraction(1000));
+
+ *voltage = (uint16_t)fV_NL.partial.real;
+ break;
+ } else
+ fV_x = fAdd(fV_x, fStepSize);
+ }
+
+ return result;
+}
+
+/** atomctrl_get_voltage_evv_on_sclk gets voltage via call to ATOM COMMAND table.
+ * @param hwmgr input: pointer to hwManager
+ * @param voltage_type input: type of EVV voltage VDDC or VDDGFX
+ * @param sclk input: in 10Khz unit. DPM state SCLK frequency
+ * which is define in PPTable SCLK/VDDC dependence
+ * table associated with this virtual_voltage_Id
+ * @param virtual_voltage_Id input: voltage id which match per voltage DPM state: 0xff01, 0xff02.. 0xff08
+ * @param voltage output: real voltage level in unit of mv
+ */
+int atomctrl_get_voltage_evv_on_sclk(
+ struct pp_hwmgr *hwmgr,
+ uint8_t voltage_type,
+ uint32_t sclk, uint16_t virtual_voltage_Id,
+ uint16_t *voltage)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ GET_VOLTAGE_INFO_INPUT_PARAMETER_V1_2 get_voltage_info_param_space;
+ int result;
+
+ get_voltage_info_param_space.ucVoltageType =
+ voltage_type;
+ get_voltage_info_param_space.ucVoltageMode =
+ ATOM_GET_VOLTAGE_EVV_VOLTAGE;
+ get_voltage_info_param_space.usVoltageLevel =
+ cpu_to_le16(virtual_voltage_Id);
+ get_voltage_info_param_space.ulSCLKFreq =
+ cpu_to_le32(sclk);
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, GetVoltageInfo),
+ (uint32_t *)&get_voltage_info_param_space);
+
+ *voltage = result ? 0 :
+ le16_to_cpu(((GET_EVV_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_2 *)
+ (&get_voltage_info_param_space))->usVoltageLevel);
+
+ return result;
+}
+
+/**
+ * atomctrl_get_voltage_evv gets voltage via call to ATOM COMMAND table.
+ * @param hwmgr input: pointer to hwManager
+ * @param virtual_voltage_id input: voltage id which match per voltage DPM state: 0xff01, 0xff02.. 0xff08
+ * @param voltage output: real voltage level in unit of mv
+ */
+int atomctrl_get_voltage_evv(struct pp_hwmgr *hwmgr,
+ uint16_t virtual_voltage_id,
+ uint16_t *voltage)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ GET_VOLTAGE_INFO_INPUT_PARAMETER_V1_2 get_voltage_info_param_space;
+ int result;
+ int entry_id;
+
+ /* search for leakage voltage ID 0xff01 ~ 0xff08 and sckl */
+ for (entry_id = 0; entry_id < hwmgr->dyn_state.vddc_dependency_on_sclk->count; entry_id++) {
+ if (hwmgr->dyn_state.vddc_dependency_on_sclk->entries[entry_id].v == virtual_voltage_id) {
+ /* found */
+ break;
+ }
+ }
+
+ if (entry_id >= hwmgr->dyn_state.vddc_dependency_on_sclk->count) {
+ pr_debug("Can't find requested voltage id in vddc_dependency_on_sclk table!\n");
+ return -EINVAL;
+ }
+
+ get_voltage_info_param_space.ucVoltageType = VOLTAGE_TYPE_VDDC;
+ get_voltage_info_param_space.ucVoltageMode = ATOM_GET_VOLTAGE_EVV_VOLTAGE;
+ get_voltage_info_param_space.usVoltageLevel = virtual_voltage_id;
+ get_voltage_info_param_space.ulSCLKFreq =
+ cpu_to_le32(hwmgr->dyn_state.vddc_dependency_on_sclk->entries[entry_id].clk);
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, GetVoltageInfo),
+ (uint32_t *)&get_voltage_info_param_space);
+
+ if (0 != result)
+ return result;
+
+ *voltage = le16_to_cpu(((GET_EVV_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_2 *)
+ (&get_voltage_info_param_space))->usVoltageLevel);
+
+ return result;
+}
+
+/**
+ * Get the mpll reference clock in 10KHz
+ */
+uint32_t atomctrl_get_mpll_reference_clock(struct pp_hwmgr *hwmgr)
+{
+ ATOM_COMMON_TABLE_HEADER *fw_info;
+ uint32_t clock;
+ u8 frev, crev;
+ u16 size;
+
+ fw_info = (ATOM_COMMON_TABLE_HEADER *)
+ smu_atom_get_data_table(hwmgr->adev,
+ GetIndexIntoMasterTable(DATA, FirmwareInfo),
+ &size, &frev, &crev);
+
+ if (fw_info == NULL)
+ clock = 2700;
+ else {
+ if ((fw_info->ucTableFormatRevision == 2) &&
+ (le16_to_cpu(fw_info->usStructureSize) >= sizeof(ATOM_FIRMWARE_INFO_V2_1))) {
+ ATOM_FIRMWARE_INFO_V2_1 *fwInfo_2_1 =
+ (ATOM_FIRMWARE_INFO_V2_1 *)fw_info;
+ clock = (uint32_t)(le16_to_cpu(fwInfo_2_1->usMemoryReferenceClock));
+ } else {
+ ATOM_FIRMWARE_INFO *fwInfo_0_0 =
+ (ATOM_FIRMWARE_INFO *)fw_info;
+ clock = (uint32_t)(le16_to_cpu(fwInfo_0_0->usReferenceClock));
+ }
+ }
+
+ return clock;
+}
+
+/**
+ * Get the asic internal spread spectrum table
+ */
+static ATOM_ASIC_INTERNAL_SS_INFO *asic_internal_ss_get_ss_table(void *device)
+{
+ ATOM_ASIC_INTERNAL_SS_INFO *table = NULL;
+ u8 frev, crev;
+ u16 size;
+
+ table = (ATOM_ASIC_INTERNAL_SS_INFO *)
+ smu_atom_get_data_table(device,
+ GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info),
+ &size, &frev, &crev);
+
+ return table;
+}
+
+/**
+ * Get the asic internal spread spectrum assignment
+ */
+static int asic_internal_ss_get_ss_asignment(struct pp_hwmgr *hwmgr,
+ const uint8_t clockSource,
+ const uint32_t clockSpeed,
+ pp_atomctrl_internal_ss_info *ssEntry)
+{
+ ATOM_ASIC_INTERNAL_SS_INFO *table;
+ ATOM_ASIC_SS_ASSIGNMENT *ssInfo;
+ int entry_found = 0;
+
+ memset(ssEntry, 0x00, sizeof(pp_atomctrl_internal_ss_info));
+
+ table = asic_internal_ss_get_ss_table(hwmgr->adev);
+
+ if (NULL == table)
+ return -1;
+
+ ssInfo = &table->asSpreadSpectrum[0];
+
+ while (((uint8_t *)ssInfo - (uint8_t *)table) <
+ le16_to_cpu(table->sHeader.usStructureSize)) {
+ if ((clockSource == ssInfo->ucClockIndication) &&
+ ((uint32_t)clockSpeed <= le32_to_cpu(ssInfo->ulTargetClockRange))) {
+ entry_found = 1;
+ break;
+ }
+
+ ssInfo = (ATOM_ASIC_SS_ASSIGNMENT *)((uint8_t *)ssInfo +
+ sizeof(ATOM_ASIC_SS_ASSIGNMENT));
+ }
+
+ if (entry_found) {
+ ssEntry->speed_spectrum_percentage =
+ le16_to_cpu(ssInfo->usSpreadSpectrumPercentage);
+ ssEntry->speed_spectrum_rate = le16_to_cpu(ssInfo->usSpreadRateInKhz);
+
+ if (((GET_DATA_TABLE_MAJOR_REVISION(table) == 2) &&
+ (GET_DATA_TABLE_MINOR_REVISION(table) >= 2)) ||
+ (GET_DATA_TABLE_MAJOR_REVISION(table) == 3)) {
+ ssEntry->speed_spectrum_rate /= 100;
+ }
+
+ switch (ssInfo->ucSpreadSpectrumMode) {
+ case 0:
+ ssEntry->speed_spectrum_mode =
+ pp_atomctrl_spread_spectrum_mode_down;
+ break;
+ case 1:
+ ssEntry->speed_spectrum_mode =
+ pp_atomctrl_spread_spectrum_mode_center;
+ break;
+ default:
+ ssEntry->speed_spectrum_mode =
+ pp_atomctrl_spread_spectrum_mode_down;
+ break;
+ }
+ }
+
+ return entry_found ? 0 : 1;
+}
+
+/**
+ * Get the memory clock spread spectrum info
+ */
+int atomctrl_get_memory_clock_spread_spectrum(
+ struct pp_hwmgr *hwmgr,
+ const uint32_t memory_clock,
+ pp_atomctrl_internal_ss_info *ssInfo)
+{
+ return asic_internal_ss_get_ss_asignment(hwmgr,
+ ASIC_INTERNAL_MEMORY_SS, memory_clock, ssInfo);
+}
+/**
+ * Get the engine clock spread spectrum info
+ */
+int atomctrl_get_engine_clock_spread_spectrum(
+ struct pp_hwmgr *hwmgr,
+ const uint32_t engine_clock,
+ pp_atomctrl_internal_ss_info *ssInfo)
+{
+ return asic_internal_ss_get_ss_asignment(hwmgr,
+ ASIC_INTERNAL_ENGINE_SS, engine_clock, ssInfo);
+}
+
+int atomctrl_read_efuse(struct pp_hwmgr *hwmgr, uint16_t start_index,
+ uint16_t end_index, uint32_t mask, uint32_t *efuse)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ int result;
+ READ_EFUSE_VALUE_PARAMETER efuse_param;
+
+ efuse_param.sEfuse.usEfuseIndex = cpu_to_le16((start_index / 32) * 4);
+ efuse_param.sEfuse.ucBitShift = (uint8_t)
+ (start_index - ((start_index / 32) * 32));
+ efuse_param.sEfuse.ucBitLength = (uint8_t)
+ ((end_index - start_index) + 1);
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, ReadEfuseValue),
+ (uint32_t *)&efuse_param);
+ *efuse = result ? 0 : le32_to_cpu(efuse_param.ulEfuseValue) & mask;
+
+ return result;
+}
+
+int atomctrl_set_ac_timing_ai(struct pp_hwmgr *hwmgr, uint32_t memory_clock,
+ uint8_t level)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ DYNAMICE_MEMORY_SETTINGS_PARAMETER_V2_1 memory_clock_parameters;
+ int result;
+
+ memory_clock_parameters.asDPMMCReg.ulClock.ulClockFreq =
+ memory_clock & SET_CLOCK_FREQ_MASK;
+ memory_clock_parameters.asDPMMCReg.ulClock.ulComputeClockFlag =
+ ADJUST_MC_SETTING_PARAM;
+ memory_clock_parameters.asDPMMCReg.ucMclkDPMState = level;
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, DynamicMemorySettings),
+ (uint32_t *)&memory_clock_parameters);
+
+ return result;
+}
+
+int atomctrl_get_voltage_evv_on_sclk_ai(struct pp_hwmgr *hwmgr, uint8_t voltage_type,
+ uint32_t sclk, uint16_t virtual_voltage_Id, uint32_t *voltage)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ int result;
+ GET_VOLTAGE_INFO_INPUT_PARAMETER_V1_3 get_voltage_info_param_space;
+
+ get_voltage_info_param_space.ucVoltageType = voltage_type;
+ get_voltage_info_param_space.ucVoltageMode = ATOM_GET_VOLTAGE_EVV_VOLTAGE;
+ get_voltage_info_param_space.usVoltageLevel = cpu_to_le16(virtual_voltage_Id);
+ get_voltage_info_param_space.ulSCLKFreq = cpu_to_le32(sclk);
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, GetVoltageInfo),
+ (uint32_t *)&get_voltage_info_param_space);
+
+ *voltage = result ? 0 :
+ le32_to_cpu(((GET_EVV_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_3 *)(&get_voltage_info_param_space))->ulVoltageLevel);
+
+ return result;
+}
+
+int atomctrl_get_smc_sclk_range_table(struct pp_hwmgr *hwmgr, struct pp_atom_ctrl_sclk_range_table *table)
+{
+
+ int i;
+ u8 frev, crev;
+ u16 size;
+
+ ATOM_SMU_INFO_V2_1 *psmu_info =
+ (ATOM_SMU_INFO_V2_1 *)smu_atom_get_data_table(hwmgr->adev,
+ GetIndexIntoMasterTable(DATA, SMU_Info),
+ &size, &frev, &crev);
+
+
+ for (i = 0; i < psmu_info->ucSclkEntryNum; i++) {
+ table->entry[i].ucVco_setting = psmu_info->asSclkFcwRangeEntry[i].ucVco_setting;
+ table->entry[i].ucPostdiv = psmu_info->asSclkFcwRangeEntry[i].ucPostdiv;
+ table->entry[i].usFcw_pcc =
+ le16_to_cpu(psmu_info->asSclkFcwRangeEntry[i].ucFcw_pcc);
+ table->entry[i].usFcw_trans_upper =
+ le16_to_cpu(psmu_info->asSclkFcwRangeEntry[i].ucFcw_trans_upper);
+ table->entry[i].usRcw_trans_lower =
+ le16_to_cpu(psmu_info->asSclkFcwRangeEntry[i].ucRcw_trans_lower);
+ }
+
+ return 0;
+}
+
+int atomctrl_get_avfs_information(struct pp_hwmgr *hwmgr,
+ struct pp_atom_ctrl__avfs_parameters *param)
+{
+ ATOM_ASIC_PROFILING_INFO_V3_6 *profile = NULL;
+
+ if (param == NULL)
+ return -EINVAL;
+
+ profile = (ATOM_ASIC_PROFILING_INFO_V3_6 *)
+ smu_atom_get_data_table(hwmgr->adev,
+ GetIndexIntoMasterTable(DATA, ASIC_ProfilingInfo),
+ NULL, NULL, NULL);
+ if (!profile)
+ return -1;
+
+ param->ulAVFS_meanNsigma_Acontant0 = le32_to_cpu(profile->ulAVFS_meanNsigma_Acontant0);
+ param->ulAVFS_meanNsigma_Acontant1 = le32_to_cpu(profile->ulAVFS_meanNsigma_Acontant1);
+ param->ulAVFS_meanNsigma_Acontant2 = le32_to_cpu(profile->ulAVFS_meanNsigma_Acontant2);
+ param->usAVFS_meanNsigma_DC_tol_sigma = le16_to_cpu(profile->usAVFS_meanNsigma_DC_tol_sigma);
+ param->usAVFS_meanNsigma_Platform_mean = le16_to_cpu(profile->usAVFS_meanNsigma_Platform_mean);
+ param->usAVFS_meanNsigma_Platform_sigma = le16_to_cpu(profile->usAVFS_meanNsigma_Platform_sigma);
+ param->ulGB_VDROOP_TABLE_CKSOFF_a0 = le32_to_cpu(profile->ulGB_VDROOP_TABLE_CKSOFF_a0);
+ param->ulGB_VDROOP_TABLE_CKSOFF_a1 = le32_to_cpu(profile->ulGB_VDROOP_TABLE_CKSOFF_a1);
+ param->ulGB_VDROOP_TABLE_CKSOFF_a2 = le32_to_cpu(profile->ulGB_VDROOP_TABLE_CKSOFF_a2);
+ param->ulGB_VDROOP_TABLE_CKSON_a0 = le32_to_cpu(profile->ulGB_VDROOP_TABLE_CKSON_a0);
+ param->ulGB_VDROOP_TABLE_CKSON_a1 = le32_to_cpu(profile->ulGB_VDROOP_TABLE_CKSON_a1);
+ param->ulGB_VDROOP_TABLE_CKSON_a2 = le32_to_cpu(profile->ulGB_VDROOP_TABLE_CKSON_a2);
+ param->ulAVFSGB_FUSE_TABLE_CKSOFF_m1 = le32_to_cpu(profile->ulAVFSGB_FUSE_TABLE_CKSOFF_m1);
+ param->usAVFSGB_FUSE_TABLE_CKSOFF_m2 = le16_to_cpu(profile->usAVFSGB_FUSE_TABLE_CKSOFF_m2);
+ param->ulAVFSGB_FUSE_TABLE_CKSOFF_b = le32_to_cpu(profile->ulAVFSGB_FUSE_TABLE_CKSOFF_b);
+ param->ulAVFSGB_FUSE_TABLE_CKSON_m1 = le32_to_cpu(profile->ulAVFSGB_FUSE_TABLE_CKSON_m1);
+ param->usAVFSGB_FUSE_TABLE_CKSON_m2 = le16_to_cpu(profile->usAVFSGB_FUSE_TABLE_CKSON_m2);
+ param->ulAVFSGB_FUSE_TABLE_CKSON_b = le32_to_cpu(profile->ulAVFSGB_FUSE_TABLE_CKSON_b);
+ param->usMaxVoltage_0_25mv = le16_to_cpu(profile->usMaxVoltage_0_25mv);
+ param->ucEnableGB_VDROOP_TABLE_CKSOFF = profile->ucEnableGB_VDROOP_TABLE_CKSOFF;
+ param->ucEnableGB_VDROOP_TABLE_CKSON = profile->ucEnableGB_VDROOP_TABLE_CKSON;
+ param->ucEnableGB_FUSE_TABLE_CKSOFF = profile->ucEnableGB_FUSE_TABLE_CKSOFF;
+ param->ucEnableGB_FUSE_TABLE_CKSON = profile->ucEnableGB_FUSE_TABLE_CKSON;
+ param->usPSM_Age_ComFactor = le16_to_cpu(profile->usPSM_Age_ComFactor);
+ param->ucEnableApplyAVFS_CKS_OFF_Voltage = profile->ucEnableApplyAVFS_CKS_OFF_Voltage;
+
+ return 0;
+}
+
+int atomctrl_get_svi2_info(struct pp_hwmgr *hwmgr, uint8_t voltage_type,
+ uint8_t *svd_gpio_id, uint8_t *svc_gpio_id,
+ uint16_t *load_line)
+{
+ ATOM_VOLTAGE_OBJECT_INFO_V3_1 *voltage_info =
+ (ATOM_VOLTAGE_OBJECT_INFO_V3_1 *)get_voltage_info_table(hwmgr->adev);
+
+ const ATOM_VOLTAGE_OBJECT_V3 *voltage_object;
+
+ PP_ASSERT_WITH_CODE((NULL != voltage_info),
+ "Could not find Voltage Table in BIOS.", return -EINVAL);
+
+ voltage_object = atomctrl_lookup_voltage_type_v3
+ (voltage_info, voltage_type, VOLTAGE_OBJ_SVID2);
+
+ *svd_gpio_id = voltage_object->asSVID2Obj.ucSVDGpioId;
+ *svc_gpio_id = voltage_object->asSVID2Obj.ucSVCGpioId;
+ *load_line = voltage_object->asSVID2Obj.usLoadLine_PSI;
+
+ return 0;
+}
+
+int atomctrl_get_leakage_id_from_efuse(struct pp_hwmgr *hwmgr, uint16_t *virtual_voltage_id)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ SET_VOLTAGE_PS_ALLOCATION allocation;
+ SET_VOLTAGE_PARAMETERS_V1_3 *voltage_parameters =
+ (SET_VOLTAGE_PARAMETERS_V1_3 *)&allocation.sASICSetVoltage;
+ int result;
+
+ voltage_parameters->ucVoltageMode = ATOM_GET_LEAKAGE_ID;
+
+ result = amdgpu_atom_execute_table(adev->mode_info.atom_context,
+ GetIndexIntoMasterTable(COMMAND, SetVoltage),
+ (uint32_t *)voltage_parameters);
+
+ *virtual_voltage_id = voltage_parameters->usVoltageLevel;
+
+ return result;
+}
+
+int atomctrl_get_leakage_vddc_base_on_leakage(struct pp_hwmgr *hwmgr,
+ uint16_t *vddc, uint16_t *vddci,
+ uint16_t virtual_voltage_id,
+ uint16_t efuse_voltage_id)
+{
+ int i, j;
+ int ix;
+ u16 *leakage_bin, *vddc_id_buf, *vddc_buf, *vddci_id_buf, *vddci_buf;
+ ATOM_ASIC_PROFILING_INFO_V2_1 *profile;
+
+ *vddc = 0;
+ *vddci = 0;
+
+ ix = GetIndexIntoMasterTable(DATA, ASIC_ProfilingInfo);
+
+ profile = (ATOM_ASIC_PROFILING_INFO_V2_1 *)
+ smu_atom_get_data_table(hwmgr->adev,
+ ix,
+ NULL, NULL, NULL);
+ if (!profile)
+ return -EINVAL;
+
+ if ((profile->asHeader.ucTableFormatRevision >= 2) &&
+ (profile->asHeader.ucTableContentRevision >= 1) &&
+ (profile->asHeader.usStructureSize >= sizeof(ATOM_ASIC_PROFILING_INFO_V2_1))) {
+ leakage_bin = (u16 *)((char *)profile + profile->usLeakageBinArrayOffset);
+ vddc_id_buf = (u16 *)((char *)profile + profile->usElbVDDC_IdArrayOffset);
+ vddc_buf = (u16 *)((char *)profile + profile->usElbVDDC_LevelArrayOffset);
+ if (profile->ucElbVDDC_Num > 0) {
+ for (i = 0; i < profile->ucElbVDDC_Num; i++) {
+ if (vddc_id_buf[i] == virtual_voltage_id) {
+ for (j = 0; j < profile->ucLeakageBinNum; j++) {
+ if (efuse_voltage_id <= leakage_bin[j]) {
+ *vddc = vddc_buf[j * profile->ucElbVDDC_Num + i];
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ vddci_id_buf = (u16 *)((char *)profile + profile->usElbVDDCI_IdArrayOffset);
+ vddci_buf = (u16 *)((char *)profile + profile->usElbVDDCI_LevelArrayOffset);
+ if (profile->ucElbVDDCI_Num > 0) {
+ for (i = 0; i < profile->ucElbVDDCI_Num; i++) {
+ if (vddci_id_buf[i] == virtual_voltage_id) {
+ for (j = 0; j < profile->ucLeakageBinNum; j++) {
+ if (efuse_voltage_id <= leakage_bin[j]) {
+ *vddci = vddci_buf[j * profile->ucElbVDDCI_Num + i];
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+void atomctrl_get_voltage_range(struct pp_hwmgr *hwmgr, uint32_t *max_vddc,
+ uint32_t *min_vddc)
+{
+ void *profile;
+
+ profile = smu_atom_get_data_table(hwmgr->adev,
+ GetIndexIntoMasterTable(DATA, ASIC_ProfilingInfo),
+ NULL, NULL, NULL);
+
+ if (profile) {
+ switch (hwmgr->chip_id) {
+ case CHIP_TONGA:
+ case CHIP_FIJI:
+ *max_vddc = le32_to_cpu(((ATOM_ASIC_PROFILING_INFO_V3_3 *)profile)->ulMaxVddc) / 4;
+ *min_vddc = le32_to_cpu(((ATOM_ASIC_PROFILING_INFO_V3_3 *)profile)->ulMinVddc) / 4;
+ return;
+ case CHIP_POLARIS11:
+ case CHIP_POLARIS10:
+ case CHIP_POLARIS12:
+ *max_vddc = le32_to_cpu(((ATOM_ASIC_PROFILING_INFO_V3_6 *)profile)->ulMaxVddc) / 100;
+ *min_vddc = le32_to_cpu(((ATOM_ASIC_PROFILING_INFO_V3_6 *)profile)->ulMinVddc) / 100;
+ return;
+ default:
+ break;
+ }
+ }
+ *max_vddc = 0;
+ *min_vddc = 0;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.h
new file mode 100644
index 000000000000..76ed2e413594
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.h
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 PP_ATOMVOLTAGECTRL_H
+#define PP_ATOMVOLTAGECTRL_H
+
+#include "hwmgr.h"
+
+/* As returned from PowerConnectorDetectionTable. */
+#define PP_ATOM_POWER_BUDGET_DISABLE_OVERDRIVE 0x80
+#define PP_ATOM_POWER_BUDGET_SHOW_WARNING 0x40
+#define PP_ATOM_POWER_BUDGET_SHOW_WAIVER 0x20
+#define PP_ATOM_POWER_POWER_BUDGET_BEHAVIOUR 0x0F
+
+/* New functions for Evergreen and beyond. */
+#define PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES 32
+
+struct pp_atomctrl_clock_dividers {
+ uint32_t pll_post_divider;
+ uint32_t pll_feedback_divider;
+ uint32_t pll_ref_divider;
+ bool enable_post_divider;
+};
+
+typedef struct pp_atomctrl_clock_dividers pp_atomctrl_clock_dividers;
+
+union pp_atomctrl_tcipll_fb_divider {
+ struct {
+ uint32_t ul_fb_div_frac : 14;
+ uint32_t ul_fb_div : 12;
+ uint32_t un_used : 6;
+ };
+ uint32_t ul_fb_divider;
+};
+
+typedef union pp_atomctrl_tcipll_fb_divider pp_atomctrl_tcipll_fb_divider;
+
+struct pp_atomctrl_clock_dividers_rv730 {
+ uint32_t pll_post_divider;
+ pp_atomctrl_tcipll_fb_divider mpll_feedback_divider;
+ uint32_t pll_ref_divider;
+ bool enable_post_divider;
+ bool enable_dithen;
+ uint32_t vco_mode;
+};
+typedef struct pp_atomctrl_clock_dividers_rv730 pp_atomctrl_clock_dividers_rv730;
+
+
+struct pp_atomctrl_clock_dividers_kong {
+ uint32_t pll_post_divider;
+ uint32_t real_clock;
+};
+typedef struct pp_atomctrl_clock_dividers_kong pp_atomctrl_clock_dividers_kong;
+
+struct pp_atomctrl_clock_dividers_ci {
+ uint32_t pll_post_divider; /* post divider value */
+ uint32_t real_clock;
+ pp_atomctrl_tcipll_fb_divider ul_fb_div; /* Output Parameter: PLL FB divider */
+ uint8_t uc_pll_ref_div; /* Output Parameter: PLL ref divider */
+ uint8_t uc_pll_post_div; /* Output Parameter: PLL post divider */
+ uint8_t uc_pll_cntl_flag; /*Output Flags: control flag */
+};
+typedef struct pp_atomctrl_clock_dividers_ci pp_atomctrl_clock_dividers_ci;
+
+struct pp_atomctrl_clock_dividers_vi {
+ uint32_t pll_post_divider; /* post divider value */
+ uint32_t real_clock;
+ pp_atomctrl_tcipll_fb_divider ul_fb_div; /*Output Parameter: PLL FB divider */
+ uint8_t uc_pll_ref_div; /*Output Parameter: PLL ref divider */
+ uint8_t uc_pll_post_div; /*Output Parameter: PLL post divider */
+ uint8_t uc_pll_cntl_flag; /*Output Flags: control flag */
+};
+typedef struct pp_atomctrl_clock_dividers_vi pp_atomctrl_clock_dividers_vi;
+
+struct pp_atomctrl_clock_dividers_ai {
+ u16 usSclk_fcw_frac;
+ u16 usSclk_fcw_int;
+ u8 ucSclkPostDiv;
+ u8 ucSclkVcoMode;
+ u8 ucSclkPllRange;
+ u8 ucSscEnable;
+ u16 usSsc_fcw1_frac;
+ u16 usSsc_fcw1_int;
+ u16 usReserved;
+ u16 usPcc_fcw_int;
+ u16 usSsc_fcw_slew_frac;
+ u16 usPcc_fcw_slew_frac;
+};
+typedef struct pp_atomctrl_clock_dividers_ai pp_atomctrl_clock_dividers_ai;
+
+
+union pp_atomctrl_s_mpll_fb_divider {
+ struct {
+ uint32_t cl_kf : 12;
+ uint32_t clk_frac : 12;
+ uint32_t un_used : 8;
+ };
+ uint32_t ul_fb_divider;
+};
+typedef union pp_atomctrl_s_mpll_fb_divider pp_atomctrl_s_mpll_fb_divider;
+
+enum pp_atomctrl_spread_spectrum_mode {
+ pp_atomctrl_spread_spectrum_mode_down = 0,
+ pp_atomctrl_spread_spectrum_mode_center
+};
+typedef enum pp_atomctrl_spread_spectrum_mode pp_atomctrl_spread_spectrum_mode;
+
+struct pp_atomctrl_memory_clock_param {
+ pp_atomctrl_s_mpll_fb_divider mpll_fb_divider;
+ uint32_t mpll_post_divider;
+ uint32_t bw_ctrl;
+ uint32_t dll_speed;
+ uint32_t vco_mode;
+ uint32_t yclk_sel;
+ uint32_t qdr;
+ uint32_t half_rate;
+};
+typedef struct pp_atomctrl_memory_clock_param pp_atomctrl_memory_clock_param;
+
+struct pp_atomctrl_memory_clock_param_ai {
+ uint32_t ulClock;
+ uint32_t ulPostDiv;
+ uint16_t ulMclk_fcw_frac;
+ uint16_t ulMclk_fcw_int;
+};
+typedef struct pp_atomctrl_memory_clock_param_ai pp_atomctrl_memory_clock_param_ai;
+
+struct pp_atomctrl_internal_ss_info {
+ uint32_t speed_spectrum_percentage; /* in 1/100 percentage */
+ uint32_t speed_spectrum_rate; /* in KHz */
+ pp_atomctrl_spread_spectrum_mode speed_spectrum_mode;
+};
+typedef struct pp_atomctrl_internal_ss_info pp_atomctrl_internal_ss_info;
+
+#ifndef NUMBER_OF_M3ARB_PARAMS
+#define NUMBER_OF_M3ARB_PARAMS 3
+#endif
+
+#ifndef NUMBER_OF_M3ARB_PARAM_SETS
+#define NUMBER_OF_M3ARB_PARAM_SETS 10
+#endif
+
+struct pp_atomctrl_kong_system_info {
+ uint32_t ul_bootup_uma_clock; /* in 10kHz unit */
+ uint16_t us_max_nb_voltage; /* high NB voltage, calculated using current VDDNB (D24F2xDC) and VDDNB offset fuse; */
+ uint16_t us_min_nb_voltage; /* low NB voltage, calculated using current VDDNB (D24F2xDC) and VDDNB offset fuse; */
+ uint16_t us_bootup_nb_voltage; /* boot up NB voltage */
+ uint8_t uc_htc_tmp_lmt; /* bit [22:16] of D24F3x64 Hardware Thermal Control (HTC) Register, may not be needed, TBD */
+ uint8_t uc_tj_offset; /* bit [28:22] of D24F3xE4 Thermtrip Status Register,may not be needed, TBD */
+ /* 0: default 1: uvd 2: fs-3d */
+ uint32_t ul_csr_m3_srb_cntl[NUMBER_OF_M3ARB_PARAM_SETS][NUMBER_OF_M3ARB_PARAMS];/* arrays with values for CSR M3 arbiter for default */
+};
+typedef struct pp_atomctrl_kong_system_info pp_atomctrl_kong_system_info;
+
+struct pp_atomctrl_memory_info {
+ uint8_t memory_vendor;
+ uint8_t memory_type;
+};
+typedef struct pp_atomctrl_memory_info pp_atomctrl_memory_info;
+
+#define MAX_AC_TIMING_ENTRIES 16
+
+struct pp_atomctrl_memory_clock_range_table {
+ uint8_t num_entries;
+ uint8_t rsv[3];
+
+ uint32_t mclk[MAX_AC_TIMING_ENTRIES];
+};
+typedef struct pp_atomctrl_memory_clock_range_table pp_atomctrl_memory_clock_range_table;
+
+struct pp_atomctrl_voltage_table_entry {
+ uint16_t value;
+ uint32_t smio_low;
+};
+
+typedef struct pp_atomctrl_voltage_table_entry pp_atomctrl_voltage_table_entry;
+
+struct pp_atomctrl_voltage_table {
+ uint32_t count;
+ uint32_t mask_low;
+ uint32_t phase_delay; /* Used for ATOM_GPIO_VOLTAGE_OBJECT_V3 and later */
+ pp_atomctrl_voltage_table_entry entries[PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES];
+};
+
+typedef struct pp_atomctrl_voltage_table pp_atomctrl_voltage_table;
+
+#define VBIOS_MC_REGISTER_ARRAY_SIZE 32
+#define VBIOS_MAX_AC_TIMING_ENTRIES 20
+
+struct pp_atomctrl_mc_reg_entry {
+ uint32_t mclk_max;
+ uint32_t mc_data[VBIOS_MC_REGISTER_ARRAY_SIZE];
+};
+typedef struct pp_atomctrl_mc_reg_entry pp_atomctrl_mc_reg_entry;
+
+struct pp_atomctrl_mc_register_address {
+ uint16_t s1;
+ uint8_t uc_pre_reg_data;
+};
+
+typedef struct pp_atomctrl_mc_register_address pp_atomctrl_mc_register_address;
+
+#define MAX_SCLK_RANGE 8
+
+struct pp_atom_ctrl_sclk_range_table_entry{
+ uint8_t ucVco_setting;
+ uint8_t ucPostdiv;
+ uint16_t usFcw_pcc;
+ uint16_t usFcw_trans_upper;
+ uint16_t usRcw_trans_lower;
+};
+
+
+struct pp_atom_ctrl_sclk_range_table{
+ struct pp_atom_ctrl_sclk_range_table_entry entry[MAX_SCLK_RANGE];
+};
+
+struct pp_atomctrl_mc_reg_table {
+ uint8_t last; /* number of registers */
+ uint8_t num_entries; /* number of AC timing entries */
+ pp_atomctrl_mc_reg_entry mc_reg_table_entry[VBIOS_MAX_AC_TIMING_ENTRIES];
+ pp_atomctrl_mc_register_address mc_reg_address[VBIOS_MC_REGISTER_ARRAY_SIZE];
+};
+typedef struct pp_atomctrl_mc_reg_table pp_atomctrl_mc_reg_table;
+
+struct pp_atomctrl_gpio_pin_assignment {
+ uint16_t us_gpio_pin_aindex;
+ uint8_t uc_gpio_pin_bit_shift;
+};
+typedef struct pp_atomctrl_gpio_pin_assignment pp_atomctrl_gpio_pin_assignment;
+
+struct pp_atom_ctrl__avfs_parameters {
+ uint32_t ulAVFS_meanNsigma_Acontant0;
+ uint32_t ulAVFS_meanNsigma_Acontant1;
+ uint32_t ulAVFS_meanNsigma_Acontant2;
+ uint16_t usAVFS_meanNsigma_DC_tol_sigma;
+ uint16_t usAVFS_meanNsigma_Platform_mean;
+ uint16_t usAVFS_meanNsigma_Platform_sigma;
+ uint32_t ulGB_VDROOP_TABLE_CKSOFF_a0;
+ uint32_t ulGB_VDROOP_TABLE_CKSOFF_a1;
+ uint32_t ulGB_VDROOP_TABLE_CKSOFF_a2;
+ uint32_t ulGB_VDROOP_TABLE_CKSON_a0;
+ uint32_t ulGB_VDROOP_TABLE_CKSON_a1;
+ uint32_t ulGB_VDROOP_TABLE_CKSON_a2;
+ uint32_t ulAVFSGB_FUSE_TABLE_CKSOFF_m1;
+ uint16_t usAVFSGB_FUSE_TABLE_CKSOFF_m2;
+ uint32_t ulAVFSGB_FUSE_TABLE_CKSOFF_b;
+ uint32_t ulAVFSGB_FUSE_TABLE_CKSON_m1;
+ uint16_t usAVFSGB_FUSE_TABLE_CKSON_m2;
+ uint32_t ulAVFSGB_FUSE_TABLE_CKSON_b;
+ uint16_t usMaxVoltage_0_25mv;
+ uint8_t ucEnableGB_VDROOP_TABLE_CKSOFF;
+ uint8_t ucEnableGB_VDROOP_TABLE_CKSON;
+ uint8_t ucEnableGB_FUSE_TABLE_CKSOFF;
+ uint8_t ucEnableGB_FUSE_TABLE_CKSON;
+ uint16_t usPSM_Age_ComFactor;
+ uint8_t ucEnableApplyAVFS_CKS_OFF_Voltage;
+ uint8_t ucReserved;
+};
+
+extern bool atomctrl_get_pp_assign_pin(struct pp_hwmgr *hwmgr, const uint32_t pinId, pp_atomctrl_gpio_pin_assignment *gpio_pin_assignment);
+extern int atomctrl_get_voltage_evv_on_sclk(struct pp_hwmgr *hwmgr, uint8_t voltage_type, uint32_t sclk, uint16_t virtual_voltage_Id, uint16_t *voltage);
+extern int atomctrl_get_voltage_evv(struct pp_hwmgr *hwmgr, uint16_t virtual_voltage_id, uint16_t *voltage);
+extern uint32_t atomctrl_get_mpll_reference_clock(struct pp_hwmgr *hwmgr);
+extern int atomctrl_get_memory_clock_spread_spectrum(struct pp_hwmgr *hwmgr, const uint32_t memory_clock, pp_atomctrl_internal_ss_info *ssInfo);
+extern int atomctrl_get_engine_clock_spread_spectrum(struct pp_hwmgr *hwmgr, const uint32_t engine_clock, pp_atomctrl_internal_ss_info *ssInfo);
+extern int atomctrl_initialize_mc_reg_table(struct pp_hwmgr *hwmgr, uint8_t module_index, pp_atomctrl_mc_reg_table *table);
+extern int atomctrl_set_engine_dram_timings_rv770(struct pp_hwmgr *hwmgr, uint32_t engine_clock, uint32_t memory_clock);
+extern uint32_t atomctrl_get_reference_clock(struct pp_hwmgr *hwmgr);
+extern int atomctrl_get_memory_pll_dividers_si(struct pp_hwmgr *hwmgr, uint32_t clock_value, pp_atomctrl_memory_clock_param *mpll_param, bool strobe_mode);
+extern int atomctrl_get_engine_pll_dividers_vi(struct pp_hwmgr *hwmgr, uint32_t clock_value, pp_atomctrl_clock_dividers_vi *dividers);
+extern int atomctrl_get_dfs_pll_dividers_vi(struct pp_hwmgr *hwmgr, uint32_t clock_value, pp_atomctrl_clock_dividers_vi *dividers);
+extern bool atomctrl_is_voltage_controlled_by_gpio_v3(struct pp_hwmgr *hwmgr, uint8_t voltage_type, uint8_t voltage_mode);
+extern int atomctrl_get_voltage_table_v3(struct pp_hwmgr *hwmgr, uint8_t voltage_type, uint8_t voltage_mode, pp_atomctrl_voltage_table *voltage_table);
+extern int atomctrl_get_memory_pll_dividers_vi(struct pp_hwmgr *hwmgr,
+ uint32_t clock_value, pp_atomctrl_memory_clock_param *mpll_param);
+extern int atomctrl_get_memory_pll_dividers_ai(struct pp_hwmgr *hwmgr,
+ uint32_t clock_value, pp_atomctrl_memory_clock_param_ai *mpll_param);
+extern int atomctrl_get_engine_pll_dividers_kong(struct pp_hwmgr *hwmgr,
+ uint32_t clock_value,
+ pp_atomctrl_clock_dividers_kong *dividers);
+extern int atomctrl_read_efuse(struct pp_hwmgr *hwmgr, uint16_t start_index,
+ uint16_t end_index, uint32_t mask, uint32_t *efuse);
+extern int atomctrl_calculate_voltage_evv_on_sclk(struct pp_hwmgr *hwmgr, uint8_t voltage_type,
+ uint32_t sclk, uint16_t virtual_voltage_Id, uint16_t *voltage, uint16_t dpm_level, bool debug);
+extern int atomctrl_get_engine_pll_dividers_ai(struct pp_hwmgr *hwmgr, uint32_t clock_value, pp_atomctrl_clock_dividers_ai *dividers);
+extern int atomctrl_set_ac_timing_ai(struct pp_hwmgr *hwmgr, uint32_t memory_clock,
+ uint8_t level);
+extern int atomctrl_get_voltage_evv_on_sclk_ai(struct pp_hwmgr *hwmgr, uint8_t voltage_type,
+ uint32_t sclk, uint16_t virtual_voltage_Id, uint32_t *voltage);
+extern int atomctrl_get_smc_sclk_range_table(struct pp_hwmgr *hwmgr, struct pp_atom_ctrl_sclk_range_table *table);
+
+extern int atomctrl_get_avfs_information(struct pp_hwmgr *hwmgr, struct pp_atom_ctrl__avfs_parameters *param);
+
+extern int atomctrl_get_svi2_info(struct pp_hwmgr *hwmgr, uint8_t voltage_type,
+ uint8_t *svd_gpio_id, uint8_t *svc_gpio_id,
+ uint16_t *load_line);
+
+extern int atomctrl_get_leakage_vddc_base_on_leakage(struct pp_hwmgr *hwmgr,
+ uint16_t *vddc, uint16_t *vddci,
+ uint16_t virtual_voltage_id,
+ uint16_t efuse_voltage_id);
+extern int atomctrl_get_leakage_id_from_efuse(struct pp_hwmgr *hwmgr, uint16_t *virtual_voltage_id);
+
+extern void atomctrl_get_voltage_range(struct pp_hwmgr *hwmgr, uint32_t *max_vddc,
+ uint32_t *min_vddc);
+#endif
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.c
new file mode 100644
index 000000000000..615cf2c09e54
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.c
@@ -0,0 +1,710 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "ppatomfwctrl.h"
+#include "atomfirmware.h"
+#include "atom.h"
+#include "pp_debug.h"
+
+static const union atom_voltage_object_v4 *pp_atomfwctrl_lookup_voltage_type_v4(
+ const struct atom_voltage_objects_info_v4_1 *voltage_object_info_table,
+ uint8_t voltage_type, uint8_t voltage_mode)
+{
+ unsigned int size = le16_to_cpu(
+ voltage_object_info_table->table_header.structuresize);
+ unsigned int offset =
+ offsetof(struct atom_voltage_objects_info_v4_1, voltage_object[0]);
+ unsigned long start = (unsigned long)voltage_object_info_table;
+
+ while (offset < size) {
+ const union atom_voltage_object_v4 *voltage_object =
+ (const union atom_voltage_object_v4 *)(start + offset);
+
+ if (voltage_type == voltage_object->gpio_voltage_obj.header.voltage_type &&
+ voltage_mode == voltage_object->gpio_voltage_obj.header.voltage_mode)
+ return voltage_object;
+
+ offset += le16_to_cpu(voltage_object->gpio_voltage_obj.header.object_size);
+
+ }
+
+ return NULL;
+}
+
+static struct atom_voltage_objects_info_v4_1 *pp_atomfwctrl_get_voltage_info_table(
+ struct pp_hwmgr *hwmgr)
+{
+ const void *table_address;
+ uint16_t idx;
+
+ idx = GetIndexIntoMasterDataTable(voltageobject_info);
+ table_address = smu_atom_get_data_table(hwmgr->adev,
+ idx, NULL, NULL, NULL);
+
+ PP_ASSERT_WITH_CODE(table_address,
+ "Error retrieving BIOS Table Address!",
+ return NULL);
+
+ return (struct atom_voltage_objects_info_v4_1 *)table_address;
+}
+
+/**
+* Returns TRUE if the given voltage type is controlled by GPIO pins.
+* voltage_type is one of SET_VOLTAGE_TYPE_ASIC_VDDC, SET_VOLTAGE_TYPE_ASIC_MVDDC, SET_VOLTAGE_TYPE_ASIC_MVDDQ.
+* voltage_mode is one of ATOM_SET_VOLTAGE, ATOM_SET_VOLTAGE_PHASE
+*/
+bool pp_atomfwctrl_is_voltage_controlled_by_gpio_v4(struct pp_hwmgr *hwmgr,
+ uint8_t voltage_type, uint8_t voltage_mode)
+{
+ struct atom_voltage_objects_info_v4_1 *voltage_info =
+ (struct atom_voltage_objects_info_v4_1 *)
+ pp_atomfwctrl_get_voltage_info_table(hwmgr);
+ bool ret;
+
+ /* If we cannot find the table do NOT try to control this voltage. */
+ PP_ASSERT_WITH_CODE(voltage_info,
+ "Could not find Voltage Table in BIOS.",
+ return false);
+
+ ret = (pp_atomfwctrl_lookup_voltage_type_v4(voltage_info,
+ voltage_type, voltage_mode)) ? true : false;
+
+ return ret;
+}
+
+int pp_atomfwctrl_get_voltage_table_v4(struct pp_hwmgr *hwmgr,
+ uint8_t voltage_type, uint8_t voltage_mode,
+ struct pp_atomfwctrl_voltage_table *voltage_table)
+{
+ struct atom_voltage_objects_info_v4_1 *voltage_info =
+ (struct atom_voltage_objects_info_v4_1 *)
+ pp_atomfwctrl_get_voltage_info_table(hwmgr);
+ const union atom_voltage_object_v4 *voltage_object;
+ unsigned int i;
+ int result = 0;
+
+ PP_ASSERT_WITH_CODE(voltage_info,
+ "Could not find Voltage Table in BIOS.",
+ return -1);
+
+ voltage_object = pp_atomfwctrl_lookup_voltage_type_v4(voltage_info,
+ voltage_type, voltage_mode);
+
+ if (!voltage_object)
+ return -1;
+
+ voltage_table->count = 0;
+ if (voltage_mode == VOLTAGE_OBJ_GPIO_LUT) {
+ PP_ASSERT_WITH_CODE(
+ (voltage_object->gpio_voltage_obj.gpio_entry_num <=
+ PP_ATOMFWCTRL_MAX_VOLTAGE_ENTRIES),
+ "Too many voltage entries!",
+ result = -1);
+
+ if (!result) {
+ for (i = 0; i < voltage_object->gpio_voltage_obj.
+ gpio_entry_num; i++) {
+ voltage_table->entries[i].value =
+ le16_to_cpu(voltage_object->gpio_voltage_obj.
+ voltage_gpio_lut[i].voltage_level_mv);
+ voltage_table->entries[i].smio_low =
+ le32_to_cpu(voltage_object->gpio_voltage_obj.
+ voltage_gpio_lut[i].voltage_gpio_reg_val);
+ }
+ voltage_table->count =
+ voltage_object->gpio_voltage_obj.gpio_entry_num;
+ voltage_table->mask_low =
+ le32_to_cpu(
+ voltage_object->gpio_voltage_obj.gpio_mask_val);
+ voltage_table->phase_delay =
+ voltage_object->gpio_voltage_obj.phase_delay_us;
+ }
+ } else if (voltage_mode == VOLTAGE_OBJ_SVID2) {
+ voltage_table->psi1_enable =
+ (voltage_object->svid2_voltage_obj.loadline_psi1 & 0x20) >> 5;
+ voltage_table->psi0_enable =
+ voltage_object->svid2_voltage_obj.psi0_enable & 0x1;
+ voltage_table->max_vid_step =
+ voltage_object->svid2_voltage_obj.maxvstep;
+ voltage_table->telemetry_offset =
+ voltage_object->svid2_voltage_obj.telemetry_offset;
+ voltage_table->telemetry_slope =
+ voltage_object->svid2_voltage_obj.telemetry_gain;
+ } else
+ PP_ASSERT_WITH_CODE(false,
+ "Unsupported Voltage Object Mode!",
+ result = -1);
+
+ return result;
+}
+
+
+static struct atom_gpio_pin_lut_v2_1 *pp_atomfwctrl_get_gpio_lookup_table(
+ struct pp_hwmgr *hwmgr)
+{
+ const void *table_address;
+ uint16_t idx;
+
+ idx = GetIndexIntoMasterDataTable(gpio_pin_lut);
+ table_address = smu_atom_get_data_table(hwmgr->adev,
+ idx, NULL, NULL, NULL);
+ PP_ASSERT_WITH_CODE(table_address,
+ "Error retrieving BIOS Table Address!",
+ return NULL);
+
+ return (struct atom_gpio_pin_lut_v2_1 *)table_address;
+}
+
+static bool pp_atomfwctrl_lookup_gpio_pin(
+ struct atom_gpio_pin_lut_v2_1 *gpio_lookup_table,
+ const uint32_t pin_id,
+ struct pp_atomfwctrl_gpio_pin_assignment *gpio_pin_assignment)
+{
+ unsigned int size = le16_to_cpu(
+ gpio_lookup_table->table_header.structuresize);
+ unsigned int offset =
+ offsetof(struct atom_gpio_pin_lut_v2_1, gpio_pin[0]);
+ unsigned long start = (unsigned long)gpio_lookup_table;
+
+ while (offset < size) {
+ const struct atom_gpio_pin_assignment *pin_assignment =
+ (const struct atom_gpio_pin_assignment *)(start + offset);
+
+ if (pin_id == pin_assignment->gpio_id) {
+ gpio_pin_assignment->uc_gpio_pin_bit_shift =
+ pin_assignment->gpio_bitshift;
+ gpio_pin_assignment->us_gpio_pin_aindex =
+ le16_to_cpu(pin_assignment->data_a_reg_index);
+ return true;
+ }
+ offset += offsetof(struct atom_gpio_pin_assignment, gpio_id) + 1;
+ }
+ return false;
+}
+
+/**
+* Returns TRUE if the given pin id find in lookup table.
+*/
+bool pp_atomfwctrl_get_pp_assign_pin(struct pp_hwmgr *hwmgr,
+ const uint32_t pin_id,
+ struct pp_atomfwctrl_gpio_pin_assignment *gpio_pin_assignment)
+{
+ bool ret = false;
+ struct atom_gpio_pin_lut_v2_1 *gpio_lookup_table =
+ pp_atomfwctrl_get_gpio_lookup_table(hwmgr);
+
+ /* If we cannot find the table do NOT try to control this voltage. */
+ PP_ASSERT_WITH_CODE(gpio_lookup_table,
+ "Could not find GPIO lookup Table in BIOS.",
+ return false);
+
+ ret = pp_atomfwctrl_lookup_gpio_pin(gpio_lookup_table,
+ pin_id, gpio_pin_assignment);
+
+ return ret;
+}
+
+/**
+* Enter to SelfRefresh mode.
+* @param hwmgr
+*/
+int pp_atomfwctrl_enter_self_refresh(struct pp_hwmgr *hwmgr)
+{
+ /* 0 - no action
+ * 1 - leave power to video memory always on
+ */
+ return 0;
+}
+
+/** pp_atomfwctrl_get_gpu_pll_dividers_vega10().
+ *
+ * @param hwmgr input parameter: pointer to HwMgr
+ * @param clock_type input parameter: Clock type: 1 - GFXCLK, 2 - UCLK, 0 - All other clocks
+ * @param clock_value input parameter: Clock
+ * @param dividers output parameter:Clock dividers
+ */
+int pp_atomfwctrl_get_gpu_pll_dividers_vega10(struct pp_hwmgr *hwmgr,
+ uint32_t clock_type, uint32_t clock_value,
+ struct pp_atomfwctrl_clock_dividers_soc15 *dividers)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ struct compute_gpu_clock_input_parameter_v1_8 pll_parameters;
+ struct compute_gpu_clock_output_parameter_v1_8 *pll_output;
+ uint32_t idx;
+
+ pll_parameters.gpuclock_10khz = (uint32_t)clock_value;
+ pll_parameters.gpu_clock_type = clock_type;
+
+ idx = GetIndexIntoMasterCmdTable(computegpuclockparam);
+
+ if (amdgpu_atom_execute_table(
+ adev->mode_info.atom_context, idx, (uint32_t *)&pll_parameters))
+ return -EINVAL;
+
+ pll_output = (struct compute_gpu_clock_output_parameter_v1_8 *)
+ &pll_parameters;
+ dividers->ulClock = le32_to_cpu(pll_output->gpuclock_10khz);
+ dividers->ulDid = le32_to_cpu(pll_output->dfs_did);
+ dividers->ulPll_fb_mult = le32_to_cpu(pll_output->pll_fb_mult);
+ dividers->ulPll_ss_fbsmult = le32_to_cpu(pll_output->pll_ss_fbsmult);
+ dividers->usPll_ss_slew_frac = le16_to_cpu(pll_output->pll_ss_slew_frac);
+ dividers->ucPll_ss_enable = pll_output->pll_ss_enable;
+
+ return 0;
+}
+
+int pp_atomfwctrl_get_avfs_information(struct pp_hwmgr *hwmgr,
+ struct pp_atomfwctrl_avfs_parameters *param)
+{
+ uint16_t idx;
+ uint8_t format_revision, content_revision;
+
+ struct atom_asic_profiling_info_v4_1 *profile;
+ struct atom_asic_profiling_info_v4_2 *profile_v4_2;
+
+ idx = GetIndexIntoMasterDataTable(asic_profiling_info);
+ profile = (struct atom_asic_profiling_info_v4_1 *)
+ smu_atom_get_data_table(hwmgr->adev,
+ idx, NULL, NULL, NULL);
+
+ if (!profile)
+ return -1;
+
+ format_revision = ((struct atom_common_table_header *)profile)->format_revision;
+ content_revision = ((struct atom_common_table_header *)profile)->content_revision;
+
+ if (format_revision == 4 && content_revision == 1) {
+ param->ulMaxVddc = le32_to_cpu(profile->maxvddc);
+ param->ulMinVddc = le32_to_cpu(profile->minvddc);
+ param->ulMeanNsigmaAcontant0 =
+ le32_to_cpu(profile->avfs_meannsigma_acontant0);
+ param->ulMeanNsigmaAcontant1 =
+ le32_to_cpu(profile->avfs_meannsigma_acontant1);
+ param->ulMeanNsigmaAcontant2 =
+ le32_to_cpu(profile->avfs_meannsigma_acontant2);
+ param->usMeanNsigmaDcTolSigma =
+ le16_to_cpu(profile->avfs_meannsigma_dc_tol_sigma);
+ param->usMeanNsigmaPlatformMean =
+ le16_to_cpu(profile->avfs_meannsigma_platform_mean);
+ param->usMeanNsigmaPlatformSigma =
+ le16_to_cpu(profile->avfs_meannsigma_platform_sigma);
+ param->ulGbVdroopTableCksoffA0 =
+ le32_to_cpu(profile->gb_vdroop_table_cksoff_a0);
+ param->ulGbVdroopTableCksoffA1 =
+ le32_to_cpu(profile->gb_vdroop_table_cksoff_a1);
+ param->ulGbVdroopTableCksoffA2 =
+ le32_to_cpu(profile->gb_vdroop_table_cksoff_a2);
+ param->ulGbVdroopTableCksonA0 =
+ le32_to_cpu(profile->gb_vdroop_table_ckson_a0);
+ param->ulGbVdroopTableCksonA1 =
+ le32_to_cpu(profile->gb_vdroop_table_ckson_a1);
+ param->ulGbVdroopTableCksonA2 =
+ le32_to_cpu(profile->gb_vdroop_table_ckson_a2);
+ param->ulGbFuseTableCksoffM1 =
+ le32_to_cpu(profile->avfsgb_fuse_table_cksoff_m1);
+ param->ulGbFuseTableCksoffM2 =
+ le32_to_cpu(profile->avfsgb_fuse_table_cksoff_m2);
+ param->ulGbFuseTableCksoffB =
+ le32_to_cpu(profile->avfsgb_fuse_table_cksoff_b);
+ param->ulGbFuseTableCksonM1 =
+ le32_to_cpu(profile->avfsgb_fuse_table_ckson_m1);
+ param->ulGbFuseTableCksonM2 =
+ le32_to_cpu(profile->avfsgb_fuse_table_ckson_m2);
+ param->ulGbFuseTableCksonB =
+ le32_to_cpu(profile->avfsgb_fuse_table_ckson_b);
+
+ param->ucEnableGbVdroopTableCkson =
+ profile->enable_gb_vdroop_table_ckson;
+ param->ucEnableGbFuseTableCkson =
+ profile->enable_gb_fuse_table_ckson;
+ param->usPsmAgeComfactor =
+ le16_to_cpu(profile->psm_age_comfactor);
+
+ param->ulDispclk2GfxclkM1 =
+ le32_to_cpu(profile->dispclk2gfxclk_a);
+ param->ulDispclk2GfxclkM2 =
+ le32_to_cpu(profile->dispclk2gfxclk_b);
+ param->ulDispclk2GfxclkB =
+ le32_to_cpu(profile->dispclk2gfxclk_c);
+ param->ulDcefclk2GfxclkM1 =
+ le32_to_cpu(profile->dcefclk2gfxclk_a);
+ param->ulDcefclk2GfxclkM2 =
+ le32_to_cpu(profile->dcefclk2gfxclk_b);
+ param->ulDcefclk2GfxclkB =
+ le32_to_cpu(profile->dcefclk2gfxclk_c);
+ param->ulPixelclk2GfxclkM1 =
+ le32_to_cpu(profile->pixclk2gfxclk_a);
+ param->ulPixelclk2GfxclkM2 =
+ le32_to_cpu(profile->pixclk2gfxclk_b);
+ param->ulPixelclk2GfxclkB =
+ le32_to_cpu(profile->pixclk2gfxclk_c);
+ param->ulPhyclk2GfxclkM1 =
+ le32_to_cpu(profile->phyclk2gfxclk_a);
+ param->ulPhyclk2GfxclkM2 =
+ le32_to_cpu(profile->phyclk2gfxclk_b);
+ param->ulPhyclk2GfxclkB =
+ le32_to_cpu(profile->phyclk2gfxclk_c);
+ param->ulAcgGbVdroopTableA0 = 0;
+ param->ulAcgGbVdroopTableA1 = 0;
+ param->ulAcgGbVdroopTableA2 = 0;
+ param->ulAcgGbFuseTableM1 = 0;
+ param->ulAcgGbFuseTableM2 = 0;
+ param->ulAcgGbFuseTableB = 0;
+ param->ucAcgEnableGbVdroopTable = 0;
+ param->ucAcgEnableGbFuseTable = 0;
+ } else if (format_revision == 4 && content_revision == 2) {
+ profile_v4_2 = (struct atom_asic_profiling_info_v4_2 *)profile;
+ param->ulMaxVddc = le32_to_cpu(profile_v4_2->maxvddc);
+ param->ulMinVddc = le32_to_cpu(profile_v4_2->minvddc);
+ param->ulMeanNsigmaAcontant0 =
+ le32_to_cpu(profile_v4_2->avfs_meannsigma_acontant0);
+ param->ulMeanNsigmaAcontant1 =
+ le32_to_cpu(profile_v4_2->avfs_meannsigma_acontant1);
+ param->ulMeanNsigmaAcontant2 =
+ le32_to_cpu(profile_v4_2->avfs_meannsigma_acontant2);
+ param->usMeanNsigmaDcTolSigma =
+ le16_to_cpu(profile_v4_2->avfs_meannsigma_dc_tol_sigma);
+ param->usMeanNsigmaPlatformMean =
+ le16_to_cpu(profile_v4_2->avfs_meannsigma_platform_mean);
+ param->usMeanNsigmaPlatformSigma =
+ le16_to_cpu(profile_v4_2->avfs_meannsigma_platform_sigma);
+ param->ulGbVdroopTableCksoffA0 =
+ le32_to_cpu(profile_v4_2->gb_vdroop_table_cksoff_a0);
+ param->ulGbVdroopTableCksoffA1 =
+ le32_to_cpu(profile_v4_2->gb_vdroop_table_cksoff_a1);
+ param->ulGbVdroopTableCksoffA2 =
+ le32_to_cpu(profile_v4_2->gb_vdroop_table_cksoff_a2);
+ param->ulGbVdroopTableCksonA0 =
+ le32_to_cpu(profile_v4_2->gb_vdroop_table_ckson_a0);
+ param->ulGbVdroopTableCksonA1 =
+ le32_to_cpu(profile_v4_2->gb_vdroop_table_ckson_a1);
+ param->ulGbVdroopTableCksonA2 =
+ le32_to_cpu(profile_v4_2->gb_vdroop_table_ckson_a2);
+ param->ulGbFuseTableCksoffM1 =
+ le32_to_cpu(profile_v4_2->avfsgb_fuse_table_cksoff_m1);
+ param->ulGbFuseTableCksoffM2 =
+ le32_to_cpu(profile_v4_2->avfsgb_fuse_table_cksoff_m2);
+ param->ulGbFuseTableCksoffB =
+ le32_to_cpu(profile_v4_2->avfsgb_fuse_table_cksoff_b);
+ param->ulGbFuseTableCksonM1 =
+ le32_to_cpu(profile_v4_2->avfsgb_fuse_table_ckson_m1);
+ param->ulGbFuseTableCksonM2 =
+ le32_to_cpu(profile_v4_2->avfsgb_fuse_table_ckson_m2);
+ param->ulGbFuseTableCksonB =
+ le32_to_cpu(profile_v4_2->avfsgb_fuse_table_ckson_b);
+
+ param->ucEnableGbVdroopTableCkson =
+ profile_v4_2->enable_gb_vdroop_table_ckson;
+ param->ucEnableGbFuseTableCkson =
+ profile_v4_2->enable_gb_fuse_table_ckson;
+ param->usPsmAgeComfactor =
+ le16_to_cpu(profile_v4_2->psm_age_comfactor);
+
+ param->ulDispclk2GfxclkM1 =
+ le32_to_cpu(profile_v4_2->dispclk2gfxclk_a);
+ param->ulDispclk2GfxclkM2 =
+ le32_to_cpu(profile_v4_2->dispclk2gfxclk_b);
+ param->ulDispclk2GfxclkB =
+ le32_to_cpu(profile_v4_2->dispclk2gfxclk_c);
+ param->ulDcefclk2GfxclkM1 =
+ le32_to_cpu(profile_v4_2->dcefclk2gfxclk_a);
+ param->ulDcefclk2GfxclkM2 =
+ le32_to_cpu(profile_v4_2->dcefclk2gfxclk_b);
+ param->ulDcefclk2GfxclkB =
+ le32_to_cpu(profile_v4_2->dcefclk2gfxclk_c);
+ param->ulPixelclk2GfxclkM1 =
+ le32_to_cpu(profile_v4_2->pixclk2gfxclk_a);
+ param->ulPixelclk2GfxclkM2 =
+ le32_to_cpu(profile_v4_2->pixclk2gfxclk_b);
+ param->ulPixelclk2GfxclkB =
+ le32_to_cpu(profile_v4_2->pixclk2gfxclk_c);
+ param->ulPhyclk2GfxclkM1 =
+ le32_to_cpu(profile->phyclk2gfxclk_a);
+ param->ulPhyclk2GfxclkM2 =
+ le32_to_cpu(profile_v4_2->phyclk2gfxclk_b);
+ param->ulPhyclk2GfxclkB =
+ le32_to_cpu(profile_v4_2->phyclk2gfxclk_c);
+ param->ulAcgGbVdroopTableA0 = le32_to_cpu(profile_v4_2->acg_gb_vdroop_table_a0);
+ param->ulAcgGbVdroopTableA1 = le32_to_cpu(profile_v4_2->acg_gb_vdroop_table_a1);
+ param->ulAcgGbVdroopTableA2 = le32_to_cpu(profile_v4_2->acg_gb_vdroop_table_a2);
+ param->ulAcgGbFuseTableM1 = le32_to_cpu(profile_v4_2->acg_avfsgb_fuse_table_m1);
+ param->ulAcgGbFuseTableM2 = le32_to_cpu(profile_v4_2->acg_avfsgb_fuse_table_m2);
+ param->ulAcgGbFuseTableB = le32_to_cpu(profile_v4_2->acg_avfsgb_fuse_table_b);
+ param->ucAcgEnableGbVdroopTable = le32_to_cpu(profile_v4_2->enable_acg_gb_vdroop_table);
+ param->ucAcgEnableGbFuseTable = le32_to_cpu(profile_v4_2->enable_acg_gb_fuse_table);
+ } else {
+ pr_info("Invalid VBIOS AVFS ProfilingInfo Revision!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int pp_atomfwctrl_get_gpio_information(struct pp_hwmgr *hwmgr,
+ struct pp_atomfwctrl_gpio_parameters *param)
+{
+ struct atom_smu_info_v3_1 *info;
+ uint16_t idx;
+
+ idx = GetIndexIntoMasterDataTable(smu_info);
+ info = (struct atom_smu_info_v3_1 *)
+ smu_atom_get_data_table(hwmgr->adev,
+ idx, NULL, NULL, NULL);
+
+ if (!info) {
+ pr_info("Error retrieving BIOS smu_info Table Address!");
+ return -1;
+ }
+
+ param->ucAcDcGpio = info->ac_dc_gpio_bit;
+ param->ucAcDcPolarity = info->ac_dc_polarity;
+ param->ucVR0HotGpio = info->vr0hot_gpio_bit;
+ param->ucVR0HotPolarity = info->vr0hot_polarity;
+ param->ucVR1HotGpio = info->vr1hot_gpio_bit;
+ param->ucVR1HotPolarity = info->vr1hot_polarity;
+ param->ucFwCtfGpio = info->fw_ctf_gpio_bit;
+ param->ucFwCtfPolarity = info->fw_ctf_polarity;
+
+ return 0;
+}
+
+int pp_atomfwctrl_get_clk_information_by_clkid(struct pp_hwmgr *hwmgr,
+ uint8_t clk_id, uint8_t syspll_id,
+ uint32_t *frequency)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ struct atom_get_smu_clock_info_parameters_v3_1 parameters;
+ struct atom_get_smu_clock_info_output_parameters_v3_1 *output;
+ uint32_t ix;
+
+ parameters.clk_id = clk_id;
+ parameters.syspll_id = syspll_id;
+ parameters.command = GET_SMU_CLOCK_INFO_V3_1_GET_CLOCK_FREQ;
+ parameters.dfsdid = 0;
+
+ ix = GetIndexIntoMasterCmdTable(getsmuclockinfo);
+
+ if (amdgpu_atom_execute_table(
+ adev->mode_info.atom_context, ix, (uint32_t *)&parameters))
+ return -EINVAL;
+
+ output = (struct atom_get_smu_clock_info_output_parameters_v3_1 *)&parameters;
+ *frequency = le32_to_cpu(output->atom_smu_outputclkfreq.smu_clock_freq_hz) / 10000;
+
+ return 0;
+}
+
+static void pp_atomfwctrl_copy_vbios_bootup_values_3_2(struct pp_hwmgr *hwmgr,
+ struct pp_atomfwctrl_bios_boot_up_values *boot_values,
+ struct atom_firmware_info_v3_2 *fw_info)
+{
+ uint32_t frequency = 0;
+
+ boot_values->ulRevision = fw_info->firmware_revision;
+ boot_values->ulGfxClk = fw_info->bootup_sclk_in10khz;
+ boot_values->ulUClk = fw_info->bootup_mclk_in10khz;
+ boot_values->usVddc = fw_info->bootup_vddc_mv;
+ boot_values->usVddci = fw_info->bootup_vddci_mv;
+ boot_values->usMvddc = fw_info->bootup_mvddc_mv;
+ boot_values->usVddGfx = fw_info->bootup_vddgfx_mv;
+ boot_values->ucCoolingID = fw_info->coolingsolution_id;
+ boot_values->ulSocClk = 0;
+ boot_values->ulDCEFClk = 0;
+
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_SOCCLK_ID, SMU11_SYSPLL0_ID, &frequency))
+ boot_values->ulSocClk = frequency;
+
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_DCEFCLK_ID, SMU11_SYSPLL0_ID, &frequency))
+ boot_values->ulDCEFClk = frequency;
+
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_ECLK_ID, SMU11_SYSPLL0_ID, &frequency))
+ boot_values->ulEClk = frequency;
+
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_VCLK_ID, SMU11_SYSPLL0_ID, &frequency))
+ boot_values->ulVClk = frequency;
+
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_DCLK_ID, SMU11_SYSPLL0_ID, &frequency))
+ boot_values->ulDClk = frequency;
+
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL1_0_FCLK_ID, SMU11_SYSPLL1_2_ID, &frequency))
+ boot_values->ulFClk = frequency;
+}
+
+static void pp_atomfwctrl_copy_vbios_bootup_values_3_1(struct pp_hwmgr *hwmgr,
+ struct pp_atomfwctrl_bios_boot_up_values *boot_values,
+ struct atom_firmware_info_v3_1 *fw_info)
+{
+ uint32_t frequency = 0;
+
+ boot_values->ulRevision = fw_info->firmware_revision;
+ boot_values->ulGfxClk = fw_info->bootup_sclk_in10khz;
+ boot_values->ulUClk = fw_info->bootup_mclk_in10khz;
+ boot_values->usVddc = fw_info->bootup_vddc_mv;
+ boot_values->usVddci = fw_info->bootup_vddci_mv;
+ boot_values->usMvddc = fw_info->bootup_mvddc_mv;
+ boot_values->usVddGfx = fw_info->bootup_vddgfx_mv;
+ boot_values->ucCoolingID = fw_info->coolingsolution_id;
+ boot_values->ulSocClk = 0;
+ boot_values->ulDCEFClk = 0;
+
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_SOCCLK_ID, 0, &frequency))
+ boot_values->ulSocClk = frequency;
+
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_DCEFCLK_ID, 0, &frequency))
+ boot_values->ulDCEFClk = frequency;
+
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_ECLK_ID, 0, &frequency))
+ boot_values->ulEClk = frequency;
+
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_VCLK_ID, 0, &frequency))
+ boot_values->ulVClk = frequency;
+
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_DCLK_ID, 0, &frequency))
+ boot_values->ulDClk = frequency;
+}
+
+int pp_atomfwctrl_get_vbios_bootup_values(struct pp_hwmgr *hwmgr,
+ struct pp_atomfwctrl_bios_boot_up_values *boot_values)
+{
+ struct atom_firmware_info_v3_2 *fwinfo_3_2;
+ struct atom_firmware_info_v3_1 *fwinfo_3_1;
+ struct atom_common_table_header *info = NULL;
+ uint16_t ix;
+
+ ix = GetIndexIntoMasterDataTable(firmwareinfo);
+ info = (struct atom_common_table_header *)
+ smu_atom_get_data_table(hwmgr->adev,
+ ix, NULL, NULL, NULL);
+
+ if (!info) {
+ pr_info("Error retrieving BIOS firmwareinfo!");
+ return -EINVAL;
+ }
+
+ if ((info->format_revision == 3) && (info->content_revision == 2)) {
+ fwinfo_3_2 = (struct atom_firmware_info_v3_2 *)info;
+ pp_atomfwctrl_copy_vbios_bootup_values_3_2(hwmgr,
+ boot_values, fwinfo_3_2);
+ } else if ((info->format_revision == 3) && (info->content_revision == 1)) {
+ fwinfo_3_1 = (struct atom_firmware_info_v3_1 *)info;
+ pp_atomfwctrl_copy_vbios_bootup_values_3_1(hwmgr,
+ boot_values, fwinfo_3_1);
+ } else {
+ pr_info("Fw info table revision does not match!");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int pp_atomfwctrl_get_smc_dpm_information(struct pp_hwmgr *hwmgr,
+ struct pp_atomfwctrl_smc_dpm_parameters *param)
+{
+ struct atom_smc_dpm_info_v4_1 *info;
+ uint16_t ix;
+
+ ix = GetIndexIntoMasterDataTable(smc_dpm_info);
+ info = (struct atom_smc_dpm_info_v4_1 *)
+ smu_atom_get_data_table(hwmgr->adev,
+ ix, NULL, NULL, NULL);
+ if (!info) {
+ pr_info("Error retrieving BIOS Table Address!");
+ return -EINVAL;
+ }
+
+ param->liquid1_i2c_address = info->liquid1_i2c_address;
+ param->liquid2_i2c_address = info->liquid2_i2c_address;
+ param->vr_i2c_address = info->vr_i2c_address;
+ param->plx_i2c_address = info->plx_i2c_address;
+
+ param->liquid_i2c_linescl = info->liquid_i2c_linescl;
+ param->liquid_i2c_linesda = info->liquid_i2c_linesda;
+ param->vr_i2c_linescl = info->vr_i2c_linescl;
+ param->vr_i2c_linesda = info->vr_i2c_linesda;
+
+ param->plx_i2c_linescl = info->plx_i2c_linescl;
+ param->plx_i2c_linesda = info->plx_i2c_linesda;
+ param->vrsensorpresent = info->vrsensorpresent;
+ param->liquidsensorpresent = info->liquidsensorpresent;
+
+ param->maxvoltagestepgfx = info->maxvoltagestepgfx;
+ param->maxvoltagestepsoc = info->maxvoltagestepsoc;
+
+ param->vddgfxvrmapping = info->vddgfxvrmapping;
+ param->vddsocvrmapping = info->vddsocvrmapping;
+ param->vddmem0vrmapping = info->vddmem0vrmapping;
+ param->vddmem1vrmapping = info->vddmem1vrmapping;
+
+ param->gfxulvphasesheddingmask = info->gfxulvphasesheddingmask;
+ param->soculvphasesheddingmask = info->soculvphasesheddingmask;
+
+ param->gfxmaxcurrent = info->gfxmaxcurrent;
+ param->gfxoffset = info->gfxoffset;
+ param->padding_telemetrygfx = info->padding_telemetrygfx;
+
+ param->socmaxcurrent = info->socmaxcurrent;
+ param->socoffset = info->socoffset;
+ param->padding_telemetrysoc = info->padding_telemetrysoc;
+
+ param->mem0maxcurrent = info->mem0maxcurrent;
+ param->mem0offset = info->mem0offset;
+ param->padding_telemetrymem0 = info->padding_telemetrymem0;
+
+ param->mem1maxcurrent = info->mem1maxcurrent;
+ param->mem1offset = info->mem1offset;
+ param->padding_telemetrymem1 = info->padding_telemetrymem1;
+
+ param->acdcgpio = info->acdcgpio;
+ param->acdcpolarity = info->acdcpolarity;
+ param->vr0hotgpio = info->vr0hotgpio;
+ param->vr0hotpolarity = info->vr0hotpolarity;
+
+ param->vr1hotgpio = info->vr1hotgpio;
+ param->vr1hotpolarity = info->vr1hotpolarity;
+ param->padding1 = info->padding1;
+ param->padding2 = info->padding2;
+
+ param->ledpin0 = info->ledpin0;
+ param->ledpin1 = info->ledpin1;
+ param->ledpin2 = info->ledpin2;
+
+ param->pllgfxclkspreadenabled = info->pllgfxclkspreadenabled;
+ param->pllgfxclkspreadpercent = info->pllgfxclkspreadpercent;
+ param->pllgfxclkspreadfreq = info->pllgfxclkspreadfreq;
+
+ param->uclkspreadenabled = info->uclkspreadenabled;
+ param->uclkspreadpercent = info->uclkspreadpercent;
+ param->uclkspreadfreq = info->uclkspreadfreq;
+
+ param->socclkspreadenabled = info->socclkspreadenabled;
+ param->socclkspreadpercent = info->socclkspreadpercent;
+ param->socclkspreadfreq = info->socclkspreadfreq;
+
+ param->acggfxclkspreadenabled = info->acggfxclkspreadenabled;
+ param->acggfxclkspreadpercent = info->acggfxclkspreadpercent;
+ param->acggfxclkspreadfreq = info->acggfxclkspreadfreq;
+
+ param->Vr2_I2C_address = info->Vr2_I2C_address;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.h
new file mode 100644
index 000000000000..b7e2651b570b
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.h
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 PP_ATOMFWCTRL_H
+#define PP_ATOMFWCTRL_H
+
+#include "hwmgr.h"
+
+typedef enum atom_smu9_syspll0_clock_id BIOS_CLKID;
+
+#define GetIndexIntoMasterCmdTable(FieldName) \
+ (((char*)(&((struct atom_master_list_of_command_functions_v2_1*)0)->FieldName)-(char*)0)/sizeof(uint16_t))
+#define GetIndexIntoMasterDataTable(FieldName) \
+ (((char*)(&((struct atom_master_list_of_data_tables_v2_1*)0)->FieldName)-(char*)0)/sizeof(uint16_t))
+
+#define PP_ATOMFWCTRL_MAX_VOLTAGE_ENTRIES 32
+
+struct pp_atomfwctrl_voltage_table_entry {
+ uint16_t value;
+ uint32_t smio_low;
+};
+
+struct pp_atomfwctrl_voltage_table {
+ uint32_t count;
+ uint32_t mask_low;
+ uint32_t phase_delay;
+ uint8_t psi0_enable;
+ uint8_t psi1_enable;
+ uint8_t max_vid_step;
+ uint8_t telemetry_offset;
+ uint8_t telemetry_slope;
+ struct pp_atomfwctrl_voltage_table_entry entries[PP_ATOMFWCTRL_MAX_VOLTAGE_ENTRIES];
+};
+
+struct pp_atomfwctrl_gpio_pin_assignment {
+ uint16_t us_gpio_pin_aindex;
+ uint8_t uc_gpio_pin_bit_shift;
+};
+
+struct pp_atomfwctrl_clock_dividers_soc15 {
+ uint32_t ulClock; /* the actual clock */
+ uint32_t ulDid; /* DFS divider */
+ uint32_t ulPll_fb_mult; /* Feedback Multiplier: bit 8:0 int, bit 15:12 post_div, bit 31:16 frac */
+ uint32_t ulPll_ss_fbsmult; /* Spread FB Multiplier: bit 8:0 int, bit 31:16 frac */
+ uint16_t usPll_ss_slew_frac;
+ uint8_t ucPll_ss_enable;
+ uint8_t ucReserve;
+ uint32_t ulReserve[2];
+};
+
+struct pp_atomfwctrl_avfs_parameters {
+ uint32_t ulMaxVddc;
+ uint32_t ulMinVddc;
+
+ uint32_t ulMeanNsigmaAcontant0;
+ uint32_t ulMeanNsigmaAcontant1;
+ uint32_t ulMeanNsigmaAcontant2;
+ uint16_t usMeanNsigmaDcTolSigma;
+ uint16_t usMeanNsigmaPlatformMean;
+ uint16_t usMeanNsigmaPlatformSigma;
+ uint32_t ulGbVdroopTableCksoffA0;
+ uint32_t ulGbVdroopTableCksoffA1;
+ uint32_t ulGbVdroopTableCksoffA2;
+ uint32_t ulGbVdroopTableCksonA0;
+ uint32_t ulGbVdroopTableCksonA1;
+ uint32_t ulGbVdroopTableCksonA2;
+
+ uint32_t ulGbFuseTableCksoffM1;
+ uint32_t ulGbFuseTableCksoffM2;
+ uint32_t ulGbFuseTableCksoffB;
+
+ uint32_t ulGbFuseTableCksonM1;
+ uint32_t ulGbFuseTableCksonM2;
+ uint32_t ulGbFuseTableCksonB;
+
+ uint8_t ucEnableGbVdroopTableCkson;
+ uint8_t ucEnableGbFuseTableCkson;
+ uint16_t usPsmAgeComfactor;
+
+ uint32_t ulDispclk2GfxclkM1;
+ uint32_t ulDispclk2GfxclkM2;
+ uint32_t ulDispclk2GfxclkB;
+ uint32_t ulDcefclk2GfxclkM1;
+ uint32_t ulDcefclk2GfxclkM2;
+ uint32_t ulDcefclk2GfxclkB;
+ uint32_t ulPixelclk2GfxclkM1;
+ uint32_t ulPixelclk2GfxclkM2;
+ uint32_t ulPixelclk2GfxclkB;
+ uint32_t ulPhyclk2GfxclkM1;
+ uint32_t ulPhyclk2GfxclkM2;
+ uint32_t ulPhyclk2GfxclkB;
+ uint32_t ulAcgGbVdroopTableA0;
+ uint32_t ulAcgGbVdroopTableA1;
+ uint32_t ulAcgGbVdroopTableA2;
+ uint32_t ulAcgGbFuseTableM1;
+ uint32_t ulAcgGbFuseTableM2;
+ uint32_t ulAcgGbFuseTableB;
+ uint32_t ucAcgEnableGbVdroopTable;
+ uint32_t ucAcgEnableGbFuseTable;
+};
+
+struct pp_atomfwctrl_gpio_parameters {
+ uint8_t ucAcDcGpio;
+ uint8_t ucAcDcPolarity;
+ uint8_t ucVR0HotGpio;
+ uint8_t ucVR0HotPolarity;
+ uint8_t ucVR1HotGpio;
+ uint8_t ucVR1HotPolarity;
+ uint8_t ucFwCtfGpio;
+ uint8_t ucFwCtfPolarity;
+};
+
+struct pp_atomfwctrl_bios_boot_up_values {
+ uint32_t ulRevision;
+ uint32_t ulGfxClk;
+ uint32_t ulUClk;
+ uint32_t ulSocClk;
+ uint32_t ulDCEFClk;
+ uint32_t ulEClk;
+ uint32_t ulVClk;
+ uint32_t ulDClk;
+ uint32_t ulFClk;
+ uint16_t usVddc;
+ uint16_t usVddci;
+ uint16_t usMvddc;
+ uint16_t usVddGfx;
+ uint8_t ucCoolingID;
+};
+
+struct pp_atomfwctrl_smc_dpm_parameters
+{
+ uint8_t liquid1_i2c_address;
+ uint8_t liquid2_i2c_address;
+ uint8_t vr_i2c_address;
+ uint8_t plx_i2c_address;
+ uint8_t liquid_i2c_linescl;
+ uint8_t liquid_i2c_linesda;
+ uint8_t vr_i2c_linescl;
+ uint8_t vr_i2c_linesda;
+ uint8_t plx_i2c_linescl;
+ uint8_t plx_i2c_linesda;
+ uint8_t vrsensorpresent;
+ uint8_t liquidsensorpresent;
+ uint16_t maxvoltagestepgfx;
+ uint16_t maxvoltagestepsoc;
+ uint8_t vddgfxvrmapping;
+ uint8_t vddsocvrmapping;
+ uint8_t vddmem0vrmapping;
+ uint8_t vddmem1vrmapping;
+ uint8_t gfxulvphasesheddingmask;
+ uint8_t soculvphasesheddingmask;
+
+ uint16_t gfxmaxcurrent;
+ uint8_t gfxoffset;
+ uint8_t padding_telemetrygfx;
+ uint16_t socmaxcurrent;
+ uint8_t socoffset;
+ uint8_t padding_telemetrysoc;
+ uint16_t mem0maxcurrent;
+ uint8_t mem0offset;
+ uint8_t padding_telemetrymem0;
+ uint16_t mem1maxcurrent;
+ uint8_t mem1offset;
+ uint8_t padding_telemetrymem1;
+
+ uint8_t acdcgpio;
+ uint8_t acdcpolarity;
+ uint8_t vr0hotgpio;
+ uint8_t vr0hotpolarity;
+ uint8_t vr1hotgpio;
+ uint8_t vr1hotpolarity;
+ uint8_t padding1;
+ uint8_t padding2;
+
+ uint8_t ledpin0;
+ uint8_t ledpin1;
+ uint8_t ledpin2;
+
+ uint8_t pllgfxclkspreadenabled;
+ uint8_t pllgfxclkspreadpercent;
+ uint16_t pllgfxclkspreadfreq;
+
+ uint8_t uclkspreadenabled;
+ uint8_t uclkspreadpercent;
+ uint16_t uclkspreadfreq;
+
+ uint8_t socclkspreadenabled;
+ uint8_t socclkspreadpercent;
+ uint16_t socclkspreadfreq;
+
+ uint8_t acggfxclkspreadenabled;
+ uint8_t acggfxclkspreadpercent;
+ uint16_t acggfxclkspreadfreq;
+
+ uint8_t Vr2_I2C_address;
+};
+
+int pp_atomfwctrl_get_gpu_pll_dividers_vega10(struct pp_hwmgr *hwmgr,
+ uint32_t clock_type, uint32_t clock_value,
+ struct pp_atomfwctrl_clock_dividers_soc15 *dividers);
+int pp_atomfwctrl_enter_self_refresh(struct pp_hwmgr *hwmgr);
+bool pp_atomfwctrl_get_pp_assign_pin(struct pp_hwmgr *hwmgr, const uint32_t pin_id,
+ struct pp_atomfwctrl_gpio_pin_assignment *gpio_pin_assignment);
+
+int pp_atomfwctrl_get_voltage_table_v4(struct pp_hwmgr *hwmgr, uint8_t voltage_type,
+ uint8_t voltage_mode, struct pp_atomfwctrl_voltage_table *voltage_table);
+bool pp_atomfwctrl_is_voltage_controlled_by_gpio_v4(struct pp_hwmgr *hwmgr,
+ uint8_t voltage_type, uint8_t voltage_mode);
+
+int pp_atomfwctrl_get_avfs_information(struct pp_hwmgr *hwmgr,
+ struct pp_atomfwctrl_avfs_parameters *param);
+int pp_atomfwctrl_get_gpio_information(struct pp_hwmgr *hwmgr,
+ struct pp_atomfwctrl_gpio_parameters *param);
+
+int pp_atomfwctrl_get_vbios_bootup_values(struct pp_hwmgr *hwmgr,
+ struct pp_atomfwctrl_bios_boot_up_values *boot_values);
+int pp_atomfwctrl_get_smc_dpm_information(struct pp_hwmgr *hwmgr,
+ struct pp_atomfwctrl_smc_dpm_parameters *param);
+int pp_atomfwctrl_get_clk_information_by_clkid(struct pp_hwmgr *hwmgr,
+ uint8_t clk_id, uint8_t syspll_id,
+ uint32_t *frequency);
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppevvmath.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppevvmath.h
new file mode 100644
index 000000000000..8f50a038396c
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppevvmath.h
@@ -0,0 +1,555 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <asm/div64.h>
+
+#define SHIFT_AMOUNT 16 /* We multiply all original integers with 2^SHIFT_AMOUNT to get the fInt representation */
+
+#define PRECISION 5 /* Change this value to change the number of decimal places in the final output - 5 is a good default */
+
+#define SHIFTED_2 (2 << SHIFT_AMOUNT)
+#define MAX (1 << (SHIFT_AMOUNT - 1)) - 1 /* 32767 - Might change in the future */
+
+/* -------------------------------------------------------------------------------
+ * NEW TYPE - fINT
+ * -------------------------------------------------------------------------------
+ * A variable of type fInt can be accessed in 3 ways using the dot (.) operator
+ * fInt A;
+ * A.full => The full number as it is. Generally not easy to read
+ * A.partial.real => Only the integer portion
+ * A.partial.decimal => Only the fractional portion
+ */
+typedef union _fInt {
+ int full;
+ struct _partial {
+ unsigned int decimal: SHIFT_AMOUNT; /*Needs to always be unsigned*/
+ int real: 32 - SHIFT_AMOUNT;
+ } partial;
+} fInt;
+
+/* -------------------------------------------------------------------------------
+ * Function Declarations
+ * -------------------------------------------------------------------------------
+ */
+static fInt ConvertToFraction(int); /* Use this to convert an INT to a FINT */
+static fInt Convert_ULONG_ToFraction(uint32_t); /* Use this to convert an uint32_t to a FINT */
+static fInt GetScaledFraction(int, int); /* Use this to convert an INT to a FINT after scaling it by a factor */
+static int ConvertBackToInteger(fInt); /* Convert a FINT back to an INT that is scaled by 1000 (i.e. last 3 digits are the decimal digits) */
+
+static fInt fNegate(fInt); /* Returns -1 * input fInt value */
+static fInt fAdd (fInt, fInt); /* Returns the sum of two fInt numbers */
+static fInt fSubtract (fInt A, fInt B); /* Returns A-B - Sometimes easier than Adding negative numbers */
+static fInt fMultiply (fInt, fInt); /* Returns the product of two fInt numbers */
+static fInt fDivide (fInt A, fInt B); /* Returns A/B */
+static fInt fGetSquare(fInt); /* Returns the square of a fInt number */
+static fInt fSqrt(fInt); /* Returns the Square Root of a fInt number */
+
+static int uAbs(int); /* Returns the Absolute value of the Int */
+static int uPow(int base, int exponent); /* Returns base^exponent an INT */
+
+static void SolveQuadracticEqn(fInt, fInt, fInt, fInt[]); /* Returns the 2 roots via the array */
+static bool Equal(fInt, fInt); /* Returns true if two fInts are equal to each other */
+static bool GreaterThan(fInt A, fInt B); /* Returns true if A > B */
+
+static fInt fExponential(fInt exponent); /* Can be used to calculate e^exponent */
+static fInt fNaturalLog(fInt value); /* Can be used to calculate ln(value) */
+
+/* Fuse decoding functions
+ * -------------------------------------------------------------------------------------
+ */
+static fInt fDecodeLinearFuse(uint32_t fuse_value, fInt f_min, fInt f_range, uint32_t bitlength);
+static fInt fDecodeLogisticFuse(uint32_t fuse_value, fInt f_average, fInt f_range, uint32_t bitlength);
+static fInt fDecodeLeakageID (uint32_t leakageID_fuse, fInt ln_max_div_min, fInt f_min, uint32_t bitlength);
+
+/* Internal Support Functions - Use these ONLY for testing or adding to internal functions
+ * -------------------------------------------------------------------------------------
+ * Some of the following functions take two INTs as their input - This is unsafe for a variety of reasons.
+ */
+static fInt Divide (int, int); /* Divide two INTs and return result as FINT */
+static fInt fNegate(fInt);
+
+static int uGetScaledDecimal (fInt); /* Internal function */
+static int GetReal (fInt A); /* Internal function */
+
+/* -------------------------------------------------------------------------------------
+ * TROUBLESHOOTING INFORMATION
+ * -------------------------------------------------------------------------------------
+ * 1) ConvertToFraction - InputOutOfRangeException: Only accepts numbers smaller than MAX (default: 32767)
+ * 2) fAdd - OutputOutOfRangeException: Output bigger than MAX (default: 32767)
+ * 3) fMultiply - OutputOutOfRangeException:
+ * 4) fGetSquare - OutputOutOfRangeException:
+ * 5) fDivide - DivideByZeroException
+ * 6) fSqrt - NegativeSquareRootException: Input cannot be a negative number
+ */
+
+/* -------------------------------------------------------------------------------------
+ * START OF CODE
+ * -------------------------------------------------------------------------------------
+ */
+static fInt fExponential(fInt exponent) /*Can be used to calculate e^exponent*/
+{
+ uint32_t i;
+ bool bNegated = false;
+
+ fInt fPositiveOne = ConvertToFraction(1);
+ fInt fZERO = ConvertToFraction(0);
+
+ fInt lower_bound = Divide(78, 10000);
+ fInt solution = fPositiveOne; /*Starting off with baseline of 1 */
+ fInt error_term;
+
+ static const uint32_t k_array[11] = {55452, 27726, 13863, 6931, 4055, 2231, 1178, 606, 308, 155, 78};
+ static const uint32_t expk_array[11] = {2560000, 160000, 40000, 20000, 15000, 12500, 11250, 10625, 10313, 10156, 10078};
+
+ if (GreaterThan(fZERO, exponent)) {
+ exponent = fNegate(exponent);
+ bNegated = true;
+ }
+
+ while (GreaterThan(exponent, lower_bound)) {
+ for (i = 0; i < 11; i++) {
+ if (GreaterThan(exponent, GetScaledFraction(k_array[i], 10000))) {
+ exponent = fSubtract(exponent, GetScaledFraction(k_array[i], 10000));
+ solution = fMultiply(solution, GetScaledFraction(expk_array[i], 10000));
+ }
+ }
+ }
+
+ error_term = fAdd(fPositiveOne, exponent);
+
+ solution = fMultiply(solution, error_term);
+
+ if (bNegated)
+ solution = fDivide(fPositiveOne, solution);
+
+ return solution;
+}
+
+static fInt fNaturalLog(fInt value)
+{
+ uint32_t i;
+ fInt upper_bound = Divide(8, 1000);
+ fInt fNegativeOne = ConvertToFraction(-1);
+ fInt solution = ConvertToFraction(0); /*Starting off with baseline of 0 */
+ fInt error_term;
+
+ static const uint32_t k_array[10] = {160000, 40000, 20000, 15000, 12500, 11250, 10625, 10313, 10156, 10078};
+ static const uint32_t logk_array[10] = {27726, 13863, 6931, 4055, 2231, 1178, 606, 308, 155, 78};
+
+ while (GreaterThan(fAdd(value, fNegativeOne), upper_bound)) {
+ for (i = 0; i < 10; i++) {
+ if (GreaterThan(value, GetScaledFraction(k_array[i], 10000))) {
+ value = fDivide(value, GetScaledFraction(k_array[i], 10000));
+ solution = fAdd(solution, GetScaledFraction(logk_array[i], 10000));
+ }
+ }
+ }
+
+ error_term = fAdd(fNegativeOne, value);
+
+ return (fAdd(solution, error_term));
+}
+
+static fInt fDecodeLinearFuse(uint32_t fuse_value, fInt f_min, fInt f_range, uint32_t bitlength)
+{
+ fInt f_fuse_value = Convert_ULONG_ToFraction(fuse_value);
+ fInt f_bit_max_value = Convert_ULONG_ToFraction((uPow(2, bitlength)) - 1);
+
+ fInt f_decoded_value;
+
+ f_decoded_value = fDivide(f_fuse_value, f_bit_max_value);
+ f_decoded_value = fMultiply(f_decoded_value, f_range);
+ f_decoded_value = fAdd(f_decoded_value, f_min);
+
+ return f_decoded_value;
+}
+
+
+static fInt fDecodeLogisticFuse(uint32_t fuse_value, fInt f_average, fInt f_range, uint32_t bitlength)
+{
+ fInt f_fuse_value = Convert_ULONG_ToFraction(fuse_value);
+ fInt f_bit_max_value = Convert_ULONG_ToFraction((uPow(2, bitlength)) - 1);
+
+ fInt f_CONSTANT_NEG13 = ConvertToFraction(-13);
+ fInt f_CONSTANT1 = ConvertToFraction(1);
+
+ fInt f_decoded_value;
+
+ f_decoded_value = fSubtract(fDivide(f_bit_max_value, f_fuse_value), f_CONSTANT1);
+ f_decoded_value = fNaturalLog(f_decoded_value);
+ f_decoded_value = fMultiply(f_decoded_value, fDivide(f_range, f_CONSTANT_NEG13));
+ f_decoded_value = fAdd(f_decoded_value, f_average);
+
+ return f_decoded_value;
+}
+
+static fInt fDecodeLeakageID (uint32_t leakageID_fuse, fInt ln_max_div_min, fInt f_min, uint32_t bitlength)
+{
+ fInt fLeakage;
+ fInt f_bit_max_value = Convert_ULONG_ToFraction((uPow(2, bitlength)) - 1);
+
+ fLeakage = fMultiply(ln_max_div_min, Convert_ULONG_ToFraction(leakageID_fuse));
+ fLeakage = fDivide(fLeakage, f_bit_max_value);
+ fLeakage = fExponential(fLeakage);
+ fLeakage = fMultiply(fLeakage, f_min);
+
+ return fLeakage;
+}
+
+static fInt ConvertToFraction(int X) /*Add all range checking here. Is it possible to make fInt a private declaration? */
+{
+ fInt temp;
+
+ if (X <= MAX)
+ temp.full = (X << SHIFT_AMOUNT);
+ else
+ temp.full = 0;
+
+ return temp;
+}
+
+static fInt fNegate(fInt X)
+{
+ fInt CONSTANT_NEGONE = ConvertToFraction(-1);
+ return (fMultiply(X, CONSTANT_NEGONE));
+}
+
+static fInt Convert_ULONG_ToFraction(uint32_t X)
+{
+ fInt temp;
+
+ if (X <= MAX)
+ temp.full = (X << SHIFT_AMOUNT);
+ else
+ temp.full = 0;
+
+ return temp;
+}
+
+static fInt GetScaledFraction(int X, int factor)
+{
+ int times_shifted, factor_shifted;
+ bool bNEGATED;
+ fInt fValue;
+
+ times_shifted = 0;
+ factor_shifted = 0;
+ bNEGATED = false;
+
+ if (X < 0) {
+ X = -1*X;
+ bNEGATED = true;
+ }
+
+ if (factor < 0) {
+ factor = -1*factor;
+ bNEGATED = !bNEGATED; /*If bNEGATED = true due to X < 0, this will cover the case of negative cancelling negative */
+ }
+
+ if ((X > MAX) || factor > MAX) {
+ if ((X/factor) <= MAX) {
+ while (X > MAX) {
+ X = X >> 1;
+ times_shifted++;
+ }
+
+ while (factor > MAX) {
+ factor = factor >> 1;
+ factor_shifted++;
+ }
+ } else {
+ fValue.full = 0;
+ return fValue;
+ }
+ }
+
+ if (factor == 1)
+ return ConvertToFraction(X);
+
+ fValue = fDivide(ConvertToFraction(X * uPow(-1, bNEGATED)), ConvertToFraction(factor));
+
+ fValue.full = fValue.full << times_shifted;
+ fValue.full = fValue.full >> factor_shifted;
+
+ return fValue;
+}
+
+/* Addition using two fInts */
+static fInt fAdd (fInt X, fInt Y)
+{
+ fInt Sum;
+
+ Sum.full = X.full + Y.full;
+
+ return Sum;
+}
+
+/* Addition using two fInts */
+static fInt fSubtract (fInt X, fInt Y)
+{
+ fInt Difference;
+
+ Difference.full = X.full - Y.full;
+
+ return Difference;
+}
+
+static bool Equal(fInt A, fInt B)
+{
+ if (A.full == B.full)
+ return true;
+ else
+ return false;
+}
+
+static bool GreaterThan(fInt A, fInt B)
+{
+ if (A.full > B.full)
+ return true;
+ else
+ return false;
+}
+
+static fInt fMultiply (fInt X, fInt Y) /* Uses 64-bit integers (int64_t) */
+{
+ fInt Product;
+ int64_t tempProduct;
+ bool X_LessThanOne, Y_LessThanOne;
+
+ X_LessThanOne = (X.partial.real == 0 && X.partial.decimal != 0 && X.full >= 0);
+ Y_LessThanOne = (Y.partial.real == 0 && Y.partial.decimal != 0 && Y.full >= 0);
+
+ /*The following is for a very specific common case: Non-zero number with ONLY fractional portion*/
+ /* TEMPORARILY DISABLED - CAN BE USED TO IMPROVE PRECISION
+
+ if (X_LessThanOne && Y_LessThanOne) {
+ Product.full = X.full * Y.full;
+ return Product
+ }*/
+
+ tempProduct = ((int64_t)X.full) * ((int64_t)Y.full); /*Q(16,16)*Q(16,16) = Q(32, 32) - Might become a negative number! */
+ tempProduct = tempProduct >> 16; /*Remove lagging 16 bits - Will lose some precision from decimal; */
+ Product.full = (int)tempProduct; /*The int64_t will lose the leading 16 bits that were part of the integer portion */
+
+ return Product;
+}
+
+static fInt fDivide (fInt X, fInt Y)
+{
+ fInt fZERO, fQuotient;
+ int64_t longlongX, longlongY;
+
+ fZERO = ConvertToFraction(0);
+
+ if (Equal(Y, fZERO))
+ return fZERO;
+
+ longlongX = (int64_t)X.full;
+ longlongY = (int64_t)Y.full;
+
+ longlongX = longlongX << 16; /*Q(16,16) -> Q(32,32) */
+
+ div64_s64(longlongX, longlongY); /*Q(32,32) divided by Q(16,16) = Q(16,16) Back to original format */
+
+ fQuotient.full = (int)longlongX;
+ return fQuotient;
+}
+
+static int ConvertBackToInteger (fInt A) /*THIS is the function that will be used to check with the Golden settings table*/
+{
+ fInt fullNumber, scaledDecimal, scaledReal;
+
+ scaledReal.full = GetReal(A) * uPow(10, PRECISION-1); /* DOUBLE CHECK THISSSS!!! */
+
+ scaledDecimal.full = uGetScaledDecimal(A);
+
+ fullNumber = fAdd(scaledDecimal,scaledReal);
+
+ return fullNumber.full;
+}
+
+static fInt fGetSquare(fInt A)
+{
+ return fMultiply(A,A);
+}
+
+/* x_new = x_old - (x_old^2 - C) / (2 * x_old) */
+static fInt fSqrt(fInt num)
+{
+ fInt F_divide_Fprime, Fprime;
+ fInt test;
+ fInt twoShifted;
+ int seed, counter, error;
+ fInt x_new, x_old, C, y;
+
+ fInt fZERO = ConvertToFraction(0);
+
+ /* (0 > num) is the same as (num < 0), i.e., num is negative */
+
+ if (GreaterThan(fZERO, num) || Equal(fZERO, num))
+ return fZERO;
+
+ C = num;
+
+ if (num.partial.real > 3000)
+ seed = 60;
+ else if (num.partial.real > 1000)
+ seed = 30;
+ else if (num.partial.real > 100)
+ seed = 10;
+ else
+ seed = 2;
+
+ counter = 0;
+
+ if (Equal(num, fZERO)) /*Square Root of Zero is zero */
+ return fZERO;
+
+ twoShifted = ConvertToFraction(2);
+ x_new = ConvertToFraction(seed);
+
+ do {
+ counter++;
+
+ x_old.full = x_new.full;
+
+ test = fGetSquare(x_old); /*1.75*1.75 is reverting back to 1 when shifted down */
+ y = fSubtract(test, C); /*y = f(x) = x^2 - C; */
+
+ Fprime = fMultiply(twoShifted, x_old);
+ F_divide_Fprime = fDivide(y, Fprime);
+
+ x_new = fSubtract(x_old, F_divide_Fprime);
+
+ error = ConvertBackToInteger(x_new) - ConvertBackToInteger(x_old);
+
+ if (counter > 20) /*20 is already way too many iterations. If we dont have an answer by then, we never will*/
+ return x_new;
+
+ } while (uAbs(error) > 0);
+
+ return (x_new);
+}
+
+static void SolveQuadracticEqn(fInt A, fInt B, fInt C, fInt Roots[])
+{
+ fInt *pRoots = &Roots[0];
+ fInt temp, root_first, root_second;
+ fInt f_CONSTANT10, f_CONSTANT100;
+
+ f_CONSTANT100 = ConvertToFraction(100);
+ f_CONSTANT10 = ConvertToFraction(10);
+
+ while(GreaterThan(A, f_CONSTANT100) || GreaterThan(B, f_CONSTANT100) || GreaterThan(C, f_CONSTANT100)) {
+ A = fDivide(A, f_CONSTANT10);
+ B = fDivide(B, f_CONSTANT10);
+ C = fDivide(C, f_CONSTANT10);
+ }
+
+ temp = fMultiply(ConvertToFraction(4), A); /* root = 4*A */
+ temp = fMultiply(temp, C); /* root = 4*A*C */
+ temp = fSubtract(fGetSquare(B), temp); /* root = b^2 - 4AC */
+ temp = fSqrt(temp); /*root = Sqrt (b^2 - 4AC); */
+
+ root_first = fSubtract(fNegate(B), temp); /* b - Sqrt(b^2 - 4AC) */
+ root_second = fAdd(fNegate(B), temp); /* b + Sqrt(b^2 - 4AC) */
+
+ root_first = fDivide(root_first, ConvertToFraction(2)); /* [b +- Sqrt(b^2 - 4AC)]/[2] */
+ root_first = fDivide(root_first, A); /*[b +- Sqrt(b^2 - 4AC)]/[2*A] */
+
+ root_second = fDivide(root_second, ConvertToFraction(2)); /* [b +- Sqrt(b^2 - 4AC)]/[2] */
+ root_second = fDivide(root_second, A); /*[b +- Sqrt(b^2 - 4AC)]/[2*A] */
+
+ *(pRoots + 0) = root_first;
+ *(pRoots + 1) = root_second;
+}
+
+/* -----------------------------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ * -----------------------------------------------------------------------------
+ */
+
+/* Conversion Functions */
+static int GetReal (fInt A)
+{
+ return (A.full >> SHIFT_AMOUNT);
+}
+
+static fInt Divide (int X, int Y)
+{
+ fInt A, B, Quotient;
+
+ A.full = X << SHIFT_AMOUNT;
+ B.full = Y << SHIFT_AMOUNT;
+
+ Quotient = fDivide(A, B);
+
+ return Quotient;
+}
+
+static int uGetScaledDecimal (fInt A) /*Converts the fractional portion to whole integers - Costly function */
+{
+ int dec[PRECISION];
+ int i, scaledDecimal = 0, tmp = A.partial.decimal;
+
+ for (i = 0; i < PRECISION; i++) {
+ dec[i] = tmp / (1 << SHIFT_AMOUNT);
+ tmp = tmp - ((1 << SHIFT_AMOUNT)*dec[i]);
+ tmp *= 10;
+ scaledDecimal = scaledDecimal + dec[i]*uPow(10, PRECISION - 1 -i);
+ }
+
+ return scaledDecimal;
+}
+
+static int uPow(int base, int power)
+{
+ if (power == 0)
+ return 1;
+ else
+ return (base)*uPow(base, power - 1);
+}
+
+static int uAbs(int X)
+{
+ if (X < 0)
+ return (X * -1);
+ else
+ return X;
+}
+
+static fInt fRoundUpByStepSize(fInt A, fInt fStepSize, bool error_term)
+{
+ fInt solution;
+
+ solution = fDivide(A, fStepSize);
+ solution.partial.decimal = 0; /*All fractional digits changes to 0 */
+
+ if (error_term)
+ solution.partial.real += 1; /*Error term of 1 added */
+
+ solution = fMultiply(solution, fStepSize);
+ solution = fAdd(solution, fStepSize);
+
+ return solution;
+}
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pppcielanes.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pppcielanes.c
new file mode 100644
index 000000000000..186496a34cbe
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pppcielanes.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <linux/types.h>
+#include "atom-types.h"
+#include "atombios.h"
+#include "pppcielanes.h"
+
+/** \file
+ * Functions related to PCIe lane changes.
+ */
+
+/* For converting from number of lanes to lane bits. */
+static const unsigned char pp_r600_encode_lanes[] = {
+ 0, /* 0 Not Supported */
+ 1, /* 1 Lane */
+ 2, /* 2 Lanes */
+ 0, /* 3 Not Supported */
+ 3, /* 4 Lanes */
+ 0, /* 5 Not Supported */
+ 0, /* 6 Not Supported */
+ 0, /* 7 Not Supported */
+ 4, /* 8 Lanes */
+ 0, /* 9 Not Supported */
+ 0, /* 10 Not Supported */
+ 0, /* 11 Not Supported */
+ 5, /* 12 Lanes (Not actually supported) */
+ 0, /* 13 Not Supported */
+ 0, /* 14 Not Supported */
+ 0, /* 15 Not Supported */
+ 6 /* 16 Lanes */
+};
+
+static const unsigned char pp_r600_decoded_lanes[8] = { 16, 1, 2, 4, 8, 12, 16, };
+
+uint8_t encode_pcie_lane_width(uint32_t num_lanes)
+{
+ return pp_r600_encode_lanes[num_lanes];
+}
+
+uint8_t decode_pcie_lane_width(uint32_t num_lanes)
+{
+ return pp_r600_decoded_lanes[num_lanes];
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pppcielanes.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pppcielanes.h
new file mode 100644
index 000000000000..70b163b35570
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pppcielanes.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 PP_PCIELANES_H
+#define PP_PCIELANES_H
+
+extern uint8_t encode_pcie_lane_width(uint32_t num_lanes);
+extern uint8_t decode_pcie_lane_width(uint32_t num_lanes);
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pptable_v1_0.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pptable_v1_0.h
new file mode 100644
index 000000000000..1e870f58dd12
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/pptable_v1_0.h
@@ -0,0 +1,436 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 TONGA_PPTABLE_H
+#define TONGA_PPTABLE_H
+
+/** \file
+ * This is a PowerPlay table header file
+ */
+#pragma pack(push, 1)
+
+#include "hwmgr.h"
+
+#define ATOM_TONGA_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK 0x0f
+#define ATOM_TONGA_PP_FANPARAMETERS_NOFAN 0x80 /* No fan is connected to this controller. */
+
+#define ATOM_TONGA_PP_THERMALCONTROLLER_NONE 0
+#define ATOM_TONGA_PP_THERMALCONTROLLER_LM96163 17
+#define ATOM_TONGA_PP_THERMALCONTROLLER_TONGA 21
+#define ATOM_TONGA_PP_THERMALCONTROLLER_FIJI 22
+
+/*
+ * Thermal controller 'combo type' to use an external controller for Fan control and an internal controller for thermal.
+ * We probably should reserve the bit 0x80 for this use.
+ * To keep the number of these types low we should also use the same code for all ASICs (i.e. do not distinguish RV6xx and RV7xx Internal here).
+ * The driver can pick the correct internal controller based on the ASIC.
+ */
+
+#define ATOM_TONGA_PP_THERMALCONTROLLER_ADT7473_WITH_INTERNAL 0x89 /* ADT7473 Fan Control + Internal Thermal Controller */
+#define ATOM_TONGA_PP_THERMALCONTROLLER_EMC2103_WITH_INTERNAL 0x8D /* EMC2103 Fan Control + Internal Thermal Controller */
+
+/*/* ATOM_TONGA_POWERPLAYTABLE::ulPlatformCaps */
+#define ATOM_TONGA_PP_PLATFORM_CAP_VDDGFX_CONTROL 0x1 /* This cap indicates whether vddgfx will be a separated power rail. */
+#define ATOM_TONGA_PP_PLATFORM_CAP_POWERPLAY 0x2 /* This cap indicates whether this is a mobile part and CCC need to show Powerplay page. */
+#define ATOM_TONGA_PP_PLATFORM_CAP_SBIOSPOWERSOURCE 0x4 /* This cap indicates whether power source notificaiton is done by SBIOS directly. */
+#define ATOM_TONGA_PP_PLATFORM_CAP_DISABLE_VOLTAGE_ISLAND 0x8 /* Enable the option to overwrite voltage island feature to be disabled, regardless of VddGfx power rail support. */
+#define ____RETIRE16____ 0x10
+#define ATOM_TONGA_PP_PLATFORM_CAP_HARDWAREDC 0x20 /* This cap indicates whether power source notificaiton is done by GPIO directly. */
+#define ____RETIRE64____ 0x40
+#define ____RETIRE128____ 0x80
+#define ____RETIRE256____ 0x100
+#define ____RETIRE512____ 0x200
+#define ____RETIRE1024____ 0x400
+#define ____RETIRE2048____ 0x800
+#define ATOM_TONGA_PP_PLATFORM_CAP_MVDD_CONTROL 0x1000 /* This cap indicates dynamic MVDD is required. Uncheck to disable it. */
+#define ____RETIRE2000____ 0x2000
+#define ____RETIRE4000____ 0x4000
+#define ATOM_TONGA_PP_PLATFORM_CAP_VDDCI_CONTROL 0x8000 /* This cap indicates dynamic VDDCI is required. Uncheck to disable it. */
+#define ____RETIRE10000____ 0x10000
+#define ATOM_TONGA_PP_PLATFORM_CAP_BACO 0x20000 /* Enable to indicate the driver supports BACO state. */
+
+#define ATOM_TONGA_PP_PLATFORM_CAP_OUTPUT_THERMAL2GPIO17 0x100000 /* Enable to indicate the driver supports thermal2GPIO17. */
+#define ATOM_TONGA_PP_PLATFORM_COMBINE_PCC_WITH_THERMAL_SIGNAL 0x1000000 /* Enable to indicate if thermal and PCC are sharing the same GPIO */
+#define ATOM_TONGA_PLATFORM_LOAD_POST_PRODUCTION_FIRMWARE 0x2000000
+
+/* ATOM_PPLIB_NONCLOCK_INFO::usClassification */
+#define ATOM_PPLIB_CLASSIFICATION_UI_MASK 0x0007
+#define ATOM_PPLIB_CLASSIFICATION_UI_SHIFT 0
+#define ATOM_PPLIB_CLASSIFICATION_UI_NONE 0
+#define ATOM_PPLIB_CLASSIFICATION_UI_BATTERY 1
+#define ATOM_PPLIB_CLASSIFICATION_UI_BALANCED 3
+#define ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE 5
+/* 2, 4, 6, 7 are reserved */
+
+#define ATOM_PPLIB_CLASSIFICATION_BOOT 0x0008
+#define ATOM_PPLIB_CLASSIFICATION_THERMAL 0x0010
+#define ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE 0x0020
+#define ATOM_PPLIB_CLASSIFICATION_REST 0x0040
+#define ATOM_PPLIB_CLASSIFICATION_FORCED 0x0080
+#define ATOM_PPLIB_CLASSIFICATION_ACPI 0x1000
+
+/* ATOM_PPLIB_NONCLOCK_INFO::usClassification2 */
+#define ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2 0x0001
+
+#define ATOM_Tonga_DISALLOW_ON_DC 0x00004000
+#define ATOM_Tonga_ENABLE_VARIBRIGHT 0x00008000
+
+#define ATOM_Tonga_TABLE_REVISION_TONGA 7
+
+typedef struct _ATOM_Tonga_POWERPLAYTABLE {
+ ATOM_COMMON_TABLE_HEADER sHeader;
+
+ UCHAR ucTableRevision;
+ USHORT usTableSize; /*the size of header structure */
+
+ ULONG ulGoldenPPID;
+ ULONG ulGoldenRevision;
+ USHORT usFormatID;
+
+ USHORT usVoltageTime; /*in microseconds */
+ ULONG ulPlatformCaps; /*See ATOM_Tonga_CAPS_* */
+
+ ULONG ulMaxODEngineClock; /*For Overdrive. */
+ ULONG ulMaxODMemoryClock; /*For Overdrive. */
+
+ USHORT usPowerControlLimit;
+ USHORT usUlvVoltageOffset; /*in mv units */
+
+ USHORT usStateArrayOffset; /*points to ATOM_Tonga_State_Array */
+ USHORT usFanTableOffset; /*points to ATOM_Tonga_Fan_Table */
+ USHORT usThermalControllerOffset; /*points to ATOM_Tonga_Thermal_Controller */
+ USHORT usReserv; /*CustomThermalPolicy removed for Tonga. Keep this filed as reserved. */
+
+ USHORT usMclkDependencyTableOffset; /*points to ATOM_Tonga_MCLK_Dependency_Table */
+ USHORT usSclkDependencyTableOffset; /*points to ATOM_Tonga_SCLK_Dependency_Table */
+ USHORT usVddcLookupTableOffset; /*points to ATOM_Tonga_Voltage_Lookup_Table */
+ USHORT usVddgfxLookupTableOffset; /*points to ATOM_Tonga_Voltage_Lookup_Table */
+
+ USHORT usMMDependencyTableOffset; /*points to ATOM_Tonga_MM_Dependency_Table */
+
+ USHORT usVCEStateTableOffset; /*points to ATOM_Tonga_VCE_State_Table; */
+
+ USHORT usPPMTableOffset; /*points to ATOM_Tonga_PPM_Table */
+ USHORT usPowerTuneTableOffset; /*points to ATOM_PowerTune_Table */
+
+ USHORT usHardLimitTableOffset; /*points to ATOM_Tonga_Hard_Limit_Table */
+
+ USHORT usPCIETableOffset; /*points to ATOM_Tonga_PCIE_Table */
+
+ USHORT usGPIOTableOffset; /*points to ATOM_Tonga_GPIO_Table */
+
+ USHORT usReserved[6]; /*TODO: modify reserved size to fit structure aligning */
+} ATOM_Tonga_POWERPLAYTABLE;
+
+typedef struct _ATOM_Tonga_State {
+ UCHAR ucEngineClockIndexHigh;
+ UCHAR ucEngineClockIndexLow;
+
+ UCHAR ucMemoryClockIndexHigh;
+ UCHAR ucMemoryClockIndexLow;
+
+ UCHAR ucPCIEGenLow;
+ UCHAR ucPCIEGenHigh;
+
+ UCHAR ucPCIELaneLow;
+ UCHAR ucPCIELaneHigh;
+
+ USHORT usClassification;
+ ULONG ulCapsAndSettings;
+ USHORT usClassification2;
+ UCHAR ucUnused[4];
+} ATOM_Tonga_State;
+
+typedef struct _ATOM_Tonga_State_Array {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries. */
+ ATOM_Tonga_State entries[1]; /* Dynamically allocate entries. */
+} ATOM_Tonga_State_Array;
+
+typedef struct _ATOM_Tonga_MCLK_Dependency_Record {
+ UCHAR ucVddcInd; /* Vddc voltage */
+ USHORT usVddci;
+ USHORT usVddgfxOffset; /* Offset relative to Vddc voltage */
+ USHORT usMvdd;
+ ULONG ulMclk;
+ USHORT usReserved;
+} ATOM_Tonga_MCLK_Dependency_Record;
+
+typedef struct _ATOM_Tonga_MCLK_Dependency_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries. */
+ ATOM_Tonga_MCLK_Dependency_Record entries[1]; /* Dynamically allocate entries. */
+} ATOM_Tonga_MCLK_Dependency_Table;
+
+typedef struct _ATOM_Tonga_SCLK_Dependency_Record {
+ UCHAR ucVddInd; /* Base voltage */
+ USHORT usVddcOffset; /* Offset relative to base voltage */
+ ULONG ulSclk;
+ USHORT usEdcCurrent;
+ UCHAR ucReliabilityTemperature;
+ UCHAR ucCKSVOffsetandDisable; /* Bits 0~6: Voltage offset for CKS, Bit 7: Disable/enable for the SCLK level. */
+} ATOM_Tonga_SCLK_Dependency_Record;
+
+typedef struct _ATOM_Tonga_SCLK_Dependency_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries. */
+ ATOM_Tonga_SCLK_Dependency_Record entries[1]; /* Dynamically allocate entries. */
+} ATOM_Tonga_SCLK_Dependency_Table;
+
+typedef struct _ATOM_Polaris_SCLK_Dependency_Record {
+ UCHAR ucVddInd; /* Base voltage */
+ USHORT usVddcOffset; /* Offset relative to base voltage */
+ ULONG ulSclk;
+ USHORT usEdcCurrent;
+ UCHAR ucReliabilityTemperature;
+ UCHAR ucCKSVOffsetandDisable; /* Bits 0~6: Voltage offset for CKS, Bit 7: Disable/enable for the SCLK level. */
+ ULONG ulSclkOffset;
+} ATOM_Polaris_SCLK_Dependency_Record;
+
+typedef struct _ATOM_Polaris_SCLK_Dependency_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries. */
+ ATOM_Polaris_SCLK_Dependency_Record entries[1]; /* Dynamically allocate entries. */
+} ATOM_Polaris_SCLK_Dependency_Table;
+
+typedef struct _ATOM_Tonga_PCIE_Record {
+ UCHAR ucPCIEGenSpeed;
+ UCHAR usPCIELaneWidth;
+ UCHAR ucReserved[2];
+} ATOM_Tonga_PCIE_Record;
+
+typedef struct _ATOM_Tonga_PCIE_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries. */
+ ATOM_Tonga_PCIE_Record entries[1]; /* Dynamically allocate entries. */
+} ATOM_Tonga_PCIE_Table;
+
+typedef struct _ATOM_Polaris10_PCIE_Record {
+ UCHAR ucPCIEGenSpeed;
+ UCHAR usPCIELaneWidth;
+ UCHAR ucReserved[2];
+ ULONG ulPCIE_Sclk;
+} ATOM_Polaris10_PCIE_Record;
+
+typedef struct _ATOM_Polaris10_PCIE_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries. */
+ ATOM_Polaris10_PCIE_Record entries[1]; /* Dynamically allocate entries. */
+} ATOM_Polaris10_PCIE_Table;
+
+
+typedef struct _ATOM_Tonga_MM_Dependency_Record {
+ UCHAR ucVddcInd; /* VDDC voltage */
+ USHORT usVddgfxOffset; /* Offset relative to VDDC voltage */
+ ULONG ulDClk; /* UVD D-clock */
+ ULONG ulVClk; /* UVD V-clock */
+ ULONG ulEClk; /* VCE clock */
+ ULONG ulAClk; /* ACP clock */
+ ULONG ulSAMUClk; /* SAMU clock */
+} ATOM_Tonga_MM_Dependency_Record;
+
+typedef struct _ATOM_Tonga_MM_Dependency_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries. */
+ ATOM_Tonga_MM_Dependency_Record entries[1]; /* Dynamically allocate entries. */
+} ATOM_Tonga_MM_Dependency_Table;
+
+typedef struct _ATOM_Tonga_Voltage_Lookup_Record {
+ USHORT usVdd; /* Base voltage */
+ USHORT usCACLow;
+ USHORT usCACMid;
+ USHORT usCACHigh;
+} ATOM_Tonga_Voltage_Lookup_Record;
+
+typedef struct _ATOM_Tonga_Voltage_Lookup_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries. */
+ ATOM_Tonga_Voltage_Lookup_Record entries[1]; /* Dynamically allocate entries. */
+} ATOM_Tonga_Voltage_Lookup_Table;
+
+typedef struct _ATOM_Tonga_Fan_Table {
+ UCHAR ucRevId; /* Change this if the table format changes or version changes so that the other fields are not the same. */
+ UCHAR ucTHyst; /* Temperature hysteresis. Integer. */
+ USHORT usTMin; /* The temperature, in 0.01 centigrades, below which we just run at a minimal PWM. */
+ USHORT usTMed; /* The middle temperature where we change slopes. */
+ USHORT usTHigh; /* The high point above TMed for adjusting the second slope. */
+ USHORT usPWMMin; /* The minimum PWM value in percent (0.01% increments). */
+ USHORT usPWMMed; /* The PWM value (in percent) at TMed. */
+ USHORT usPWMHigh; /* The PWM value at THigh. */
+ USHORT usTMax; /* The max temperature */
+ UCHAR ucFanControlMode; /* Legacy or Fuzzy Fan mode */
+ USHORT usFanPWMMax; /* Maximum allowed fan power in percent */
+ USHORT usFanOutputSensitivity; /* Sensitivity of fan reaction to temepature changes */
+ USHORT usFanRPMMax; /* The default value in RPM */
+ ULONG ulMinFanSCLKAcousticLimit; /* Minimum Fan Controller SCLK Frequency Acoustic Limit. */
+ UCHAR ucTargetTemperature; /* Advanced fan controller target temperature. */
+ UCHAR ucMinimumPWMLimit; /* The minimum PWM that the advanced fan controller can set. This should be set to the highest PWM that will run the fan at its lowest RPM. */
+ USHORT usReserved;
+} ATOM_Tonga_Fan_Table;
+
+typedef struct _ATOM_Fiji_Fan_Table {
+ UCHAR ucRevId; /* Change this if the table format changes or version changes so that the other fields are not the same. */
+ UCHAR ucTHyst; /* Temperature hysteresis. Integer. */
+ USHORT usTMin; /* The temperature, in 0.01 centigrades, below which we just run at a minimal PWM. */
+ USHORT usTMed; /* The middle temperature where we change slopes. */
+ USHORT usTHigh; /* The high point above TMed for adjusting the second slope. */
+ USHORT usPWMMin; /* The minimum PWM value in percent (0.01% increments). */
+ USHORT usPWMMed; /* The PWM value (in percent) at TMed. */
+ USHORT usPWMHigh; /* The PWM value at THigh. */
+ USHORT usTMax; /* The max temperature */
+ UCHAR ucFanControlMode; /* Legacy or Fuzzy Fan mode */
+ USHORT usFanPWMMax; /* Maximum allowed fan power in percent */
+ USHORT usFanOutputSensitivity; /* Sensitivity of fan reaction to temepature changes */
+ USHORT usFanRPMMax; /* The default value in RPM */
+ ULONG ulMinFanSCLKAcousticLimit; /* Minimum Fan Controller SCLK Frequency Acoustic Limit. */
+ UCHAR ucTargetTemperature; /* Advanced fan controller target temperature. */
+ UCHAR ucMinimumPWMLimit; /* The minimum PWM that the advanced fan controller can set. This should be set to the highest PWM that will run the fan at its lowest RPM. */
+ USHORT usFanGainEdge;
+ USHORT usFanGainHotspot;
+ USHORT usFanGainLiquid;
+ USHORT usFanGainVrVddc;
+ USHORT usFanGainVrMvdd;
+ USHORT usFanGainPlx;
+ USHORT usFanGainHbm;
+ USHORT usReserved;
+} ATOM_Fiji_Fan_Table;
+
+typedef struct _ATOM_Tonga_Thermal_Controller {
+ UCHAR ucRevId;
+ UCHAR ucType; /* one of ATOM_TONGA_PP_THERMALCONTROLLER_* */
+ UCHAR ucI2cLine; /* as interpreted by DAL I2C */
+ UCHAR ucI2cAddress;
+ UCHAR ucFanParameters; /* Fan Control Parameters. */
+ UCHAR ucFanMinRPM; /* Fan Minimum RPM (hundreds) -- for display purposes only. */
+ UCHAR ucFanMaxRPM; /* Fan Maximum RPM (hundreds) -- for display purposes only. */
+ UCHAR ucReserved;
+ UCHAR ucFlags; /* to be defined */
+} ATOM_Tonga_Thermal_Controller;
+
+typedef struct _ATOM_Tonga_VCE_State_Record {
+ UCHAR ucVCEClockIndex; /*index into usVCEDependencyTableOffset of 'ATOM_Tonga_MM_Dependency_Table' type */
+ UCHAR ucFlag; /* 2 bits indicates memory p-states */
+ UCHAR ucSCLKIndex; /*index into ATOM_Tonga_SCLK_Dependency_Table */
+ UCHAR ucMCLKIndex; /*index into ATOM_Tonga_MCLK_Dependency_Table */
+} ATOM_Tonga_VCE_State_Record;
+
+typedef struct _ATOM_Tonga_VCE_State_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries;
+ ATOM_Tonga_VCE_State_Record entries[1];
+} ATOM_Tonga_VCE_State_Table;
+
+typedef struct _ATOM_Tonga_PowerTune_Table {
+ UCHAR ucRevId;
+ USHORT usTDP;
+ USHORT usConfigurableTDP;
+ USHORT usTDC;
+ USHORT usBatteryPowerLimit;
+ USHORT usSmallPowerLimit;
+ USHORT usLowCACLeakage;
+ USHORT usHighCACLeakage;
+ USHORT usMaximumPowerDeliveryLimit;
+ USHORT usTjMax;
+ USHORT usPowerTuneDataSetID;
+ USHORT usEDCLimit;
+ USHORT usSoftwareShutdownTemp;
+ USHORT usClockStretchAmount;
+ USHORT usReserve[2];
+} ATOM_Tonga_PowerTune_Table;
+
+typedef struct _ATOM_Fiji_PowerTune_Table {
+ UCHAR ucRevId;
+ USHORT usTDP;
+ USHORT usConfigurableTDP;
+ USHORT usTDC;
+ USHORT usBatteryPowerLimit;
+ USHORT usSmallPowerLimit;
+ USHORT usLowCACLeakage;
+ USHORT usHighCACLeakage;
+ USHORT usMaximumPowerDeliveryLimit;
+ USHORT usTjMax; /* For Fiji, this is also usTemperatureLimitEdge; */
+ USHORT usPowerTuneDataSetID;
+ USHORT usEDCLimit;
+ USHORT usSoftwareShutdownTemp;
+ USHORT usClockStretchAmount;
+ USHORT usTemperatureLimitHotspot; /*The following are added for Fiji */
+ USHORT usTemperatureLimitLiquid1;
+ USHORT usTemperatureLimitLiquid2;
+ USHORT usTemperatureLimitVrVddc;
+ USHORT usTemperatureLimitVrMvdd;
+ USHORT usTemperatureLimitPlx;
+ UCHAR ucLiquid1_I2C_address; /*Liquid */
+ UCHAR ucLiquid2_I2C_address;
+ UCHAR ucLiquid_I2C_Line;
+ UCHAR ucVr_I2C_address; /*VR */
+ UCHAR ucVr_I2C_Line;
+ UCHAR ucPlx_I2C_address; /*PLX */
+ UCHAR ucPlx_I2C_Line;
+ USHORT usReserved;
+} ATOM_Fiji_PowerTune_Table;
+
+#define ATOM_PPM_A_A 1
+#define ATOM_PPM_A_I 2
+typedef struct _ATOM_Tonga_PPM_Table {
+ UCHAR ucRevId;
+ UCHAR ucPpmDesign; /*A+I or A+A */
+ USHORT usCpuCoreNumber;
+ ULONG ulPlatformTDP;
+ ULONG ulSmallACPlatformTDP;
+ ULONG ulPlatformTDC;
+ ULONG ulSmallACPlatformTDC;
+ ULONG ulApuTDP;
+ ULONG ulDGpuTDP;
+ ULONG ulDGpuUlvPower;
+ ULONG ulTjmax;
+} ATOM_Tonga_PPM_Table;
+
+typedef struct _ATOM_Tonga_Hard_Limit_Record {
+ ULONG ulSCLKLimit;
+ ULONG ulMCLKLimit;
+ USHORT usVddcLimit;
+ USHORT usVddciLimit;
+ USHORT usVddgfxLimit;
+} ATOM_Tonga_Hard_Limit_Record;
+
+typedef struct _ATOM_Tonga_Hard_Limit_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries;
+ ATOM_Tonga_Hard_Limit_Record entries[1];
+} ATOM_Tonga_Hard_Limit_Table;
+
+typedef struct _ATOM_Tonga_GPIO_Table {
+ UCHAR ucRevId;
+ UCHAR ucVRHotTriggeredSclkDpmIndex; /* If VRHot signal is triggered SCLK will be limited to this DPM level */
+ UCHAR ucReserve[5];
+} ATOM_Tonga_GPIO_Table;
+
+typedef struct _PPTable_Generic_SubTable_Header {
+ UCHAR ucRevId;
+} PPTable_Generic_SubTable_Header;
+
+
+#pragma pack(pop)
+
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c
new file mode 100644
index 000000000000..b760f95e7fa7
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c
@@ -0,0 +1,1337 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "pp_debug.h"
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "process_pptables_v1_0.h"
+#include "ppatomctrl.h"
+#include "atombios.h"
+#include "hwmgr.h"
+#include "cgs_common.h"
+#include "pptable_v1_0.h"
+
+/**
+ * Private Function used during initialization.
+ * @param hwmgr Pointer to the hardware manager.
+ * @param setIt A flag indication if the capability should be set (TRUE) or reset (FALSE).
+ * @param cap Which capability to set/reset.
+ */
+static void set_hw_cap(struct pp_hwmgr *hwmgr, bool setIt, enum phm_platform_caps cap)
+{
+ if (setIt)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, cap);
+ else
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps, cap);
+}
+
+
+/**
+ * Private Function used during initialization.
+ * @param hwmgr Pointer to the hardware manager.
+ * @param powerplay_caps the bit array (from BIOS) of capability bits.
+ * @exception the current implementation always returns 1.
+ */
+static int set_platform_caps(struct pp_hwmgr *hwmgr, uint32_t powerplay_caps)
+{
+ PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE16____),
+ "ATOM_PP_PLATFORM_CAP_ASPM_L1 is not supported!", continue);
+ PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE64____),
+ "ATOM_PP_PLATFORM_CAP_GEMINIPRIMARY is not supported!", continue);
+ PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE512____),
+ "ATOM_PP_PLATFORM_CAP_SIDEPORTCONTROL is not supported!", continue);
+ PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE1024____),
+ "ATOM_PP_PLATFORM_CAP_TURNOFFPLL_ASPML1 is not supported!", continue);
+ PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE2048____),
+ "ATOM_PP_PLATFORM_CAP_HTLINKCONTROL is not supported!", continue);
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_POWERPLAY),
+ PHM_PlatformCaps_PowerPlaySupport
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_SBIOSPOWERSOURCE),
+ PHM_PlatformCaps_BiosPowerSourceControl
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_HARDWAREDC),
+ PHM_PlatformCaps_AutomaticDCTransition
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_MVDD_CONTROL),
+ PHM_PlatformCaps_EnableMVDDControl
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_VDDCI_CONTROL),
+ PHM_PlatformCaps_ControlVDDCI
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_VDDGFX_CONTROL),
+ PHM_PlatformCaps_ControlVDDGFX
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_BACO),
+ PHM_PlatformCaps_BACO
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_DISABLE_VOLTAGE_ISLAND),
+ PHM_PlatformCaps_DisableVoltageIsland
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_COMBINE_PCC_WITH_THERMAL_SIGNAL),
+ PHM_PlatformCaps_CombinePCCWithThermalSignal
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_TONGA_PLATFORM_LOAD_POST_PRODUCTION_FIRMWARE),
+ PHM_PlatformCaps_LoadPostProductionFirmware
+ );
+
+ return 0;
+}
+
+/**
+ * Private Function to get the PowerPlay Table Address.
+ */
+static const void *get_powerplay_table(struct pp_hwmgr *hwmgr)
+{
+ int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
+
+ u16 size;
+ u8 frev, crev;
+ void *table_address = (void *)hwmgr->soft_pp_table;
+
+ if (!table_address) {
+ table_address = (ATOM_Tonga_POWERPLAYTABLE *)
+ smu_atom_get_data_table(hwmgr->adev,
+ index, &size, &frev, &crev);
+ hwmgr->soft_pp_table = table_address; /*Cache the result in RAM.*/
+ hwmgr->soft_pp_table_size = size;
+ }
+
+ return table_address;
+}
+
+static int get_vddc_lookup_table(
+ struct pp_hwmgr *hwmgr,
+ phm_ppt_v1_voltage_lookup_table **lookup_table,
+ const ATOM_Tonga_Voltage_Lookup_Table *vddc_lookup_pp_tables,
+ uint32_t max_levels
+ )
+{
+ uint32_t table_size, i;
+ phm_ppt_v1_voltage_lookup_table *table;
+ phm_ppt_v1_voltage_lookup_record *record;
+ ATOM_Tonga_Voltage_Lookup_Record *atom_record;
+
+ PP_ASSERT_WITH_CODE((0 != vddc_lookup_pp_tables->ucNumEntries),
+ "Invalid CAC Leakage PowerPlay Table!", return 1);
+
+ table_size = sizeof(uint32_t) +
+ sizeof(phm_ppt_v1_voltage_lookup_record) * max_levels;
+
+ table = kzalloc(table_size, GFP_KERNEL);
+
+ if (NULL == table)
+ return -ENOMEM;
+
+ table->count = vddc_lookup_pp_tables->ucNumEntries;
+
+ for (i = 0; i < vddc_lookup_pp_tables->ucNumEntries; i++) {
+ record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ phm_ppt_v1_voltage_lookup_record,
+ entries, table, i);
+ atom_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ ATOM_Tonga_Voltage_Lookup_Record,
+ entries, vddc_lookup_pp_tables, i);
+ record->us_calculated = 0;
+ record->us_vdd = le16_to_cpu(atom_record->usVdd);
+ record->us_cac_low = le16_to_cpu(atom_record->usCACLow);
+ record->us_cac_mid = le16_to_cpu(atom_record->usCACMid);
+ record->us_cac_high = le16_to_cpu(atom_record->usCACHigh);
+ }
+
+ *lookup_table = table;
+
+ return 0;
+}
+
+/**
+ * Private Function used during initialization.
+ * Initialize Platform Power Management Parameter table
+ * @param hwmgr Pointer to the hardware manager.
+ * @param atom_ppm_table Pointer to PPM table in VBIOS
+ */
+static int get_platform_power_management_table(
+ struct pp_hwmgr *hwmgr,
+ ATOM_Tonga_PPM_Table *atom_ppm_table)
+{
+ struct phm_ppm_table *ptr = kzalloc(sizeof(ATOM_Tonga_PPM_Table), GFP_KERNEL);
+ struct phm_ppt_v1_information *pp_table_information =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ if (NULL == ptr)
+ return -ENOMEM;
+
+ ptr->ppm_design
+ = atom_ppm_table->ucPpmDesign;
+ ptr->cpu_core_number
+ = le16_to_cpu(atom_ppm_table->usCpuCoreNumber);
+ ptr->platform_tdp
+ = le32_to_cpu(atom_ppm_table->ulPlatformTDP);
+ ptr->small_ac_platform_tdp
+ = le32_to_cpu(atom_ppm_table->ulSmallACPlatformTDP);
+ ptr->platform_tdc
+ = le32_to_cpu(atom_ppm_table->ulPlatformTDC);
+ ptr->small_ac_platform_tdc
+ = le32_to_cpu(atom_ppm_table->ulSmallACPlatformTDC);
+ ptr->apu_tdp
+ = le32_to_cpu(atom_ppm_table->ulApuTDP);
+ ptr->dgpu_tdp
+ = le32_to_cpu(atom_ppm_table->ulDGpuTDP);
+ ptr->dgpu_ulv_power
+ = le32_to_cpu(atom_ppm_table->ulDGpuUlvPower);
+ ptr->tj_max
+ = le32_to_cpu(atom_ppm_table->ulTjmax);
+
+ pp_table_information->ppm_parameter_table = ptr;
+
+ return 0;
+}
+
+/**
+ * Private Function used during initialization.
+ * Initialize TDP limits for DPM2
+ * @param hwmgr Pointer to the hardware manager.
+ * @param powerplay_table Pointer to the PowerPlay Table.
+ */
+static int init_dpm_2_parameters(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table
+ )
+{
+ int result = 0;
+ struct phm_ppt_v1_information *pp_table_information = (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ ATOM_Tonga_PPM_Table *atom_ppm_table;
+ uint32_t disable_ppm = 0;
+ uint32_t disable_power_control = 0;
+
+ pp_table_information->us_ulv_voltage_offset =
+ le16_to_cpu(powerplay_table->usUlvVoltageOffset);
+
+ pp_table_information->ppm_parameter_table = NULL;
+ pp_table_information->vddc_lookup_table = NULL;
+ pp_table_information->vddgfx_lookup_table = NULL;
+ /* TDP limits */
+ hwmgr->platform_descriptor.TDPODLimit =
+ le16_to_cpu(powerplay_table->usPowerControlLimit);
+ hwmgr->platform_descriptor.TDPAdjustment = 0;
+ hwmgr->platform_descriptor.VidAdjustment = 0;
+ hwmgr->platform_descriptor.VidAdjustmentPolarity = 0;
+ hwmgr->platform_descriptor.VidMinLimit = 0;
+ hwmgr->platform_descriptor.VidMaxLimit = 1500000;
+ hwmgr->platform_descriptor.VidStep = 6250;
+
+ disable_power_control = 0;
+ if (0 == disable_power_control) {
+ /* enable TDP overdrive (PowerControl) feature as well if supported */
+ if (hwmgr->platform_descriptor.TDPODLimit != 0)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PowerControl);
+ }
+
+ if (0 != powerplay_table->usVddcLookupTableOffset) {
+ const ATOM_Tonga_Voltage_Lookup_Table *pVddcCACTable =
+ (ATOM_Tonga_Voltage_Lookup_Table *)(((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usVddcLookupTableOffset));
+
+ result = get_vddc_lookup_table(hwmgr,
+ &pp_table_information->vddc_lookup_table, pVddcCACTable, 16);
+ }
+
+ if (0 != powerplay_table->usVddgfxLookupTableOffset) {
+ const ATOM_Tonga_Voltage_Lookup_Table *pVddgfxCACTable =
+ (ATOM_Tonga_Voltage_Lookup_Table *)(((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usVddgfxLookupTableOffset));
+
+ result = get_vddc_lookup_table(hwmgr,
+ &pp_table_information->vddgfx_lookup_table, pVddgfxCACTable, 16);
+ }
+
+ disable_ppm = 0;
+ if (0 == disable_ppm) {
+ atom_ppm_table = (ATOM_Tonga_PPM_Table *)
+ (((unsigned long)powerplay_table) + le16_to_cpu(powerplay_table->usPPMTableOffset));
+
+ if (0 != powerplay_table->usPPMTableOffset) {
+ if (get_platform_power_management_table(hwmgr, atom_ppm_table) == 0) {
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EnablePlatformPowerManagement);
+ }
+ }
+ }
+
+ return result;
+}
+
+static int get_valid_clk(
+ struct pp_hwmgr *hwmgr,
+ struct phm_clock_array **clk_table,
+ phm_ppt_v1_clock_voltage_dependency_table const *clk_volt_pp_table
+ )
+{
+ uint32_t table_size, i;
+ struct phm_clock_array *table;
+ phm_ppt_v1_clock_voltage_dependency_record *dep_record;
+
+ PP_ASSERT_WITH_CODE((0 != clk_volt_pp_table->count),
+ "Invalid PowerPlay Table!", return -1);
+
+ table_size = sizeof(uint32_t) +
+ sizeof(uint32_t) * clk_volt_pp_table->count;
+
+ table = kzalloc(table_size, GFP_KERNEL);
+
+ if (NULL == table)
+ return -ENOMEM;
+
+ table->count = (uint32_t)clk_volt_pp_table->count;
+
+ for (i = 0; i < table->count; i++) {
+ dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ phm_ppt_v1_clock_voltage_dependency_record,
+ entries, clk_volt_pp_table, i);
+ table->values[i] = (uint32_t)dep_record->clk;
+ }
+ *clk_table = table;
+
+ return 0;
+}
+
+static int get_hard_limits(
+ struct pp_hwmgr *hwmgr,
+ struct phm_clock_and_voltage_limits *limits,
+ ATOM_Tonga_Hard_Limit_Table const *limitable
+ )
+{
+ PP_ASSERT_WITH_CODE((0 != limitable->ucNumEntries), "Invalid PowerPlay Table!", return -1);
+
+ /* currently we always take entries[0] parameters */
+ limits->sclk = le32_to_cpu(limitable->entries[0].ulSCLKLimit);
+ limits->mclk = le32_to_cpu(limitable->entries[0].ulMCLKLimit);
+ limits->vddc = le16_to_cpu(limitable->entries[0].usVddcLimit);
+ limits->vddci = le16_to_cpu(limitable->entries[0].usVddciLimit);
+ limits->vddgfx = le16_to_cpu(limitable->entries[0].usVddgfxLimit);
+
+ return 0;
+}
+
+static int get_mclk_voltage_dependency_table(
+ struct pp_hwmgr *hwmgr,
+ phm_ppt_v1_clock_voltage_dependency_table **pp_tonga_mclk_dep_table,
+ ATOM_Tonga_MCLK_Dependency_Table const *mclk_dep_table
+ )
+{
+ uint32_t table_size, i;
+ phm_ppt_v1_clock_voltage_dependency_table *mclk_table;
+ phm_ppt_v1_clock_voltage_dependency_record *mclk_table_record;
+ ATOM_Tonga_MCLK_Dependency_Record *mclk_dep_record;
+
+ PP_ASSERT_WITH_CODE((0 != mclk_dep_table->ucNumEntries),
+ "Invalid PowerPlay Table!", return -1);
+
+ table_size = sizeof(uint32_t) + sizeof(phm_ppt_v1_clock_voltage_dependency_record)
+ * mclk_dep_table->ucNumEntries;
+
+ mclk_table = kzalloc(table_size, GFP_KERNEL);
+
+ if (NULL == mclk_table)
+ return -ENOMEM;
+
+ mclk_table->count = (uint32_t)mclk_dep_table->ucNumEntries;
+
+ for (i = 0; i < mclk_dep_table->ucNumEntries; i++) {
+ mclk_table_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ phm_ppt_v1_clock_voltage_dependency_record,
+ entries, mclk_table, i);
+ mclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ ATOM_Tonga_MCLK_Dependency_Record,
+ entries, mclk_dep_table, i);
+ mclk_table_record->vddInd = mclk_dep_record->ucVddcInd;
+ mclk_table_record->vdd_offset = le16_to_cpu(mclk_dep_record->usVddgfxOffset);
+ mclk_table_record->vddci = le16_to_cpu(mclk_dep_record->usVddci);
+ mclk_table_record->mvdd = le16_to_cpu(mclk_dep_record->usMvdd);
+ mclk_table_record->clk = le32_to_cpu(mclk_dep_record->ulMclk);
+ }
+
+ *pp_tonga_mclk_dep_table = mclk_table;
+
+ return 0;
+}
+
+static int get_sclk_voltage_dependency_table(
+ struct pp_hwmgr *hwmgr,
+ phm_ppt_v1_clock_voltage_dependency_table **pp_tonga_sclk_dep_table,
+ PPTable_Generic_SubTable_Header const *sclk_dep_table
+ )
+{
+ uint32_t table_size, i;
+ phm_ppt_v1_clock_voltage_dependency_table *sclk_table;
+ phm_ppt_v1_clock_voltage_dependency_record *sclk_table_record;
+
+ if (sclk_dep_table->ucRevId < 1) {
+ const ATOM_Tonga_SCLK_Dependency_Table *tonga_table =
+ (ATOM_Tonga_SCLK_Dependency_Table *)sclk_dep_table;
+ ATOM_Tonga_SCLK_Dependency_Record *sclk_dep_record;
+
+ PP_ASSERT_WITH_CODE((0 != tonga_table->ucNumEntries),
+ "Invalid PowerPlay Table!", return -1);
+
+ table_size = sizeof(uint32_t) + sizeof(phm_ppt_v1_clock_voltage_dependency_record)
+ * tonga_table->ucNumEntries;
+
+ sclk_table = kzalloc(table_size, GFP_KERNEL);
+
+ if (NULL == sclk_table)
+ return -ENOMEM;
+
+ sclk_table->count = (uint32_t)tonga_table->ucNumEntries;
+
+ for (i = 0; i < tonga_table->ucNumEntries; i++) {
+ sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ ATOM_Tonga_SCLK_Dependency_Record,
+ entries, tonga_table, i);
+ sclk_table_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ phm_ppt_v1_clock_voltage_dependency_record,
+ entries, sclk_table, i);
+ sclk_table_record->vddInd = sclk_dep_record->ucVddInd;
+ sclk_table_record->vdd_offset = le16_to_cpu(sclk_dep_record->usVddcOffset);
+ sclk_table_record->clk = le32_to_cpu(sclk_dep_record->ulSclk);
+ sclk_table_record->cks_enable =
+ (((sclk_dep_record->ucCKSVOffsetandDisable & 0x80) >> 7) == 0) ? 1 : 0;
+ sclk_table_record->cks_voffset = (sclk_dep_record->ucCKSVOffsetandDisable & 0x7F);
+ }
+ } else {
+ const ATOM_Polaris_SCLK_Dependency_Table *polaris_table =
+ (ATOM_Polaris_SCLK_Dependency_Table *)sclk_dep_table;
+ ATOM_Polaris_SCLK_Dependency_Record *sclk_dep_record;
+
+ PP_ASSERT_WITH_CODE((0 != polaris_table->ucNumEntries),
+ "Invalid PowerPlay Table!", return -1);
+
+ table_size = sizeof(uint32_t) + sizeof(phm_ppt_v1_clock_voltage_dependency_record)
+ * polaris_table->ucNumEntries;
+
+ sclk_table = kzalloc(table_size, GFP_KERNEL);
+
+ if (NULL == sclk_table)
+ return -ENOMEM;
+
+ sclk_table->count = (uint32_t)polaris_table->ucNumEntries;
+
+ for (i = 0; i < polaris_table->ucNumEntries; i++) {
+ sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ ATOM_Polaris_SCLK_Dependency_Record,
+ entries, polaris_table, i);
+ sclk_table_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ phm_ppt_v1_clock_voltage_dependency_record,
+ entries, sclk_table, i);
+ sclk_table_record->vddInd = sclk_dep_record->ucVddInd;
+ sclk_table_record->vdd_offset = le16_to_cpu(sclk_dep_record->usVddcOffset);
+ sclk_table_record->clk = le32_to_cpu(sclk_dep_record->ulSclk);
+ sclk_table_record->cks_enable =
+ (((sclk_dep_record->ucCKSVOffsetandDisable & 0x80) >> 7) == 0) ? 1 : 0;
+ sclk_table_record->cks_voffset = (sclk_dep_record->ucCKSVOffsetandDisable & 0x7F);
+ sclk_table_record->sclk_offset = le32_to_cpu(sclk_dep_record->ulSclkOffset);
+ }
+ }
+ *pp_tonga_sclk_dep_table = sclk_table;
+
+ return 0;
+}
+
+static int get_pcie_table(
+ struct pp_hwmgr *hwmgr,
+ phm_ppt_v1_pcie_table **pp_tonga_pcie_table,
+ PPTable_Generic_SubTable_Header const *ptable
+ )
+{
+ uint32_t table_size, i, pcie_count;
+ phm_ppt_v1_pcie_table *pcie_table;
+ struct phm_ppt_v1_information *pp_table_information =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ phm_ppt_v1_pcie_record *pcie_record;
+
+ if (ptable->ucRevId < 1) {
+ const ATOM_Tonga_PCIE_Table *atom_pcie_table = (ATOM_Tonga_PCIE_Table *)ptable;
+ ATOM_Tonga_PCIE_Record *atom_pcie_record;
+
+ PP_ASSERT_WITH_CODE((atom_pcie_table->ucNumEntries != 0),
+ "Invalid PowerPlay Table!", return -1);
+
+ table_size = sizeof(uint32_t) +
+ sizeof(phm_ppt_v1_pcie_record) * atom_pcie_table->ucNumEntries;
+
+ pcie_table = kzalloc(table_size, GFP_KERNEL);
+
+ if (pcie_table == NULL)
+ return -ENOMEM;
+
+ /*
+ * Make sure the number of pcie entries are less than or equal to sclk dpm levels.
+ * Since first PCIE entry is for ULV, #pcie has to be <= SclkLevel + 1.
+ */
+ pcie_count = (pp_table_information->vdd_dep_on_sclk->count) + 1;
+ if ((uint32_t)atom_pcie_table->ucNumEntries <= pcie_count)
+ pcie_count = (uint32_t)atom_pcie_table->ucNumEntries;
+ else
+ pr_err("Number of Pcie Entries exceed the number of SCLK Dpm Levels! Disregarding the excess entries...\n");
+
+ pcie_table->count = pcie_count;
+ for (i = 0; i < pcie_count; i++) {
+ pcie_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ phm_ppt_v1_pcie_record,
+ entries, pcie_table, i);
+ atom_pcie_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ ATOM_Tonga_PCIE_Record,
+ entries, atom_pcie_table, i);
+ pcie_record->gen_speed = atom_pcie_record->ucPCIEGenSpeed;
+ pcie_record->lane_width = le16_to_cpu(atom_pcie_record->usPCIELaneWidth);
+ }
+
+ *pp_tonga_pcie_table = pcie_table;
+ } else {
+ /* Polaris10/Polaris11 and newer. */
+ const ATOM_Polaris10_PCIE_Table *atom_pcie_table = (ATOM_Polaris10_PCIE_Table *)ptable;
+ ATOM_Polaris10_PCIE_Record *atom_pcie_record;
+
+ PP_ASSERT_WITH_CODE((atom_pcie_table->ucNumEntries != 0),
+ "Invalid PowerPlay Table!", return -1);
+
+ table_size = sizeof(uint32_t) +
+ sizeof(phm_ppt_v1_pcie_record) * atom_pcie_table->ucNumEntries;
+
+ pcie_table = kzalloc(table_size, GFP_KERNEL);
+
+ if (pcie_table == NULL)
+ return -ENOMEM;
+
+ /*
+ * Make sure the number of pcie entries are less than or equal to sclk dpm levels.
+ * Since first PCIE entry is for ULV, #pcie has to be <= SclkLevel + 1.
+ */
+ pcie_count = (pp_table_information->vdd_dep_on_sclk->count) + 1;
+ if ((uint32_t)atom_pcie_table->ucNumEntries <= pcie_count)
+ pcie_count = (uint32_t)atom_pcie_table->ucNumEntries;
+ else
+ pr_err("Number of Pcie Entries exceed the number of SCLK Dpm Levels! Disregarding the excess entries...\n");
+
+ pcie_table->count = pcie_count;
+
+ for (i = 0; i < pcie_count; i++) {
+ pcie_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ phm_ppt_v1_pcie_record,
+ entries, pcie_table, i);
+ atom_pcie_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ ATOM_Polaris10_PCIE_Record,
+ entries, atom_pcie_table, i);
+ pcie_record->gen_speed = atom_pcie_record->ucPCIEGenSpeed;
+ pcie_record->lane_width = le16_to_cpu(atom_pcie_record->usPCIELaneWidth);
+ pcie_record->pcie_sclk = le32_to_cpu(atom_pcie_record->ulPCIE_Sclk);
+ }
+
+ *pp_tonga_pcie_table = pcie_table;
+ }
+
+ return 0;
+}
+
+static int get_cac_tdp_table(
+ struct pp_hwmgr *hwmgr,
+ struct phm_cac_tdp_table **cac_tdp_table,
+ const PPTable_Generic_SubTable_Header * table
+ )
+{
+ uint32_t table_size;
+ struct phm_cac_tdp_table *tdp_table;
+
+ table_size = sizeof(uint32_t) + sizeof(struct phm_cac_tdp_table);
+ tdp_table = kzalloc(table_size, GFP_KERNEL);
+
+ if (NULL == tdp_table)
+ return -ENOMEM;
+
+ hwmgr->dyn_state.cac_dtp_table = kzalloc(table_size, GFP_KERNEL);
+
+ if (NULL == hwmgr->dyn_state.cac_dtp_table) {
+ kfree(tdp_table);
+ return -ENOMEM;
+ }
+
+ if (table->ucRevId < 3) {
+ const ATOM_Tonga_PowerTune_Table *tonga_table =
+ (ATOM_Tonga_PowerTune_Table *)table;
+ tdp_table->usTDP = le16_to_cpu(tonga_table->usTDP);
+ tdp_table->usConfigurableTDP =
+ le16_to_cpu(tonga_table->usConfigurableTDP);
+ tdp_table->usTDC = le16_to_cpu(tonga_table->usTDC);
+ tdp_table->usBatteryPowerLimit =
+ le16_to_cpu(tonga_table->usBatteryPowerLimit);
+ tdp_table->usSmallPowerLimit =
+ le16_to_cpu(tonga_table->usSmallPowerLimit);
+ tdp_table->usLowCACLeakage =
+ le16_to_cpu(tonga_table->usLowCACLeakage);
+ tdp_table->usHighCACLeakage =
+ le16_to_cpu(tonga_table->usHighCACLeakage);
+ tdp_table->usMaximumPowerDeliveryLimit =
+ le16_to_cpu(tonga_table->usMaximumPowerDeliveryLimit);
+ tdp_table->usDefaultTargetOperatingTemp =
+ le16_to_cpu(tonga_table->usTjMax);
+ tdp_table->usTargetOperatingTemp =
+ le16_to_cpu(tonga_table->usTjMax); /*Set the initial temp to the same as default */
+ tdp_table->usPowerTuneDataSetID =
+ le16_to_cpu(tonga_table->usPowerTuneDataSetID);
+ tdp_table->usSoftwareShutdownTemp =
+ le16_to_cpu(tonga_table->usSoftwareShutdownTemp);
+ tdp_table->usClockStretchAmount =
+ le16_to_cpu(tonga_table->usClockStretchAmount);
+ } else { /* Fiji and newer */
+ const ATOM_Fiji_PowerTune_Table *fijitable =
+ (ATOM_Fiji_PowerTune_Table *)table;
+ tdp_table->usTDP = le16_to_cpu(fijitable->usTDP);
+ tdp_table->usConfigurableTDP = le16_to_cpu(fijitable->usConfigurableTDP);
+ tdp_table->usTDC = le16_to_cpu(fijitable->usTDC);
+ tdp_table->usBatteryPowerLimit = le16_to_cpu(fijitable->usBatteryPowerLimit);
+ tdp_table->usSmallPowerLimit = le16_to_cpu(fijitable->usSmallPowerLimit);
+ tdp_table->usLowCACLeakage = le16_to_cpu(fijitable->usLowCACLeakage);
+ tdp_table->usHighCACLeakage = le16_to_cpu(fijitable->usHighCACLeakage);
+ tdp_table->usMaximumPowerDeliveryLimit =
+ le16_to_cpu(fijitable->usMaximumPowerDeliveryLimit);
+ tdp_table->usDefaultTargetOperatingTemp =
+ le16_to_cpu(fijitable->usTjMax);
+ tdp_table->usTargetOperatingTemp =
+ le16_to_cpu(fijitable->usTjMax); /*Set the initial temp to the same as default */
+ tdp_table->usPowerTuneDataSetID =
+ le16_to_cpu(fijitable->usPowerTuneDataSetID);
+ tdp_table->usSoftwareShutdownTemp =
+ le16_to_cpu(fijitable->usSoftwareShutdownTemp);
+ tdp_table->usClockStretchAmount =
+ le16_to_cpu(fijitable->usClockStretchAmount);
+ tdp_table->usTemperatureLimitHotspot =
+ le16_to_cpu(fijitable->usTemperatureLimitHotspot);
+ tdp_table->usTemperatureLimitLiquid1 =
+ le16_to_cpu(fijitable->usTemperatureLimitLiquid1);
+ tdp_table->usTemperatureLimitLiquid2 =
+ le16_to_cpu(fijitable->usTemperatureLimitLiquid2);
+ tdp_table->usTemperatureLimitVrVddc =
+ le16_to_cpu(fijitable->usTemperatureLimitVrVddc);
+ tdp_table->usTemperatureLimitVrMvdd =
+ le16_to_cpu(fijitable->usTemperatureLimitVrMvdd);
+ tdp_table->usTemperatureLimitPlx =
+ le16_to_cpu(fijitable->usTemperatureLimitPlx);
+ tdp_table->ucLiquid1_I2C_address =
+ fijitable->ucLiquid1_I2C_address;
+ tdp_table->ucLiquid2_I2C_address =
+ fijitable->ucLiquid2_I2C_address;
+ tdp_table->ucLiquid_I2C_Line =
+ fijitable->ucLiquid_I2C_Line;
+ tdp_table->ucVr_I2C_address = fijitable->ucVr_I2C_address;
+ tdp_table->ucVr_I2C_Line = fijitable->ucVr_I2C_Line;
+ tdp_table->ucPlx_I2C_address = fijitable->ucPlx_I2C_address;
+ tdp_table->ucPlx_I2C_Line = fijitable->ucPlx_I2C_Line;
+ }
+
+ *cac_tdp_table = tdp_table;
+
+ return 0;
+}
+
+static int get_mm_clock_voltage_table(
+ struct pp_hwmgr *hwmgr,
+ phm_ppt_v1_mm_clock_voltage_dependency_table **tonga_mm_table,
+ const ATOM_Tonga_MM_Dependency_Table * mm_dependency_table
+ )
+{
+ uint32_t table_size, i;
+ const ATOM_Tonga_MM_Dependency_Record *mm_dependency_record;
+ phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table;
+ phm_ppt_v1_mm_clock_voltage_dependency_record *mm_table_record;
+
+ PP_ASSERT_WITH_CODE((0 != mm_dependency_table->ucNumEntries),
+ "Invalid PowerPlay Table!", return -1);
+ table_size = sizeof(uint32_t) +
+ sizeof(phm_ppt_v1_mm_clock_voltage_dependency_record)
+ * mm_dependency_table->ucNumEntries;
+ mm_table = kzalloc(table_size, GFP_KERNEL);
+
+ if (NULL == mm_table)
+ return -ENOMEM;
+
+ mm_table->count = mm_dependency_table->ucNumEntries;
+
+ for (i = 0; i < mm_dependency_table->ucNumEntries; i++) {
+ mm_dependency_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ ATOM_Tonga_MM_Dependency_Record,
+ entries, mm_dependency_table, i);
+ mm_table_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ phm_ppt_v1_mm_clock_voltage_dependency_record,
+ entries, mm_table, i);
+ mm_table_record->vddcInd = mm_dependency_record->ucVddcInd;
+ mm_table_record->vddgfx_offset = le16_to_cpu(mm_dependency_record->usVddgfxOffset);
+ mm_table_record->aclk = le32_to_cpu(mm_dependency_record->ulAClk);
+ mm_table_record->samclock = le32_to_cpu(mm_dependency_record->ulSAMUClk);
+ mm_table_record->eclk = le32_to_cpu(mm_dependency_record->ulEClk);
+ mm_table_record->vclk = le32_to_cpu(mm_dependency_record->ulVClk);
+ mm_table_record->dclk = le32_to_cpu(mm_dependency_record->ulDClk);
+ }
+
+ *tonga_mm_table = mm_table;
+
+ return 0;
+}
+
+static int get_gpio_table(struct pp_hwmgr *hwmgr,
+ struct phm_ppt_v1_gpio_table **pp_tonga_gpio_table,
+ const ATOM_Tonga_GPIO_Table *atom_gpio_table)
+{
+ uint32_t table_size;
+ struct phm_ppt_v1_gpio_table *pp_gpio_table;
+ struct phm_ppt_v1_information *pp_table_information =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ table_size = sizeof(struct phm_ppt_v1_gpio_table);
+ pp_gpio_table = kzalloc(table_size, GFP_KERNEL);
+ if (!pp_gpio_table)
+ return -ENOMEM;
+
+ if (pp_table_information->vdd_dep_on_sclk->count <
+ atom_gpio_table->ucVRHotTriggeredSclkDpmIndex)
+ PP_ASSERT_WITH_CODE(false,
+ "SCLK DPM index for VRHot cannot exceed the total sclk level count!",);
+ else
+ pp_gpio_table->vrhot_triggered_sclk_dpm_index =
+ atom_gpio_table->ucVRHotTriggeredSclkDpmIndex;
+
+ *pp_tonga_gpio_table = pp_gpio_table;
+
+ return 0;
+}
+/**
+ * Private Function used during initialization.
+ * Initialize clock voltage dependency
+ * @param hwmgr Pointer to the hardware manager.
+ * @param powerplay_table Pointer to the PowerPlay Table.
+ */
+static int init_clock_voltage_dependency(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table
+ )
+{
+ int result = 0;
+ struct phm_ppt_v1_information *pp_table_information =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ const ATOM_Tonga_MM_Dependency_Table *mm_dependency_table =
+ (const ATOM_Tonga_MM_Dependency_Table *)(((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usMMDependencyTableOffset));
+ const PPTable_Generic_SubTable_Header *pPowerTuneTable =
+ (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usPowerTuneTableOffset));
+ const ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table =
+ (const ATOM_Tonga_MCLK_Dependency_Table *)(((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usMclkDependencyTableOffset));
+ const PPTable_Generic_SubTable_Header *sclk_dep_table =
+ (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usSclkDependencyTableOffset));
+ const ATOM_Tonga_Hard_Limit_Table *pHardLimits =
+ (const ATOM_Tonga_Hard_Limit_Table *)(((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usHardLimitTableOffset));
+ const PPTable_Generic_SubTable_Header *pcie_table =
+ (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usPCIETableOffset));
+ const ATOM_Tonga_GPIO_Table *gpio_table =
+ (const ATOM_Tonga_GPIO_Table *)(((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usGPIOTableOffset));
+
+ pp_table_information->vdd_dep_on_sclk = NULL;
+ pp_table_information->vdd_dep_on_mclk = NULL;
+ pp_table_information->mm_dep_table = NULL;
+ pp_table_information->pcie_table = NULL;
+ pp_table_information->gpio_table = NULL;
+
+ if (powerplay_table->usMMDependencyTableOffset != 0)
+ result = get_mm_clock_voltage_table(hwmgr,
+ &pp_table_information->mm_dep_table, mm_dependency_table);
+
+ if (result == 0 && powerplay_table->usPowerTuneTableOffset != 0)
+ result = get_cac_tdp_table(hwmgr,
+ &pp_table_information->cac_dtp_table, pPowerTuneTable);
+
+ if (result == 0 && powerplay_table->usSclkDependencyTableOffset != 0)
+ result = get_sclk_voltage_dependency_table(hwmgr,
+ &pp_table_information->vdd_dep_on_sclk, sclk_dep_table);
+
+ if (result == 0 && powerplay_table->usMclkDependencyTableOffset != 0)
+ result = get_mclk_voltage_dependency_table(hwmgr,
+ &pp_table_information->vdd_dep_on_mclk, mclk_dep_table);
+
+ if (result == 0 && powerplay_table->usPCIETableOffset != 0)
+ result = get_pcie_table(hwmgr,
+ &pp_table_information->pcie_table, pcie_table);
+
+ if (result == 0 && powerplay_table->usHardLimitTableOffset != 0)
+ result = get_hard_limits(hwmgr,
+ &pp_table_information->max_clock_voltage_on_dc, pHardLimits);
+
+ hwmgr->dyn_state.max_clock_voltage_on_dc.sclk =
+ pp_table_information->max_clock_voltage_on_dc.sclk;
+ hwmgr->dyn_state.max_clock_voltage_on_dc.mclk =
+ pp_table_information->max_clock_voltage_on_dc.mclk;
+ hwmgr->dyn_state.max_clock_voltage_on_dc.vddc =
+ pp_table_information->max_clock_voltage_on_dc.vddc;
+ hwmgr->dyn_state.max_clock_voltage_on_dc.vddci =
+ pp_table_information->max_clock_voltage_on_dc.vddci;
+
+ if (result == 0 && (NULL != pp_table_information->vdd_dep_on_mclk)
+ && (0 != pp_table_information->vdd_dep_on_mclk->count))
+ result = get_valid_clk(hwmgr, &pp_table_information->valid_mclk_values,
+ pp_table_information->vdd_dep_on_mclk);
+
+ if (result == 0 && (NULL != pp_table_information->vdd_dep_on_sclk)
+ && (0 != pp_table_information->vdd_dep_on_sclk->count))
+ result = get_valid_clk(hwmgr, &pp_table_information->valid_sclk_values,
+ pp_table_information->vdd_dep_on_sclk);
+
+ if (!result && gpio_table)
+ result = get_gpio_table(hwmgr, &pp_table_information->gpio_table,
+ gpio_table);
+
+ return result;
+}
+
+/** Retrieves the (signed) Overdrive limits from VBIOS.
+ * The max engine clock, memory clock and max temperature come from the firmware info table.
+ *
+ * The information is placed into the platform descriptor.
+ *
+ * @param hwmgr source of the VBIOS table and owner of the platform descriptor to be updated.
+ * @param powerplay_table the address of the PowerPlay table.
+ *
+ * @return 1 as long as the firmware info table was present and of a supported version.
+ */
+static int init_over_drive_limits(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table)
+{
+ hwmgr->platform_descriptor.overdriveLimit.engineClock =
+ le32_to_cpu(powerplay_table->ulMaxODEngineClock);
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock =
+ le32_to_cpu(powerplay_table->ulMaxODMemoryClock);
+
+ hwmgr->platform_descriptor.minOverdriveVDDC = 0;
+ hwmgr->platform_descriptor.maxOverdriveVDDC = 0;
+ hwmgr->platform_descriptor.overdriveVDDCStep = 0;
+
+ return 0;
+}
+
+/**
+ * Private Function used during initialization.
+ * Inspect the PowerPlay table for obvious signs of corruption.
+ * @param hwmgr Pointer to the hardware manager.
+ * @param powerplay_table Pointer to the PowerPlay Table.
+ * @exception This implementation always returns 1.
+ */
+static int init_thermal_controller(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table
+ )
+{
+ const PPTable_Generic_SubTable_Header *fan_table;
+ ATOM_Tonga_Thermal_Controller *thermal_controller;
+
+ thermal_controller = (ATOM_Tonga_Thermal_Controller *)
+ (((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usThermalControllerOffset));
+ PP_ASSERT_WITH_CODE((0 != powerplay_table->usThermalControllerOffset),
+ "Thermal controller table not set!", return -1);
+
+ hwmgr->thermal_controller.ucType = thermal_controller->ucType;
+ hwmgr->thermal_controller.ucI2cLine = thermal_controller->ucI2cLine;
+ hwmgr->thermal_controller.ucI2cAddress = thermal_controller->ucI2cAddress;
+
+ hwmgr->thermal_controller.fanInfo.bNoFan =
+ (0 != (thermal_controller->ucFanParameters & ATOM_TONGA_PP_FANPARAMETERS_NOFAN));
+
+ hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution =
+ thermal_controller->ucFanParameters &
+ ATOM_TONGA_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK;
+
+ hwmgr->thermal_controller.fanInfo.ulMinRPM
+ = thermal_controller->ucFanMinRPM * 100UL;
+ hwmgr->thermal_controller.fanInfo.ulMaxRPM
+ = thermal_controller->ucFanMaxRPM * 100UL;
+
+ set_hw_cap(
+ hwmgr,
+ ATOM_TONGA_PP_THERMALCONTROLLER_NONE != hwmgr->thermal_controller.ucType,
+ PHM_PlatformCaps_ThermalController
+ );
+
+ if (0 == powerplay_table->usFanTableOffset) {
+ hwmgr->thermal_controller.use_hw_fan_control = 1;
+ return 0;
+ }
+
+ fan_table = (const PPTable_Generic_SubTable_Header *)
+ (((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usFanTableOffset));
+
+ PP_ASSERT_WITH_CODE((0 != powerplay_table->usFanTableOffset),
+ "Fan table not set!", return -1);
+ PP_ASSERT_WITH_CODE((0 < fan_table->ucRevId),
+ "Unsupported fan table format!", return -1);
+
+ hwmgr->thermal_controller.advanceFanControlParameters.ulCycleDelay
+ = 100000;
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+
+ if (fan_table->ucRevId < 8) {
+ const ATOM_Tonga_Fan_Table *tonga_fan_table =
+ (ATOM_Tonga_Fan_Table *)fan_table;
+ hwmgr->thermal_controller.advanceFanControlParameters.ucTHyst
+ = tonga_fan_table->ucTHyst;
+ hwmgr->thermal_controller.advanceFanControlParameters.usTMin
+ = le16_to_cpu(tonga_fan_table->usTMin);
+ hwmgr->thermal_controller.advanceFanControlParameters.usTMed
+ = le16_to_cpu(tonga_fan_table->usTMed);
+ hwmgr->thermal_controller.advanceFanControlParameters.usTHigh
+ = le16_to_cpu(tonga_fan_table->usTHigh);
+ hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin
+ = le16_to_cpu(tonga_fan_table->usPWMMin);
+ hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed
+ = le16_to_cpu(tonga_fan_table->usPWMMed);
+ hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh
+ = le16_to_cpu(tonga_fan_table->usPWMHigh);
+ hwmgr->thermal_controller.advanceFanControlParameters.usTMax
+ = 10900; /* hard coded */
+ hwmgr->thermal_controller.advanceFanControlParameters.usTMax
+ = le16_to_cpu(tonga_fan_table->usTMax);
+ hwmgr->thermal_controller.advanceFanControlParameters.ucFanControlMode
+ = tonga_fan_table->ucFanControlMode;
+ hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanPWM
+ = le16_to_cpu(tonga_fan_table->usFanPWMMax);
+ hwmgr->thermal_controller.advanceFanControlParameters.usDefaultFanOutputSensitivity
+ = 4836;
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity
+ = le16_to_cpu(tonga_fan_table->usFanOutputSensitivity);
+ hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanRPM
+ = le16_to_cpu(tonga_fan_table->usFanRPMMax);
+ hwmgr->thermal_controller.advanceFanControlParameters.ulMinFanSCLKAcousticLimit
+ = (le32_to_cpu(tonga_fan_table->ulMinFanSCLKAcousticLimit) / 100); /* PPTable stores it in 10Khz unit for 2 decimal places. SMC wants MHz. */
+ hwmgr->thermal_controller.advanceFanControlParameters.ucTargetTemperature
+ = tonga_fan_table->ucTargetTemperature;
+ hwmgr->thermal_controller.advanceFanControlParameters.ucMinimumPWMLimit
+ = tonga_fan_table->ucMinimumPWMLimit;
+ } else {
+ const ATOM_Fiji_Fan_Table *fiji_fan_table =
+ (ATOM_Fiji_Fan_Table *)fan_table;
+ hwmgr->thermal_controller.advanceFanControlParameters.ucTHyst
+ = fiji_fan_table->ucTHyst;
+ hwmgr->thermal_controller.advanceFanControlParameters.usTMin
+ = le16_to_cpu(fiji_fan_table->usTMin);
+ hwmgr->thermal_controller.advanceFanControlParameters.usTMed
+ = le16_to_cpu(fiji_fan_table->usTMed);
+ hwmgr->thermal_controller.advanceFanControlParameters.usTHigh
+ = le16_to_cpu(fiji_fan_table->usTHigh);
+ hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin
+ = le16_to_cpu(fiji_fan_table->usPWMMin);
+ hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed
+ = le16_to_cpu(fiji_fan_table->usPWMMed);
+ hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh
+ = le16_to_cpu(fiji_fan_table->usPWMHigh);
+ hwmgr->thermal_controller.advanceFanControlParameters.usTMax
+ = le16_to_cpu(fiji_fan_table->usTMax);
+ hwmgr->thermal_controller.advanceFanControlParameters.ucFanControlMode
+ = fiji_fan_table->ucFanControlMode;
+ hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanPWM
+ = le16_to_cpu(fiji_fan_table->usFanPWMMax);
+ hwmgr->thermal_controller.advanceFanControlParameters.usDefaultFanOutputSensitivity
+ = 4836;
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity
+ = le16_to_cpu(fiji_fan_table->usFanOutputSensitivity);
+ hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanRPM
+ = le16_to_cpu(fiji_fan_table->usFanRPMMax);
+ hwmgr->thermal_controller.advanceFanControlParameters.ulMinFanSCLKAcousticLimit
+ = (le32_to_cpu(fiji_fan_table->ulMinFanSCLKAcousticLimit) / 100); /* PPTable stores it in 10Khz unit for 2 decimal places. SMC wants MHz. */
+ hwmgr->thermal_controller.advanceFanControlParameters.ucTargetTemperature
+ = fiji_fan_table->ucTargetTemperature;
+ hwmgr->thermal_controller.advanceFanControlParameters.ucMinimumPWMLimit
+ = fiji_fan_table->ucMinimumPWMLimit;
+
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainEdge
+ = le16_to_cpu(fiji_fan_table->usFanGainEdge);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainHotspot
+ = le16_to_cpu(fiji_fan_table->usFanGainHotspot);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainLiquid
+ = le16_to_cpu(fiji_fan_table->usFanGainLiquid);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainVrVddc
+ = le16_to_cpu(fiji_fan_table->usFanGainVrVddc);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainVrMvdd
+ = le16_to_cpu(fiji_fan_table->usFanGainVrMvdd);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainPlx
+ = le16_to_cpu(fiji_fan_table->usFanGainPlx);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainHbm
+ = le16_to_cpu(fiji_fan_table->usFanGainHbm);
+ }
+
+ return 0;
+}
+
+/**
+ * Private Function used during initialization.
+ * Inspect the PowerPlay table for obvious signs of corruption.
+ * @param hwmgr Pointer to the hardware manager.
+ * @param powerplay_table Pointer to the PowerPlay Table.
+ * @exception 2 if the powerplay table is incorrect.
+ */
+static int check_powerplay_tables(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table
+ )
+{
+ const ATOM_Tonga_State_Array *state_arrays;
+
+ state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usStateArrayOffset));
+
+ PP_ASSERT_WITH_CODE((ATOM_Tonga_TABLE_REVISION_TONGA <=
+ powerplay_table->sHeader.ucTableFormatRevision),
+ "Unsupported PPTable format!", return -1);
+ PP_ASSERT_WITH_CODE((0 != powerplay_table->usStateArrayOffset),
+ "State table is not set!", return -1);
+ PP_ASSERT_WITH_CODE((0 < powerplay_table->sHeader.usStructureSize),
+ "Invalid PowerPlay Table!", return -1);
+ PP_ASSERT_WITH_CODE((0 < state_arrays->ucNumEntries),
+ "Invalid PowerPlay Table!", return -1);
+
+ return 0;
+}
+
+static int pp_tables_v1_0_initialize(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table;
+
+ hwmgr->pptable = kzalloc(sizeof(struct phm_ppt_v1_information), GFP_KERNEL);
+
+ PP_ASSERT_WITH_CODE((NULL != hwmgr->pptable),
+ "Failed to allocate hwmgr->pptable!", return -ENOMEM);
+
+ powerplay_table = get_powerplay_table(hwmgr);
+
+ PP_ASSERT_WITH_CODE((NULL != powerplay_table),
+ "Missing PowerPlay Table!", return -1);
+
+ result = check_powerplay_tables(hwmgr, powerplay_table);
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "check_powerplay_tables failed", return result);
+
+ result = set_platform_caps(hwmgr,
+ le32_to_cpu(powerplay_table->ulPlatformCaps));
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "set_platform_caps failed", return result);
+
+ result = init_thermal_controller(hwmgr, powerplay_table);
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "init_thermal_controller failed", return result);
+
+ result = init_over_drive_limits(hwmgr, powerplay_table);
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "init_over_drive_limits failed", return result);
+
+ result = init_clock_voltage_dependency(hwmgr, powerplay_table);
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "init_clock_voltage_dependency failed", return result);
+
+ result = init_dpm_2_parameters(hwmgr, powerplay_table);
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "init_dpm_2_parameters failed", return result);
+
+ return result;
+}
+
+static int pp_tables_v1_0_uninitialize(struct pp_hwmgr *hwmgr)
+{
+ struct phm_ppt_v1_information *pp_table_information =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ kfree(pp_table_information->vdd_dep_on_sclk);
+ pp_table_information->vdd_dep_on_sclk = NULL;
+
+ kfree(pp_table_information->vdd_dep_on_mclk);
+ pp_table_information->vdd_dep_on_mclk = NULL;
+
+ kfree(pp_table_information->valid_mclk_values);
+ pp_table_information->valid_mclk_values = NULL;
+
+ kfree(pp_table_information->valid_sclk_values);
+ pp_table_information->valid_sclk_values = NULL;
+
+ kfree(pp_table_information->vddc_lookup_table);
+ pp_table_information->vddc_lookup_table = NULL;
+
+ kfree(pp_table_information->vddgfx_lookup_table);
+ pp_table_information->vddgfx_lookup_table = NULL;
+
+ kfree(pp_table_information->mm_dep_table);
+ pp_table_information->mm_dep_table = NULL;
+
+ kfree(pp_table_information->cac_dtp_table);
+ pp_table_information->cac_dtp_table = NULL;
+
+ kfree(hwmgr->dyn_state.cac_dtp_table);
+ hwmgr->dyn_state.cac_dtp_table = NULL;
+
+ kfree(pp_table_information->ppm_parameter_table);
+ pp_table_information->ppm_parameter_table = NULL;
+
+ kfree(pp_table_information->pcie_table);
+ pp_table_information->pcie_table = NULL;
+
+ kfree(pp_table_information->gpio_table);
+ pp_table_information->gpio_table = NULL;
+
+ kfree(hwmgr->pptable);
+ hwmgr->pptable = NULL;
+
+ return 0;
+}
+
+const struct pp_table_func pptable_v1_0_funcs = {
+ .pptable_init = pp_tables_v1_0_initialize,
+ .pptable_fini = pp_tables_v1_0_uninitialize,
+};
+
+int get_number_of_powerplay_table_entries_v1_0(struct pp_hwmgr *hwmgr)
+{
+ ATOM_Tonga_State_Array const *state_arrays;
+ const ATOM_Tonga_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr);
+
+ PP_ASSERT_WITH_CODE((NULL != pp_table),
+ "Missing PowerPlay Table!", return -1);
+ PP_ASSERT_WITH_CODE((pp_table->sHeader.ucTableFormatRevision >=
+ ATOM_Tonga_TABLE_REVISION_TONGA),
+ "Incorrect PowerPlay table revision!", return -1);
+
+ state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)pp_table) +
+ le16_to_cpu(pp_table->usStateArrayOffset));
+
+ return (uint32_t)(state_arrays->ucNumEntries);
+}
+
+/**
+* Private function to convert flags stored in the BIOS to software flags in PowerPlay.
+*/
+static uint32_t make_classification_flags(struct pp_hwmgr *hwmgr,
+ uint16_t classification, uint16_t classification2)
+{
+ uint32_t result = 0;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_BOOT)
+ result |= PP_StateClassificationFlag_Boot;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_THERMAL)
+ result |= PP_StateClassificationFlag_Thermal;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE)
+ result |= PP_StateClassificationFlag_LimitedPowerSource;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_REST)
+ result |= PP_StateClassificationFlag_Rest;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_FORCED)
+ result |= PP_StateClassificationFlag_Forced;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_ACPI)
+ result |= PP_StateClassificationFlag_ACPI;
+
+ if (classification2 & ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2)
+ result |= PP_StateClassificationFlag_LimitedPowerSource_2;
+
+ return result;
+}
+
+static int ppt_get_num_of_vce_state_table_entries_v1_0(struct pp_hwmgr *hwmgr)
+{
+ const ATOM_Tonga_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr);
+ const ATOM_Tonga_VCE_State_Table *vce_state_table;
+
+
+ if (pp_table == NULL)
+ return 0;
+
+ vce_state_table = (void *)pp_table +
+ le16_to_cpu(pp_table->usVCEStateTableOffset);
+
+ return vce_state_table->ucNumEntries;
+}
+
+static int ppt_get_vce_state_table_entry_v1_0(struct pp_hwmgr *hwmgr, uint32_t i,
+ struct amd_vce_state *vce_state, void **clock_info, uint32_t *flag)
+{
+ const ATOM_Tonga_VCE_State_Record *vce_state_record;
+ ATOM_Tonga_SCLK_Dependency_Record *sclk_dep_record;
+ ATOM_Tonga_MCLK_Dependency_Record *mclk_dep_record;
+ ATOM_Tonga_MM_Dependency_Record *mm_dep_record;
+ const ATOM_Tonga_POWERPLAYTABLE *pptable = get_powerplay_table(hwmgr);
+ const ATOM_Tonga_VCE_State_Table *vce_state_table = (ATOM_Tonga_VCE_State_Table *)(((unsigned long)pptable)
+ + le16_to_cpu(pptable->usVCEStateTableOffset));
+ const ATOM_Tonga_SCLK_Dependency_Table *sclk_dep_table = (ATOM_Tonga_SCLK_Dependency_Table *)(((unsigned long)pptable)
+ + le16_to_cpu(pptable->usSclkDependencyTableOffset));
+ const ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table = (ATOM_Tonga_MCLK_Dependency_Table *)(((unsigned long)pptable)
+ + le16_to_cpu(pptable->usMclkDependencyTableOffset));
+ const ATOM_Tonga_MM_Dependency_Table *mm_dep_table = (ATOM_Tonga_MM_Dependency_Table *)(((unsigned long)pptable)
+ + le16_to_cpu(pptable->usMMDependencyTableOffset));
+
+ PP_ASSERT_WITH_CODE((i < vce_state_table->ucNumEntries),
+ "Requested state entry ID is out of range!",
+ return -EINVAL);
+
+ vce_state_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ ATOM_Tonga_VCE_State_Record,
+ entries, vce_state_table, i);
+ sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ ATOM_Tonga_SCLK_Dependency_Record,
+ entries, sclk_dep_table,
+ vce_state_record->ucSCLKIndex);
+ mm_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ ATOM_Tonga_MM_Dependency_Record,
+ entries, mm_dep_table,
+ vce_state_record->ucVCEClockIndex);
+ *flag = vce_state_record->ucFlag;
+
+ vce_state->evclk = le32_to_cpu(mm_dep_record->ulEClk);
+ vce_state->ecclk = le32_to_cpu(mm_dep_record->ulEClk);
+ vce_state->sclk = le32_to_cpu(sclk_dep_record->ulSclk);
+
+ if (vce_state_record->ucMCLKIndex >= mclk_dep_table->ucNumEntries)
+ mclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ ATOM_Tonga_MCLK_Dependency_Record,
+ entries, mclk_dep_table,
+ mclk_dep_table->ucNumEntries - 1);
+ else
+ mclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ ATOM_Tonga_MCLK_Dependency_Record,
+ entries, mclk_dep_table,
+ vce_state_record->ucMCLKIndex);
+
+ vce_state->mclk = le32_to_cpu(mclk_dep_record->ulMclk);
+ return 0;
+}
+
+/**
+* Create a Power State out of an entry in the PowerPlay table.
+* This function is called by the hardware back-end.
+* @param hwmgr Pointer to the hardware manager.
+* @param entry_index The index of the entry to be extracted from the table.
+* @param power_state The address of the PowerState instance being created.
+* @return -1 if the entry cannot be retrieved.
+*/
+int get_powerplay_table_entry_v1_0(struct pp_hwmgr *hwmgr,
+ uint32_t entry_index, struct pp_power_state *power_state,
+ int (*call_back_func)(struct pp_hwmgr *, void *,
+ struct pp_power_state *, void *, uint32_t))
+{
+ int result = 0;
+ const ATOM_Tonga_State_Array *state_arrays;
+ const ATOM_Tonga_State *state_entry;
+ const ATOM_Tonga_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr);
+ int i, j;
+ uint32_t flags = 0;
+
+ PP_ASSERT_WITH_CODE((NULL != pp_table), "Missing PowerPlay Table!", return -1;);
+ power_state->classification.bios_index = entry_index;
+
+ if (pp_table->sHeader.ucTableFormatRevision >=
+ ATOM_Tonga_TABLE_REVISION_TONGA) {
+ state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)pp_table) +
+ le16_to_cpu(pp_table->usStateArrayOffset));
+
+ PP_ASSERT_WITH_CODE((0 < pp_table->usStateArrayOffset),
+ "Invalid PowerPlay Table State Array Offset.", return -1);
+ PP_ASSERT_WITH_CODE((0 < state_arrays->ucNumEntries),
+ "Invalid PowerPlay Table State Array.", return -1);
+ PP_ASSERT_WITH_CODE((entry_index <= state_arrays->ucNumEntries),
+ "Invalid PowerPlay Table State Array Entry.", return -1);
+
+ state_entry = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ ATOM_Tonga_State, entries,
+ state_arrays, entry_index);
+
+ result = call_back_func(hwmgr, (void *)state_entry, power_state,
+ (void *)pp_table,
+ make_classification_flags(hwmgr,
+ le16_to_cpu(state_entry->usClassification),
+ le16_to_cpu(state_entry->usClassification2)));
+ }
+
+ if (!result && (power_state->classification.flags &
+ PP_StateClassificationFlag_Boot))
+ result = hwmgr->hwmgr_func->patch_boot_state(hwmgr, &(power_state->hardware));
+
+ hwmgr->num_vce_state_tables = i = ppt_get_num_of_vce_state_table_entries_v1_0(hwmgr);
+
+ if ((i != 0) && (i <= AMD_MAX_VCE_LEVELS)) {
+ for (j = 0; j < i; j++)
+ ppt_get_vce_state_table_entry_v1_0(hwmgr, j, &(hwmgr->vce_states[j]), NULL, &flags);
+ }
+
+ return result;
+}
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.h
new file mode 100644
index 000000000000..b9710abdff01
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _PROCESSPPTABLES_V1_0_H
+#define _PROCESSPPTABLES_V1_0_H
+
+#include "hwmgr.h"
+
+extern const struct pp_table_func pptable_v1_0_funcs;
+extern int get_number_of_powerplay_table_entries_v1_0(struct pp_hwmgr *hwmgr);
+extern int get_powerplay_table_entry_v1_0(struct pp_hwmgr *hwmgr, uint32_t entry_index,
+ struct pp_power_state *power_state, int (*call_back_func)(struct pp_hwmgr *, void *,
+ struct pp_power_state *, void *, uint32_t));
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c
new file mode 100644
index 000000000000..719597c5d27d
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c
@@ -0,0 +1,1732 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "pp_debug.h"
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <drm/amdgpu_drm.h>
+#include "processpptables.h"
+#include <atom-types.h>
+#include <atombios.h>
+#include "pptable.h"
+#include "power_state.h"
+#include "hwmgr.h"
+#include "hardwaremanager.h"
+
+
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2 12
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3 14
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4 16
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V5 18
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6 20
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7 22
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V8 24
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V9 26
+
+#define NUM_BITS_CLOCK_INFO_ARRAY_INDEX 6
+
+static uint16_t get_vce_table_offset(struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ uint16_t vce_table_offset = 0;
+
+ if (le16_to_cpu(powerplay_table->usTableSize) >=
+ sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) {
+ const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 =
+ (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table;
+
+ if (powerplay_table3->usExtendendedHeaderOffset > 0) {
+ const ATOM_PPLIB_EXTENDEDHEADER *extended_header =
+ (const ATOM_PPLIB_EXTENDEDHEADER *)
+ (((unsigned long)powerplay_table3) +
+ le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset));
+ if (le16_to_cpu(extended_header->usSize) >=
+ SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2)
+ vce_table_offset = le16_to_cpu(extended_header->usVCETableOffset);
+ }
+ }
+
+ return vce_table_offset;
+}
+
+static uint16_t get_vce_clock_info_array_offset(struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ uint16_t table_offset = get_vce_table_offset(hwmgr,
+ powerplay_table);
+
+ if (table_offset > 0)
+ return table_offset + 1;
+
+ return 0;
+}
+
+static uint16_t get_vce_clock_info_array_size(struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ uint16_t table_offset = get_vce_clock_info_array_offset(hwmgr,
+ powerplay_table);
+ uint16_t table_size = 0;
+
+ if (table_offset > 0) {
+ const VCEClockInfoArray *p = (const VCEClockInfoArray *)
+ (((unsigned long) powerplay_table) + table_offset);
+ table_size = sizeof(uint8_t) + p->ucNumEntries * sizeof(VCEClockInfo);
+ }
+
+ return table_size;
+}
+
+static uint16_t get_vce_clock_voltage_limit_table_offset(struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ uint16_t table_offset = get_vce_clock_info_array_offset(hwmgr,
+ powerplay_table);
+
+ if (table_offset > 0)
+ return table_offset + get_vce_clock_info_array_size(hwmgr,
+ powerplay_table);
+
+ return 0;
+}
+
+static uint16_t get_vce_clock_voltage_limit_table_size(struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ uint16_t table_offset = get_vce_clock_voltage_limit_table_offset(hwmgr, powerplay_table);
+ uint16_t table_size = 0;
+
+ if (table_offset > 0) {
+ const ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *ptable =
+ (const ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *)(((unsigned long) powerplay_table) + table_offset);
+
+ table_size = sizeof(uint8_t) + ptable->numEntries * sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record);
+ }
+ return table_size;
+}
+
+static uint16_t get_vce_state_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ uint16_t table_offset = get_vce_clock_voltage_limit_table_offset(hwmgr, powerplay_table);
+
+ if (table_offset > 0)
+ return table_offset + get_vce_clock_voltage_limit_table_size(hwmgr, powerplay_table);
+
+ return 0;
+}
+
+static const ATOM_PPLIB_VCE_State_Table *get_vce_state_table(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ uint16_t table_offset = get_vce_state_table_offset(hwmgr, powerplay_table);
+
+ if (table_offset > 0)
+ return (const ATOM_PPLIB_VCE_State_Table *)(((unsigned long) powerplay_table) + table_offset);
+
+ return NULL;
+}
+
+static uint16_t get_uvd_table_offset(struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ uint16_t uvd_table_offset = 0;
+
+ if (le16_to_cpu(powerplay_table->usTableSize) >=
+ sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) {
+ const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 =
+ (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table;
+ if (powerplay_table3->usExtendendedHeaderOffset > 0) {
+ const ATOM_PPLIB_EXTENDEDHEADER *extended_header =
+ (const ATOM_PPLIB_EXTENDEDHEADER *)
+ (((unsigned long)powerplay_table3) +
+ le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset));
+ if (le16_to_cpu(extended_header->usSize) >=
+ SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3)
+ uvd_table_offset = le16_to_cpu(extended_header->usUVDTableOffset);
+ }
+ }
+ return uvd_table_offset;
+}
+
+static uint16_t get_uvd_clock_info_array_offset(struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ uint16_t table_offset = get_uvd_table_offset(hwmgr,
+ powerplay_table);
+
+ if (table_offset > 0)
+ return table_offset + 1;
+ return 0;
+}
+
+static uint16_t get_uvd_clock_info_array_size(struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ uint16_t table_offset = get_uvd_clock_info_array_offset(hwmgr,
+ powerplay_table);
+ uint16_t table_size = 0;
+
+ if (table_offset > 0) {
+ const UVDClockInfoArray *p = (const UVDClockInfoArray *)
+ (((unsigned long) powerplay_table)
+ + table_offset);
+ table_size = sizeof(UCHAR) +
+ p->ucNumEntries * sizeof(UVDClockInfo);
+ }
+
+ return table_size;
+}
+
+static uint16_t get_uvd_clock_voltage_limit_table_offset(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ uint16_t table_offset = get_uvd_clock_info_array_offset(hwmgr,
+ powerplay_table);
+
+ if (table_offset > 0)
+ return table_offset +
+ get_uvd_clock_info_array_size(hwmgr, powerplay_table);
+
+ return 0;
+}
+
+static uint16_t get_samu_table_offset(struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ uint16_t samu_table_offset = 0;
+
+ if (le16_to_cpu(powerplay_table->usTableSize) >=
+ sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) {
+ const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 =
+ (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table;
+ if (powerplay_table3->usExtendendedHeaderOffset > 0) {
+ const ATOM_PPLIB_EXTENDEDHEADER *extended_header =
+ (const ATOM_PPLIB_EXTENDEDHEADER *)
+ (((unsigned long)powerplay_table3) +
+ le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset));
+ if (le16_to_cpu(extended_header->usSize) >=
+ SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4)
+ samu_table_offset = le16_to_cpu(extended_header->usSAMUTableOffset);
+ }
+ }
+
+ return samu_table_offset;
+}
+
+static uint16_t get_samu_clock_voltage_limit_table_offset(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ uint16_t table_offset = get_samu_table_offset(hwmgr,
+ powerplay_table);
+
+ if (table_offset > 0)
+ return table_offset + 1;
+
+ return 0;
+}
+
+static uint16_t get_acp_table_offset(struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ uint16_t acp_table_offset = 0;
+
+ if (le16_to_cpu(powerplay_table->usTableSize) >=
+ sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) {
+ const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 =
+ (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table;
+ if (powerplay_table3->usExtendendedHeaderOffset > 0) {
+ const ATOM_PPLIB_EXTENDEDHEADER *pExtendedHeader =
+ (const ATOM_PPLIB_EXTENDEDHEADER *)
+ (((unsigned long)powerplay_table3) +
+ le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset));
+ if (le16_to_cpu(pExtendedHeader->usSize) >=
+ SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6)
+ acp_table_offset = le16_to_cpu(pExtendedHeader->usACPTableOffset);
+ }
+ }
+
+ return acp_table_offset;
+}
+
+static uint16_t get_acp_clock_voltage_limit_table_offset(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ uint16_t tableOffset = get_acp_table_offset(hwmgr, powerplay_table);
+
+ if (tableOffset > 0)
+ return tableOffset + 1;
+
+ return 0;
+}
+
+static uint16_t get_cacp_tdp_table_offset(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ uint16_t cacTdpTableOffset = 0;
+
+ if (le16_to_cpu(powerplay_table->usTableSize) >=
+ sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) {
+ const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 =
+ (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table;
+ if (powerplay_table3->usExtendendedHeaderOffset > 0) {
+ const ATOM_PPLIB_EXTENDEDHEADER *pExtendedHeader =
+ (const ATOM_PPLIB_EXTENDEDHEADER *)
+ (((unsigned long)powerplay_table3) +
+ le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset));
+ if (le16_to_cpu(pExtendedHeader->usSize) >=
+ SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7)
+ cacTdpTableOffset = le16_to_cpu(pExtendedHeader->usPowerTuneTableOffset);
+ }
+ }
+
+ return cacTdpTableOffset;
+}
+
+static int get_cac_tdp_table(struct pp_hwmgr *hwmgr,
+ struct phm_cac_tdp_table **ptable,
+ const ATOM_PowerTune_Table *table,
+ uint16_t us_maximum_power_delivery_limit)
+{
+ unsigned long table_size;
+ struct phm_cac_tdp_table *tdp_table;
+
+ table_size = sizeof(unsigned long) + sizeof(struct phm_cac_tdp_table);
+
+ tdp_table = kzalloc(table_size, GFP_KERNEL);
+ if (NULL == tdp_table)
+ return -ENOMEM;
+
+ tdp_table->usTDP = le16_to_cpu(table->usTDP);
+ tdp_table->usConfigurableTDP = le16_to_cpu(table->usConfigurableTDP);
+ tdp_table->usTDC = le16_to_cpu(table->usTDC);
+ tdp_table->usBatteryPowerLimit = le16_to_cpu(table->usBatteryPowerLimit);
+ tdp_table->usSmallPowerLimit = le16_to_cpu(table->usSmallPowerLimit);
+ tdp_table->usLowCACLeakage = le16_to_cpu(table->usLowCACLeakage);
+ tdp_table->usHighCACLeakage = le16_to_cpu(table->usHighCACLeakage);
+ tdp_table->usMaximumPowerDeliveryLimit = us_maximum_power_delivery_limit;
+
+ *ptable = tdp_table;
+
+ return 0;
+}
+
+static uint16_t get_sclk_vdd_gfx_table_offset(struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ uint16_t sclk_vdd_gfx_table_offset = 0;
+
+ if (le16_to_cpu(powerplay_table->usTableSize) >=
+ sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) {
+ const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 =
+ (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table;
+ if (powerplay_table3->usExtendendedHeaderOffset > 0) {
+ const ATOM_PPLIB_EXTENDEDHEADER *pExtendedHeader =
+ (const ATOM_PPLIB_EXTENDEDHEADER *)
+ (((unsigned long)powerplay_table3) +
+ le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset));
+ if (le16_to_cpu(pExtendedHeader->usSize) >=
+ SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V8)
+ sclk_vdd_gfx_table_offset =
+ le16_to_cpu(pExtendedHeader->usSclkVddgfxTableOffset);
+ }
+ }
+
+ return sclk_vdd_gfx_table_offset;
+}
+
+static uint16_t get_sclk_vdd_gfx_clock_voltage_dependency_table_offset(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ uint16_t tableOffset = get_sclk_vdd_gfx_table_offset(hwmgr, powerplay_table);
+
+ if (tableOffset > 0)
+ return tableOffset;
+
+ return 0;
+}
+
+
+static int get_clock_voltage_dependency_table(struct pp_hwmgr *hwmgr,
+ struct phm_clock_voltage_dependency_table **ptable,
+ const ATOM_PPLIB_Clock_Voltage_Dependency_Table *table)
+{
+
+ unsigned long table_size, i;
+ struct phm_clock_voltage_dependency_table *dep_table;
+
+ table_size = sizeof(unsigned long) +
+ sizeof(struct phm_clock_voltage_dependency_table)
+ * table->ucNumEntries;
+
+ dep_table = kzalloc(table_size, GFP_KERNEL);
+ if (NULL == dep_table)
+ return -ENOMEM;
+
+ dep_table->count = (unsigned long)table->ucNumEntries;
+
+ for (i = 0; i < dep_table->count; i++) {
+ dep_table->entries[i].clk =
+ ((unsigned long)table->entries[i].ucClockHigh << 16) |
+ le16_to_cpu(table->entries[i].usClockLow);
+ dep_table->entries[i].v =
+ (unsigned long)le16_to_cpu(table->entries[i].usVoltage);
+ }
+
+ *ptable = dep_table;
+
+ return 0;
+}
+
+static int get_valid_clk(struct pp_hwmgr *hwmgr,
+ struct phm_clock_array **ptable,
+ const struct phm_clock_voltage_dependency_table *table)
+{
+ unsigned long table_size, i;
+ struct phm_clock_array *clock_table;
+
+ table_size = sizeof(unsigned long) + sizeof(unsigned long) * table->count;
+ clock_table = kzalloc(table_size, GFP_KERNEL);
+ if (NULL == clock_table)
+ return -ENOMEM;
+
+ clock_table->count = (unsigned long)table->count;
+
+ for (i = 0; i < clock_table->count; i++)
+ clock_table->values[i] = (unsigned long)table->entries[i].clk;
+
+ *ptable = clock_table;
+
+ return 0;
+}
+
+static int get_clock_voltage_limit(struct pp_hwmgr *hwmgr,
+ struct phm_clock_and_voltage_limits *limits,
+ const ATOM_PPLIB_Clock_Voltage_Limit_Table *table)
+{
+ limits->sclk = ((unsigned long)table->entries[0].ucSclkHigh << 16) |
+ le16_to_cpu(table->entries[0].usSclkLow);
+ limits->mclk = ((unsigned long)table->entries[0].ucMclkHigh << 16) |
+ le16_to_cpu(table->entries[0].usMclkLow);
+ limits->vddc = (unsigned long)le16_to_cpu(table->entries[0].usVddc);
+ limits->vddci = (unsigned long)le16_to_cpu(table->entries[0].usVddci);
+
+ return 0;
+}
+
+
+static void set_hw_cap(struct pp_hwmgr *hwmgr, bool enable,
+ enum phm_platform_caps cap)
+{
+ if (enable)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, cap);
+ else
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps, cap);
+}
+
+static int set_platform_caps(struct pp_hwmgr *hwmgr,
+ unsigned long powerplay_caps)
+{
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_POWERPLAY),
+ PHM_PlatformCaps_PowerPlaySupport
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_SBIOSPOWERSOURCE),
+ PHM_PlatformCaps_BiosPowerSourceControl
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_ASPM_L0s),
+ PHM_PlatformCaps_EnableASPML0s
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_ASPM_L1),
+ PHM_PlatformCaps_EnableASPML1
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS),
+ PHM_PlatformCaps_EnableBackbias
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC),
+ PHM_PlatformCaps_AutomaticDCTransition
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_GEMINIPRIMARY),
+ PHM_PlatformCaps_GeminiPrimary
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC),
+ PHM_PlatformCaps_StepVddc
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_VOLTAGECONTROL),
+ PHM_PlatformCaps_EnableVoltageControl
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_SIDEPORTCONTROL),
+ PHM_PlatformCaps_EnableSideportControl
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_TURNOFFPLL_ASPML1),
+ PHM_PlatformCaps_TurnOffPll_ASPML1
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_HTLINKCONTROL),
+ PHM_PlatformCaps_EnableHTLinkControl
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_MVDDCONTROL),
+ PHM_PlatformCaps_EnableMVDDControl
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_VDDCI_CONTROL),
+ PHM_PlatformCaps_ControlVDDCI
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_REGULATOR_HOT),
+ PHM_PlatformCaps_RegulatorHot
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_GOTO_BOOT_ON_ALERT),
+ PHM_PlatformCaps_BootStateOnAlert
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_DONT_WAIT_FOR_VBLANK_ON_ALERT),
+ PHM_PlatformCaps_DontWaitForVBlankOnAlert
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_BACO),
+ PHM_PlatformCaps_BACO
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_NEW_CAC_VOLTAGE),
+ PHM_PlatformCaps_NewCACVoltage
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_REVERT_GPIO5_POLARITY),
+ PHM_PlatformCaps_RevertGPIO5Polarity
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_OUTPUT_THERMAL2GPIO17),
+ PHM_PlatformCaps_Thermal2GPIO17
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_VRHOT_GPIO_CONFIGURABLE),
+ PHM_PlatformCaps_VRHotGPIOConfigurable
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_TEMP_INVERSION),
+ PHM_PlatformCaps_TempInversion
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_EVV),
+ PHM_PlatformCaps_EVV
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_COMBINE_PCC_WITH_THERMAL_SIGNAL),
+ PHM_PlatformCaps_CombinePCCWithThermalSignal
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_LOAD_POST_PRODUCTION_FIRMWARE),
+ PHM_PlatformCaps_LoadPostProductionFirmware
+ );
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_PP_PLATFORM_CAP_DISABLE_USING_ACTUAL_TEMPERATURE_FOR_POWER_CALC),
+ PHM_PlatformCaps_DisableUsingActualTemperatureForPowerCalc
+ );
+
+ return 0;
+}
+
+static PP_StateClassificationFlags make_classification_flags(
+ struct pp_hwmgr *hwmgr,
+ USHORT classification,
+ USHORT classification2)
+{
+ PP_StateClassificationFlags result = 0;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_BOOT)
+ result |= PP_StateClassificationFlag_Boot;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_THERMAL)
+ result |= PP_StateClassificationFlag_Thermal;
+
+ if (classification &
+ ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE)
+ result |= PP_StateClassificationFlag_LimitedPowerSource;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_REST)
+ result |= PP_StateClassificationFlag_Rest;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_FORCED)
+ result |= PP_StateClassificationFlag_Forced;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE)
+ result |= PP_StateClassificationFlag_3DPerformance;
+
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_OVERDRIVETEMPLATE)
+ result |= PP_StateClassificationFlag_ACOverdriveTemplate;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
+ result |= PP_StateClassificationFlag_Uvd;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_HDSTATE)
+ result |= PP_StateClassificationFlag_UvdHD;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_SDSTATE)
+ result |= PP_StateClassificationFlag_UvdSD;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_HD2STATE)
+ result |= PP_StateClassificationFlag_HD2;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_ACPI)
+ result |= PP_StateClassificationFlag_ACPI;
+
+ if (classification2 & ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2)
+ result |= PP_StateClassificationFlag_LimitedPowerSource_2;
+
+
+ if (classification2 & ATOM_PPLIB_CLASSIFICATION2_ULV)
+ result |= PP_StateClassificationFlag_ULV;
+
+ if (classification2 & ATOM_PPLIB_CLASSIFICATION2_MVC)
+ result |= PP_StateClassificationFlag_UvdMVC;
+
+ return result;
+}
+
+static int init_non_clock_fields(struct pp_hwmgr *hwmgr,
+ struct pp_power_state *ps,
+ uint8_t version,
+ const ATOM_PPLIB_NONCLOCK_INFO *pnon_clock_info) {
+ unsigned long rrr_index;
+ unsigned long tmp;
+
+ ps->classification.ui_label = (le16_to_cpu(pnon_clock_info->usClassification) &
+ ATOM_PPLIB_CLASSIFICATION_UI_MASK) >> ATOM_PPLIB_CLASSIFICATION_UI_SHIFT;
+ ps->classification.flags = make_classification_flags(hwmgr,
+ le16_to_cpu(pnon_clock_info->usClassification),
+ le16_to_cpu(pnon_clock_info->usClassification2));
+
+ ps->classification.temporary_state = false;
+ ps->classification.to_be_deleted = false;
+ tmp = le32_to_cpu(pnon_clock_info->ulCapsAndSettings) &
+ ATOM_PPLIB_SINGLE_DISPLAY_ONLY;
+
+ ps->validation.singleDisplayOnly = (0 != tmp);
+
+ tmp = le32_to_cpu(pnon_clock_info->ulCapsAndSettings) &
+ ATOM_PPLIB_DISALLOW_ON_DC;
+
+ ps->validation.disallowOnDC = (0 != tmp);
+
+ ps->pcie.lanes = ((le32_to_cpu(pnon_clock_info->ulCapsAndSettings) &
+ ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >>
+ ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT) + 1;
+
+ ps->pcie.lanes = 0;
+
+ ps->display.disableFrameModulation = false;
+
+ rrr_index = (le32_to_cpu(pnon_clock_info->ulCapsAndSettings) &
+ ATOM_PPLIB_LIMITED_REFRESHRATE_VALUE_MASK) >>
+ ATOM_PPLIB_LIMITED_REFRESHRATE_VALUE_SHIFT;
+
+ if (rrr_index != ATOM_PPLIB_LIMITED_REFRESHRATE_UNLIMITED) {
+ static const uint8_t look_up[(ATOM_PPLIB_LIMITED_REFRESHRATE_VALUE_MASK >> ATOM_PPLIB_LIMITED_REFRESHRATE_VALUE_SHIFT) + 1] = \
+ { 0, 50, 0 };
+
+ ps->display.refreshrateSource = PP_RefreshrateSource_Explicit;
+ ps->display.explicitRefreshrate = look_up[rrr_index];
+ ps->display.limitRefreshrate = true;
+
+ if (ps->display.explicitRefreshrate == 0)
+ ps->display.limitRefreshrate = false;
+ } else
+ ps->display.limitRefreshrate = false;
+
+ tmp = le32_to_cpu(pnon_clock_info->ulCapsAndSettings) &
+ ATOM_PPLIB_ENABLE_VARIBRIGHT;
+
+ ps->display.enableVariBright = (0 != tmp);
+
+ tmp = le32_to_cpu(pnon_clock_info->ulCapsAndSettings) &
+ ATOM_PPLIB_SWSTATE_MEMORY_DLL_OFF;
+
+ ps->memory.dllOff = (0 != tmp);
+
+ ps->memory.m3arb = (le32_to_cpu(pnon_clock_info->ulCapsAndSettings) &
+ ATOM_PPLIB_M3ARB_MASK) >> ATOM_PPLIB_M3ARB_SHIFT;
+
+ ps->temperatures.min = PP_TEMPERATURE_UNITS_PER_CENTIGRADES *
+ pnon_clock_info->ucMinTemperature;
+
+ ps->temperatures.max = PP_TEMPERATURE_UNITS_PER_CENTIGRADES *
+ pnon_clock_info->ucMaxTemperature;
+
+ tmp = le32_to_cpu(pnon_clock_info->ulCapsAndSettings) &
+ ATOM_PPLIB_SOFTWARE_DISABLE_LOADBALANCING;
+
+ ps->software.disableLoadBalancing = tmp;
+
+ tmp = le32_to_cpu(pnon_clock_info->ulCapsAndSettings) &
+ ATOM_PPLIB_SOFTWARE_ENABLE_SLEEP_FOR_TIMESTAMPS;
+
+ ps->software.enableSleepForTimestamps = (0 != tmp);
+
+ ps->validation.supportedPowerLevels = pnon_clock_info->ucRequiredPower;
+
+ if (ATOM_PPLIB_NONCLOCKINFO_VER1 < version) {
+ ps->uvd_clocks.VCLK = le32_to_cpu(pnon_clock_info->ulVCLK);
+ ps->uvd_clocks.DCLK = le32_to_cpu(pnon_clock_info->ulDCLK);
+ } else {
+ ps->uvd_clocks.VCLK = 0;
+ ps->uvd_clocks.DCLK = 0;
+ }
+
+ return 0;
+}
+
+static ULONG size_of_entry_v2(ULONG num_dpm_levels)
+{
+ return (sizeof(UCHAR) + sizeof(UCHAR) +
+ (num_dpm_levels * sizeof(UCHAR)));
+}
+
+static const ATOM_PPLIB_STATE_V2 *get_state_entry_v2(
+ const StateArray * pstate_arrays,
+ ULONG entry_index)
+{
+ ULONG i;
+ const ATOM_PPLIB_STATE_V2 *pstate;
+
+ pstate = pstate_arrays->states;
+ if (entry_index <= pstate_arrays->ucNumEntries) {
+ for (i = 0; i < entry_index; i++)
+ pstate = (ATOM_PPLIB_STATE_V2 *)(
+ (unsigned long)pstate +
+ size_of_entry_v2(pstate->ucNumDPMLevels));
+ }
+ return pstate;
+}
+
+static const unsigned char soft_dummy_pp_table[] = {
+ 0xe1, 0x01, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x4a, 0x00, 0x6c, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x42, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x4e, 0x00, 0x88, 0x00, 0x00, 0x9e, 0x00, 0x17, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x18, 0x05, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x43, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x8e, 0x01, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 0x08, 0x30, 0x75, 0x00, 0x80, 0x00, 0xa0, 0x8c,
+ 0x00, 0x7e, 0x00, 0x71, 0xa5, 0x00, 0x7c, 0x00, 0xe5, 0xc8, 0x00, 0x70, 0x00, 0x91, 0xf4, 0x00,
+ 0x64, 0x00, 0x40, 0x19, 0x01, 0x5a, 0x00, 0x0e, 0x28, 0x01, 0x52, 0x00, 0x80, 0x38, 0x01, 0x4a,
+ 0x00, 0x00, 0x09, 0x30, 0x75, 0x00, 0x30, 0x75, 0x00, 0x40, 0x9c, 0x00, 0x40, 0x9c, 0x00, 0x59,
+ 0xd8, 0x00, 0x59, 0xd8, 0x00, 0x91, 0xf4, 0x00, 0x91, 0xf4, 0x00, 0x0e, 0x28, 0x01, 0x0e, 0x28,
+ 0x01, 0x90, 0x5f, 0x01, 0x90, 0x5f, 0x01, 0x00, 0x77, 0x01, 0x00, 0x77, 0x01, 0xca, 0x91, 0x01,
+ 0xca, 0x91, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x7e, 0x00, 0x01,
+ 0x7c, 0x00, 0x02, 0x70, 0x00, 0x03, 0x64, 0x00, 0x04, 0x5a, 0x00, 0x05, 0x52, 0x00, 0x06, 0x4a,
+ 0x00, 0x07, 0x08, 0x08, 0x00, 0x08, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x03,
+ 0x02, 0x04, 0x02, 0x00, 0x08, 0x40, 0x9c, 0x00, 0x30, 0x75, 0x00, 0x74, 0xb5, 0x00, 0xa0, 0x8c,
+ 0x00, 0x60, 0xea, 0x00, 0x74, 0xb5, 0x00, 0x0e, 0x28, 0x01, 0x60, 0xea, 0x00, 0x90, 0x5f, 0x01,
+ 0x40, 0x19, 0x01, 0xb2, 0xb0, 0x01, 0x90, 0x5f, 0x01, 0xc0, 0xd4, 0x01, 0x00, 0x77, 0x01, 0x5e,
+ 0xff, 0x01, 0xca, 0x91, 0x01, 0x08, 0x80, 0x00, 0x00, 0x7e, 0x00, 0x01, 0x7c, 0x00, 0x02, 0x70,
+ 0x00, 0x03, 0x64, 0x00, 0x04, 0x5a, 0x00, 0x05, 0x52, 0x00, 0x06, 0x4a, 0x00, 0x07, 0x00, 0x08,
+ 0x80, 0x00, 0x30, 0x75, 0x00, 0x7e, 0x00, 0x40, 0x9c, 0x00, 0x7c, 0x00, 0x59, 0xd8, 0x00, 0x70,
+ 0x00, 0xdc, 0x0b, 0x01, 0x64, 0x00, 0x80, 0x38, 0x01, 0x5a, 0x00, 0x80, 0x38, 0x01, 0x52, 0x00,
+ 0x80, 0x38, 0x01, 0x4a, 0x00, 0x80, 0x38, 0x01, 0x08, 0x30, 0x75, 0x00, 0x80, 0x00, 0xa0, 0x8c,
+ 0x00, 0x7e, 0x00, 0x71, 0xa5, 0x00, 0x7c, 0x00, 0xe5, 0xc8, 0x00, 0x74, 0x00, 0x91, 0xf4, 0x00,
+ 0x66, 0x00, 0x40, 0x19, 0x01, 0x58, 0x00, 0x0e, 0x28, 0x01, 0x52, 0x00, 0x80, 0x38, 0x01, 0x4a,
+ 0x00
+};
+
+static const ATOM_PPLIB_POWERPLAYTABLE *get_powerplay_table(
+ struct pp_hwmgr *hwmgr)
+{
+ const void *table_addr = hwmgr->soft_pp_table;
+ uint8_t frev, crev;
+ uint16_t size;
+
+ if (!table_addr) {
+ if (hwmgr->chip_id == CHIP_RAVEN) {
+ table_addr = &soft_dummy_pp_table[0];
+ hwmgr->soft_pp_table = &soft_dummy_pp_table[0];
+ hwmgr->soft_pp_table_size = sizeof(soft_dummy_pp_table);
+ } else {
+ table_addr = smu_atom_get_data_table(hwmgr->adev,
+ GetIndexIntoMasterTable(DATA, PowerPlayInfo),
+ &size, &frev, &crev);
+ hwmgr->soft_pp_table = table_addr;
+ hwmgr->soft_pp_table_size = size;
+ }
+ }
+
+ return (const ATOM_PPLIB_POWERPLAYTABLE *)table_addr;
+}
+
+int pp_tables_get_response_times(struct pp_hwmgr *hwmgr,
+ uint32_t *vol_rep_time, uint32_t *bb_rep_time)
+{
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_tab = get_powerplay_table(hwmgr);
+
+ PP_ASSERT_WITH_CODE(NULL != powerplay_tab,
+ "Missing PowerPlay Table!", return -EINVAL);
+
+ *vol_rep_time = (uint32_t)le16_to_cpu(powerplay_tab->usVoltageTime);
+ *bb_rep_time = (uint32_t)le16_to_cpu(powerplay_tab->usBackbiasTime);
+
+ return 0;
+}
+
+int pp_tables_get_num_of_entries(struct pp_hwmgr *hwmgr,
+ unsigned long *num_of_entries)
+{
+ const StateArray *pstate_arrays;
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table = get_powerplay_table(hwmgr);
+
+ if (powerplay_table == NULL)
+ return -1;
+
+ if (powerplay_table->sHeader.ucTableFormatRevision >= 6) {
+ pstate_arrays = (StateArray *)(((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usStateArrayOffset));
+
+ *num_of_entries = (unsigned long)(pstate_arrays->ucNumEntries);
+ } else
+ *num_of_entries = (unsigned long)(powerplay_table->ucNumStates);
+
+ return 0;
+}
+
+int pp_tables_get_entry(struct pp_hwmgr *hwmgr,
+ unsigned long entry_index,
+ struct pp_power_state *ps,
+ pp_tables_hw_clock_info_callback func)
+{
+ int i;
+ const StateArray *pstate_arrays;
+ const ATOM_PPLIB_STATE_V2 *pstate_entry_v2;
+ const ATOM_PPLIB_NONCLOCK_INFO *pnon_clock_info;
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table = get_powerplay_table(hwmgr);
+ int result = 0;
+ int res = 0;
+
+ const ClockInfoArray *pclock_arrays;
+
+ const NonClockInfoArray *pnon_clock_arrays;
+
+ const ATOM_PPLIB_STATE *pstate_entry;
+
+ if (powerplay_table == NULL)
+ return -1;
+
+ ps->classification.bios_index = entry_index;
+
+ if (powerplay_table->sHeader.ucTableFormatRevision >= 6) {
+ pstate_arrays = (StateArray *)(((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usStateArrayOffset));
+
+ if (entry_index > pstate_arrays->ucNumEntries)
+ return -1;
+
+ pstate_entry_v2 = get_state_entry_v2(pstate_arrays, entry_index);
+ pclock_arrays = (ClockInfoArray *)(((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usClockInfoArrayOffset));
+
+ pnon_clock_arrays = (NonClockInfoArray *)(((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset));
+
+ pnon_clock_info = (ATOM_PPLIB_NONCLOCK_INFO *)((unsigned long)(pnon_clock_arrays->nonClockInfo) +
+ (pstate_entry_v2->nonClockInfoIndex * pnon_clock_arrays->ucEntrySize));
+
+ result = init_non_clock_fields(hwmgr, ps, pnon_clock_arrays->ucEntrySize, pnon_clock_info);
+
+ for (i = 0; i < pstate_entry_v2->ucNumDPMLevels; i++) {
+ const void *pclock_info = (const void *)(
+ (unsigned long)(pclock_arrays->clockInfo) +
+ (pstate_entry_v2->clockInfoIndex[i] * pclock_arrays->ucEntrySize));
+ res = func(hwmgr, &ps->hardware, i, pclock_info);
+ if ((0 == result) && (0 != res))
+ result = res;
+ }
+ } else {
+ if (entry_index > powerplay_table->ucNumStates)
+ return -1;
+
+ pstate_entry = (ATOM_PPLIB_STATE *)((unsigned long)powerplay_table +
+ le16_to_cpu(powerplay_table->usStateArrayOffset) +
+ entry_index * powerplay_table->ucStateEntrySize);
+
+ pnon_clock_info = (ATOM_PPLIB_NONCLOCK_INFO *)((unsigned long)powerplay_table +
+ le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset) +
+ pstate_entry->ucNonClockStateIndex *
+ powerplay_table->ucNonClockSize);
+
+ result = init_non_clock_fields(hwmgr, ps,
+ powerplay_table->ucNonClockSize,
+ pnon_clock_info);
+
+ for (i = 0; i < powerplay_table->ucStateEntrySize-1; i++) {
+ const void *pclock_info = (const void *)((unsigned long)powerplay_table +
+ le16_to_cpu(powerplay_table->usClockInfoArrayOffset) +
+ pstate_entry->ucClockStateIndices[i] *
+ powerplay_table->ucClockInfoSize);
+
+ int res = func(hwmgr, &ps->hardware, i, pclock_info);
+
+ if ((0 == result) && (0 != res))
+ result = res;
+ }
+ }
+
+ if ((0 == result) && (0 != (ps->classification.flags & PP_StateClassificationFlag_Boot))) {
+ if (hwmgr->chip_family < AMDGPU_FAMILY_RV)
+ result = hwmgr->hwmgr_func->patch_boot_state(hwmgr, &(ps->hardware));
+ }
+
+ return result;
+}
+
+static int init_powerplay_tables(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table
+)
+{
+ return 0;
+}
+
+
+static int init_thermal_controller(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ hwmgr->thermal_controller.ucType =
+ powerplay_table->sThermalController.ucType;
+ hwmgr->thermal_controller.ucI2cLine =
+ powerplay_table->sThermalController.ucI2cLine;
+ hwmgr->thermal_controller.ucI2cAddress =
+ powerplay_table->sThermalController.ucI2cAddress;
+
+ hwmgr->thermal_controller.fanInfo.bNoFan =
+ (0 != (powerplay_table->sThermalController.ucFanParameters &
+ ATOM_PP_FANPARAMETERS_NOFAN));
+
+ hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution =
+ powerplay_table->sThermalController.ucFanParameters &
+ ATOM_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK;
+
+ hwmgr->thermal_controller.fanInfo.ulMinRPM
+ = powerplay_table->sThermalController.ucFanMinRPM * 100UL;
+ hwmgr->thermal_controller.fanInfo.ulMaxRPM
+ = powerplay_table->sThermalController.ucFanMaxRPM * 100UL;
+
+ set_hw_cap(hwmgr,
+ ATOM_PP_THERMALCONTROLLER_NONE != hwmgr->thermal_controller.ucType,
+ PHM_PlatformCaps_ThermalController);
+
+ hwmgr->thermal_controller.use_hw_fan_control = 1;
+
+ return 0;
+}
+
+static int init_overdrive_limits_V1_4(struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table,
+ const ATOM_FIRMWARE_INFO_V1_4 *fw_info)
+{
+ hwmgr->platform_descriptor.overdriveLimit.engineClock =
+ le32_to_cpu(fw_info->ulASICMaxEngineClock);
+
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock =
+ le32_to_cpu(fw_info->ulASICMaxMemoryClock);
+
+ hwmgr->platform_descriptor.maxOverdriveVDDC =
+ le32_to_cpu(fw_info->ul3DAccelerationEngineClock) & 0x7FF;
+
+ hwmgr->platform_descriptor.minOverdriveVDDC =
+ le16_to_cpu(fw_info->usBootUpVDDCVoltage);
+
+ hwmgr->platform_descriptor.maxOverdriveVDDC =
+ le16_to_cpu(fw_info->usBootUpVDDCVoltage);
+
+ hwmgr->platform_descriptor.overdriveVDDCStep = 0;
+ return 0;
+}
+
+static int init_overdrive_limits_V2_1(struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table,
+ const ATOM_FIRMWARE_INFO_V2_1 *fw_info)
+{
+ const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3;
+ const ATOM_PPLIB_EXTENDEDHEADER *header;
+
+ if (le16_to_cpu(powerplay_table->usTableSize) <
+ sizeof(ATOM_PPLIB_POWERPLAYTABLE3))
+ return 0;
+
+ powerplay_table3 = (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table;
+
+ if (0 == powerplay_table3->usExtendendedHeaderOffset)
+ return 0;
+
+ header = (ATOM_PPLIB_EXTENDEDHEADER *)(((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset));
+
+ hwmgr->platform_descriptor.overdriveLimit.engineClock = le32_to_cpu(header->ulMaxEngineClock);
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock = le32_to_cpu(header->ulMaxMemoryClock);
+
+
+ hwmgr->platform_descriptor.minOverdriveVDDC = 0;
+ hwmgr->platform_descriptor.maxOverdriveVDDC = 0;
+ hwmgr->platform_descriptor.overdriveVDDCStep = 0;
+
+ return 0;
+}
+
+static int init_overdrive_limits(struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ int result = 0;
+ uint8_t frev, crev;
+ uint16_t size;
+
+ const ATOM_COMMON_TABLE_HEADER *fw_info = NULL;
+
+ hwmgr->platform_descriptor.overdriveLimit.engineClock = 0;
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock = 0;
+ hwmgr->platform_descriptor.minOverdriveVDDC = 0;
+ hwmgr->platform_descriptor.maxOverdriveVDDC = 0;
+ hwmgr->platform_descriptor.overdriveVDDCStep = 0;
+
+ if (hwmgr->chip_id == CHIP_RAVEN)
+ return 0;
+
+ /* We assume here that fw_info is unchanged if this call fails.*/
+ fw_info = smu_atom_get_data_table(hwmgr->adev,
+ GetIndexIntoMasterTable(DATA, FirmwareInfo),
+ &size, &frev, &crev);
+
+ if ((fw_info->ucTableFormatRevision == 1)
+ && (le16_to_cpu(fw_info->usStructureSize) >= sizeof(ATOM_FIRMWARE_INFO_V1_4)))
+ result = init_overdrive_limits_V1_4(hwmgr,
+ powerplay_table,
+ (const ATOM_FIRMWARE_INFO_V1_4 *)fw_info);
+
+ else if ((fw_info->ucTableFormatRevision == 2)
+ && (le16_to_cpu(fw_info->usStructureSize) >= sizeof(ATOM_FIRMWARE_INFO_V2_1)))
+ result = init_overdrive_limits_V2_1(hwmgr,
+ powerplay_table,
+ (const ATOM_FIRMWARE_INFO_V2_1 *)fw_info);
+
+ return result;
+}
+
+static int get_uvd_clock_voltage_limit_table(struct pp_hwmgr *hwmgr,
+ struct phm_uvd_clock_voltage_dependency_table **ptable,
+ const ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table *table,
+ const UVDClockInfoArray *array)
+{
+ unsigned long table_size, i;
+ struct phm_uvd_clock_voltage_dependency_table *uvd_table;
+
+ table_size = sizeof(unsigned long) +
+ sizeof(struct phm_uvd_clock_voltage_dependency_table) *
+ table->numEntries;
+
+ uvd_table = kzalloc(table_size, GFP_KERNEL);
+ if (NULL == uvd_table)
+ return -ENOMEM;
+
+ uvd_table->count = table->numEntries;
+
+ for (i = 0; i < table->numEntries; i++) {
+ const UVDClockInfo *entry =
+ &array->entries[table->entries[i].ucUVDClockInfoIndex];
+ uvd_table->entries[i].v = (unsigned long)le16_to_cpu(table->entries[i].usVoltage);
+ uvd_table->entries[i].vclk = ((unsigned long)entry->ucVClkHigh << 16)
+ | le16_to_cpu(entry->usVClkLow);
+ uvd_table->entries[i].dclk = ((unsigned long)entry->ucDClkHigh << 16)
+ | le16_to_cpu(entry->usDClkLow);
+ }
+
+ *ptable = uvd_table;
+
+ return 0;
+}
+
+static int get_vce_clock_voltage_limit_table(struct pp_hwmgr *hwmgr,
+ struct phm_vce_clock_voltage_dependency_table **ptable,
+ const ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *table,
+ const VCEClockInfoArray *array)
+{
+ unsigned long table_size, i;
+ struct phm_vce_clock_voltage_dependency_table *vce_table = NULL;
+
+ table_size = sizeof(unsigned long) +
+ sizeof(struct phm_vce_clock_voltage_dependency_table)
+ * table->numEntries;
+
+ vce_table = kzalloc(table_size, GFP_KERNEL);
+ if (NULL == vce_table)
+ return -ENOMEM;
+
+ vce_table->count = table->numEntries;
+ for (i = 0; i < table->numEntries; i++) {
+ const VCEClockInfo *entry = &array->entries[table->entries[i].ucVCEClockInfoIndex];
+
+ vce_table->entries[i].v = (unsigned long)le16_to_cpu(table->entries[i].usVoltage);
+ vce_table->entries[i].evclk = ((unsigned long)entry->ucEVClkHigh << 16)
+ | le16_to_cpu(entry->usEVClkLow);
+ vce_table->entries[i].ecclk = ((unsigned long)entry->ucECClkHigh << 16)
+ | le16_to_cpu(entry->usECClkLow);
+ }
+
+ *ptable = vce_table;
+
+ return 0;
+}
+
+static int get_samu_clock_voltage_limit_table(struct pp_hwmgr *hwmgr,
+ struct phm_samu_clock_voltage_dependency_table **ptable,
+ const ATOM_PPLIB_SAMClk_Voltage_Limit_Table *table)
+{
+ unsigned long table_size, i;
+ struct phm_samu_clock_voltage_dependency_table *samu_table;
+
+ table_size = sizeof(unsigned long) +
+ sizeof(struct phm_samu_clock_voltage_dependency_table) *
+ table->numEntries;
+
+ samu_table = kzalloc(table_size, GFP_KERNEL);
+ if (NULL == samu_table)
+ return -ENOMEM;
+
+ samu_table->count = table->numEntries;
+
+ for (i = 0; i < table->numEntries; i++) {
+ samu_table->entries[i].v = (unsigned long)le16_to_cpu(table->entries[i].usVoltage);
+ samu_table->entries[i].samclk = ((unsigned long)table->entries[i].ucSAMClockHigh << 16)
+ | le16_to_cpu(table->entries[i].usSAMClockLow);
+ }
+
+ *ptable = samu_table;
+
+ return 0;
+}
+
+static int get_acp_clock_voltage_limit_table(struct pp_hwmgr *hwmgr,
+ struct phm_acp_clock_voltage_dependency_table **ptable,
+ const ATOM_PPLIB_ACPClk_Voltage_Limit_Table *table)
+{
+ unsigned table_size, i;
+ struct phm_acp_clock_voltage_dependency_table *acp_table;
+
+ table_size = sizeof(unsigned long) +
+ sizeof(struct phm_acp_clock_voltage_dependency_table) *
+ table->numEntries;
+
+ acp_table = kzalloc(table_size, GFP_KERNEL);
+ if (NULL == acp_table)
+ return -ENOMEM;
+
+ acp_table->count = (unsigned long)table->numEntries;
+
+ for (i = 0; i < table->numEntries; i++) {
+ acp_table->entries[i].v = (unsigned long)le16_to_cpu(table->entries[i].usVoltage);
+ acp_table->entries[i].acpclk = ((unsigned long)table->entries[i].ucACPClockHigh << 16)
+ | le16_to_cpu(table->entries[i].usACPClockLow);
+ }
+
+ *ptable = acp_table;
+
+ return 0;
+}
+
+static int init_clock_voltage_dependency(struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ ATOM_PPLIB_Clock_Voltage_Dependency_Table *table;
+ ATOM_PPLIB_Clock_Voltage_Limit_Table *limit_table;
+ int result = 0;
+
+ uint16_t vce_clock_info_array_offset;
+ uint16_t uvd_clock_info_array_offset;
+ uint16_t table_offset;
+
+ hwmgr->dyn_state.vddc_dependency_on_sclk = NULL;
+ hwmgr->dyn_state.vddci_dependency_on_mclk = NULL;
+ hwmgr->dyn_state.vddc_dependency_on_mclk = NULL;
+ hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL;
+ hwmgr->dyn_state.mvdd_dependency_on_mclk = NULL;
+ hwmgr->dyn_state.vce_clock_voltage_dependency_table = NULL;
+ hwmgr->dyn_state.uvd_clock_voltage_dependency_table = NULL;
+ hwmgr->dyn_state.samu_clock_voltage_dependency_table = NULL;
+ hwmgr->dyn_state.acp_clock_voltage_dependency_table = NULL;
+ hwmgr->dyn_state.ppm_parameter_table = NULL;
+ hwmgr->dyn_state.vdd_gfx_dependency_on_sclk = NULL;
+
+ vce_clock_info_array_offset = get_vce_clock_info_array_offset(
+ hwmgr, powerplay_table);
+ table_offset = get_vce_clock_voltage_limit_table_offset(hwmgr,
+ powerplay_table);
+ if (vce_clock_info_array_offset > 0 && table_offset > 0) {
+ const VCEClockInfoArray *array = (const VCEClockInfoArray *)
+ (((unsigned long) powerplay_table) +
+ vce_clock_info_array_offset);
+ const ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *table =
+ (const ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *)
+ (((unsigned long) powerplay_table) + table_offset);
+ result = get_vce_clock_voltage_limit_table(hwmgr,
+ &hwmgr->dyn_state.vce_clock_voltage_dependency_table,
+ table, array);
+ }
+
+ uvd_clock_info_array_offset = get_uvd_clock_info_array_offset(hwmgr, powerplay_table);
+ table_offset = get_uvd_clock_voltage_limit_table_offset(hwmgr, powerplay_table);
+
+ if (uvd_clock_info_array_offset > 0 && table_offset > 0) {
+ const UVDClockInfoArray *array = (const UVDClockInfoArray *)
+ (((unsigned long) powerplay_table) +
+ uvd_clock_info_array_offset);
+ const ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table *ptable =
+ (const ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table *)
+ (((unsigned long) powerplay_table) + table_offset);
+ result = get_uvd_clock_voltage_limit_table(hwmgr,
+ &hwmgr->dyn_state.uvd_clock_voltage_dependency_table, ptable, array);
+ }
+
+ table_offset = get_samu_clock_voltage_limit_table_offset(hwmgr,
+ powerplay_table);
+
+ if (table_offset > 0) {
+ const ATOM_PPLIB_SAMClk_Voltage_Limit_Table *ptable =
+ (const ATOM_PPLIB_SAMClk_Voltage_Limit_Table *)
+ (((unsigned long) powerplay_table) + table_offset);
+ result = get_samu_clock_voltage_limit_table(hwmgr,
+ &hwmgr->dyn_state.samu_clock_voltage_dependency_table, ptable);
+ }
+
+ table_offset = get_acp_clock_voltage_limit_table_offset(hwmgr,
+ powerplay_table);
+
+ if (table_offset > 0) {
+ const ATOM_PPLIB_ACPClk_Voltage_Limit_Table *ptable =
+ (const ATOM_PPLIB_ACPClk_Voltage_Limit_Table *)
+ (((unsigned long) powerplay_table) + table_offset);
+ result = get_acp_clock_voltage_limit_table(hwmgr,
+ &hwmgr->dyn_state.acp_clock_voltage_dependency_table, ptable);
+ }
+
+ table_offset = get_cacp_tdp_table_offset(hwmgr, powerplay_table);
+ if (table_offset > 0) {
+ UCHAR rev_id = *(UCHAR *)(((unsigned long)powerplay_table) + table_offset);
+
+ if (rev_id > 0) {
+ const ATOM_PPLIB_POWERTUNE_Table_V1 *tune_table =
+ (const ATOM_PPLIB_POWERTUNE_Table_V1 *)
+ (((unsigned long) powerplay_table) + table_offset);
+ result = get_cac_tdp_table(hwmgr, &hwmgr->dyn_state.cac_dtp_table,
+ &tune_table->power_tune_table,
+ le16_to_cpu(tune_table->usMaximumPowerDeliveryLimit));
+ hwmgr->dyn_state.cac_dtp_table->usDefaultTargetOperatingTemp =
+ le16_to_cpu(tune_table->usTjMax);
+ } else {
+ const ATOM_PPLIB_POWERTUNE_Table *tune_table =
+ (const ATOM_PPLIB_POWERTUNE_Table *)
+ (((unsigned long) powerplay_table) + table_offset);
+ result = get_cac_tdp_table(hwmgr,
+ &hwmgr->dyn_state.cac_dtp_table,
+ &tune_table->power_tune_table, 255);
+ }
+ }
+
+ if (le16_to_cpu(powerplay_table->usTableSize) >=
+ sizeof(ATOM_PPLIB_POWERPLAYTABLE4)) {
+ const ATOM_PPLIB_POWERPLAYTABLE4 *powerplay_table4 =
+ (const ATOM_PPLIB_POWERPLAYTABLE4 *)powerplay_table;
+ if (0 != powerplay_table4->usVddcDependencyOnSCLKOffset) {
+ table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *)
+ (((unsigned long) powerplay_table4) +
+ le16_to_cpu(powerplay_table4->usVddcDependencyOnSCLKOffset));
+ result = get_clock_voltage_dependency_table(hwmgr,
+ &hwmgr->dyn_state.vddc_dependency_on_sclk, table);
+ }
+
+ if (result == 0 && (0 != powerplay_table4->usVddciDependencyOnMCLKOffset)) {
+ table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *)
+ (((unsigned long) powerplay_table4) +
+ le16_to_cpu(powerplay_table4->usVddciDependencyOnMCLKOffset));
+ result = get_clock_voltage_dependency_table(hwmgr,
+ &hwmgr->dyn_state.vddci_dependency_on_mclk, table);
+ }
+
+ if (result == 0 && (0 != powerplay_table4->usVddcDependencyOnMCLKOffset)) {
+ table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *)
+ (((unsigned long) powerplay_table4) +
+ le16_to_cpu(powerplay_table4->usVddcDependencyOnMCLKOffset));
+ result = get_clock_voltage_dependency_table(hwmgr,
+ &hwmgr->dyn_state.vddc_dependency_on_mclk, table);
+ }
+
+ if (result == 0 && (0 != powerplay_table4->usMaxClockVoltageOnDCOffset)) {
+ limit_table = (ATOM_PPLIB_Clock_Voltage_Limit_Table *)
+ (((unsigned long) powerplay_table4) +
+ le16_to_cpu(powerplay_table4->usMaxClockVoltageOnDCOffset));
+ result = get_clock_voltage_limit(hwmgr,
+ &hwmgr->dyn_state.max_clock_voltage_on_dc, limit_table);
+ }
+
+ if (result == 0 && (NULL != hwmgr->dyn_state.vddc_dependency_on_mclk) &&
+ (0 != hwmgr->dyn_state.vddc_dependency_on_mclk->count))
+ result = get_valid_clk(hwmgr, &hwmgr->dyn_state.valid_mclk_values,
+ hwmgr->dyn_state.vddc_dependency_on_mclk);
+
+ if(result == 0 && (NULL != hwmgr->dyn_state.vddc_dependency_on_sclk) &&
+ (0 != hwmgr->dyn_state.vddc_dependency_on_sclk->count))
+ result = get_valid_clk(hwmgr,
+ &hwmgr->dyn_state.valid_sclk_values,
+ hwmgr->dyn_state.vddc_dependency_on_sclk);
+
+ if (result == 0 && (0 != powerplay_table4->usMvddDependencyOnMCLKOffset)) {
+ table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *)
+ (((unsigned long) powerplay_table4) +
+ le16_to_cpu(powerplay_table4->usMvddDependencyOnMCLKOffset));
+ result = get_clock_voltage_dependency_table(hwmgr,
+ &hwmgr->dyn_state.mvdd_dependency_on_mclk, table);
+ }
+ }
+
+ table_offset = get_sclk_vdd_gfx_clock_voltage_dependency_table_offset(hwmgr,
+ powerplay_table);
+
+ if (table_offset > 0) {
+ table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *)
+ (((unsigned long) powerplay_table) + table_offset);
+ result = get_clock_voltage_dependency_table(hwmgr,
+ &hwmgr->dyn_state.vdd_gfx_dependency_on_sclk, table);
+ }
+
+ return result;
+}
+
+static int get_cac_leakage_table(struct pp_hwmgr *hwmgr,
+ struct phm_cac_leakage_table **ptable,
+ const ATOM_PPLIB_CAC_Leakage_Table *table)
+{
+ struct phm_cac_leakage_table *cac_leakage_table;
+ unsigned long table_size, i;
+
+ if (hwmgr == NULL || table == NULL || ptable == NULL)
+ return -EINVAL;
+
+ table_size = sizeof(ULONG) +
+ (sizeof(struct phm_cac_leakage_table) * table->ucNumEntries);
+
+ cac_leakage_table = kzalloc(table_size, GFP_KERNEL);
+
+ if (cac_leakage_table == NULL)
+ return -ENOMEM;
+
+ cac_leakage_table->count = (ULONG)table->ucNumEntries;
+
+ for (i = 0; i < cac_leakage_table->count; i++) {
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EVV)) {
+ cac_leakage_table->entries[i].Vddc1 = le16_to_cpu(table->entries[i].usVddc1);
+ cac_leakage_table->entries[i].Vddc2 = le16_to_cpu(table->entries[i].usVddc2);
+ cac_leakage_table->entries[i].Vddc3 = le16_to_cpu(table->entries[i].usVddc3);
+ } else {
+ cac_leakage_table->entries[i].Vddc = le16_to_cpu(table->entries[i].usVddc);
+ cac_leakage_table->entries[i].Leakage = le32_to_cpu(table->entries[i].ulLeakageValue);
+ }
+ }
+
+ *ptable = cac_leakage_table;
+
+ return 0;
+}
+
+static int get_platform_power_management_table(struct pp_hwmgr *hwmgr,
+ ATOM_PPLIB_PPM_Table *atom_ppm_table)
+{
+ struct phm_ppm_table *ptr = kzalloc(sizeof(struct phm_ppm_table), GFP_KERNEL);
+
+ if (NULL == ptr)
+ return -ENOMEM;
+
+ ptr->ppm_design = atom_ppm_table->ucPpmDesign;
+ ptr->cpu_core_number = le16_to_cpu(atom_ppm_table->usCpuCoreNumber);
+ ptr->platform_tdp = le32_to_cpu(atom_ppm_table->ulPlatformTDP);
+ ptr->small_ac_platform_tdp = le32_to_cpu(atom_ppm_table->ulSmallACPlatformTDP);
+ ptr->platform_tdc = le32_to_cpu(atom_ppm_table->ulPlatformTDC);
+ ptr->small_ac_platform_tdc = le32_to_cpu(atom_ppm_table->ulSmallACPlatformTDC);
+ ptr->apu_tdp = le32_to_cpu(atom_ppm_table->ulApuTDP);
+ ptr->dgpu_tdp = le32_to_cpu(atom_ppm_table->ulDGpuTDP);
+ ptr->dgpu_ulv_power = le32_to_cpu(atom_ppm_table->ulDGpuUlvPower);
+ ptr->tj_max = le32_to_cpu(atom_ppm_table->ulTjmax);
+ hwmgr->dyn_state.ppm_parameter_table = ptr;
+
+ return 0;
+}
+
+static int init_dpm2_parameters(struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ int result = 0;
+
+ if (le16_to_cpu(powerplay_table->usTableSize) >=
+ sizeof(ATOM_PPLIB_POWERPLAYTABLE5)) {
+ const ATOM_PPLIB_POWERPLAYTABLE5 *ptable5 =
+ (const ATOM_PPLIB_POWERPLAYTABLE5 *)powerplay_table;
+ const ATOM_PPLIB_POWERPLAYTABLE4 *ptable4 =
+ (const ATOM_PPLIB_POWERPLAYTABLE4 *)
+ (&ptable5->basicTable4);
+ const ATOM_PPLIB_POWERPLAYTABLE3 *ptable3 =
+ (const ATOM_PPLIB_POWERPLAYTABLE3 *)
+ (&ptable4->basicTable3);
+ const ATOM_PPLIB_EXTENDEDHEADER *extended_header;
+ uint16_t table_offset;
+ ATOM_PPLIB_PPM_Table *atom_ppm_table;
+
+ hwmgr->platform_descriptor.TDPLimit = le32_to_cpu(ptable5->ulTDPLimit);
+ hwmgr->platform_descriptor.nearTDPLimit = le32_to_cpu(ptable5->ulNearTDPLimit);
+
+ hwmgr->platform_descriptor.TDPODLimit = le16_to_cpu(ptable5->usTDPODLimit);
+ hwmgr->platform_descriptor.TDPAdjustment = 0;
+
+ hwmgr->platform_descriptor.VidAdjustment = 0;
+ hwmgr->platform_descriptor.VidAdjustmentPolarity = 0;
+ hwmgr->platform_descriptor.VidMinLimit = 0;
+ hwmgr->platform_descriptor.VidMaxLimit = 1500000;
+ hwmgr->platform_descriptor.VidStep = 6250;
+
+ hwmgr->platform_descriptor.nearTDPLimitAdjusted = le32_to_cpu(ptable5->ulNearTDPLimit);
+
+ if (hwmgr->platform_descriptor.TDPODLimit != 0)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PowerControl);
+
+ hwmgr->platform_descriptor.SQRampingThreshold = le32_to_cpu(ptable5->ulSQRampingThreshold);
+
+ hwmgr->platform_descriptor.CACLeakage = le32_to_cpu(ptable5->ulCACLeakage);
+
+ hwmgr->dyn_state.cac_leakage_table = NULL;
+
+ if (0 != ptable5->usCACLeakageTableOffset) {
+ const ATOM_PPLIB_CAC_Leakage_Table *pCAC_leakage_table =
+ (ATOM_PPLIB_CAC_Leakage_Table *)(((unsigned long)ptable5) +
+ le16_to_cpu(ptable5->usCACLeakageTableOffset));
+ result = get_cac_leakage_table(hwmgr,
+ &hwmgr->dyn_state.cac_leakage_table, pCAC_leakage_table);
+ }
+
+ hwmgr->platform_descriptor.LoadLineSlope = le16_to_cpu(ptable5->usLoadLineSlope);
+
+ hwmgr->dyn_state.ppm_parameter_table = NULL;
+
+ if (0 != ptable3->usExtendendedHeaderOffset) {
+ extended_header = (const ATOM_PPLIB_EXTENDEDHEADER *)
+ (((unsigned long)powerplay_table) +
+ le16_to_cpu(ptable3->usExtendendedHeaderOffset));
+ if ((extended_header->usPPMTableOffset > 0) &&
+ le16_to_cpu(extended_header->usSize) >=
+ SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V5) {
+ table_offset = le16_to_cpu(extended_header->usPPMTableOffset);
+ atom_ppm_table = (ATOM_PPLIB_PPM_Table *)
+ (((unsigned long)powerplay_table) + table_offset);
+ if (0 == get_platform_power_management_table(hwmgr, atom_ppm_table))
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EnablePlatformPowerManagement);
+ }
+ }
+ }
+ return result;
+}
+
+static int init_phase_shedding_table(struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
+{
+ if (le16_to_cpu(powerplay_table->usTableSize) >=
+ sizeof(ATOM_PPLIB_POWERPLAYTABLE4)) {
+ const ATOM_PPLIB_POWERPLAYTABLE4 *powerplay_table4 =
+ (const ATOM_PPLIB_POWERPLAYTABLE4 *)powerplay_table;
+
+ if (0 != powerplay_table4->usVddcPhaseShedLimitsTableOffset) {
+ const ATOM_PPLIB_PhaseSheddingLimits_Table *ptable =
+ (ATOM_PPLIB_PhaseSheddingLimits_Table *)
+ (((unsigned long)powerplay_table4) +
+ le16_to_cpu(powerplay_table4->usVddcPhaseShedLimitsTableOffset));
+ struct phm_phase_shedding_limits_table *table;
+ unsigned long size, i;
+
+
+ size = sizeof(unsigned long) +
+ (sizeof(struct phm_phase_shedding_limits_table) *
+ ptable->ucNumEntries);
+
+ table = kzalloc(size, GFP_KERNEL);
+
+ if (table == NULL)
+ return -ENOMEM;
+
+ table->count = (unsigned long)ptable->ucNumEntries;
+
+ for (i = 0; i < table->count; i++) {
+ table->entries[i].Voltage = (unsigned long)le16_to_cpu(ptable->entries[i].usVoltage);
+ table->entries[i].Sclk = ((unsigned long)ptable->entries[i].ucSclkHigh << 16)
+ | le16_to_cpu(ptable->entries[i].usSclkLow);
+ table->entries[i].Mclk = ((unsigned long)ptable->entries[i].ucMclkHigh << 16)
+ | le16_to_cpu(ptable->entries[i].usMclkLow);
+ }
+ hwmgr->dyn_state.vddc_phase_shed_limits_table = table;
+ }
+ }
+
+ return 0;
+}
+
+static int get_number_of_vce_state_table_entries(
+ struct pp_hwmgr *hwmgr)
+{
+ const ATOM_PPLIB_POWERPLAYTABLE *table =
+ get_powerplay_table(hwmgr);
+ const ATOM_PPLIB_VCE_State_Table *vce_table =
+ get_vce_state_table(hwmgr, table);
+
+ if (vce_table)
+ return vce_table->numEntries;
+
+ return 0;
+}
+
+static int get_vce_state_table_entry(struct pp_hwmgr *hwmgr,
+ unsigned long i,
+ struct amd_vce_state *vce_state,
+ void **clock_info,
+ unsigned long *flag)
+{
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table = get_powerplay_table(hwmgr);
+
+ const ATOM_PPLIB_VCE_State_Table *vce_state_table = get_vce_state_table(hwmgr, powerplay_table);
+
+ unsigned short vce_clock_info_array_offset = get_vce_clock_info_array_offset(hwmgr, powerplay_table);
+
+ const VCEClockInfoArray *vce_clock_info_array = (const VCEClockInfoArray *)(((unsigned long) powerplay_table) + vce_clock_info_array_offset);
+
+ const ClockInfoArray *clock_arrays = (ClockInfoArray *)(((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usClockInfoArrayOffset));
+
+ const ATOM_PPLIB_VCE_State_Record *record = &vce_state_table->entries[i];
+
+ const VCEClockInfo *vce_clock_info = &vce_clock_info_array->entries[record->ucVCEClockInfoIndex];
+
+ unsigned long clockInfoIndex = record->ucClockInfoIndex & 0x3F;
+
+ *flag = (record->ucClockInfoIndex >> NUM_BITS_CLOCK_INFO_ARRAY_INDEX);
+
+ vce_state->evclk = ((uint32_t)vce_clock_info->ucEVClkHigh << 16) | le16_to_cpu(vce_clock_info->usEVClkLow);
+ vce_state->ecclk = ((uint32_t)vce_clock_info->ucECClkHigh << 16) | le16_to_cpu(vce_clock_info->usECClkLow);
+
+ *clock_info = (void *)((unsigned long)(clock_arrays->clockInfo) + (clockInfoIndex * clock_arrays->ucEntrySize));
+
+ return 0;
+}
+
+
+static int pp_tables_initialize(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table;
+
+ if (hwmgr->chip_id == CHIP_RAVEN)
+ return 0;
+
+ hwmgr->need_pp_table_upload = true;
+
+ powerplay_table = get_powerplay_table(hwmgr);
+
+ result = init_powerplay_tables(hwmgr, powerplay_table);
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "init_powerplay_tables failed", return result);
+
+ result = set_platform_caps(hwmgr,
+ le32_to_cpu(powerplay_table->ulPlatformCaps));
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "set_platform_caps failed", return result);
+
+ result = init_thermal_controller(hwmgr, powerplay_table);
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "init_thermal_controller failed", return result);
+
+ result = init_overdrive_limits(hwmgr, powerplay_table);
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "init_overdrive_limits failed", return result);
+
+ result = init_clock_voltage_dependency(hwmgr,
+ powerplay_table);
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "init_clock_voltage_dependency failed", return result);
+
+ result = init_dpm2_parameters(hwmgr, powerplay_table);
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "init_dpm2_parameters failed", return result);
+
+ result = init_phase_shedding_table(hwmgr, powerplay_table);
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "init_phase_shedding_table failed", return result);
+
+ return result;
+}
+
+static int pp_tables_uninitialize(struct pp_hwmgr *hwmgr)
+{
+ if (hwmgr->chip_id == CHIP_RAVEN)
+ return 0;
+
+ kfree(hwmgr->dyn_state.vddc_dependency_on_sclk);
+ hwmgr->dyn_state.vddc_dependency_on_sclk = NULL;
+
+ kfree(hwmgr->dyn_state.vddci_dependency_on_mclk);
+ hwmgr->dyn_state.vddci_dependency_on_mclk = NULL;
+
+ kfree(hwmgr->dyn_state.vddc_dependency_on_mclk);
+ hwmgr->dyn_state.vddc_dependency_on_mclk = NULL;
+
+ kfree(hwmgr->dyn_state.mvdd_dependency_on_mclk);
+ hwmgr->dyn_state.mvdd_dependency_on_mclk = NULL;
+
+ kfree(hwmgr->dyn_state.valid_mclk_values);
+ hwmgr->dyn_state.valid_mclk_values = NULL;
+
+ kfree(hwmgr->dyn_state.valid_sclk_values);
+ hwmgr->dyn_state.valid_sclk_values = NULL;
+
+ kfree(hwmgr->dyn_state.cac_leakage_table);
+ hwmgr->dyn_state.cac_leakage_table = NULL;
+
+ kfree(hwmgr->dyn_state.vddc_phase_shed_limits_table);
+ hwmgr->dyn_state.vddc_phase_shed_limits_table = NULL;
+
+ kfree(hwmgr->dyn_state.vce_clock_voltage_dependency_table);
+ hwmgr->dyn_state.vce_clock_voltage_dependency_table = NULL;
+
+ kfree(hwmgr->dyn_state.uvd_clock_voltage_dependency_table);
+ hwmgr->dyn_state.uvd_clock_voltage_dependency_table = NULL;
+
+ kfree(hwmgr->dyn_state.samu_clock_voltage_dependency_table);
+ hwmgr->dyn_state.samu_clock_voltage_dependency_table = NULL;
+
+ kfree(hwmgr->dyn_state.acp_clock_voltage_dependency_table);
+ hwmgr->dyn_state.acp_clock_voltage_dependency_table = NULL;
+
+ kfree(hwmgr->dyn_state.cac_dtp_table);
+ hwmgr->dyn_state.cac_dtp_table = NULL;
+
+ kfree(hwmgr->dyn_state.ppm_parameter_table);
+ hwmgr->dyn_state.ppm_parameter_table = NULL;
+
+ kfree(hwmgr->dyn_state.vdd_gfx_dependency_on_sclk);
+ hwmgr->dyn_state.vdd_gfx_dependency_on_sclk = NULL;
+
+ return 0;
+}
+
+const struct pp_table_func pptable_funcs = {
+ .pptable_init = pp_tables_initialize,
+ .pptable_fini = pp_tables_uninitialize,
+ .pptable_get_number_of_vce_state_table_entries =
+ get_number_of_vce_state_table_entries,
+ .pptable_get_vce_state_table_entry =
+ get_vce_state_table_entry,
+};
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.h
new file mode 100644
index 000000000000..baddaa75693b
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ * Interface Functions related to the BIOS PowerPlay Tables.
+ *
+ */
+
+#ifndef PROCESSPPTABLES_H
+#define PROCESSPPTABLES_H
+
+struct pp_hwmgr;
+struct pp_power_state;
+struct pp_hw_power_state;
+
+extern const struct pp_table_func pptable_funcs;
+
+typedef int (*pp_tables_hw_clock_info_callback)(struct pp_hwmgr *hwmgr,
+ struct pp_hw_power_state *hw_ps,
+ unsigned int index,
+ const void *clock_info);
+
+int pp_tables_get_num_of_entries(struct pp_hwmgr *hwmgr,
+ unsigned long *num_of_entries);
+
+int pp_tables_get_entry(struct pp_hwmgr *hwmgr,
+ unsigned long entry_index,
+ struct pp_power_state *ps,
+ pp_tables_hw_clock_info_callback func);
+
+int pp_tables_get_response_times(struct pp_hwmgr *hwmgr,
+ uint32_t *vol_rep_time, uint32_t *bb_rep_time);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.c
new file mode 100644
index 000000000000..cf60f3992303
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.c
@@ -0,0 +1,1494 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "pp_debug.h"
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include "atom-types.h"
+#include "atombios.h"
+#include "processpptables.h"
+#include "cgs_common.h"
+#include "smumgr.h"
+#include "hwmgr.h"
+#include "hardwaremanager.h"
+#include "rv_ppsmc.h"
+#include "smu10_hwmgr.h"
+#include "power_state.h"
+#include "soc15_common.h"
+#include "smu10.h"
+#include "asic_reg/pwr/pwr_10_0_offset.h"
+#include "asic_reg/pwr/pwr_10_0_sh_mask.h"
+
+#define SMU10_MAX_DEEPSLEEP_DIVIDER_ID 5
+#define SMU10_MINIMUM_ENGINE_CLOCK 800 /* 8Mhz, the low boundary of engine clock allowed on this chip */
+#define SCLK_MIN_DIV_INTV_SHIFT 12
+#define SMU10_DISPCLK_BYPASS_THRESHOLD 10000 /* 100Mhz */
+#define SMC_RAM_END 0x40000
+
+static const unsigned long SMU10_Magic = (unsigned long) PHM_Rv_Magic;
+
+
+static int smu10_display_clock_voltage_request(struct pp_hwmgr *hwmgr,
+ struct pp_display_clock_request *clock_req)
+{
+ struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+ enum amd_pp_clock_type clk_type = clock_req->clock_type;
+ uint32_t clk_freq = clock_req->clock_freq_in_khz / 1000;
+ PPSMC_Msg msg;
+
+ switch (clk_type) {
+ case amd_pp_dcf_clock:
+ if (clk_freq == smu10_data->dcf_actual_hard_min_freq)
+ return 0;
+ msg = PPSMC_MSG_SetHardMinDcefclkByFreq;
+ smu10_data->dcf_actual_hard_min_freq = clk_freq;
+ break;
+ case amd_pp_soc_clock:
+ msg = PPSMC_MSG_SetHardMinSocclkByFreq;
+ break;
+ case amd_pp_f_clock:
+ if (clk_freq == smu10_data->f_actual_hard_min_freq)
+ return 0;
+ smu10_data->f_actual_hard_min_freq = clk_freq;
+ msg = PPSMC_MSG_SetHardMinFclkByFreq;
+ break;
+ default:
+ pr_info("[DisplayClockVoltageRequest]Invalid Clock Type!");
+ return -EINVAL;
+ }
+ smum_send_msg_to_smc_with_parameter(hwmgr, msg, clk_freq, NULL);
+
+ return 0;
+}
+
+static struct smu10_power_state *cast_smu10_ps(struct pp_hw_power_state *hw_ps)
+{
+ if (SMU10_Magic != hw_ps->magic)
+ return NULL;
+
+ return (struct smu10_power_state *)hw_ps;
+}
+
+static const struct smu10_power_state *cast_const_smu10_ps(
+ const struct pp_hw_power_state *hw_ps)
+{
+ if (SMU10_Magic != hw_ps->magic)
+ return NULL;
+
+ return (struct smu10_power_state *)hw_ps;
+}
+
+static int smu10_initialize_dpm_defaults(struct pp_hwmgr *hwmgr)
+{
+ struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+
+ smu10_data->dce_slow_sclk_threshold = 30000;
+ smu10_data->thermal_auto_throttling_treshold = 0;
+ smu10_data->is_nb_dpm_enabled = 1;
+ smu10_data->dpm_flags = 1;
+ smu10_data->need_min_deep_sleep_dcefclk = true;
+ smu10_data->num_active_display = 0;
+ smu10_data->deep_sleep_dcefclk = 0;
+
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkDeepSleep);
+
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkThrottleLowNotification);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PowerPlaySupport);
+ return 0;
+}
+
+static int smu10_construct_max_power_limits_table(struct pp_hwmgr *hwmgr,
+ struct phm_clock_and_voltage_limits *table)
+{
+ return 0;
+}
+
+static int smu10_init_dynamic_state_adjustment_rule_settings(
+ struct pp_hwmgr *hwmgr)
+{
+ struct phm_clock_voltage_dependency_table *table_clk_vlt;
+
+ table_clk_vlt = kzalloc(struct_size(table_clk_vlt, entries, 7),
+ GFP_KERNEL);
+
+ if (NULL == table_clk_vlt) {
+ pr_err("Can not allocate memory!\n");
+ return -ENOMEM;
+ }
+
+ table_clk_vlt->count = 8;
+ table_clk_vlt->entries[0].clk = PP_DAL_POWERLEVEL_0;
+ table_clk_vlt->entries[0].v = 0;
+ table_clk_vlt->entries[1].clk = PP_DAL_POWERLEVEL_1;
+ table_clk_vlt->entries[1].v = 1;
+ table_clk_vlt->entries[2].clk = PP_DAL_POWERLEVEL_2;
+ table_clk_vlt->entries[2].v = 2;
+ table_clk_vlt->entries[3].clk = PP_DAL_POWERLEVEL_3;
+ table_clk_vlt->entries[3].v = 3;
+ table_clk_vlt->entries[4].clk = PP_DAL_POWERLEVEL_4;
+ table_clk_vlt->entries[4].v = 4;
+ table_clk_vlt->entries[5].clk = PP_DAL_POWERLEVEL_5;
+ table_clk_vlt->entries[5].v = 5;
+ table_clk_vlt->entries[6].clk = PP_DAL_POWERLEVEL_6;
+ table_clk_vlt->entries[6].v = 6;
+ table_clk_vlt->entries[7].clk = PP_DAL_POWERLEVEL_7;
+ table_clk_vlt->entries[7].v = 7;
+ hwmgr->dyn_state.vddc_dep_on_dal_pwrl = table_clk_vlt;
+
+ return 0;
+}
+
+static int smu10_get_system_info_data(struct pp_hwmgr *hwmgr)
+{
+ struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)hwmgr->backend;
+
+ smu10_data->sys_info.htc_hyst_lmt = 5;
+ smu10_data->sys_info.htc_tmp_lmt = 203;
+
+ if (smu10_data->thermal_auto_throttling_treshold == 0)
+ smu10_data->thermal_auto_throttling_treshold = 203;
+
+ smu10_construct_max_power_limits_table (hwmgr,
+ &hwmgr->dyn_state.max_clock_voltage_on_ac);
+
+ smu10_init_dynamic_state_adjustment_rule_settings(hwmgr);
+
+ return 0;
+}
+
+static int smu10_construct_boot_state(struct pp_hwmgr *hwmgr)
+{
+ return 0;
+}
+
+static int smu10_set_clock_limit(struct pp_hwmgr *hwmgr, const void *input)
+{
+ struct PP_Clocks clocks = {0};
+ struct pp_display_clock_request clock_req;
+
+ clocks.dcefClock = hwmgr->display_config->min_dcef_set_clk;
+ clock_req.clock_type = amd_pp_dcf_clock;
+ clock_req.clock_freq_in_khz = clocks.dcefClock * 10;
+
+ PP_ASSERT_WITH_CODE(!smu10_display_clock_voltage_request(hwmgr, &clock_req),
+ "Attempt to set DCF Clock Failed!", return -EINVAL);
+
+ return 0;
+}
+
+static int smu10_set_min_deep_sleep_dcefclk(struct pp_hwmgr *hwmgr, uint32_t clock)
+{
+ struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+
+ if (clock && smu10_data->deep_sleep_dcefclk != clock) {
+ smu10_data->deep_sleep_dcefclk = clock;
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetMinDeepSleepDcefclk,
+ smu10_data->deep_sleep_dcefclk,
+ NULL);
+ }
+ return 0;
+}
+
+static int smu10_set_hard_min_dcefclk_by_freq(struct pp_hwmgr *hwmgr, uint32_t clock)
+{
+ struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+
+ if (clock && smu10_data->dcf_actual_hard_min_freq != clock) {
+ smu10_data->dcf_actual_hard_min_freq = clock;
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinDcefclkByFreq,
+ smu10_data->dcf_actual_hard_min_freq,
+ NULL);
+ }
+ return 0;
+}
+
+static int smu10_set_hard_min_fclk_by_freq(struct pp_hwmgr *hwmgr, uint32_t clock)
+{
+ struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+
+ if (clock && smu10_data->f_actual_hard_min_freq != clock) {
+ smu10_data->f_actual_hard_min_freq = clock;
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinFclkByFreq,
+ smu10_data->f_actual_hard_min_freq,
+ NULL);
+ }
+ return 0;
+}
+
+static int smu10_set_hard_min_gfxclk_by_freq(struct pp_hwmgr *hwmgr, uint32_t clock)
+{
+ struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+
+ if (clock && smu10_data->gfx_actual_soft_min_freq != clock) {
+ smu10_data->gfx_actual_soft_min_freq = clock;
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinGfxClk,
+ smu10_data->gfx_actual_soft_min_freq,
+ NULL);
+ }
+ return 0;
+}
+
+static int smu10_set_soft_max_gfxclk_by_freq(struct pp_hwmgr *hwmgr, uint32_t clock)
+{
+ struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+
+ if (clock && smu10_data->gfx_max_freq_limit != (clock * 100)) {
+ smu10_data->gfx_max_freq_limit = clock * 100;
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxGfxClk,
+ clock,
+ NULL);
+ }
+ return 0;
+}
+
+static int smu10_set_active_display_count(struct pp_hwmgr *hwmgr, uint32_t count)
+{
+ struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+
+ if (smu10_data->num_active_display != count) {
+ smu10_data->num_active_display = count;
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDisplayCount,
+ smu10_data->num_active_display,
+ NULL);
+ }
+
+ return 0;
+}
+
+static int smu10_set_power_state_tasks(struct pp_hwmgr *hwmgr, const void *input)
+{
+ return smu10_set_clock_limit(hwmgr, input);
+}
+
+static int smu10_init_power_gate_state(struct pp_hwmgr *hwmgr)
+{
+ struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ smu10_data->vcn_power_gated = true;
+ smu10_data->isp_tileA_power_gated = true;
+ smu10_data->isp_tileB_power_gated = true;
+
+ if (adev->pg_flags & AMD_PG_SUPPORT_GFX_PG)
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetGfxCGPG,
+ true,
+ NULL);
+ else
+ return 0;
+}
+
+
+static int smu10_setup_asic_task(struct pp_hwmgr *hwmgr)
+{
+ return smu10_init_power_gate_state(hwmgr);
+}
+
+static int smu10_reset_cc6_data(struct pp_hwmgr *hwmgr)
+{
+ struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+
+ smu10_data->separation_time = 0;
+ smu10_data->cc6_disable = false;
+ smu10_data->pstate_disable = false;
+ smu10_data->cc6_setting_changed = false;
+
+ return 0;
+}
+
+static int smu10_power_off_asic(struct pp_hwmgr *hwmgr)
+{
+ return smu10_reset_cc6_data(hwmgr);
+}
+
+static bool smu10_is_gfx_on(struct pp_hwmgr *hwmgr)
+{
+ uint32_t reg;
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ reg = RREG32_SOC15(PWR, 0, mmPWR_MISC_CNTL_STATUS);
+ if ((reg & PWR_MISC_CNTL_STATUS__PWR_GFXOFF_STATUS_MASK) ==
+ (0x2 << PWR_MISC_CNTL_STATUS__PWR_GFXOFF_STATUS__SHIFT))
+ return true;
+
+ return false;
+}
+
+static int smu10_disable_gfx_off(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ if (adev->pm.pp_feature & PP_GFXOFF_MASK) {
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_DisableGfxOff, NULL);
+
+ /* confirm gfx is back to "on" state */
+ while (!smu10_is_gfx_on(hwmgr))
+ msleep(1);
+ }
+
+ return 0;
+}
+
+static int smu10_disable_dpm_tasks(struct pp_hwmgr *hwmgr)
+{
+ return 0;
+}
+
+static int smu10_enable_gfx_off(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ if (adev->pm.pp_feature & PP_GFXOFF_MASK)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_EnableGfxOff, NULL);
+
+ return 0;
+}
+
+static int smu10_enable_dpm_tasks(struct pp_hwmgr *hwmgr)
+{
+ return 0;
+}
+
+static int smu10_gfx_off_control(struct pp_hwmgr *hwmgr, bool enable)
+{
+ if (enable)
+ return smu10_enable_gfx_off(hwmgr);
+ else
+ return smu10_disable_gfx_off(hwmgr);
+}
+
+static int smu10_apply_state_adjust_rules(struct pp_hwmgr *hwmgr,
+ struct pp_power_state *prequest_ps,
+ const struct pp_power_state *pcurrent_ps)
+{
+ return 0;
+}
+
+/* temporary hardcoded clock voltage breakdown tables */
+static const DpmClock_t VddDcfClk[]= {
+ { 300, 2600},
+ { 600, 3200},
+ { 600, 3600},
+};
+
+static const DpmClock_t VddSocClk[]= {
+ { 478, 2600},
+ { 722, 3200},
+ { 722, 3600},
+};
+
+static const DpmClock_t VddFClk[]= {
+ { 400, 2600},
+ {1200, 3200},
+ {1200, 3600},
+};
+
+static const DpmClock_t VddDispClk[]= {
+ { 435, 2600},
+ { 661, 3200},
+ {1086, 3600},
+};
+
+static const DpmClock_t VddDppClk[]= {
+ { 435, 2600},
+ { 661, 3200},
+ { 661, 3600},
+};
+
+static const DpmClock_t VddPhyClk[]= {
+ { 540, 2600},
+ { 810, 3200},
+ { 810, 3600},
+};
+
+static int smu10_get_clock_voltage_dependency_table(struct pp_hwmgr *hwmgr,
+ struct smu10_voltage_dependency_table **pptable,
+ uint32_t num_entry, const DpmClock_t *pclk_dependency_table)
+{
+ uint32_t i;
+ struct smu10_voltage_dependency_table *ptable;
+
+ ptable = kzalloc(struct_size(ptable, entries, num_entry), GFP_KERNEL);
+ if (NULL == ptable)
+ return -ENOMEM;
+
+ ptable->count = num_entry;
+
+ for (i = 0; i < ptable->count; i++) {
+ ptable->entries[i].clk = pclk_dependency_table->Freq * 100;
+ ptable->entries[i].vol = pclk_dependency_table->Vol;
+ pclk_dependency_table++;
+ }
+
+ *pptable = ptable;
+
+ return 0;
+}
+
+
+static int smu10_populate_clock_table(struct pp_hwmgr *hwmgr)
+{
+ uint32_t result;
+
+ struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+ DpmClocks_t *table = &(smu10_data->clock_table);
+ struct smu10_clock_voltage_information *pinfo = &(smu10_data->clock_vol_info);
+
+ result = smum_smc_table_manager(hwmgr, (uint8_t *)table, SMU10_CLOCKTABLE, true);
+
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Attempt to copy clock table from smc failed",
+ return result);
+
+ if (0 == result && table->DcefClocks[0].Freq != 0) {
+ smu10_get_clock_voltage_dependency_table(hwmgr, &pinfo->vdd_dep_on_dcefclk,
+ NUM_DCEFCLK_DPM_LEVELS,
+ &smu10_data->clock_table.DcefClocks[0]);
+ smu10_get_clock_voltage_dependency_table(hwmgr, &pinfo->vdd_dep_on_socclk,
+ NUM_SOCCLK_DPM_LEVELS,
+ &smu10_data->clock_table.SocClocks[0]);
+ smu10_get_clock_voltage_dependency_table(hwmgr, &pinfo->vdd_dep_on_fclk,
+ NUM_FCLK_DPM_LEVELS,
+ &smu10_data->clock_table.FClocks[0]);
+ smu10_get_clock_voltage_dependency_table(hwmgr, &pinfo->vdd_dep_on_mclk,
+ NUM_MEMCLK_DPM_LEVELS,
+ &smu10_data->clock_table.MemClocks[0]);
+ } else {
+ smu10_get_clock_voltage_dependency_table(hwmgr, &pinfo->vdd_dep_on_dcefclk,
+ ARRAY_SIZE(VddDcfClk),
+ &VddDcfClk[0]);
+ smu10_get_clock_voltage_dependency_table(hwmgr, &pinfo->vdd_dep_on_socclk,
+ ARRAY_SIZE(VddSocClk),
+ &VddSocClk[0]);
+ smu10_get_clock_voltage_dependency_table(hwmgr, &pinfo->vdd_dep_on_fclk,
+ ARRAY_SIZE(VddFClk),
+ &VddFClk[0]);
+ }
+ smu10_get_clock_voltage_dependency_table(hwmgr, &pinfo->vdd_dep_on_dispclk,
+ ARRAY_SIZE(VddDispClk),
+ &VddDispClk[0]);
+ smu10_get_clock_voltage_dependency_table(hwmgr, &pinfo->vdd_dep_on_dppclk,
+ ARRAY_SIZE(VddDppClk), &VddDppClk[0]);
+ smu10_get_clock_voltage_dependency_table(hwmgr, &pinfo->vdd_dep_on_phyclk,
+ ARRAY_SIZE(VddPhyClk), &VddPhyClk[0]);
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMinGfxclkFrequency, &result);
+ smu10_data->gfx_min_freq_limit = result / 10 * 1000;
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMaxGfxclkFrequency, &result);
+ smu10_data->gfx_max_freq_limit = result / 10 * 1000;
+
+ return 0;
+}
+
+static int smu10_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+ struct smu10_hwmgr *data;
+
+ data = kzalloc(sizeof(struct smu10_hwmgr), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ hwmgr->backend = data;
+
+ result = smu10_initialize_dpm_defaults(hwmgr);
+ if (result != 0) {
+ pr_err("smu10_initialize_dpm_defaults failed\n");
+ return result;
+ }
+
+ smu10_populate_clock_table(hwmgr);
+
+ result = smu10_get_system_info_data(hwmgr);
+ if (result != 0) {
+ pr_err("smu10_get_system_info_data failed\n");
+ return result;
+ }
+
+ smu10_construct_boot_state(hwmgr);
+
+ hwmgr->platform_descriptor.hardwareActivityPerformanceLevels =
+ SMU10_MAX_HARDWARE_POWERLEVELS;
+
+ hwmgr->platform_descriptor.hardwarePerformanceLevels =
+ SMU10_MAX_HARDWARE_POWERLEVELS;
+
+ hwmgr->platform_descriptor.vbiosInterruptId = 0;
+
+ hwmgr->platform_descriptor.clockStep.engineClock = 500;
+
+ hwmgr->platform_descriptor.clockStep.memoryClock = 500;
+
+ hwmgr->platform_descriptor.minimumClocksReductionPercentage = 50;
+
+ hwmgr->pstate_sclk = SMU10_UMD_PSTATE_GFXCLK * 100;
+ hwmgr->pstate_mclk = SMU10_UMD_PSTATE_FCLK * 100;
+
+ /* enable the pp_od_clk_voltage sysfs file */
+ hwmgr->od_enabled = 1;
+
+ return result;
+}
+
+static int smu10_hwmgr_backend_fini(struct pp_hwmgr *hwmgr)
+{
+ struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+ struct smu10_clock_voltage_information *pinfo = &(smu10_data->clock_vol_info);
+
+ kfree(pinfo->vdd_dep_on_dcefclk);
+ pinfo->vdd_dep_on_dcefclk = NULL;
+ kfree(pinfo->vdd_dep_on_socclk);
+ pinfo->vdd_dep_on_socclk = NULL;
+ kfree(pinfo->vdd_dep_on_fclk);
+ pinfo->vdd_dep_on_fclk = NULL;
+ kfree(pinfo->vdd_dep_on_dispclk);
+ pinfo->vdd_dep_on_dispclk = NULL;
+ kfree(pinfo->vdd_dep_on_dppclk);
+ pinfo->vdd_dep_on_dppclk = NULL;
+ kfree(pinfo->vdd_dep_on_phyclk);
+ pinfo->vdd_dep_on_phyclk = NULL;
+
+ kfree(hwmgr->dyn_state.vddc_dep_on_dal_pwrl);
+ hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL;
+
+ kfree(hwmgr->backend);
+ hwmgr->backend = NULL;
+
+ return 0;
+}
+
+static int smu10_dpm_force_dpm_level(struct pp_hwmgr *hwmgr,
+ enum amd_dpm_forced_level level)
+{
+ struct smu10_hwmgr *data = hwmgr->backend;
+ uint32_t min_sclk = hwmgr->display_config->min_core_set_clock;
+ uint32_t min_mclk = hwmgr->display_config->min_mem_set_clock/100;
+ uint32_t index_fclk = data->clock_vol_info.vdd_dep_on_fclk->count - 1;
+ uint32_t index_socclk = data->clock_vol_info.vdd_dep_on_socclk->count - 1;
+
+ if (hwmgr->smu_version < 0x1E3700) {
+ pr_info("smu firmware version too old, can not set dpm level\n");
+ return 0;
+ }
+
+ if (min_sclk < data->gfx_min_freq_limit)
+ min_sclk = data->gfx_min_freq_limit;
+
+ min_sclk /= 100; /* transfer 10KHz to MHz */
+ if (min_mclk < data->clock_table.FClocks[0].Freq)
+ min_mclk = data->clock_table.FClocks[0].Freq;
+
+ switch (level) {
+ case AMD_DPM_FORCED_LEVEL_HIGH:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinGfxClk,
+ data->gfx_max_freq_limit/100,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinFclkByFreq,
+ SMU10_UMD_PSTATE_PEAK_FCLK,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinSocclkByFreq,
+ SMU10_UMD_PSTATE_PEAK_SOCCLK,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinVcn,
+ SMU10_UMD_PSTATE_VCE,
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxGfxClk,
+ data->gfx_max_freq_limit/100,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxFclkByFreq,
+ SMU10_UMD_PSTATE_PEAK_FCLK,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxSocclkByFreq,
+ SMU10_UMD_PSTATE_PEAK_SOCCLK,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxVcn,
+ SMU10_UMD_PSTATE_VCE,
+ NULL);
+ break;
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinGfxClk,
+ min_sclk,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxGfxClk,
+ min_sclk,
+ NULL);
+ break;
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinFclkByFreq,
+ min_mclk,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxFclkByFreq,
+ min_mclk,
+ NULL);
+ break;
+ case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinGfxClk,
+ SMU10_UMD_PSTATE_GFXCLK,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinFclkByFreq,
+ SMU10_UMD_PSTATE_FCLK,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinSocclkByFreq,
+ SMU10_UMD_PSTATE_SOCCLK,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinVcn,
+ SMU10_UMD_PSTATE_PROFILE_VCE,
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxGfxClk,
+ SMU10_UMD_PSTATE_GFXCLK,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxFclkByFreq,
+ SMU10_UMD_PSTATE_FCLK,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxSocclkByFreq,
+ SMU10_UMD_PSTATE_SOCCLK,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxVcn,
+ SMU10_UMD_PSTATE_PROFILE_VCE,
+ NULL);
+ break;
+ case AMD_DPM_FORCED_LEVEL_AUTO:
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinGfxClk,
+ min_sclk,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinFclkByFreq,
+ hwmgr->display_config->num_display > 3 ?
+ data->clock_vol_info.vdd_dep_on_fclk->entries[0].clk :
+ min_mclk,
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinSocclkByFreq,
+ data->clock_vol_info.vdd_dep_on_socclk->entries[0].clk,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinVcn,
+ SMU10_UMD_PSTATE_MIN_VCE,
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxGfxClk,
+ data->gfx_max_freq_limit/100,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxFclkByFreq,
+ data->clock_vol_info.vdd_dep_on_fclk->entries[index_fclk].clk,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxSocclkByFreq,
+ data->clock_vol_info.vdd_dep_on_socclk->entries[index_socclk].clk,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxVcn,
+ SMU10_UMD_PSTATE_VCE,
+ NULL);
+ break;
+ case AMD_DPM_FORCED_LEVEL_LOW:
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinGfxClk,
+ data->gfx_min_freq_limit/100,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxGfxClk,
+ data->gfx_min_freq_limit/100,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinFclkByFreq,
+ min_mclk,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxFclkByFreq,
+ min_mclk,
+ NULL);
+ break;
+ case AMD_DPM_FORCED_LEVEL_MANUAL:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_EXIT:
+ default:
+ break;
+ }
+ return 0;
+}
+
+static uint32_t smu10_dpm_get_mclk(struct pp_hwmgr *hwmgr, bool low)
+{
+ struct smu10_hwmgr *data;
+
+ if (hwmgr == NULL)
+ return -EINVAL;
+
+ data = (struct smu10_hwmgr *)(hwmgr->backend);
+
+ if (low)
+ return data->clock_vol_info.vdd_dep_on_fclk->entries[0].clk;
+ else
+ return data->clock_vol_info.vdd_dep_on_fclk->entries[
+ data->clock_vol_info.vdd_dep_on_fclk->count - 1].clk;
+}
+
+static uint32_t smu10_dpm_get_sclk(struct pp_hwmgr *hwmgr, bool low)
+{
+ struct smu10_hwmgr *data;
+
+ if (hwmgr == NULL)
+ return -EINVAL;
+
+ data = (struct smu10_hwmgr *)(hwmgr->backend);
+
+ if (low)
+ return data->gfx_min_freq_limit;
+ else
+ return data->gfx_max_freq_limit;
+}
+
+static int smu10_dpm_patch_boot_state(struct pp_hwmgr *hwmgr,
+ struct pp_hw_power_state *hw_ps)
+{
+ return 0;
+}
+
+static int smu10_dpm_get_pp_table_entry_callback(
+ struct pp_hwmgr *hwmgr,
+ struct pp_hw_power_state *hw_ps,
+ unsigned int index,
+ const void *clock_info)
+{
+ struct smu10_power_state *smu10_ps = cast_smu10_ps(hw_ps);
+
+ smu10_ps->levels[index].engine_clock = 0;
+
+ smu10_ps->levels[index].vddc_index = 0;
+ smu10_ps->level = index + 1;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SclkDeepSleep)) {
+ smu10_ps->levels[index].ds_divider_index = 5;
+ smu10_ps->levels[index].ss_divider_index = 5;
+ }
+
+ return 0;
+}
+
+static int smu10_dpm_get_num_of_pp_table_entries(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ unsigned long ret = 0;
+
+ result = pp_tables_get_num_of_entries(hwmgr, &ret);
+
+ return result ? 0 : ret;
+}
+
+static int smu10_dpm_get_pp_table_entry(struct pp_hwmgr *hwmgr,
+ unsigned long entry, struct pp_power_state *ps)
+{
+ int result;
+ struct smu10_power_state *smu10_ps;
+
+ ps->hardware.magic = SMU10_Magic;
+
+ smu10_ps = cast_smu10_ps(&(ps->hardware));
+
+ result = pp_tables_get_entry(hwmgr, entry, ps,
+ smu10_dpm_get_pp_table_entry_callback);
+
+ smu10_ps->uvd_clocks.vclk = ps->uvd_clocks.VCLK;
+ smu10_ps->uvd_clocks.dclk = ps->uvd_clocks.DCLK;
+
+ return result;
+}
+
+static int smu10_get_power_state_size(struct pp_hwmgr *hwmgr)
+{
+ return sizeof(struct smu10_power_state);
+}
+
+static int smu10_set_cpu_power_state(struct pp_hwmgr *hwmgr)
+{
+ return 0;
+}
+
+
+static int smu10_store_cc6_data(struct pp_hwmgr *hwmgr, uint32_t separation_time,
+ bool cc6_disable, bool pstate_disable, bool pstate_switch_disable)
+{
+ struct smu10_hwmgr *data = (struct smu10_hwmgr *)(hwmgr->backend);
+
+ if (separation_time != data->separation_time ||
+ cc6_disable != data->cc6_disable ||
+ pstate_disable != data->pstate_disable) {
+ data->separation_time = separation_time;
+ data->cc6_disable = cc6_disable;
+ data->pstate_disable = pstate_disable;
+ data->cc6_setting_changed = true;
+ }
+ return 0;
+}
+
+static int smu10_get_dal_power_level(struct pp_hwmgr *hwmgr,
+ struct amd_pp_simple_clock_info *info)
+{
+ return -EINVAL;
+}
+
+static int smu10_force_clock_level(struct pp_hwmgr *hwmgr,
+ enum pp_clock_type type, uint32_t mask)
+{
+ struct smu10_hwmgr *data = hwmgr->backend;
+ struct smu10_voltage_dependency_table *mclk_table =
+ data->clock_vol_info.vdd_dep_on_fclk;
+ uint32_t low, high;
+
+ low = mask ? (ffs(mask) - 1) : 0;
+ high = mask ? (fls(mask) - 1) : 0;
+
+ switch (type) {
+ case PP_SCLK:
+ if (low > 2 || high > 2) {
+ pr_info("Currently sclk only support 3 levels on RV\n");
+ return -EINVAL;
+ }
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinGfxClk,
+ low == 2 ? data->gfx_max_freq_limit/100 :
+ low == 1 ? SMU10_UMD_PSTATE_GFXCLK :
+ data->gfx_min_freq_limit/100,
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxGfxClk,
+ high == 0 ? data->gfx_min_freq_limit/100 :
+ high == 1 ? SMU10_UMD_PSTATE_GFXCLK :
+ data->gfx_max_freq_limit/100,
+ NULL);
+ break;
+
+ case PP_MCLK:
+ if (low > mclk_table->count - 1 || high > mclk_table->count - 1)
+ return -EINVAL;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinFclkByFreq,
+ mclk_table->entries[low].clk/100,
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxFclkByFreq,
+ mclk_table->entries[high].clk/100,
+ NULL);
+ break;
+
+ case PP_PCIE:
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int smu10_print_clock_levels(struct pp_hwmgr *hwmgr,
+ enum pp_clock_type type, char *buf)
+{
+ struct smu10_hwmgr *data = (struct smu10_hwmgr *)(hwmgr->backend);
+ struct smu10_voltage_dependency_table *mclk_table =
+ data->clock_vol_info.vdd_dep_on_fclk;
+ uint32_t i, now, size = 0;
+
+ switch (type) {
+ case PP_SCLK:
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetGfxclkFrequency, &now);
+
+ /* driver only know min/max gfx_clk, Add level 1 for all other gfx clks */
+ if (now == data->gfx_max_freq_limit/100)
+ i = 2;
+ else if (now == data->gfx_min_freq_limit/100)
+ i = 0;
+ else
+ i = 1;
+
+ size += sprintf(buf + size, "0: %uMhz %s\n",
+ data->gfx_min_freq_limit/100,
+ i == 0 ? "*" : "");
+ size += sprintf(buf + size, "1: %uMhz %s\n",
+ i == 1 ? now : SMU10_UMD_PSTATE_GFXCLK,
+ i == 1 ? "*" : "");
+ size += sprintf(buf + size, "2: %uMhz %s\n",
+ data->gfx_max_freq_limit/100,
+ i == 2 ? "*" : "");
+ break;
+ case PP_MCLK:
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetFclkFrequency, &now);
+
+ for (i = 0; i < mclk_table->count; i++)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ i,
+ mclk_table->entries[i].clk / 100,
+ ((mclk_table->entries[i].clk / 100)
+ == now) ? "*" : "");
+ break;
+ case OD_SCLK:
+ if (hwmgr->od_enabled) {
+ size = sprintf(buf, "%s:\n", "OD_SCLK");
+
+ size += sprintf(buf + size, "0: %10uMhz\n",
+ (data->gfx_actual_soft_min_freq > 0) ? data->gfx_actual_soft_min_freq : data->gfx_min_freq_limit/100);
+ size += sprintf(buf + size, "1: %10uMhz\n", data->gfx_max_freq_limit/100);
+ }
+ break;
+ case OD_RANGE:
+ if (hwmgr->od_enabled) {
+ uint32_t min_freq, max_freq = 0;
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMinGfxclkFrequency, &min_freq);
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMaxGfxclkFrequency, &max_freq);
+
+ size = sprintf(buf, "%s:\n", "OD_RANGE");
+ size += sprintf(buf + size, "SCLK: %7uMHz %10uMHz\n",
+ min_freq, max_freq);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return size;
+}
+
+static int smu10_get_performance_level(struct pp_hwmgr *hwmgr, const struct pp_hw_power_state *state,
+ PHM_PerformanceLevelDesignation designation, uint32_t index,
+ PHM_PerformanceLevel *level)
+{
+ struct smu10_hwmgr *data;
+
+ if (level == NULL || hwmgr == NULL || state == NULL)
+ return -EINVAL;
+
+ data = (struct smu10_hwmgr *)(hwmgr->backend);
+
+ if (index == 0) {
+ level->memory_clock = data->clock_vol_info.vdd_dep_on_fclk->entries[0].clk;
+ level->coreClock = data->gfx_min_freq_limit;
+ } else {
+ level->memory_clock = data->clock_vol_info.vdd_dep_on_fclk->entries[
+ data->clock_vol_info.vdd_dep_on_fclk->count - 1].clk;
+ level->coreClock = data->gfx_max_freq_limit;
+ }
+
+ level->nonLocalMemoryFreq = 0;
+ level->nonLocalMemoryWidth = 0;
+
+ return 0;
+}
+
+static int smu10_get_current_shallow_sleep_clocks(struct pp_hwmgr *hwmgr,
+ const struct pp_hw_power_state *state, struct pp_clock_info *clock_info)
+{
+ const struct smu10_power_state *ps = cast_const_smu10_ps(state);
+
+ clock_info->min_eng_clk = ps->levels[0].engine_clock / (1 << (ps->levels[0].ss_divider_index));
+ clock_info->max_eng_clk = ps->levels[ps->level - 1].engine_clock / (1 << (ps->levels[ps->level - 1].ss_divider_index));
+
+ return 0;
+}
+
+#define MEM_FREQ_LOW_LATENCY 25000
+#define MEM_FREQ_HIGH_LATENCY 80000
+#define MEM_LATENCY_HIGH 245
+#define MEM_LATENCY_LOW 35
+#define MEM_LATENCY_ERR 0xFFFF
+
+
+static uint32_t smu10_get_mem_latency(struct pp_hwmgr *hwmgr,
+ uint32_t clock)
+{
+ if (clock >= MEM_FREQ_LOW_LATENCY &&
+ clock < MEM_FREQ_HIGH_LATENCY)
+ return MEM_LATENCY_HIGH;
+ else if (clock >= MEM_FREQ_HIGH_LATENCY)
+ return MEM_LATENCY_LOW;
+ else
+ return MEM_LATENCY_ERR;
+}
+
+static int smu10_get_clock_by_type_with_latency(struct pp_hwmgr *hwmgr,
+ enum amd_pp_clock_type type,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ uint32_t i;
+ struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+ struct smu10_clock_voltage_information *pinfo = &(smu10_data->clock_vol_info);
+ struct smu10_voltage_dependency_table *pclk_vol_table;
+ bool latency_required = false;
+
+ if (pinfo == NULL)
+ return -EINVAL;
+
+ switch (type) {
+ case amd_pp_mem_clock:
+ pclk_vol_table = pinfo->vdd_dep_on_mclk;
+ latency_required = true;
+ break;
+ case amd_pp_f_clock:
+ pclk_vol_table = pinfo->vdd_dep_on_fclk;
+ latency_required = true;
+ break;
+ case amd_pp_dcf_clock:
+ pclk_vol_table = pinfo->vdd_dep_on_dcefclk;
+ break;
+ case amd_pp_disp_clock:
+ pclk_vol_table = pinfo->vdd_dep_on_dispclk;
+ break;
+ case amd_pp_phy_clock:
+ pclk_vol_table = pinfo->vdd_dep_on_phyclk;
+ break;
+ case amd_pp_dpp_clock:
+ pclk_vol_table = pinfo->vdd_dep_on_dppclk;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (pclk_vol_table == NULL || pclk_vol_table->count == 0)
+ return -EINVAL;
+
+ clocks->num_levels = 0;
+ for (i = 0; i < pclk_vol_table->count; i++) {
+ if (pclk_vol_table->entries[i].clk) {
+ clocks->data[clocks->num_levels].clocks_in_khz =
+ pclk_vol_table->entries[i].clk * 10;
+ clocks->data[clocks->num_levels].latency_in_us = latency_required ?
+ smu10_get_mem_latency(hwmgr,
+ pclk_vol_table->entries[i].clk) :
+ 0;
+ clocks->num_levels++;
+ }
+ }
+
+ return 0;
+}
+
+static int smu10_get_clock_by_type_with_voltage(struct pp_hwmgr *hwmgr,
+ enum amd_pp_clock_type type,
+ struct pp_clock_levels_with_voltage *clocks)
+{
+ uint32_t i;
+ struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+ struct smu10_clock_voltage_information *pinfo = &(smu10_data->clock_vol_info);
+ struct smu10_voltage_dependency_table *pclk_vol_table = NULL;
+
+ if (pinfo == NULL)
+ return -EINVAL;
+
+ switch (type) {
+ case amd_pp_mem_clock:
+ pclk_vol_table = pinfo->vdd_dep_on_mclk;
+ break;
+ case amd_pp_f_clock:
+ pclk_vol_table = pinfo->vdd_dep_on_fclk;
+ break;
+ case amd_pp_dcf_clock:
+ pclk_vol_table = pinfo->vdd_dep_on_dcefclk;
+ break;
+ case amd_pp_soc_clock:
+ pclk_vol_table = pinfo->vdd_dep_on_socclk;
+ break;
+ case amd_pp_disp_clock:
+ pclk_vol_table = pinfo->vdd_dep_on_dispclk;
+ break;
+ case amd_pp_phy_clock:
+ pclk_vol_table = pinfo->vdd_dep_on_phyclk;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (pclk_vol_table == NULL || pclk_vol_table->count == 0)
+ return -EINVAL;
+
+ clocks->num_levels = 0;
+ for (i = 0; i < pclk_vol_table->count; i++) {
+ if (pclk_vol_table->entries[i].clk) {
+ clocks->data[clocks->num_levels].clocks_in_khz = pclk_vol_table->entries[i].clk * 10;
+ clocks->data[clocks->num_levels].voltage_in_mv = pclk_vol_table->entries[i].vol;
+ clocks->num_levels++;
+ }
+ }
+
+ return 0;
+}
+
+
+
+static int smu10_get_max_high_clocks(struct pp_hwmgr *hwmgr, struct amd_pp_simple_clock_info *clocks)
+{
+ clocks->engine_max_clock = 80000; /* driver can't get engine clock, temp hard code to 800MHz */
+ return 0;
+}
+
+static int smu10_thermal_get_temperature(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t reg_value = RREG32_SOC15(THM, 0, mmTHM_TCON_CUR_TMP);
+ int cur_temp =
+ (reg_value & THM_TCON_CUR_TMP__CUR_TEMP_MASK) >> THM_TCON_CUR_TMP__CUR_TEMP__SHIFT;
+
+ if (cur_temp & THM_TCON_CUR_TMP__CUR_TEMP_RANGE_SEL_MASK)
+ cur_temp = ((cur_temp / 8) - 49) * PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ else
+ cur_temp = (cur_temp / 8) * PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+ return cur_temp;
+}
+
+static int smu10_read_sensor(struct pp_hwmgr *hwmgr, int idx,
+ void *value, int *size)
+{
+ struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+ uint32_t sclk, mclk;
+ int ret = 0;
+
+ switch (idx) {
+ case AMDGPU_PP_SENSOR_GFX_SCLK:
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetGfxclkFrequency, &sclk);
+ /* in units of 10KHZ */
+ *((uint32_t *)value) = sclk * 100;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GFX_MCLK:
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetFclkFrequency, &mclk);
+ /* in units of 10KHZ */
+ *((uint32_t *)value) = mclk * 100;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GPU_TEMP:
+ *((uint32_t *)value) = smu10_thermal_get_temperature(hwmgr);
+ break;
+ case AMDGPU_PP_SENSOR_VCN_POWER_STATE:
+ *(uint32_t *)value = smu10_data->vcn_power_gated ? 0 : 1;
+ *size = 4;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int smu10_set_watermarks_for_clocks_ranges(struct pp_hwmgr *hwmgr,
+ void *clock_ranges)
+{
+ struct smu10_hwmgr *data = hwmgr->backend;
+ struct dm_pp_wm_sets_with_clock_ranges_soc15 *wm_with_clock_ranges = clock_ranges;
+ Watermarks_t *table = &(data->water_marks_table);
+ struct amdgpu_device *adev = hwmgr->adev;
+ int i;
+
+ smu_set_watermarks_for_clocks_ranges(table,wm_with_clock_ranges);
+
+ if (adev->apu_flags & AMD_APU_IS_RAVEN2) {
+ for (i = 0; i < NUM_WM_RANGES; i++)
+ table->WatermarkRow[WM_DCFCLK][i].WmType = (uint8_t)0;
+
+ for (i = 0; i < NUM_WM_RANGES; i++)
+ table->WatermarkRow[WM_SOCCLK][i].WmType = (uint8_t)0;
+ }
+
+ smum_smc_table_manager(hwmgr, (uint8_t *)table, (uint16_t)SMU10_WMTABLE, false);
+ data->water_marks_exist = true;
+ return 0;
+}
+
+static int smu10_smus_notify_pwe(struct pp_hwmgr *hwmgr)
+{
+
+ return smum_send_msg_to_smc(hwmgr, PPSMC_MSG_SetRccPfcPmeRestoreRegister, NULL);
+}
+
+static int smu10_powergate_mmhub(struct pp_hwmgr *hwmgr)
+{
+ return smum_send_msg_to_smc(hwmgr, PPSMC_MSG_PowerGateMmHub, NULL);
+}
+
+static int smu10_powergate_sdma(struct pp_hwmgr *hwmgr, bool gate)
+{
+ if (gate)
+ return smum_send_msg_to_smc(hwmgr, PPSMC_MSG_PowerDownSdma, NULL);
+ else
+ return smum_send_msg_to_smc(hwmgr, PPSMC_MSG_PowerUpSdma, NULL);
+}
+
+static void smu10_powergate_vcn(struct pp_hwmgr *hwmgr, bool bgate)
+{
+ struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+
+ if (bgate) {
+ amdgpu_device_ip_set_powergating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_VCN,
+ AMD_PG_STATE_GATE);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_PowerDownVcn, 0, NULL);
+ smu10_data->vcn_power_gated = true;
+ } else {
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_PowerUpVcn, 0, NULL);
+ amdgpu_device_ip_set_powergating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_VCN,
+ AMD_PG_STATE_UNGATE);
+ smu10_data->vcn_power_gated = false;
+ }
+}
+
+static int conv_power_profile_to_pplib_workload(int power_profile)
+{
+ int pplib_workload = 0;
+
+ switch (power_profile) {
+ case PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT:
+ pplib_workload = WORKLOAD_DEFAULT_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_FULLSCREEN3D:
+ pplib_workload = WORKLOAD_PPLIB_FULL_SCREEN_3D_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_POWERSAVING:
+ pplib_workload = WORKLOAD_PPLIB_POWER_SAVING_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_VIDEO:
+ pplib_workload = WORKLOAD_PPLIB_VIDEO_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_VR:
+ pplib_workload = WORKLOAD_PPLIB_VR_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_COMPUTE:
+ pplib_workload = WORKLOAD_PPLIB_COMPUTE_BIT;
+ break;
+ }
+
+ return pplib_workload;
+}
+
+static int smu10_get_power_profile_mode(struct pp_hwmgr *hwmgr, char *buf)
+{
+ uint32_t i, size = 0;
+ static const uint8_t
+ profile_mode_setting[6][4] = {{70, 60, 0, 0,},
+ {70, 60, 1, 3,},
+ {90, 60, 0, 0,},
+ {70, 60, 0, 0,},
+ {70, 90, 0, 0,},
+ {30, 60, 0, 6,},
+ };
+ static const char *profile_name[6] = {
+ "BOOTUP_DEFAULT",
+ "3D_FULL_SCREEN",
+ "POWER_SAVING",
+ "VIDEO",
+ "VR",
+ "COMPUTE"};
+ static const char *title[6] = {"NUM",
+ "MODE_NAME",
+ "BUSY_SET_POINT",
+ "FPS",
+ "USE_RLC_BUSY",
+ "MIN_ACTIVE_LEVEL"};
+
+ if (!buf)
+ return -EINVAL;
+
+ size += sprintf(buf + size, "%s %16s %s %s %s %s\n",title[0],
+ title[1], title[2], title[3], title[4], title[5]);
+
+ for (i = 0; i <= PP_SMC_POWER_PROFILE_COMPUTE; i++)
+ size += sprintf(buf + size, "%3d %14s%s: %14d %3d %10d %14d\n",
+ i, profile_name[i], (i == hwmgr->power_profile_mode) ? "*" : " ",
+ profile_mode_setting[i][0], profile_mode_setting[i][1],
+ profile_mode_setting[i][2], profile_mode_setting[i][3]);
+
+ return size;
+}
+
+static bool smu10_is_raven1_refresh(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ if ((adev->apu_flags & AMD_APU_IS_RAVEN) &&
+ (hwmgr->smu_version >= 0x41e2b))
+ return true;
+ else
+ return false;
+}
+
+static int smu10_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, uint32_t size)
+{
+ int workload_type = 0;
+ int result = 0;
+
+ if (input[size] > PP_SMC_POWER_PROFILE_COMPUTE) {
+ pr_err("Invalid power profile mode %ld\n", input[size]);
+ return -EINVAL;
+ }
+ if (hwmgr->power_profile_mode == input[size])
+ return 0;
+
+ /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
+ workload_type =
+ conv_power_profile_to_pplib_workload(input[size]);
+ if (workload_type &&
+ smu10_is_raven1_refresh(hwmgr) &&
+ !hwmgr->gfxoff_state_changed_by_workload) {
+ smu10_gfx_off_control(hwmgr, false);
+ hwmgr->gfxoff_state_changed_by_workload = true;
+ }
+ result = smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_ActiveProcessNotify,
+ 1 << workload_type,
+ NULL);
+ if (!result)
+ hwmgr->power_profile_mode = input[size];
+ if (workload_type && hwmgr->gfxoff_state_changed_by_workload) {
+ smu10_gfx_off_control(hwmgr, true);
+ hwmgr->gfxoff_state_changed_by_workload = false;
+ }
+
+ return 0;
+}
+
+static int smu10_asic_reset(struct pp_hwmgr *hwmgr, enum SMU_ASIC_RESET_MODE mode)
+{
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DeviceDriverReset,
+ mode,
+ NULL);
+}
+
+static int smu10_set_fine_grain_clk_vol(struct pp_hwmgr *hwmgr,
+ enum PP_OD_DPM_TABLE_COMMAND type,
+ long *input, uint32_t size)
+{
+ if (!hwmgr->od_enabled) {
+ pr_err("Fine grain not support\n");
+ return -EINVAL;
+ }
+
+ if (size != 2) {
+ pr_err("Input parameter number not correct\n");
+ return -EINVAL;
+ }
+
+ if (type == PP_OD_EDIT_SCLK_VDDC_TABLE) {
+ if (input[0] == 0)
+ smu10_set_hard_min_gfxclk_by_freq(hwmgr, input[1]);
+ else if (input[0] == 1)
+ smu10_set_soft_max_gfxclk_by_freq(hwmgr, input[1]);
+ else
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct pp_hwmgr_func smu10_hwmgr_funcs = {
+ .backend_init = smu10_hwmgr_backend_init,
+ .backend_fini = smu10_hwmgr_backend_fini,
+ .apply_state_adjust_rules = smu10_apply_state_adjust_rules,
+ .force_dpm_level = smu10_dpm_force_dpm_level,
+ .get_power_state_size = smu10_get_power_state_size,
+ .powerdown_uvd = NULL,
+ .powergate_uvd = smu10_powergate_vcn,
+ .powergate_vce = NULL,
+ .get_mclk = smu10_dpm_get_mclk,
+ .get_sclk = smu10_dpm_get_sclk,
+ .patch_boot_state = smu10_dpm_patch_boot_state,
+ .get_pp_table_entry = smu10_dpm_get_pp_table_entry,
+ .get_num_of_pp_table_entries = smu10_dpm_get_num_of_pp_table_entries,
+ .set_cpu_power_state = smu10_set_cpu_power_state,
+ .store_cc6_data = smu10_store_cc6_data,
+ .force_clock_level = smu10_force_clock_level,
+ .print_clock_levels = smu10_print_clock_levels,
+ .get_dal_power_level = smu10_get_dal_power_level,
+ .get_performance_level = smu10_get_performance_level,
+ .get_current_shallow_sleep_clocks = smu10_get_current_shallow_sleep_clocks,
+ .get_clock_by_type_with_latency = smu10_get_clock_by_type_with_latency,
+ .get_clock_by_type_with_voltage = smu10_get_clock_by_type_with_voltage,
+ .set_watermarks_for_clocks_ranges = smu10_set_watermarks_for_clocks_ranges,
+ .get_max_high_clocks = smu10_get_max_high_clocks,
+ .read_sensor = smu10_read_sensor,
+ .set_active_display_count = smu10_set_active_display_count,
+ .set_min_deep_sleep_dcefclk = smu10_set_min_deep_sleep_dcefclk,
+ .dynamic_state_management_enable = smu10_enable_dpm_tasks,
+ .power_off_asic = smu10_power_off_asic,
+ .asic_setup = smu10_setup_asic_task,
+ .power_state_set = smu10_set_power_state_tasks,
+ .dynamic_state_management_disable = smu10_disable_dpm_tasks,
+ .powergate_mmhub = smu10_powergate_mmhub,
+ .smus_notify_pwe = smu10_smus_notify_pwe,
+ .display_clock_voltage_request = smu10_display_clock_voltage_request,
+ .powergate_gfx = smu10_gfx_off_control,
+ .powergate_sdma = smu10_powergate_sdma,
+ .set_hard_min_dcefclk_by_freq = smu10_set_hard_min_dcefclk_by_freq,
+ .set_hard_min_fclk_by_freq = smu10_set_hard_min_fclk_by_freq,
+ .set_hard_min_gfxclk_by_freq = smu10_set_hard_min_gfxclk_by_freq,
+ .set_soft_max_gfxclk_by_freq = smu10_set_soft_max_gfxclk_by_freq,
+ .get_power_profile_mode = smu10_get_power_profile_mode,
+ .set_power_profile_mode = smu10_set_power_profile_mode,
+ .asic_reset = smu10_asic_reset,
+ .set_fine_grain_clk_vol = smu10_set_fine_grain_clk_vol,
+};
+
+int smu10_init_function_pointers(struct pp_hwmgr *hwmgr)
+{
+ hwmgr->hwmgr_func = &smu10_hwmgr_funcs;
+ hwmgr->pptable_func = &pptable_funcs;
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.h
new file mode 100644
index 000000000000..6c9b5f060902
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.h
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 SMU10_HWMGR_H
+#define SMU10_HWMGR_H
+
+#include "hwmgr.h"
+#include "smu10_inc.h"
+#include "smu10_driver_if.h"
+#include "rv_ppsmc.h"
+
+
+#define SMU10_MAX_HARDWARE_POWERLEVELS 8
+#define SMU10_DYNCLK_NUMBER_OF_TREND_COEFFICIENTS 15
+
+#define DPMFlags_SCLK_Enabled 0x00000001
+#define DPMFlags_UVD_Enabled 0x00000002
+#define DPMFlags_VCE_Enabled 0x00000004
+#define DPMFlags_ACP_Enabled 0x00000008
+#define DPMFlags_ForceHighestValid 0x40000000
+
+/* Do not change the following, it is also defined in SMU8.h */
+#define SMU_EnabledFeatureScoreboard_AcpDpmOn 0x00000001
+#define SMU_EnabledFeatureScoreboard_SclkDpmOn 0x00200000
+#define SMU_EnabledFeatureScoreboard_UvdDpmOn 0x01000000
+#define SMU_EnabledFeatureScoreboard_VceDpmOn 0x02000000
+
+#define SMU_PHYID_SHIFT 8
+
+#define SMU10_PCIE_POWERGATING_TARGET_GFX 0
+#define SMU10_PCIE_POWERGATING_TARGET_DDI 1
+#define SMU10_PCIE_POWERGATING_TARGET_PLLCASCADE 2
+#define SMU10_PCIE_POWERGATING_TARGET_PHY 3
+
+enum VQ_TYPE {
+ CLOCK_TYPE_DCLK = 0L,
+ CLOCK_TYPE_ECLK,
+ CLOCK_TYPE_SCLK,
+ CLOCK_TYPE_CCLK,
+ VQ_GFX_CU
+};
+
+#define SUSTAINABLE_SCLK_MASK 0x00ffffff
+#define SUSTAINABLE_SCLK_SHIFT 0
+#define SUSTAINABLE_CU_MASK 0xff000000
+#define SUSTAINABLE_CU_SHIFT 24
+
+struct smu10_dpm_entry {
+ uint32_t soft_min_clk;
+ uint32_t hard_min_clk;
+ uint32_t soft_max_clk;
+ uint32_t hard_max_clk;
+};
+
+struct smu10_power_level {
+ uint32_t engine_clock;
+ uint8_t vddc_index;
+ uint8_t ds_divider_index;
+ uint8_t ss_divider_index;
+ uint8_t allow_gnb_slow;
+ uint8_t force_nbp_state;
+ uint8_t display_wm;
+ uint8_t vce_wm;
+ uint8_t num_simd_to_powerdown;
+ uint8_t hysteresis_up;
+ uint8_t rsv[3];
+};
+
+/*used for the nbpsFlags field in smu10_power state*/
+#define SMU10_POWERSTATE_FLAGS_NBPS_FORCEHIGH (1<<0)
+#define SMU10_POWERSTATE_FLAGS_NBPS_LOCKTOHIGH (1<<1)
+#define SMU10_POWERSTATE_FLAGS_NBPS_LOCKTOLOW (1<<2)
+
+#define SMU10_POWERSTATE_FLAGS_BAPM_DISABLE (1<<0)
+
+struct smu10_uvd_clocks {
+ uint32_t vclk;
+ uint32_t dclk;
+ uint32_t vclk_low_divider;
+ uint32_t vclk_high_divider;
+ uint32_t dclk_low_divider;
+ uint32_t dclk_high_divider;
+};
+
+struct pp_disable_nbpslo_flags {
+ union {
+ struct {
+ uint32_t entry : 1;
+ uint32_t display : 1;
+ uint32_t driver: 1;
+ uint32_t vce : 1;
+ uint32_t uvd : 1;
+ uint32_t acp : 1;
+ uint32_t reserved: 26;
+ } bits;
+ uint32_t u32All;
+ };
+};
+
+
+enum smu10_pstate_previous_action {
+ DO_NOTHING = 1,
+ FORCE_HIGH,
+ CANCEL_FORCE_HIGH
+};
+
+struct smu10_power_state {
+ unsigned int magic;
+ uint32_t level;
+ struct smu10_uvd_clocks uvd_clocks;
+ uint32_t evclk;
+ uint32_t ecclk;
+ uint32_t samclk;
+ uint32_t acpclk;
+ bool need_dfs_bypass;
+
+ uint32_t nbps_flags;
+ uint32_t bapm_flags;
+ uint8_t dpm0_pg_nbps_low;
+ uint8_t dpm0_pg_nbps_high;
+ uint8_t dpm_x_nbps_low;
+ uint8_t dpm_x_nbps_high;
+
+ enum smu10_pstate_previous_action action;
+
+ struct smu10_power_level levels[SMU10_MAX_HARDWARE_POWERLEVELS];
+ struct pp_disable_nbpslo_flags nbpslo_flags;
+};
+
+#define SMU10_NUM_NBPSTATES 4
+#define SMU10_NUM_NBPMEMORYCLOCK 2
+
+
+struct smu10_display_phy_info_entry {
+ uint8_t phy_present;
+ uint8_t active_lane_mapping;
+ uint8_t display_config_type;
+ uint8_t active_num_of_lanes;
+};
+
+#define SMU10_MAX_DISPLAYPHY_IDS 10
+
+struct smu10_display_phy_info {
+ bool display_phy_access_initialized;
+ struct smu10_display_phy_info_entry entries[SMU10_MAX_DISPLAYPHY_IDS];
+};
+
+#define MAX_DISPLAY_CLOCK_LEVEL 8
+
+struct smu10_system_info{
+ uint8_t htc_tmp_lmt;
+ uint8_t htc_hyst_lmt;
+};
+
+#define MAX_REGULAR_DPM_NUMBER 8
+
+struct smu10_mclk_latency_entries {
+ uint32_t frequency;
+ uint32_t latency;
+};
+
+struct smu10_mclk_latency_table {
+ uint32_t count;
+ struct smu10_mclk_latency_entries entries[MAX_REGULAR_DPM_NUMBER];
+};
+
+struct smu10_clock_voltage_dependency_record {
+ uint32_t clk;
+ uint32_t vol;
+};
+
+
+struct smu10_voltage_dependency_table {
+ uint32_t count;
+ struct smu10_clock_voltage_dependency_record entries[];
+};
+
+struct smu10_clock_voltage_information {
+ struct smu10_voltage_dependency_table *vdd_dep_on_dcefclk;
+ struct smu10_voltage_dependency_table *vdd_dep_on_socclk;
+ struct smu10_voltage_dependency_table *vdd_dep_on_fclk;
+ struct smu10_voltage_dependency_table *vdd_dep_on_mclk;
+ struct smu10_voltage_dependency_table *vdd_dep_on_dispclk;
+ struct smu10_voltage_dependency_table *vdd_dep_on_dppclk;
+ struct smu10_voltage_dependency_table *vdd_dep_on_phyclk;
+};
+
+struct smu10_hwmgr {
+ uint32_t disable_driver_thermal_policy;
+ uint32_t thermal_auto_throttling_treshold;
+ struct smu10_system_info sys_info;
+ struct smu10_mclk_latency_table mclk_latency_table;
+
+ uint32_t ddi_power_gating_disabled;
+
+ struct smu10_display_phy_info_entry display_phy_info;
+ uint32_t dce_slow_sclk_threshold;
+
+ bool disp_clk_bypass;
+ bool disp_clk_bypass_pending;
+ uint32_t bapm_enabled;
+
+ bool video_start;
+ bool battery_state;
+
+ uint32_t is_nb_dpm_enabled;
+ uint32_t is_voltage_island_enabled;
+ uint32_t disable_smu_acp_s3_handshake;
+ uint32_t disable_notify_smu_vpu_recovery;
+ bool in_vpu_recovery;
+ bool pg_acp_init;
+ uint8_t disp_config;
+
+ /* PowerTune */
+ uint32_t power_containment_features;
+ bool cac_enabled;
+ bool disable_uvd_power_tune_feature;
+ bool enable_bapm_feature;
+ bool enable_tdc_limit_feature;
+
+
+ /* SMC SRAM Address of firmware header tables */
+ uint32_t sram_end;
+ uint32_t dpm_table_start;
+ uint32_t soft_regs_start;
+
+ /* start of SMU7_Fusion_DpmTable */
+
+ uint8_t uvd_level_count;
+ uint8_t vce_level_count;
+ uint8_t acp_level_count;
+ uint8_t samu_level_count;
+
+ uint32_t fps_high_threshold;
+ uint32_t fps_low_threshold;
+
+ uint32_t dpm_flags;
+ struct smu10_dpm_entry sclk_dpm;
+ struct smu10_dpm_entry uvd_dpm;
+ struct smu10_dpm_entry vce_dpm;
+ struct smu10_dpm_entry acp_dpm;
+ bool acp_power_up_no_dsp;
+
+ uint32_t max_sclk_level;
+ uint32_t num_of_clk_entries;
+
+ /* CPU Power State */
+ uint32_t separation_time;
+ bool cc6_disable;
+ bool pstate_disable;
+ bool cc6_setting_changed;
+
+ uint32_t ulTotalActiveCUs;
+
+ bool isp_tileA_power_gated;
+ bool isp_tileB_power_gated;
+ uint32_t isp_actual_hard_min_freq;
+ uint32_t soc_actual_hard_min_freq;
+ uint32_t dcf_actual_hard_min_freq;
+
+ uint32_t f_actual_hard_min_freq;
+ uint32_t fabric_actual_soft_min_freq;
+ uint32_t vclk_soft_min;
+ uint32_t dclk_soft_min;
+ uint32_t gfx_actual_soft_min_freq;
+ uint32_t gfx_min_freq_limit;
+ uint32_t gfx_max_freq_limit; /* in 10Khz*/
+
+ bool vcn_power_gated;
+ bool vcn_dpg_mode;
+
+ bool gfx_off_controled_by_driver;
+ bool water_marks_exist;
+ Watermarks_t water_marks_table;
+ struct smu10_clock_voltage_information clock_vol_info;
+ DpmClocks_t clock_table;
+
+ uint32_t active_process_mask;
+ bool need_min_deep_sleep_dcefclk;
+ uint32_t deep_sleep_dcefclk;
+ uint32_t num_active_display;
+};
+
+struct pp_hwmgr;
+
+int smu10_init_function_pointers(struct pp_hwmgr *hwmgr);
+
+/* UMD PState SMU10 Msg Parameters in MHz */
+#define SMU10_UMD_PSTATE_GFXCLK 700
+#define SMU10_UMD_PSTATE_SOCCLK 626
+#define SMU10_UMD_PSTATE_FCLK 933
+#define SMU10_UMD_PSTATE_VCE 0x03C00320
+#define SMU10_UMD_PSTATE_PROFILE_VCE 0x02AD0229
+
+#define SMU10_UMD_PSTATE_PEAK_SOCCLK 757
+#define SMU10_UMD_PSTATE_PEAK_FCLK 1200
+
+#define SMU10_UMD_PSTATE_MIN_FCLK 400
+#define SMU10_UMD_PSTATE_MIN_SOCCLK 200
+#define SMU10_UMD_PSTATE_MIN_VCE 0x0190012C
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_inc.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_inc.h
new file mode 100644
index 000000000000..edb68e302f6f
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_inc.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 SMU10_INC_H
+#define SMU10_INC_H
+
+
+#include "asic_reg/mp/mp_10_0_default.h"
+#include "asic_reg/mp/mp_10_0_offset.h"
+#include "asic_reg/mp/mp_10_0_sh_mask.h"
+
+#include "asic_reg/nbio/nbio_7_0_default.h"
+#include "asic_reg/nbio/nbio_7_0_offset.h"
+#include "asic_reg/nbio/nbio_7_0_sh_mask.h"
+
+#include "asic_reg/thm/thm_10_0_default.h"
+#include "asic_reg/thm/thm_10_0_offset.h"
+#include "asic_reg/thm/thm_10_0_sh_mask.h"
+
+
+#define ixDDI_PHY_GEN_STATUS 0x3FCE8
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_baco.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_baco.c
new file mode 100644
index 000000000000..044cda005aed
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_baco.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "amdgpu.h"
+#include "smu7_baco.h"
+#include "tonga_baco.h"
+#include "fiji_baco.h"
+#include "polaris_baco.h"
+#include "ci_baco.h"
+
+#include "bif/bif_5_0_d.h"
+#include "bif/bif_5_0_sh_mask.h"
+
+#include "smu/smu_7_1_2_d.h"
+#include "smu/smu_7_1_2_sh_mask.h"
+
+int smu7_baco_get_capability(struct pp_hwmgr *hwmgr, bool *cap)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
+ uint32_t reg;
+
+ *cap = false;
+ if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_BACO))
+ return 0;
+
+ reg = RREG32(mmCC_BIF_BX_FUSESTRAP0);
+
+ if (reg & CC_BIF_BX_FUSESTRAP0__STRAP_BIF_PX_CAPABLE_MASK)
+ *cap = true;
+
+ return 0;
+}
+
+int smu7_baco_get_state(struct pp_hwmgr *hwmgr, enum BACO_STATE *state)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
+ uint32_t reg;
+
+ reg = RREG32(mmBACO_CNTL);
+
+ if (reg & BACO_CNTL__BACO_MODE_MASK)
+ /* gfx has already entered BACO state */
+ *state = BACO_STATE_IN;
+ else
+ *state = BACO_STATE_OUT;
+ return 0;
+}
+
+int smu7_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
+
+ switch (adev->asic_type) {
+ case CHIP_TOPAZ:
+ case CHIP_TONGA:
+ return tonga_baco_set_state(hwmgr, state);
+ case CHIP_FIJI:
+ return fiji_baco_set_state(hwmgr, state);
+ case CHIP_POLARIS10:
+ case CHIP_POLARIS11:
+ case CHIP_POLARIS12:
+ case CHIP_VEGAM:
+ return polaris_baco_set_state(hwmgr, state);
+#ifdef CONFIG_DRM_AMDGPU_CIK
+ case CHIP_BONAIRE:
+ case CHIP_HAWAII:
+ return ci_baco_set_state(hwmgr, state);
+#endif
+ default:
+ return -EINVAL;
+ }
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_baco.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_baco.h
new file mode 100644
index 000000000000..be0d98abb536
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_baco.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 __SMU7_BACO_H__
+#define __SMU7_BACO_H__
+#include "hwmgr.h"
+#include "common_baco.h"
+
+extern int smu7_baco_get_capability(struct pp_hwmgr *hwmgr, bool *cap);
+extern int smu7_baco_get_state(struct pp_hwmgr *hwmgr, enum BACO_STATE *state);
+extern int smu7_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_clockpowergating.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_clockpowergating.c
new file mode 100644
index 000000000000..f2bda3bcbbde
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_clockpowergating.c
@@ -0,0 +1,437 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "smu7_hwmgr.h"
+#include "smu7_clockpowergating.h"
+#include "smu7_common.h"
+
+static int smu7_enable_disable_uvd_dpm(struct pp_hwmgr *hwmgr, bool enable)
+{
+ return smum_send_msg_to_smc(hwmgr, enable ?
+ PPSMC_MSG_UVDDPM_Enable :
+ PPSMC_MSG_UVDDPM_Disable,
+ NULL);
+}
+
+static int smu7_enable_disable_vce_dpm(struct pp_hwmgr *hwmgr, bool enable)
+{
+ return smum_send_msg_to_smc(hwmgr, enable ?
+ PPSMC_MSG_VCEDPM_Enable :
+ PPSMC_MSG_VCEDPM_Disable,
+ NULL);
+}
+
+static int smu7_update_uvd_dpm(struct pp_hwmgr *hwmgr, bool bgate)
+{
+ if (!bgate)
+ smum_update_smc_table(hwmgr, SMU_UVD_TABLE);
+ return smu7_enable_disable_uvd_dpm(hwmgr, !bgate);
+}
+
+static int smu7_update_vce_dpm(struct pp_hwmgr *hwmgr, bool bgate)
+{
+ if (!bgate)
+ smum_update_smc_table(hwmgr, SMU_VCE_TABLE);
+ return smu7_enable_disable_vce_dpm(hwmgr, !bgate);
+}
+
+int smu7_powerdown_uvd(struct pp_hwmgr *hwmgr)
+{
+ if (phm_cf_want_uvd_power_gating(hwmgr))
+ return smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_UVDPowerOFF,
+ NULL);
+ return 0;
+}
+
+static int smu7_powerup_uvd(struct pp_hwmgr *hwmgr)
+{
+ if (phm_cf_want_uvd_power_gating(hwmgr)) {
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UVDDynamicPowerGating)) {
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_UVDPowerON, 1, NULL);
+ } else {
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_UVDPowerON, 0, NULL);
+ }
+ }
+
+ return 0;
+}
+
+static int smu7_powerdown_vce(struct pp_hwmgr *hwmgr)
+{
+ if (phm_cf_want_vce_power_gating(hwmgr))
+ return smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_VCEPowerOFF,
+ NULL);
+ return 0;
+}
+
+static int smu7_powerup_vce(struct pp_hwmgr *hwmgr)
+{
+ if (phm_cf_want_vce_power_gating(hwmgr))
+ return smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_VCEPowerON,
+ NULL);
+ return 0;
+}
+
+int smu7_disable_clock_power_gating(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ data->uvd_power_gated = false;
+ data->vce_power_gated = false;
+
+ smu7_powerup_uvd(hwmgr);
+ smu7_powerup_vce(hwmgr);
+
+ return 0;
+}
+
+void smu7_powergate_uvd(struct pp_hwmgr *hwmgr, bool bgate)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ data->uvd_power_gated = bgate;
+
+ if (bgate) {
+ amdgpu_device_ip_set_powergating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_UVD,
+ AMD_PG_STATE_GATE);
+ amdgpu_device_ip_set_clockgating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_UVD,
+ AMD_CG_STATE_GATE);
+ smu7_update_uvd_dpm(hwmgr, true);
+ smu7_powerdown_uvd(hwmgr);
+ } else {
+ smu7_powerup_uvd(hwmgr);
+ amdgpu_device_ip_set_clockgating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_UVD,
+ AMD_CG_STATE_UNGATE);
+ amdgpu_device_ip_set_powergating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_UVD,
+ AMD_PG_STATE_UNGATE);
+ smu7_update_uvd_dpm(hwmgr, false);
+ }
+
+}
+
+void smu7_powergate_vce(struct pp_hwmgr *hwmgr, bool bgate)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ data->vce_power_gated = bgate;
+
+ if (bgate) {
+ amdgpu_device_ip_set_powergating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_VCE,
+ AMD_PG_STATE_GATE);
+ amdgpu_device_ip_set_clockgating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_VCE,
+ AMD_CG_STATE_GATE);
+ smu7_update_vce_dpm(hwmgr, true);
+ smu7_powerdown_vce(hwmgr);
+ } else {
+ smu7_powerup_vce(hwmgr);
+ amdgpu_device_ip_set_clockgating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_VCE,
+ AMD_CG_STATE_UNGATE);
+ amdgpu_device_ip_set_powergating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_VCE,
+ AMD_PG_STATE_UNGATE);
+ smu7_update_vce_dpm(hwmgr, false);
+ }
+}
+
+int smu7_update_clock_gatings(struct pp_hwmgr *hwmgr,
+ const uint32_t *msg_id)
+{
+ PPSMC_Msg msg;
+ uint32_t value;
+
+ if (!(hwmgr->feature_mask & PP_ENABLE_GFX_CG_THRU_SMU))
+ return 0;
+
+ switch ((*msg_id & PP_GROUP_MASK) >> PP_GROUP_SHIFT) {
+ case PP_GROUP_GFX:
+ switch ((*msg_id & PP_BLOCK_MASK) >> PP_BLOCK_SHIFT) {
+ case PP_BLOCK_GFX_CG:
+ if (PP_STATE_SUPPORT_CG & *msg_id) {
+ msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
+ PPSMC_MSG_EnableClockGatingFeature :
+ PPSMC_MSG_DisableClockGatingFeature;
+ value = CG_GFX_CGCG_MASK;
+
+ if (smum_send_msg_to_smc_with_parameter(
+ hwmgr, msg, value, NULL))
+ return -EINVAL;
+ }
+ if (PP_STATE_SUPPORT_LS & *msg_id) {
+ msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS
+ ? PPSMC_MSG_EnableClockGatingFeature
+ : PPSMC_MSG_DisableClockGatingFeature;
+ value = CG_GFX_CGLS_MASK;
+
+ if (smum_send_msg_to_smc_with_parameter(
+ hwmgr, msg, value, NULL))
+ return -EINVAL;
+ }
+ break;
+
+ case PP_BLOCK_GFX_3D:
+ if (PP_STATE_SUPPORT_CG & *msg_id) {
+ msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
+ PPSMC_MSG_EnableClockGatingFeature :
+ PPSMC_MSG_DisableClockGatingFeature;
+ value = CG_GFX_3DCG_MASK;
+
+ if (smum_send_msg_to_smc_with_parameter(
+ hwmgr, msg, value, NULL))
+ return -EINVAL;
+ }
+
+ if (PP_STATE_SUPPORT_LS & *msg_id) {
+ msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
+ PPSMC_MSG_EnableClockGatingFeature :
+ PPSMC_MSG_DisableClockGatingFeature;
+ value = CG_GFX_3DLS_MASK;
+
+ if (smum_send_msg_to_smc_with_parameter(
+ hwmgr, msg, value, NULL))
+ return -EINVAL;
+ }
+ break;
+
+ case PP_BLOCK_GFX_RLC:
+ if (PP_STATE_SUPPORT_LS & *msg_id) {
+ msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
+ PPSMC_MSG_EnableClockGatingFeature :
+ PPSMC_MSG_DisableClockGatingFeature;
+ value = CG_GFX_RLC_LS_MASK;
+
+ if (smum_send_msg_to_smc_with_parameter(
+ hwmgr, msg, value, NULL))
+ return -EINVAL;
+ }
+ break;
+
+ case PP_BLOCK_GFX_CP:
+ if (PP_STATE_SUPPORT_LS & *msg_id) {
+ msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
+ PPSMC_MSG_EnableClockGatingFeature :
+ PPSMC_MSG_DisableClockGatingFeature;
+ value = CG_GFX_CP_LS_MASK;
+
+ if (smum_send_msg_to_smc_with_parameter(
+ hwmgr, msg, value, NULL))
+ return -EINVAL;
+ }
+ break;
+
+ case PP_BLOCK_GFX_MG:
+ if (PP_STATE_SUPPORT_CG & *msg_id) {
+ msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
+ PPSMC_MSG_EnableClockGatingFeature :
+ PPSMC_MSG_DisableClockGatingFeature;
+ value = (CG_CPF_MGCG_MASK | CG_RLC_MGCG_MASK |
+ CG_GFX_OTHERS_MGCG_MASK);
+
+ if (smum_send_msg_to_smc_with_parameter(
+ hwmgr, msg, value, NULL))
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case PP_GROUP_SYS:
+ switch ((*msg_id & PP_BLOCK_MASK) >> PP_BLOCK_SHIFT) {
+ case PP_BLOCK_SYS_BIF:
+ if (PP_STATE_SUPPORT_CG & *msg_id) {
+ msg = (*msg_id & PP_STATE_MASK) & PP_STATE_CG ?
+ PPSMC_MSG_EnableClockGatingFeature :
+ PPSMC_MSG_DisableClockGatingFeature;
+ value = CG_SYS_BIF_MGCG_MASK;
+
+ if (smum_send_msg_to_smc_with_parameter(
+ hwmgr, msg, value, NULL))
+ return -EINVAL;
+ }
+ if (PP_STATE_SUPPORT_LS & *msg_id) {
+ msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
+ PPSMC_MSG_EnableClockGatingFeature :
+ PPSMC_MSG_DisableClockGatingFeature;
+ value = CG_SYS_BIF_MGLS_MASK;
+
+ if (smum_send_msg_to_smc_with_parameter(
+ hwmgr, msg, value, NULL))
+ return -EINVAL;
+ }
+ break;
+
+ case PP_BLOCK_SYS_MC:
+ if (PP_STATE_SUPPORT_CG & *msg_id) {
+ msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
+ PPSMC_MSG_EnableClockGatingFeature :
+ PPSMC_MSG_DisableClockGatingFeature;
+ value = CG_SYS_MC_MGCG_MASK;
+
+ if (smum_send_msg_to_smc_with_parameter(
+ hwmgr, msg, value, NULL))
+ return -EINVAL;
+ }
+
+ if (PP_STATE_SUPPORT_LS & *msg_id) {
+ msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
+ PPSMC_MSG_EnableClockGatingFeature :
+ PPSMC_MSG_DisableClockGatingFeature;
+ value = CG_SYS_MC_MGLS_MASK;
+
+ if (smum_send_msg_to_smc_with_parameter(
+ hwmgr, msg, value, NULL))
+ return -EINVAL;
+ }
+ break;
+
+ case PP_BLOCK_SYS_DRM:
+ if (PP_STATE_SUPPORT_CG & *msg_id) {
+ msg = (*msg_id & PP_STATE_MASK) & PP_STATE_CG ?
+ PPSMC_MSG_EnableClockGatingFeature :
+ PPSMC_MSG_DisableClockGatingFeature;
+ value = CG_SYS_DRM_MGCG_MASK;
+
+ if (smum_send_msg_to_smc_with_parameter(
+ hwmgr, msg, value, NULL))
+ return -EINVAL;
+ }
+ if (PP_STATE_SUPPORT_LS & *msg_id) {
+ msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
+ PPSMC_MSG_EnableClockGatingFeature :
+ PPSMC_MSG_DisableClockGatingFeature;
+ value = CG_SYS_DRM_MGLS_MASK;
+
+ if (smum_send_msg_to_smc_with_parameter(
+ hwmgr, msg, value, NULL))
+ return -EINVAL;
+ }
+ break;
+
+ case PP_BLOCK_SYS_HDP:
+ if (PP_STATE_SUPPORT_CG & *msg_id) {
+ msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
+ PPSMC_MSG_EnableClockGatingFeature :
+ PPSMC_MSG_DisableClockGatingFeature;
+ value = CG_SYS_HDP_MGCG_MASK;
+
+ if (smum_send_msg_to_smc_with_parameter(
+ hwmgr, msg, value, NULL))
+ return -EINVAL;
+ }
+
+ if (PP_STATE_SUPPORT_LS & *msg_id) {
+ msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
+ PPSMC_MSG_EnableClockGatingFeature :
+ PPSMC_MSG_DisableClockGatingFeature;
+ value = CG_SYS_HDP_MGLS_MASK;
+
+ if (smum_send_msg_to_smc_with_parameter(
+ hwmgr, msg, value, NULL))
+ return -EINVAL;
+ }
+ break;
+
+ case PP_BLOCK_SYS_SDMA:
+ if (PP_STATE_SUPPORT_CG & *msg_id) {
+ msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
+ PPSMC_MSG_EnableClockGatingFeature :
+ PPSMC_MSG_DisableClockGatingFeature;
+ value = CG_SYS_SDMA_MGCG_MASK;
+
+ if (smum_send_msg_to_smc_with_parameter(
+ hwmgr, msg, value, NULL))
+ return -EINVAL;
+ }
+
+ if (PP_STATE_SUPPORT_LS & *msg_id) {
+ msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
+ PPSMC_MSG_EnableClockGatingFeature :
+ PPSMC_MSG_DisableClockGatingFeature;
+ value = CG_SYS_SDMA_MGLS_MASK;
+
+ if (smum_send_msg_to_smc_with_parameter(
+ hwmgr, msg, value, NULL))
+ return -EINVAL;
+ }
+ break;
+
+ case PP_BLOCK_SYS_ROM:
+ if (PP_STATE_SUPPORT_CG & *msg_id) {
+ msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
+ PPSMC_MSG_EnableClockGatingFeature :
+ PPSMC_MSG_DisableClockGatingFeature;
+ value = CG_SYS_ROM_MASK;
+
+ if (smum_send_msg_to_smc_with_parameter(
+ hwmgr, msg, value, NULL))
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+
+ }
+ break;
+
+ default:
+ return -EINVAL;
+
+ }
+
+ return 0;
+}
+
+/* This function is for Polaris11 only for now,
+ * Powerplay will only control the static per CU Power Gating.
+ * Dynamic per CU Power Gating will be done in gfx.
+ */
+int smu7_powergate_gfx(struct pp_hwmgr *hwmgr, bool enable)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ if (enable)
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_GFX_CU_PG_ENABLE,
+ adev->gfx.cu_info.number,
+ NULL);
+ else
+ return smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_GFX_CU_PG_DISABLE,
+ NULL);
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_clockpowergating.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_clockpowergating.h
new file mode 100644
index 000000000000..fc8f8a6acc72
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_clockpowergating.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _SMU7_CLOCK_POWER_GATING_H_
+#define _SMU7_CLOCK_POWER_GATING_H_
+
+#include "smu7_hwmgr.h"
+
+void smu7_powergate_vce(struct pp_hwmgr *hwmgr, bool bgate);
+void smu7_powergate_uvd(struct pp_hwmgr *hwmgr, bool bgate);
+int smu7_powerdown_uvd(struct pp_hwmgr *hwmgr);
+int smu7_powergate_acp(struct pp_hwmgr *hwmgr, bool bgate);
+int smu7_disable_clock_power_gating(struct pp_hwmgr *hwmgr);
+int smu7_update_clock_gatings(struct pp_hwmgr *hwmgr,
+ const uint32_t *msg_id);
+int smu7_powergate_gfx(struct pp_hwmgr *hwmgr, bool enable);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_dyn_defaults.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_dyn_defaults.h
new file mode 100644
index 000000000000..3477d4dfff70
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_dyn_defaults.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _SMU7_DYN_DEFAULTS_H
+#define _SMU7_DYN_DEFAULTS_H
+
+
+/* We need to fill in the default values */
+
+
+#define SMU7_VOTINGRIGHTSCLIENTS_DFLT0 0x3FFFC102
+#define SMU7_VOTINGRIGHTSCLIENTS_DFLT1 0x000400
+#define SMU7_VOTINGRIGHTSCLIENTS_DFLT2 0xC00080
+#define SMU7_VOTINGRIGHTSCLIENTS_DFLT3 0xC00200
+#define SMU7_VOTINGRIGHTSCLIENTS_DFLT4 0xC01680
+#define SMU7_VOTINGRIGHTSCLIENTS_DFLT5 0xC00033
+#define SMU7_VOTINGRIGHTSCLIENTS_DFLT6 0xC00033
+#define SMU7_VOTINGRIGHTSCLIENTS_DFLT7 0x3FFFC000
+
+
+#define SMU7_THERMALPROTECTCOUNTER_DFLT 0x200
+#define SMU7_STATICSCREENTHRESHOLDUNIT_DFLT 0
+#define SMU7_STATICSCREENTHRESHOLD_DFLT 0x00C8
+#define SMU7_GFXIDLECLOCKSTOPTHRESHOLD_DFLT 0x200
+#define SMU7_REFERENCEDIVIDER_DFLT 4
+
+#define SMU7_ULVVOLTAGECHANGEDELAY_DFLT 1687
+
+#define SMU7_CGULVPARAMETER_DFLT 0x00040035
+#define SMU7_CGULVCONTROL_DFLT 0x00007450
+#define SMU7_TARGETACTIVITY_DFLT 50
+#define SMU7_MCLK_TARGETACTIVITY_DFLT 10
+#define SMU7_SCLK_TARGETACTIVITY_DFLT 30
+#endif
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c
new file mode 100644
index 000000000000..35629140fc7a
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c
@@ -0,0 +1,5236 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "pp_debug.h"
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+#include <drm/amdgpu_drm.h>
+#include "ppatomctrl.h"
+#include "atombios.h"
+#include "pptable_v1_0.h"
+#include "pppcielanes.h"
+#include "amd_pcie_helpers.h"
+#include "hardwaremanager.h"
+#include "process_pptables_v1_0.h"
+#include "cgs_common.h"
+
+#include "smu7_common.h"
+
+#include "hwmgr.h"
+#include "smu7_hwmgr.h"
+#include "smu_ucode_xfer_vi.h"
+#include "smu7_powertune.h"
+#include "smu7_dyn_defaults.h"
+#include "smu7_thermal.h"
+#include "smu7_clockpowergating.h"
+#include "processpptables.h"
+#include "pp_thermal.h"
+#include "smu7_baco.h"
+
+#include "ivsrcid/ivsrcid_vislands30.h"
+
+#define MC_CG_ARB_FREQ_F0 0x0a
+#define MC_CG_ARB_FREQ_F1 0x0b
+#define MC_CG_ARB_FREQ_F2 0x0c
+#define MC_CG_ARB_FREQ_F3 0x0d
+
+#define MC_CG_SEQ_DRAMCONF_S0 0x05
+#define MC_CG_SEQ_DRAMCONF_S1 0x06
+#define MC_CG_SEQ_YCLK_SUSPEND 0x04
+#define MC_CG_SEQ_YCLK_RESUME 0x0a
+
+#define SMC_CG_IND_START 0xc0030000
+#define SMC_CG_IND_END 0xc0040000
+
+#define MEM_FREQ_LOW_LATENCY 25000
+#define MEM_FREQ_HIGH_LATENCY 80000
+
+#define MEM_LATENCY_HIGH 45
+#define MEM_LATENCY_LOW 35
+#define MEM_LATENCY_ERR 0xFFFF
+
+#define MC_SEQ_MISC0_GDDR5_SHIFT 28
+#define MC_SEQ_MISC0_GDDR5_MASK 0xf0000000
+#define MC_SEQ_MISC0_GDDR5_VALUE 5
+
+#define PCIE_BUS_CLK 10000
+#define TCLK (PCIE_BUS_CLK / 10)
+
+static struct profile_mode_setting smu7_profiling[7] =
+ {{0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 0, 100, 30, 1, 0, 100, 10},
+ {1, 10, 0, 30, 0, 0, 0, 0},
+ {0, 0, 0, 0, 1, 10, 16, 31},
+ {1, 0, 11, 50, 1, 0, 100, 10},
+ {1, 0, 5, 30, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0},
+ };
+
+#define PPSMC_MSG_SetVBITimeout_VEGAM ((uint16_t) 0x310)
+
+#define ixPWR_SVI2_PLANE1_LOAD 0xC0200280
+#define PWR_SVI2_PLANE1_LOAD__PSI1_MASK 0x00000020L
+#define PWR_SVI2_PLANE1_LOAD__PSI0_EN_MASK 0x00000040L
+#define PWR_SVI2_PLANE1_LOAD__PSI1__SHIFT 0x00000005
+#define PWR_SVI2_PLANE1_LOAD__PSI0_EN__SHIFT 0x00000006
+
+/** Values for the CG_THERMAL_CTRL::DPM_EVENT_SRC field. */
+enum DPM_EVENT_SRC {
+ DPM_EVENT_SRC_ANALOG = 0,
+ DPM_EVENT_SRC_EXTERNAL = 1,
+ DPM_EVENT_SRC_DIGITAL = 2,
+ DPM_EVENT_SRC_ANALOG_OR_EXTERNAL = 3,
+ DPM_EVENT_SRC_DIGITAL_OR_EXTERNAL = 4
+};
+
+static const unsigned long PhwVIslands_Magic = (unsigned long)(PHM_VIslands_Magic);
+static int smu7_force_clock_level(struct pp_hwmgr *hwmgr,
+ enum pp_clock_type type, uint32_t mask);
+
+static struct smu7_power_state *cast_phw_smu7_power_state(
+ struct pp_hw_power_state *hw_ps)
+{
+ PP_ASSERT_WITH_CODE((PhwVIslands_Magic == hw_ps->magic),
+ "Invalid Powerstate Type!",
+ return NULL);
+
+ return (struct smu7_power_state *)hw_ps;
+}
+
+static const struct smu7_power_state *cast_const_phw_smu7_power_state(
+ const struct pp_hw_power_state *hw_ps)
+{
+ PP_ASSERT_WITH_CODE((PhwVIslands_Magic == hw_ps->magic),
+ "Invalid Powerstate Type!",
+ return NULL);
+
+ return (const struct smu7_power_state *)hw_ps;
+}
+
+/**
+ * Find the MC microcode version and store it in the HwMgr struct
+ *
+ * @param hwmgr the address of the powerplay hardware manager.
+ * @return always 0
+ */
+static int smu7_get_mc_microcode_version(struct pp_hwmgr *hwmgr)
+{
+ cgs_write_register(hwmgr->device, mmMC_SEQ_IO_DEBUG_INDEX, 0x9F);
+
+ hwmgr->microcode_version_info.MC = cgs_read_register(hwmgr->device, mmMC_SEQ_IO_DEBUG_DATA);
+
+ return 0;
+}
+
+static uint16_t smu7_get_current_pcie_speed(struct pp_hwmgr *hwmgr)
+{
+ uint32_t speedCntl = 0;
+
+ /* mmPCIE_PORT_INDEX rename as mmPCIE_INDEX */
+ speedCntl = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__PCIE,
+ ixPCIE_LC_SPEED_CNTL);
+ return((uint16_t)PHM_GET_FIELD(speedCntl,
+ PCIE_LC_SPEED_CNTL, LC_CURRENT_DATA_RATE));
+}
+
+static int smu7_get_current_pcie_lane_number(struct pp_hwmgr *hwmgr)
+{
+ uint32_t link_width;
+
+ /* mmPCIE_PORT_INDEX rename as mmPCIE_INDEX */
+ link_width = PHM_READ_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__PCIE,
+ PCIE_LC_LINK_WIDTH_CNTL, LC_LINK_WIDTH_RD);
+
+ PP_ASSERT_WITH_CODE((7 >= link_width),
+ "Invalid PCIe lane width!", return 0);
+
+ return decode_pcie_lane_width(link_width);
+}
+
+/**
+* Enable voltage control
+*
+* @param pHwMgr the address of the powerplay hardware manager.
+* @return always PP_Result_OK
+*/
+static int smu7_enable_smc_voltage_controller(struct pp_hwmgr *hwmgr)
+{
+ if (hwmgr->chip_id == CHIP_VEGAM) {
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device,
+ CGS_IND_REG__SMC, PWR_SVI2_PLANE1_LOAD, PSI1, 0);
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device,
+ CGS_IND_REG__SMC, PWR_SVI2_PLANE1_LOAD, PSI0_EN, 0);
+ }
+
+ if (hwmgr->feature_mask & PP_SMC_VOLTAGE_CONTROL_MASK)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_Voltage_Cntl_Enable, NULL);
+
+ return 0;
+}
+
+/**
+* Checks if we want to support voltage control
+*
+* @param hwmgr the address of the powerplay hardware manager.
+*/
+static bool smu7_voltage_control(const struct pp_hwmgr *hwmgr)
+{
+ const struct smu7_hwmgr *data =
+ (const struct smu7_hwmgr *)(hwmgr->backend);
+
+ return (SMU7_VOLTAGE_CONTROL_NONE != data->voltage_control);
+}
+
+/**
+* Enable voltage control
+*
+* @param hwmgr the address of the powerplay hardware manager.
+* @return always 0
+*/
+static int smu7_enable_voltage_control(struct pp_hwmgr *hwmgr)
+{
+ /* enable voltage control */
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ GENERAL_PWRMGT, VOLT_PWRMGT_EN, 1);
+
+ return 0;
+}
+
+static int phm_get_svi2_voltage_table_v0(pp_atomctrl_voltage_table *voltage_table,
+ struct phm_clock_voltage_dependency_table *voltage_dependency_table
+ )
+{
+ uint32_t i;
+
+ PP_ASSERT_WITH_CODE((NULL != voltage_table),
+ "Voltage Dependency Table empty.", return -EINVAL;);
+
+ voltage_table->mask_low = 0;
+ voltage_table->phase_delay = 0;
+ voltage_table->count = voltage_dependency_table->count;
+
+ for (i = 0; i < voltage_dependency_table->count; i++) {
+ voltage_table->entries[i].value =
+ voltage_dependency_table->entries[i].v;
+ voltage_table->entries[i].smio_low = 0;
+ }
+
+ return 0;
+}
+
+
+/**
+* Create Voltage Tables.
+*
+* @param hwmgr the address of the powerplay hardware manager.
+* @return always 0
+*/
+static int smu7_construct_voltage_tables(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)hwmgr->pptable;
+ int result = 0;
+ uint32_t tmp;
+
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
+ result = atomctrl_get_voltage_table_v3(hwmgr,
+ VOLTAGE_TYPE_MVDDC, VOLTAGE_OBJ_GPIO_LUT,
+ &(data->mvdd_voltage_table));
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Failed to retrieve MVDD table.",
+ return result);
+ } else if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->mvdd_control) {
+ if (hwmgr->pp_table_version == PP_TABLE_V1)
+ result = phm_get_svi2_mvdd_voltage_table(&(data->mvdd_voltage_table),
+ table_info->vdd_dep_on_mclk);
+ else if (hwmgr->pp_table_version == PP_TABLE_V0)
+ result = phm_get_svi2_voltage_table_v0(&(data->mvdd_voltage_table),
+ hwmgr->dyn_state.mvdd_dependency_on_mclk);
+
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Failed to retrieve SVI2 MVDD table from dependency table.",
+ return result;);
+ }
+
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
+ result = atomctrl_get_voltage_table_v3(hwmgr,
+ VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_GPIO_LUT,
+ &(data->vddci_voltage_table));
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Failed to retrieve VDDCI table.",
+ return result);
+ } else if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) {
+ if (hwmgr->pp_table_version == PP_TABLE_V1)
+ result = phm_get_svi2_vddci_voltage_table(&(data->vddci_voltage_table),
+ table_info->vdd_dep_on_mclk);
+ else if (hwmgr->pp_table_version == PP_TABLE_V0)
+ result = phm_get_svi2_voltage_table_v0(&(data->vddci_voltage_table),
+ hwmgr->dyn_state.vddci_dependency_on_mclk);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Failed to retrieve SVI2 VDDCI table from dependency table.",
+ return result);
+ }
+
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vdd_gfx_control) {
+ /* VDDGFX has only SVI2 voltage control */
+ result = phm_get_svi2_vdd_voltage_table(&(data->vddgfx_voltage_table),
+ table_info->vddgfx_lookup_table);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Failed to retrieve SVI2 VDDGFX table from lookup table.", return result;);
+ }
+
+
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->voltage_control) {
+ result = atomctrl_get_voltage_table_v3(hwmgr,
+ VOLTAGE_TYPE_VDDC, VOLTAGE_OBJ_GPIO_LUT,
+ &data->vddc_voltage_table);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Failed to retrieve VDDC table.", return result;);
+ } else if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
+
+ if (hwmgr->pp_table_version == PP_TABLE_V0)
+ result = phm_get_svi2_voltage_table_v0(&data->vddc_voltage_table,
+ hwmgr->dyn_state.vddc_dependency_on_mclk);
+ else if (hwmgr->pp_table_version == PP_TABLE_V1)
+ result = phm_get_svi2_vdd_voltage_table(&(data->vddc_voltage_table),
+ table_info->vddc_lookup_table);
+
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Failed to retrieve SVI2 VDDC table from dependency table.", return result;);
+ }
+
+ tmp = smum_get_mac_definition(hwmgr, SMU_MAX_LEVELS_VDDC);
+ PP_ASSERT_WITH_CODE(
+ (data->vddc_voltage_table.count <= tmp),
+ "Too many voltage values for VDDC. Trimming to fit state table.",
+ phm_trim_voltage_table_to_fit_state_table(tmp,
+ &(data->vddc_voltage_table)));
+
+ tmp = smum_get_mac_definition(hwmgr, SMU_MAX_LEVELS_VDDGFX);
+ PP_ASSERT_WITH_CODE(
+ (data->vddgfx_voltage_table.count <= tmp),
+ "Too many voltage values for VDDC. Trimming to fit state table.",
+ phm_trim_voltage_table_to_fit_state_table(tmp,
+ &(data->vddgfx_voltage_table)));
+
+ tmp = smum_get_mac_definition(hwmgr, SMU_MAX_LEVELS_VDDCI);
+ PP_ASSERT_WITH_CODE(
+ (data->vddci_voltage_table.count <= tmp),
+ "Too many voltage values for VDDCI. Trimming to fit state table.",
+ phm_trim_voltage_table_to_fit_state_table(tmp,
+ &(data->vddci_voltage_table)));
+
+ tmp = smum_get_mac_definition(hwmgr, SMU_MAX_LEVELS_MVDD);
+ PP_ASSERT_WITH_CODE(
+ (data->mvdd_voltage_table.count <= tmp),
+ "Too many voltage values for MVDD. Trimming to fit state table.",
+ phm_trim_voltage_table_to_fit_state_table(tmp,
+ &(data->mvdd_voltage_table)));
+
+ return 0;
+}
+
+/**
+* Programs static screed detection parameters
+*
+* @param hwmgr the address of the powerplay hardware manager.
+* @return always 0
+*/
+static int smu7_program_static_screen_threshold_parameters(
+ struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ /* Set static screen threshold unit */
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_STATIC_SCREEN_PARAMETER, STATIC_SCREEN_THRESHOLD_UNIT,
+ data->static_screen_threshold_unit);
+ /* Set static screen threshold */
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_STATIC_SCREEN_PARAMETER, STATIC_SCREEN_THRESHOLD,
+ data->static_screen_threshold);
+
+ return 0;
+}
+
+/**
+* Setup display gap for glitch free memory clock switching.
+*
+* @param hwmgr the address of the powerplay hardware manager.
+* @return always 0
+*/
+static int smu7_enable_display_gap(struct pp_hwmgr *hwmgr)
+{
+ uint32_t display_gap =
+ cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixCG_DISPLAY_GAP_CNTL);
+
+ display_gap = PHM_SET_FIELD(display_gap, CG_DISPLAY_GAP_CNTL,
+ DISP_GAP, DISPLAY_GAP_IGNORE);
+
+ display_gap = PHM_SET_FIELD(display_gap, CG_DISPLAY_GAP_CNTL,
+ DISP_GAP_MCHG, DISPLAY_GAP_VBLANK);
+
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixCG_DISPLAY_GAP_CNTL, display_gap);
+
+ return 0;
+}
+
+/**
+* Programs activity state transition voting clients
+*
+* @param hwmgr the address of the powerplay hardware manager.
+* @return always 0
+*/
+static int smu7_program_voting_clients(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ int i;
+
+ /* Clear reset for voting clients before enabling DPM */
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SCLK_PWRMGT_CNTL, RESET_SCLK_CNT, 0);
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SCLK_PWRMGT_CNTL, RESET_BUSY_CNT, 0);
+
+ for (i = 0; i < 8; i++)
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixCG_FREQ_TRAN_VOTING_0 + i * 4,
+ data->voting_rights_clients[i]);
+ return 0;
+}
+
+static int smu7_clear_voting_clients(struct pp_hwmgr *hwmgr)
+{
+ int i;
+
+ /* Reset voting clients before disabling DPM */
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SCLK_PWRMGT_CNTL, RESET_SCLK_CNT, 1);
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SCLK_PWRMGT_CNTL, RESET_BUSY_CNT, 1);
+
+ for (i = 0; i < 8; i++)
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixCG_FREQ_TRAN_VOTING_0 + i * 4, 0);
+
+ return 0;
+}
+
+/* Copy one arb setting to another and then switch the active set.
+ * arb_src and arb_dest is one of the MC_CG_ARB_FREQ_Fx constants.
+ */
+static int smu7_copy_and_switch_arb_sets(struct pp_hwmgr *hwmgr,
+ uint32_t arb_src, uint32_t arb_dest)
+{
+ uint32_t mc_arb_dram_timing;
+ uint32_t mc_arb_dram_timing2;
+ uint32_t burst_time;
+ uint32_t mc_cg_config;
+
+ switch (arb_src) {
+ case MC_CG_ARB_FREQ_F0:
+ mc_arb_dram_timing = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING);
+ mc_arb_dram_timing2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2);
+ burst_time = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0);
+ break;
+ case MC_CG_ARB_FREQ_F1:
+ mc_arb_dram_timing = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING_1);
+ mc_arb_dram_timing2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2_1);
+ burst_time = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (arb_dest) {
+ case MC_CG_ARB_FREQ_F0:
+ cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING, mc_arb_dram_timing);
+ cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2, mc_arb_dram_timing2);
+ PHM_WRITE_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0, burst_time);
+ break;
+ case MC_CG_ARB_FREQ_F1:
+ cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING_1, mc_arb_dram_timing);
+ cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2_1, mc_arb_dram_timing2);
+ PHM_WRITE_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE1, burst_time);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mc_cg_config = cgs_read_register(hwmgr->device, mmMC_CG_CONFIG);
+ mc_cg_config |= 0x0000000F;
+ cgs_write_register(hwmgr->device, mmMC_CG_CONFIG, mc_cg_config);
+ PHM_WRITE_FIELD(hwmgr->device, MC_ARB_CG, CG_ARB_REQ, arb_dest);
+
+ return 0;
+}
+
+static int smu7_reset_to_default(struct pp_hwmgr *hwmgr)
+{
+ return smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ResetToDefaults, NULL);
+}
+
+/**
+* Initial switch from ARB F0->F1
+*
+* @param hwmgr the address of the powerplay hardware manager.
+* @return always 0
+* This function is to be called from the SetPowerState table.
+*/
+static int smu7_initial_switch_from_arbf0_to_f1(struct pp_hwmgr *hwmgr)
+{
+ return smu7_copy_and_switch_arb_sets(hwmgr,
+ MC_CG_ARB_FREQ_F0, MC_CG_ARB_FREQ_F1);
+}
+
+static int smu7_force_switch_to_arbf0(struct pp_hwmgr *hwmgr)
+{
+ uint32_t tmp;
+
+ tmp = (cgs_read_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, ixSMC_SCRATCH9) &
+ 0x0000ff00) >> 8;
+
+ if (tmp == MC_CG_ARB_FREQ_F0)
+ return 0;
+
+ return smu7_copy_and_switch_arb_sets(hwmgr,
+ tmp, MC_CG_ARB_FREQ_F0);
+}
+
+static int smu7_setup_default_pcie_table(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_pcie_table *pcie_table = NULL;
+
+ uint32_t i, max_entry;
+ uint32_t tmp;
+
+ PP_ASSERT_WITH_CODE((data->use_pcie_performance_levels ||
+ data->use_pcie_power_saving_levels), "No pcie performance levels!",
+ return -EINVAL);
+
+ if (table_info != NULL)
+ pcie_table = table_info->pcie_table;
+
+ if (data->use_pcie_performance_levels &&
+ !data->use_pcie_power_saving_levels) {
+ data->pcie_gen_power_saving = data->pcie_gen_performance;
+ data->pcie_lane_power_saving = data->pcie_lane_performance;
+ } else if (!data->use_pcie_performance_levels &&
+ data->use_pcie_power_saving_levels) {
+ data->pcie_gen_performance = data->pcie_gen_power_saving;
+ data->pcie_lane_performance = data->pcie_lane_power_saving;
+ }
+ tmp = smum_get_mac_definition(hwmgr, SMU_MAX_LEVELS_LINK);
+ phm_reset_single_dpm_table(&data->dpm_table.pcie_speed_table,
+ tmp,
+ MAX_REGULAR_DPM_NUMBER);
+
+ if (pcie_table != NULL) {
+ /* max_entry is used to make sure we reserve one PCIE level
+ * for boot level (fix for A+A PSPP issue).
+ * If PCIE table from PPTable have ULV entry + 8 entries,
+ * then ignore the last entry.*/
+ max_entry = (tmp < pcie_table->count) ? tmp : pcie_table->count;
+ for (i = 1; i < max_entry; i++) {
+ phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, i - 1,
+ get_pcie_gen_support(data->pcie_gen_cap,
+ pcie_table->entries[i].gen_speed),
+ get_pcie_lane_support(data->pcie_lane_cap,
+ pcie_table->entries[i].lane_width));
+ }
+ data->dpm_table.pcie_speed_table.count = max_entry - 1;
+ smum_update_smc_table(hwmgr, SMU_BIF_TABLE);
+ } else {
+ /* Hardcode Pcie Table */
+ phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 0,
+ get_pcie_gen_support(data->pcie_gen_cap,
+ PP_Min_PCIEGen),
+ get_pcie_lane_support(data->pcie_lane_cap,
+ PP_Max_PCIELane));
+ phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 1,
+ get_pcie_gen_support(data->pcie_gen_cap,
+ PP_Min_PCIEGen),
+ get_pcie_lane_support(data->pcie_lane_cap,
+ PP_Max_PCIELane));
+ phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 2,
+ get_pcie_gen_support(data->pcie_gen_cap,
+ PP_Max_PCIEGen),
+ get_pcie_lane_support(data->pcie_lane_cap,
+ PP_Max_PCIELane));
+ phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 3,
+ get_pcie_gen_support(data->pcie_gen_cap,
+ PP_Max_PCIEGen),
+ get_pcie_lane_support(data->pcie_lane_cap,
+ PP_Max_PCIELane));
+ phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 4,
+ get_pcie_gen_support(data->pcie_gen_cap,
+ PP_Max_PCIEGen),
+ get_pcie_lane_support(data->pcie_lane_cap,
+ PP_Max_PCIELane));
+ phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 5,
+ get_pcie_gen_support(data->pcie_gen_cap,
+ PP_Max_PCIEGen),
+ get_pcie_lane_support(data->pcie_lane_cap,
+ PP_Max_PCIELane));
+
+ data->dpm_table.pcie_speed_table.count = 6;
+ }
+ /* Populate last level for boot PCIE level, but do not increment count. */
+ if (hwmgr->chip_family == AMDGPU_FAMILY_CI) {
+ for (i = 0; i <= data->dpm_table.pcie_speed_table.count; i++)
+ phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, i,
+ get_pcie_gen_support(data->pcie_gen_cap,
+ PP_Max_PCIEGen),
+ data->vbios_boot_state.pcie_lane_bootup_value);
+ } else {
+ phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table,
+ data->dpm_table.pcie_speed_table.count,
+ get_pcie_gen_support(data->pcie_gen_cap,
+ PP_Min_PCIEGen),
+ get_pcie_lane_support(data->pcie_lane_cap,
+ PP_Max_PCIELane));
+ }
+ return 0;
+}
+
+static int smu7_reset_dpm_tables(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ memset(&(data->dpm_table), 0x00, sizeof(data->dpm_table));
+
+ phm_reset_single_dpm_table(
+ &data->dpm_table.sclk_table,
+ smum_get_mac_definition(hwmgr,
+ SMU_MAX_LEVELS_GRAPHICS),
+ MAX_REGULAR_DPM_NUMBER);
+ phm_reset_single_dpm_table(
+ &data->dpm_table.mclk_table,
+ smum_get_mac_definition(hwmgr,
+ SMU_MAX_LEVELS_MEMORY), MAX_REGULAR_DPM_NUMBER);
+
+ phm_reset_single_dpm_table(
+ &data->dpm_table.vddc_table,
+ smum_get_mac_definition(hwmgr,
+ SMU_MAX_LEVELS_VDDC),
+ MAX_REGULAR_DPM_NUMBER);
+ phm_reset_single_dpm_table(
+ &data->dpm_table.vddci_table,
+ smum_get_mac_definition(hwmgr,
+ SMU_MAX_LEVELS_VDDCI), MAX_REGULAR_DPM_NUMBER);
+
+ phm_reset_single_dpm_table(
+ &data->dpm_table.mvdd_table,
+ smum_get_mac_definition(hwmgr,
+ SMU_MAX_LEVELS_MVDD),
+ MAX_REGULAR_DPM_NUMBER);
+ return 0;
+}
+/*
+ * This function is to initialize all DPM state tables
+ * for SMU7 based on the dependency table.
+ * Dynamic state patching function will then trim these
+ * state tables to the allowed range based
+ * on the power policy or external client requests,
+ * such as UVD request, etc.
+ */
+
+static int smu7_setup_dpm_tables_v0(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_clock_voltage_dependency_table *allowed_vdd_sclk_table =
+ hwmgr->dyn_state.vddc_dependency_on_sclk;
+ struct phm_clock_voltage_dependency_table *allowed_vdd_mclk_table =
+ hwmgr->dyn_state.vddc_dependency_on_mclk;
+ struct phm_cac_leakage_table *std_voltage_table =
+ hwmgr->dyn_state.cac_leakage_table;
+ uint32_t i;
+
+ PP_ASSERT_WITH_CODE(allowed_vdd_sclk_table != NULL,
+ "SCLK dependency table is missing. This table is mandatory", return -EINVAL);
+ PP_ASSERT_WITH_CODE(allowed_vdd_sclk_table->count >= 1,
+ "SCLK dependency table has to have is missing. This table is mandatory", return -EINVAL);
+
+ PP_ASSERT_WITH_CODE(allowed_vdd_mclk_table != NULL,
+ "MCLK dependency table is missing. This table is mandatory", return -EINVAL);
+ PP_ASSERT_WITH_CODE(allowed_vdd_mclk_table->count >= 1,
+ "VMCLK dependency table has to have is missing. This table is mandatory", return -EINVAL);
+
+
+ /* Initialize Sclk DPM table based on allow Sclk values*/
+ data->dpm_table.sclk_table.count = 0;
+
+ for (i = 0; i < allowed_vdd_sclk_table->count; i++) {
+ if (i == 0 || data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count-1].value !=
+ allowed_vdd_sclk_table->entries[i].clk) {
+ data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count].value =
+ allowed_vdd_sclk_table->entries[i].clk;
+ data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count].enabled = (i == 0) ? 1 : 0;
+ data->dpm_table.sclk_table.count++;
+ }
+ }
+
+ PP_ASSERT_WITH_CODE(allowed_vdd_mclk_table != NULL,
+ "MCLK dependency table is missing. This table is mandatory", return -EINVAL);
+ /* Initialize Mclk DPM table based on allow Mclk values */
+ data->dpm_table.mclk_table.count = 0;
+ for (i = 0; i < allowed_vdd_mclk_table->count; i++) {
+ if (i == 0 || data->dpm_table.mclk_table.dpm_levels[data->dpm_table.mclk_table.count-1].value !=
+ allowed_vdd_mclk_table->entries[i].clk) {
+ data->dpm_table.mclk_table.dpm_levels[data->dpm_table.mclk_table.count].value =
+ allowed_vdd_mclk_table->entries[i].clk;
+ data->dpm_table.mclk_table.dpm_levels[data->dpm_table.mclk_table.count].enabled = (i == 0) ? 1 : 0;
+ data->dpm_table.mclk_table.count++;
+ }
+ }
+
+ /* Initialize Vddc DPM table based on allow Vddc values. And populate corresponding std values. */
+ for (i = 0; i < allowed_vdd_sclk_table->count; i++) {
+ data->dpm_table.vddc_table.dpm_levels[i].value = allowed_vdd_mclk_table->entries[i].v;
+ data->dpm_table.vddc_table.dpm_levels[i].param1 = std_voltage_table->entries[i].Leakage;
+ /* param1 is for corresponding std voltage */
+ data->dpm_table.vddc_table.dpm_levels[i].enabled = true;
+ }
+
+ data->dpm_table.vddc_table.count = allowed_vdd_sclk_table->count;
+ allowed_vdd_mclk_table = hwmgr->dyn_state.vddci_dependency_on_mclk;
+
+ if (NULL != allowed_vdd_mclk_table) {
+ /* Initialize Vddci DPM table based on allow Mclk values */
+ for (i = 0; i < allowed_vdd_mclk_table->count; i++) {
+ data->dpm_table.vddci_table.dpm_levels[i].value = allowed_vdd_mclk_table->entries[i].v;
+ data->dpm_table.vddci_table.dpm_levels[i].enabled = true;
+ }
+ data->dpm_table.vddci_table.count = allowed_vdd_mclk_table->count;
+ }
+
+ allowed_vdd_mclk_table = hwmgr->dyn_state.mvdd_dependency_on_mclk;
+
+ if (NULL != allowed_vdd_mclk_table) {
+ /*
+ * Initialize MVDD DPM table based on allow Mclk
+ * values
+ */
+ for (i = 0; i < allowed_vdd_mclk_table->count; i++) {
+ data->dpm_table.mvdd_table.dpm_levels[i].value = allowed_vdd_mclk_table->entries[i].v;
+ data->dpm_table.mvdd_table.dpm_levels[i].enabled = true;
+ }
+ data->dpm_table.mvdd_table.count = allowed_vdd_mclk_table->count;
+ }
+
+ return 0;
+}
+
+static int smu7_setup_dpm_tables_v1(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ uint32_t i;
+
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_sclk_table;
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table;
+
+ if (table_info == NULL)
+ return -EINVAL;
+
+ dep_sclk_table = table_info->vdd_dep_on_sclk;
+ dep_mclk_table = table_info->vdd_dep_on_mclk;
+
+ PP_ASSERT_WITH_CODE(dep_sclk_table != NULL,
+ "SCLK dependency table is missing.",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(dep_sclk_table->count >= 1,
+ "SCLK dependency table count is 0.",
+ return -EINVAL);
+
+ PP_ASSERT_WITH_CODE(dep_mclk_table != NULL,
+ "MCLK dependency table is missing.",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(dep_mclk_table->count >= 1,
+ "MCLK dependency table count is 0",
+ return -EINVAL);
+
+ /* Initialize Sclk DPM table based on allow Sclk values */
+ data->dpm_table.sclk_table.count = 0;
+ for (i = 0; i < dep_sclk_table->count; i++) {
+ if (i == 0 || data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count - 1].value !=
+ dep_sclk_table->entries[i].clk) {
+
+ data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count].value =
+ dep_sclk_table->entries[i].clk;
+
+ data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count].enabled =
+ (i == 0) ? true : false;
+ data->dpm_table.sclk_table.count++;
+ }
+ }
+ if (hwmgr->platform_descriptor.overdriveLimit.engineClock == 0)
+ hwmgr->platform_descriptor.overdriveLimit.engineClock = dep_sclk_table->entries[i-1].clk;
+ /* Initialize Mclk DPM table based on allow Mclk values */
+ data->dpm_table.mclk_table.count = 0;
+ for (i = 0; i < dep_mclk_table->count; i++) {
+ if (i == 0 || data->dpm_table.mclk_table.dpm_levels
+ [data->dpm_table.mclk_table.count - 1].value !=
+ dep_mclk_table->entries[i].clk) {
+ data->dpm_table.mclk_table.dpm_levels[data->dpm_table.mclk_table.count].value =
+ dep_mclk_table->entries[i].clk;
+ data->dpm_table.mclk_table.dpm_levels[data->dpm_table.mclk_table.count].enabled =
+ (i == 0) ? true : false;
+ data->dpm_table.mclk_table.count++;
+ }
+ }
+
+ if (hwmgr->platform_descriptor.overdriveLimit.memoryClock == 0)
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock = dep_mclk_table->entries[i-1].clk;
+ return 0;
+}
+
+static int smu7_odn_initial_default_setting(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_odn_dpm_table *odn_table = &(data->odn_dpm_table);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ uint32_t i;
+
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_sclk_table;
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table;
+ struct phm_odn_performance_level *entries;
+
+ if (table_info == NULL)
+ return -EINVAL;
+
+ dep_sclk_table = table_info->vdd_dep_on_sclk;
+ dep_mclk_table = table_info->vdd_dep_on_mclk;
+
+ odn_table->odn_core_clock_dpm_levels.num_of_pl =
+ data->golden_dpm_table.sclk_table.count;
+ entries = odn_table->odn_core_clock_dpm_levels.entries;
+ for (i=0; i<data->golden_dpm_table.sclk_table.count; i++) {
+ entries[i].clock = data->golden_dpm_table.sclk_table.dpm_levels[i].value;
+ entries[i].enabled = true;
+ entries[i].vddc = dep_sclk_table->entries[i].vddc;
+ }
+
+ smu_get_voltage_dependency_table_ppt_v1(dep_sclk_table,
+ (struct phm_ppt_v1_clock_voltage_dependency_table *)&(odn_table->vdd_dependency_on_sclk));
+
+ odn_table->odn_memory_clock_dpm_levels.num_of_pl =
+ data->golden_dpm_table.mclk_table.count;
+ entries = odn_table->odn_memory_clock_dpm_levels.entries;
+ for (i=0; i<data->golden_dpm_table.mclk_table.count; i++) {
+ entries[i].clock = data->golden_dpm_table.mclk_table.dpm_levels[i].value;
+ entries[i].enabled = true;
+ entries[i].vddc = dep_mclk_table->entries[i].vddc;
+ }
+
+ smu_get_voltage_dependency_table_ppt_v1(dep_mclk_table,
+ (struct phm_ppt_v1_clock_voltage_dependency_table *)&(odn_table->vdd_dependency_on_mclk));
+
+ return 0;
+}
+
+static void smu7_setup_voltage_range_from_vbios(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_sclk_table;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ uint32_t min_vddc = 0;
+ uint32_t max_vddc = 0;
+
+ if (!table_info)
+ return;
+
+ dep_sclk_table = table_info->vdd_dep_on_sclk;
+
+ atomctrl_get_voltage_range(hwmgr, &max_vddc, &min_vddc);
+
+ if (min_vddc == 0 || min_vddc > 2000
+ || min_vddc > dep_sclk_table->entries[0].vddc)
+ min_vddc = dep_sclk_table->entries[0].vddc;
+
+ if (max_vddc == 0 || max_vddc > 2000
+ || max_vddc < dep_sclk_table->entries[dep_sclk_table->count-1].vddc)
+ max_vddc = dep_sclk_table->entries[dep_sclk_table->count-1].vddc;
+
+ data->odn_dpm_table.min_vddc = min_vddc;
+ data->odn_dpm_table.max_vddc = max_vddc;
+}
+
+static void smu7_check_dpm_table_updated(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_odn_dpm_table *odn_table = &(data->odn_dpm_table);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ uint32_t i;
+
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_table;
+ struct phm_ppt_v1_clock_voltage_dependency_table *odn_dep_table;
+
+ if (table_info == NULL)
+ return;
+
+ for (i = 0; i < data->dpm_table.sclk_table.count; i++) {
+ if (odn_table->odn_core_clock_dpm_levels.entries[i].clock !=
+ data->dpm_table.sclk_table.dpm_levels[i].value) {
+ data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_SCLK;
+ break;
+ }
+ }
+
+ for (i = 0; i < data->dpm_table.mclk_table.count; i++) {
+ if (odn_table->odn_memory_clock_dpm_levels.entries[i].clock !=
+ data->dpm_table.mclk_table.dpm_levels[i].value) {
+ data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_MCLK;
+ break;
+ }
+ }
+
+ dep_table = table_info->vdd_dep_on_mclk;
+ odn_dep_table = (struct phm_ppt_v1_clock_voltage_dependency_table *)&(odn_table->vdd_dependency_on_mclk);
+
+ for (i = 0; i < dep_table->count; i++) {
+ if (dep_table->entries[i].vddc != odn_dep_table->entries[i].vddc) {
+ data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_VDDC | DPMTABLE_OD_UPDATE_MCLK;
+ return;
+ }
+ }
+
+ dep_table = table_info->vdd_dep_on_sclk;
+ odn_dep_table = (struct phm_ppt_v1_clock_voltage_dependency_table *)&(odn_table->vdd_dependency_on_sclk);
+ for (i = 0; i < dep_table->count; i++) {
+ if (dep_table->entries[i].vddc != odn_dep_table->entries[i].vddc) {
+ data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_VDDC | DPMTABLE_OD_UPDATE_SCLK;
+ return;
+ }
+ }
+ if (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_VDDC) {
+ data->need_update_smu7_dpm_table &= ~DPMTABLE_OD_UPDATE_VDDC;
+ data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_SCLK | DPMTABLE_OD_UPDATE_MCLK;
+ }
+}
+
+static int smu7_setup_default_dpm_tables(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ smu7_reset_dpm_tables(hwmgr);
+
+ if (hwmgr->pp_table_version == PP_TABLE_V1)
+ smu7_setup_dpm_tables_v1(hwmgr);
+ else if (hwmgr->pp_table_version == PP_TABLE_V0)
+ smu7_setup_dpm_tables_v0(hwmgr);
+
+ smu7_setup_default_pcie_table(hwmgr);
+
+ /* save a copy of the default DPM table */
+ memcpy(&(data->golden_dpm_table), &(data->dpm_table),
+ sizeof(struct smu7_dpm_table));
+
+ /* initialize ODN table */
+ if (hwmgr->od_enabled) {
+ if (data->odn_dpm_table.max_vddc) {
+ smu7_check_dpm_table_updated(hwmgr);
+ } else {
+ smu7_setup_voltage_range_from_vbios(hwmgr);
+ smu7_odn_initial_default_setting(hwmgr);
+ }
+ }
+ return 0;
+}
+
+static int smu7_enable_vrhot_gpio_interrupt(struct pp_hwmgr *hwmgr)
+{
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_RegulatorHot))
+ return smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_EnableVRHotGPIOInterrupt,
+ NULL);
+
+ return 0;
+}
+
+static int smu7_enable_sclk_control(struct pp_hwmgr *hwmgr)
+{
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SCLK_PWRMGT_CNTL,
+ SCLK_PWRMGT_OFF, 0);
+ return 0;
+}
+
+static int smu7_enable_ulv(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (data->ulv_supported)
+ return smum_send_msg_to_smc(hwmgr, PPSMC_MSG_EnableULV, NULL);
+
+ return 0;
+}
+
+static int smu7_disable_ulv(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (data->ulv_supported)
+ return smum_send_msg_to_smc(hwmgr, PPSMC_MSG_DisableULV, NULL);
+
+ return 0;
+}
+
+static int smu7_enable_deep_sleep_master_switch(struct pp_hwmgr *hwmgr)
+{
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkDeepSleep)) {
+ if (smum_send_msg_to_smc(hwmgr, PPSMC_MSG_MASTER_DeepSleep_ON, NULL))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to enable Master Deep Sleep switch failed!",
+ return -EINVAL);
+ } else {
+ if (smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_MASTER_DeepSleep_OFF,
+ NULL)) {
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to disable Master Deep Sleep switch failed!",
+ return -EINVAL);
+ }
+ }
+
+ return 0;
+}
+
+static int smu7_disable_deep_sleep_master_switch(struct pp_hwmgr *hwmgr)
+{
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkDeepSleep)) {
+ if (smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_MASTER_DeepSleep_OFF,
+ NULL)) {
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to disable Master Deep Sleep switch failed!",
+ return -EINVAL);
+ }
+ }
+
+ return 0;
+}
+
+static int smu7_disable_sclk_vce_handshake(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t soft_register_value = 0;
+ uint32_t handshake_disables_offset = data->soft_regs_start
+ + smum_get_offsetof(hwmgr,
+ SMU_SoftRegisters, HandshakeDisables);
+
+ soft_register_value = cgs_read_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, handshake_disables_offset);
+ soft_register_value |= SMU7_VCE_SCLK_HANDSHAKE_DISABLE;
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ handshake_disables_offset, soft_register_value);
+ return 0;
+}
+
+static int smu7_disable_handshake_uvd(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t soft_register_value = 0;
+ uint32_t handshake_disables_offset = data->soft_regs_start
+ + smum_get_offsetof(hwmgr,
+ SMU_SoftRegisters, HandshakeDisables);
+
+ soft_register_value = cgs_read_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, handshake_disables_offset);
+ soft_register_value |= smum_get_mac_definition(hwmgr,
+ SMU_UVD_MCLK_HANDSHAKE_DISABLE);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ handshake_disables_offset, soft_register_value);
+ return 0;
+}
+
+static int smu7_enable_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ /* enable SCLK dpm */
+ if (!data->sclk_dpm_key_disabled) {
+ if (hwmgr->chip_id == CHIP_VEGAM)
+ smu7_disable_sclk_vce_handshake(hwmgr);
+
+ PP_ASSERT_WITH_CODE(
+ (0 == smum_send_msg_to_smc(hwmgr, PPSMC_MSG_DPM_Enable, NULL)),
+ "Failed to enable SCLK DPM during DPM Start Function!",
+ return -EINVAL);
+ }
+
+ /* enable MCLK dpm */
+ if (0 == data->mclk_dpm_key_disabled) {
+ if (!(hwmgr->feature_mask & PP_UVD_HANDSHAKE_MASK))
+ smu7_disable_handshake_uvd(hwmgr);
+
+ PP_ASSERT_WITH_CODE(
+ (0 == smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_MCLKDPM_Enable,
+ NULL)),
+ "Failed to enable MCLK DPM during DPM Start Function!",
+ return -EINVAL);
+
+ if (hwmgr->chip_family != CHIP_VEGAM)
+ PHM_WRITE_FIELD(hwmgr->device, MC_SEQ_CNTL_3, CAC_EN, 0x1);
+
+
+ if (hwmgr->chip_family == AMDGPU_FAMILY_CI) {
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, 0xc0400d30, 0x5);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, 0xc0400d3c, 0x5);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, 0xc0400d80, 0x100005);
+ udelay(10);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, 0xc0400d30, 0x400005);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, 0xc0400d3c, 0x400005);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, 0xc0400d80, 0x500005);
+ } else {
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_MC0_CNTL, 0x5);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_MC1_CNTL, 0x5);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_CPL_CNTL, 0x100005);
+ udelay(10);
+ if (hwmgr->chip_id == CHIP_VEGAM) {
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_MC0_CNTL, 0x400009);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_MC1_CNTL, 0x400009);
+ } else {
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_MC0_CNTL, 0x400005);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_MC1_CNTL, 0x400005);
+ }
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_CPL_CNTL, 0x500005);
+ }
+ }
+
+ return 0;
+}
+
+static int smu7_start_dpm(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ /*enable general power management */
+
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT,
+ GLOBAL_PWRMGT_EN, 1);
+
+ /* enable sclk deep sleep */
+
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SCLK_PWRMGT_CNTL,
+ DYNAMIC_PM_EN, 1);
+
+ /* prepare for PCIE DPM */
+
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ data->soft_regs_start +
+ smum_get_offsetof(hwmgr, SMU_SoftRegisters,
+ VoltageChangeTimeout), 0x1000);
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__PCIE,
+ SWRST_COMMAND_1, RESETLC, 0x0);
+
+ if (hwmgr->chip_family == AMDGPU_FAMILY_CI)
+ cgs_write_register(hwmgr->device, 0x1488,
+ (cgs_read_register(hwmgr->device, 0x1488) & ~0x1));
+
+ if (smu7_enable_sclk_mclk_dpm(hwmgr)) {
+ pr_err("Failed to enable Sclk DPM and Mclk DPM!");
+ return -EINVAL;
+ }
+
+ /* enable PCIE dpm */
+ if (0 == data->pcie_dpm_key_disabled) {
+ PP_ASSERT_WITH_CODE(
+ (0 == smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_PCIeDPM_Enable,
+ NULL)),
+ "Failed to enable pcie DPM during DPM Start Function!",
+ return -EINVAL);
+ }
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_Falcon_QuickTransition)) {
+ PP_ASSERT_WITH_CODE((0 == smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_EnableACDCGPIOInterrupt,
+ NULL)),
+ "Failed to enable AC DC GPIO Interrupt!",
+ );
+ }
+
+ return 0;
+}
+
+static int smu7_disable_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ /* disable SCLK dpm */
+ if (!data->sclk_dpm_key_disabled) {
+ PP_ASSERT_WITH_CODE(true == smum_is_dpm_running(hwmgr),
+ "Trying to disable SCLK DPM when DPM is disabled",
+ return 0);
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_DPM_Disable, NULL);
+ }
+
+ /* disable MCLK dpm */
+ if (!data->mclk_dpm_key_disabled) {
+ PP_ASSERT_WITH_CODE(true == smum_is_dpm_running(hwmgr),
+ "Trying to disable MCLK DPM when DPM is disabled",
+ return 0);
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_MCLKDPM_Disable, NULL);
+ }
+
+ return 0;
+}
+
+static int smu7_stop_dpm(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ /* disable general power management */
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT,
+ GLOBAL_PWRMGT_EN, 0);
+ /* disable sclk deep sleep */
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SCLK_PWRMGT_CNTL,
+ DYNAMIC_PM_EN, 0);
+
+ /* disable PCIE dpm */
+ if (!data->pcie_dpm_key_disabled) {
+ PP_ASSERT_WITH_CODE(
+ (smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_PCIeDPM_Disable,
+ NULL) == 0),
+ "Failed to disable pcie DPM during DPM Stop Function!",
+ return -EINVAL);
+ }
+
+ smu7_disable_sclk_mclk_dpm(hwmgr);
+
+ PP_ASSERT_WITH_CODE(true == smum_is_dpm_running(hwmgr),
+ "Trying to disable voltage DPM when DPM is disabled",
+ return 0);
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_Voltage_Cntl_Disable, NULL);
+
+ return 0;
+}
+
+static void smu7_set_dpm_event_sources(struct pp_hwmgr *hwmgr, uint32_t sources)
+{
+ bool protection;
+ enum DPM_EVENT_SRC src;
+
+ switch (sources) {
+ default:
+ pr_err("Unknown throttling event sources.");
+ fallthrough;
+ case 0:
+ protection = false;
+ /* src is unused */
+ break;
+ case (1 << PHM_AutoThrottleSource_Thermal):
+ protection = true;
+ src = DPM_EVENT_SRC_DIGITAL;
+ break;
+ case (1 << PHM_AutoThrottleSource_External):
+ protection = true;
+ src = DPM_EVENT_SRC_EXTERNAL;
+ break;
+ case (1 << PHM_AutoThrottleSource_External) |
+ (1 << PHM_AutoThrottleSource_Thermal):
+ protection = true;
+ src = DPM_EVENT_SRC_DIGITAL_OR_EXTERNAL;
+ break;
+ }
+ /* Order matters - don't enable thermal protection for the wrong source. */
+ if (protection) {
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_THERMAL_CTRL,
+ DPM_EVENT_SRC, src);
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT,
+ THERMAL_PROTECTION_DIS,
+ !phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ThermalController));
+ } else
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT,
+ THERMAL_PROTECTION_DIS, 1);
+}
+
+static int smu7_enable_auto_throttle_source(struct pp_hwmgr *hwmgr,
+ PHM_AutoThrottleSource source)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (!(data->active_auto_throttle_sources & (1 << source))) {
+ data->active_auto_throttle_sources |= 1 << source;
+ smu7_set_dpm_event_sources(hwmgr, data->active_auto_throttle_sources);
+ }
+ return 0;
+}
+
+static int smu7_enable_thermal_auto_throttle(struct pp_hwmgr *hwmgr)
+{
+ return smu7_enable_auto_throttle_source(hwmgr, PHM_AutoThrottleSource_Thermal);
+}
+
+static int smu7_disable_auto_throttle_source(struct pp_hwmgr *hwmgr,
+ PHM_AutoThrottleSource source)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (data->active_auto_throttle_sources & (1 << source)) {
+ data->active_auto_throttle_sources &= ~(1 << source);
+ smu7_set_dpm_event_sources(hwmgr, data->active_auto_throttle_sources);
+ }
+ return 0;
+}
+
+static int smu7_disable_thermal_auto_throttle(struct pp_hwmgr *hwmgr)
+{
+ return smu7_disable_auto_throttle_source(hwmgr, PHM_AutoThrottleSource_Thermal);
+}
+
+static int smu7_pcie_performance_request(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ data->pcie_performance_request = true;
+
+ return 0;
+}
+
+static int smu7_enable_dpm_tasks(struct pp_hwmgr *hwmgr)
+{
+ int tmp_result = 0;
+ int result = 0;
+
+ if (smu7_voltage_control(hwmgr)) {
+ tmp_result = smu7_enable_voltage_control(hwmgr);
+ PP_ASSERT_WITH_CODE(tmp_result == 0,
+ "Failed to enable voltage control!",
+ result = tmp_result);
+
+ tmp_result = smu7_construct_voltage_tables(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to construct voltage tables!",
+ result = tmp_result);
+ }
+ smum_initialize_mc_reg_table(hwmgr);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EngineSpreadSpectrumSupport))
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, 1);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ThermalController))
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, 0);
+
+ tmp_result = smu7_program_static_screen_threshold_parameters(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to program static screen threshold parameters!",
+ result = tmp_result);
+
+ tmp_result = smu7_enable_display_gap(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to enable display gap!", result = tmp_result);
+
+ tmp_result = smu7_program_voting_clients(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to program voting clients!", result = tmp_result);
+
+ tmp_result = smum_process_firmware_header(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to process firmware header!", result = tmp_result);
+
+ if (hwmgr->chip_id != CHIP_VEGAM) {
+ tmp_result = smu7_initial_switch_from_arbf0_to_f1(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to initialize switch from ArbF0 to F1!",
+ result = tmp_result);
+ }
+
+ result = smu7_setup_default_dpm_tables(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to setup default DPM tables!", return result);
+
+ tmp_result = smum_init_smc_table(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to initialize SMC table!", result = tmp_result);
+
+ tmp_result = smu7_enable_vrhot_gpio_interrupt(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to enable VR hot GPIO interrupt!", result = tmp_result);
+
+ smum_send_msg_to_smc(hwmgr, (PPSMC_Msg)PPSMC_NoDisplay, NULL);
+
+ tmp_result = smu7_enable_sclk_control(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to enable SCLK control!", result = tmp_result);
+
+ tmp_result = smu7_enable_smc_voltage_controller(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to enable voltage control!", result = tmp_result);
+
+ tmp_result = smu7_enable_ulv(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to enable ULV!", result = tmp_result);
+
+ tmp_result = smu7_enable_deep_sleep_master_switch(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to enable deep sleep master switch!", result = tmp_result);
+
+ tmp_result = smu7_enable_didt_config(hwmgr);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to enable deep sleep master switch!", result = tmp_result);
+
+ tmp_result = smu7_start_dpm(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to start DPM!", result = tmp_result);
+
+ tmp_result = smu7_enable_smc_cac(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to enable SMC CAC!", result = tmp_result);
+
+ tmp_result = smu7_enable_power_containment(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to enable power containment!", result = tmp_result);
+
+ tmp_result = smu7_power_control_set_level(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to power control set level!", result = tmp_result);
+
+ tmp_result = smu7_enable_thermal_auto_throttle(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to enable thermal auto throttle!", result = tmp_result);
+
+ tmp_result = smu7_pcie_performance_request(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "pcie performance request failed!", result = tmp_result);
+
+ return 0;
+}
+
+static int smu7_avfs_control(struct pp_hwmgr *hwmgr, bool enable)
+{
+ if (!hwmgr->avfs_supported)
+ return 0;
+
+ if (enable) {
+ if (!PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device,
+ CGS_IND_REG__SMC, FEATURE_STATUS, AVS_ON)) {
+ PP_ASSERT_WITH_CODE(!smum_send_msg_to_smc(
+ hwmgr, PPSMC_MSG_EnableAvfs, NULL),
+ "Failed to enable AVFS!",
+ return -EINVAL);
+ }
+ } else if (PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device,
+ CGS_IND_REG__SMC, FEATURE_STATUS, AVS_ON)) {
+ PP_ASSERT_WITH_CODE(!smum_send_msg_to_smc(
+ hwmgr, PPSMC_MSG_DisableAvfs, NULL),
+ "Failed to disable AVFS!",
+ return -EINVAL);
+ }
+
+ return 0;
+}
+
+static int smu7_update_avfs(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (!hwmgr->avfs_supported)
+ return 0;
+
+ if (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_VDDC) {
+ smu7_avfs_control(hwmgr, false);
+ } else if (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_SCLK) {
+ smu7_avfs_control(hwmgr, false);
+ smu7_avfs_control(hwmgr, true);
+ } else {
+ smu7_avfs_control(hwmgr, true);
+ }
+
+ return 0;
+}
+
+static int smu7_disable_dpm_tasks(struct pp_hwmgr *hwmgr)
+{
+ int tmp_result, result = 0;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ThermalController))
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, 1);
+
+ tmp_result = smu7_disable_power_containment(hwmgr);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to disable power containment!", result = tmp_result);
+
+ tmp_result = smu7_disable_smc_cac(hwmgr);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to disable SMC CAC!", result = tmp_result);
+
+ tmp_result = smu7_disable_didt_config(hwmgr);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to disable DIDT!", result = tmp_result);
+
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_SPLL_SPREAD_SPECTRUM, SSEN, 0);
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, 0);
+
+ tmp_result = smu7_disable_thermal_auto_throttle(hwmgr);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to disable thermal auto throttle!", result = tmp_result);
+
+ tmp_result = smu7_avfs_control(hwmgr, false);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to disable AVFS!", result = tmp_result);
+
+ tmp_result = smu7_stop_dpm(hwmgr);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to stop DPM!", result = tmp_result);
+
+ tmp_result = smu7_disable_deep_sleep_master_switch(hwmgr);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to disable deep sleep master switch!", result = tmp_result);
+
+ tmp_result = smu7_disable_ulv(hwmgr);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to disable ULV!", result = tmp_result);
+
+ tmp_result = smu7_clear_voting_clients(hwmgr);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to clear voting clients!", result = tmp_result);
+
+ tmp_result = smu7_reset_to_default(hwmgr);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to reset to default!", result = tmp_result);
+
+ tmp_result = smum_stop_smc(hwmgr);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to stop smc!", result = tmp_result);
+
+ tmp_result = smu7_force_switch_to_arbf0(hwmgr);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to force to switch arbf0!", result = tmp_result);
+
+ return result;
+}
+
+static void smu7_init_dpm_defaults(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ data->dll_default_on = false;
+ data->mclk_dpm0_activity_target = 0xa;
+ data->vddc_vddgfx_delta = 300;
+ data->static_screen_threshold = SMU7_STATICSCREENTHRESHOLD_DFLT;
+ data->static_screen_threshold_unit = SMU7_STATICSCREENTHRESHOLDUNIT_DFLT;
+ data->voting_rights_clients[0] = SMU7_VOTINGRIGHTSCLIENTS_DFLT0;
+ data->voting_rights_clients[1]= SMU7_VOTINGRIGHTSCLIENTS_DFLT1;
+ data->voting_rights_clients[2] = SMU7_VOTINGRIGHTSCLIENTS_DFLT2;
+ data->voting_rights_clients[3]= SMU7_VOTINGRIGHTSCLIENTS_DFLT3;
+ data->voting_rights_clients[4]= SMU7_VOTINGRIGHTSCLIENTS_DFLT4;
+ data->voting_rights_clients[5]= SMU7_VOTINGRIGHTSCLIENTS_DFLT5;
+ data->voting_rights_clients[6]= SMU7_VOTINGRIGHTSCLIENTS_DFLT6;
+ data->voting_rights_clients[7]= SMU7_VOTINGRIGHTSCLIENTS_DFLT7;
+
+ data->mclk_dpm_key_disabled = hwmgr->feature_mask & PP_MCLK_DPM_MASK ? false : true;
+ data->sclk_dpm_key_disabled = hwmgr->feature_mask & PP_SCLK_DPM_MASK ? false : true;
+ data->pcie_dpm_key_disabled = hwmgr->feature_mask & PP_PCIE_DPM_MASK ? false : true;
+ /* need to set voltage control types before EVV patching */
+ data->voltage_control = SMU7_VOLTAGE_CONTROL_NONE;
+ data->vddci_control = SMU7_VOLTAGE_CONTROL_NONE;
+ data->mvdd_control = SMU7_VOLTAGE_CONTROL_NONE;
+ data->enable_tdc_limit_feature = true;
+ data->enable_pkg_pwr_tracking_feature = true;
+ data->force_pcie_gen = PP_PCIEGenInvalid;
+ data->ulv_supported = hwmgr->feature_mask & PP_ULV_MASK ? true : false;
+ data->current_profile_setting.bupdate_sclk = 1;
+ data->current_profile_setting.sclk_up_hyst = 0;
+ data->current_profile_setting.sclk_down_hyst = 100;
+ data->current_profile_setting.sclk_activity = SMU7_SCLK_TARGETACTIVITY_DFLT;
+ data->current_profile_setting.bupdate_mclk = 1;
+ if (hwmgr->chip_id >= CHIP_POLARIS10) {
+ if (adev->gmc.vram_width == 256) {
+ data->current_profile_setting.mclk_up_hyst = 10;
+ data->current_profile_setting.mclk_down_hyst = 60;
+ data->current_profile_setting.mclk_activity = 25;
+ } else if (adev->gmc.vram_width == 128) {
+ data->current_profile_setting.mclk_up_hyst = 5;
+ data->current_profile_setting.mclk_down_hyst = 16;
+ data->current_profile_setting.mclk_activity = 20;
+ } else if (adev->gmc.vram_width == 64) {
+ data->current_profile_setting.mclk_up_hyst = 3;
+ data->current_profile_setting.mclk_down_hyst = 16;
+ data->current_profile_setting.mclk_activity = 20;
+ }
+ } else {
+ data->current_profile_setting.mclk_up_hyst = 0;
+ data->current_profile_setting.mclk_down_hyst = 100;
+ data->current_profile_setting.mclk_activity = SMU7_MCLK_TARGETACTIVITY_DFLT;
+ }
+ hwmgr->workload_mask = 1 << hwmgr->workload_prority[PP_SMC_POWER_PROFILE_FULLSCREEN3D];
+ hwmgr->power_profile_mode = PP_SMC_POWER_PROFILE_FULLSCREEN3D;
+ hwmgr->default_power_profile_mode = PP_SMC_POWER_PROFILE_FULLSCREEN3D;
+
+ if (hwmgr->chip_id == CHIP_POLARIS12 || hwmgr->is_kicker) {
+ uint8_t tmp1, tmp2;
+ uint16_t tmp3 = 0;
+ atomctrl_get_svi2_info(hwmgr, VOLTAGE_TYPE_VDDC, &tmp1, &tmp2,
+ &tmp3);
+ tmp3 = (tmp3 >> 5) & 0x3;
+ data->vddc_phase_shed_control = ((tmp3 << 1) | (tmp3 >> 1)) & 0x3;
+ } else if (hwmgr->chip_family == AMDGPU_FAMILY_CI) {
+ data->vddc_phase_shed_control = 1;
+ } else {
+ data->vddc_phase_shed_control = 0;
+ }
+
+ if (hwmgr->chip_id == CHIP_HAWAII) {
+ data->thermal_temp_setting.temperature_low = 94500;
+ data->thermal_temp_setting.temperature_high = 95000;
+ data->thermal_temp_setting.temperature_shutdown = 104000;
+ } else {
+ data->thermal_temp_setting.temperature_low = 99500;
+ data->thermal_temp_setting.temperature_high = 100000;
+ data->thermal_temp_setting.temperature_shutdown = 104000;
+ }
+
+ data->fast_watermark_threshold = 100;
+ if (atomctrl_is_voltage_controlled_by_gpio_v3(hwmgr,
+ VOLTAGE_TYPE_VDDC, VOLTAGE_OBJ_SVID2))
+ data->voltage_control = SMU7_VOLTAGE_CONTROL_BY_SVID2;
+ else if (atomctrl_is_voltage_controlled_by_gpio_v3(hwmgr,
+ VOLTAGE_TYPE_VDDC, VOLTAGE_OBJ_GPIO_LUT))
+ data->voltage_control = SMU7_VOLTAGE_CONTROL_BY_GPIO;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ControlVDDGFX)) {
+ if (atomctrl_is_voltage_controlled_by_gpio_v3(hwmgr,
+ VOLTAGE_TYPE_VDDGFX, VOLTAGE_OBJ_SVID2)) {
+ data->vdd_gfx_control = SMU7_VOLTAGE_CONTROL_BY_SVID2;
+ }
+ }
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EnableMVDDControl)) {
+ if (atomctrl_is_voltage_controlled_by_gpio_v3(hwmgr,
+ VOLTAGE_TYPE_MVDDC, VOLTAGE_OBJ_GPIO_LUT))
+ data->mvdd_control = SMU7_VOLTAGE_CONTROL_BY_GPIO;
+ else if (atomctrl_is_voltage_controlled_by_gpio_v3(hwmgr,
+ VOLTAGE_TYPE_MVDDC, VOLTAGE_OBJ_SVID2))
+ data->mvdd_control = SMU7_VOLTAGE_CONTROL_BY_SVID2;
+ }
+
+ if (SMU7_VOLTAGE_CONTROL_NONE == data->vdd_gfx_control)
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ControlVDDGFX);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ControlVDDCI)) {
+ if (atomctrl_is_voltage_controlled_by_gpio_v3(hwmgr,
+ VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_GPIO_LUT))
+ data->vddci_control = SMU7_VOLTAGE_CONTROL_BY_GPIO;
+ else if (atomctrl_is_voltage_controlled_by_gpio_v3(hwmgr,
+ VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_SVID2))
+ data->vddci_control = SMU7_VOLTAGE_CONTROL_BY_SVID2;
+ }
+
+ if (data->mvdd_control == SMU7_VOLTAGE_CONTROL_NONE)
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EnableMVDDControl);
+
+ if (data->vddci_control == SMU7_VOLTAGE_CONTROL_NONE)
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ControlVDDCI);
+
+ if ((hwmgr->pp_table_version != PP_TABLE_V0) && (hwmgr->feature_mask & PP_CLOCK_STRETCH_MASK)
+ && (table_info->cac_dtp_table->usClockStretchAmount != 0))
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ClockStretcher);
+
+ data->pcie_gen_performance.max = PP_PCIEGen1;
+ data->pcie_gen_performance.min = PP_PCIEGen3;
+ data->pcie_gen_power_saving.max = PP_PCIEGen1;
+ data->pcie_gen_power_saving.min = PP_PCIEGen3;
+ data->pcie_lane_performance.max = 0;
+ data->pcie_lane_performance.min = 16;
+ data->pcie_lane_power_saving.max = 0;
+ data->pcie_lane_power_saving.min = 16;
+
+
+ if (adev->pg_flags & AMD_PG_SUPPORT_UVD)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UVDPowerGating);
+ if (adev->pg_flags & AMD_PG_SUPPORT_VCE)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_VCEPowerGating);
+}
+
+/**
+* Get Leakage VDDC based on leakage ID.
+*
+* @param hwmgr the address of the powerplay hardware manager.
+* @return always 0
+*/
+static int smu7_get_evv_voltages(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint16_t vv_id;
+ uint16_t vddc = 0;
+ uint16_t vddgfx = 0;
+ uint16_t i, j;
+ uint32_t sclk = 0;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)hwmgr->pptable;
+ struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table = NULL;
+
+
+ for (i = 0; i < SMU7_MAX_LEAKAGE_COUNT; i++) {
+ vv_id = ATOM_VIRTUAL_VOLTAGE_ID0 + i;
+
+ if (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) {
+ if ((hwmgr->pp_table_version == PP_TABLE_V1)
+ && !phm_get_sclk_for_voltage_evv(hwmgr,
+ table_info->vddgfx_lookup_table, vv_id, &sclk)) {
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ClockStretcher)) {
+ sclk_table = table_info->vdd_dep_on_sclk;
+
+ for (j = 1; j < sclk_table->count; j++) {
+ if (sclk_table->entries[j].clk == sclk &&
+ sclk_table->entries[j].cks_enable == 0) {
+ sclk += 5000;
+ break;
+ }
+ }
+ }
+ if (0 == atomctrl_get_voltage_evv_on_sclk
+ (hwmgr, VOLTAGE_TYPE_VDDGFX, sclk,
+ vv_id, &vddgfx)) {
+ /* need to make sure vddgfx is less than 2v or else, it could burn the ASIC. */
+ PP_ASSERT_WITH_CODE((vddgfx < 2000 && vddgfx != 0), "Invalid VDDGFX value!", return -EINVAL);
+
+ /* the voltage should not be zero nor equal to leakage ID */
+ if (vddgfx != 0 && vddgfx != vv_id) {
+ data->vddcgfx_leakage.actual_voltage[data->vddcgfx_leakage.count] = vddgfx;
+ data->vddcgfx_leakage.leakage_id[data->vddcgfx_leakage.count] = vv_id;
+ data->vddcgfx_leakage.count++;
+ }
+ } else {
+ pr_info("Error retrieving EVV voltage value!\n");
+ }
+ }
+ } else {
+ if ((hwmgr->pp_table_version == PP_TABLE_V0)
+ || !phm_get_sclk_for_voltage_evv(hwmgr,
+ table_info->vddc_lookup_table, vv_id, &sclk)) {
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ClockStretcher)) {
+ if (table_info == NULL)
+ return -EINVAL;
+ sclk_table = table_info->vdd_dep_on_sclk;
+
+ for (j = 1; j < sclk_table->count; j++) {
+ if (sclk_table->entries[j].clk == sclk &&
+ sclk_table->entries[j].cks_enable == 0) {
+ sclk += 5000;
+ break;
+ }
+ }
+ }
+
+ if (phm_get_voltage_evv_on_sclk(hwmgr,
+ VOLTAGE_TYPE_VDDC,
+ sclk, vv_id, &vddc) == 0) {
+ if (vddc >= 2000 || vddc == 0)
+ return -EINVAL;
+ } else {
+ pr_debug("failed to retrieving EVV voltage!\n");
+ continue;
+ }
+
+ /* the voltage should not be zero nor equal to leakage ID */
+ if (vddc != 0 && vddc != vv_id) {
+ data->vddc_leakage.actual_voltage[data->vddc_leakage.count] = (uint16_t)(vddc);
+ data->vddc_leakage.leakage_id[data->vddc_leakage.count] = vv_id;
+ data->vddc_leakage.count++;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Change virtual leakage voltage to actual value.
+ *
+ * @param hwmgr the address of the powerplay hardware manager.
+ * @param pointer to changing voltage
+ * @param pointer to leakage table
+ */
+static void smu7_patch_ppt_v1_with_vdd_leakage(struct pp_hwmgr *hwmgr,
+ uint16_t *voltage, struct smu7_leakage_voltage *leakage_table)
+{
+ uint32_t index;
+
+ /* search for leakage voltage ID 0xff01 ~ 0xff08 */
+ for (index = 0; index < leakage_table->count; index++) {
+ /* if this voltage matches a leakage voltage ID */
+ /* patch with actual leakage voltage */
+ if (leakage_table->leakage_id[index] == *voltage) {
+ *voltage = leakage_table->actual_voltage[index];
+ break;
+ }
+ }
+
+ if (*voltage > ATOM_VIRTUAL_VOLTAGE_ID0)
+ pr_err("Voltage value looks like a Leakage ID but it's not patched \n");
+}
+
+/**
+* Patch voltage lookup table by EVV leakages.
+*
+* @param hwmgr the address of the powerplay hardware manager.
+* @param pointer to voltage lookup table
+* @param pointer to leakage table
+* @return always 0
+*/
+static int smu7_patch_lookup_table_with_leakage(struct pp_hwmgr *hwmgr,
+ phm_ppt_v1_voltage_lookup_table *lookup_table,
+ struct smu7_leakage_voltage *leakage_table)
+{
+ uint32_t i;
+
+ for (i = 0; i < lookup_table->count; i++)
+ smu7_patch_ppt_v1_with_vdd_leakage(hwmgr,
+ &lookup_table->entries[i].us_vdd, leakage_table);
+
+ return 0;
+}
+
+static int smu7_patch_clock_voltage_limits_with_vddc_leakage(
+ struct pp_hwmgr *hwmgr, struct smu7_leakage_voltage *leakage_table,
+ uint16_t *vddc)
+{
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ smu7_patch_ppt_v1_with_vdd_leakage(hwmgr, (uint16_t *)vddc, leakage_table);
+ hwmgr->dyn_state.max_clock_voltage_on_dc.vddc =
+ table_info->max_clock_voltage_on_dc.vddc;
+ return 0;
+}
+
+static int smu7_patch_voltage_dependency_tables_with_lookup_table(
+ struct pp_hwmgr *hwmgr)
+{
+ uint8_t entry_id;
+ uint8_t voltage_id;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
+ table_info->vdd_dep_on_sclk;
+ struct phm_ppt_v1_clock_voltage_dependency_table *mclk_table =
+ table_info->vdd_dep_on_mclk;
+ struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+ table_info->mm_dep_table;
+
+ if (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) {
+ for (entry_id = 0; entry_id < sclk_table->count; ++entry_id) {
+ voltage_id = sclk_table->entries[entry_id].vddInd;
+ sclk_table->entries[entry_id].vddgfx =
+ table_info->vddgfx_lookup_table->entries[voltage_id].us_vdd;
+ }
+ } else {
+ for (entry_id = 0; entry_id < sclk_table->count; ++entry_id) {
+ voltage_id = sclk_table->entries[entry_id].vddInd;
+ sclk_table->entries[entry_id].vddc =
+ table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
+ }
+ }
+
+ for (entry_id = 0; entry_id < mclk_table->count; ++entry_id) {
+ voltage_id = mclk_table->entries[entry_id].vddInd;
+ mclk_table->entries[entry_id].vddc =
+ table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
+ }
+
+ for (entry_id = 0; entry_id < mm_table->count; ++entry_id) {
+ voltage_id = mm_table->entries[entry_id].vddcInd;
+ mm_table->entries[entry_id].vddc =
+ table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
+ }
+
+ return 0;
+
+}
+
+static int phm_add_voltage(struct pp_hwmgr *hwmgr,
+ phm_ppt_v1_voltage_lookup_table *look_up_table,
+ phm_ppt_v1_voltage_lookup_record *record)
+{
+ uint32_t i;
+
+ PP_ASSERT_WITH_CODE((NULL != look_up_table),
+ "Lookup Table empty.", return -EINVAL);
+ PP_ASSERT_WITH_CODE((0 != look_up_table->count),
+ "Lookup Table empty.", return -EINVAL);
+
+ i = smum_get_mac_definition(hwmgr, SMU_MAX_LEVELS_VDDGFX);
+ PP_ASSERT_WITH_CODE((i >= look_up_table->count),
+ "Lookup Table is full.", return -EINVAL);
+
+ /* This is to avoid entering duplicate calculated records. */
+ for (i = 0; i < look_up_table->count; i++) {
+ if (look_up_table->entries[i].us_vdd == record->us_vdd) {
+ if (look_up_table->entries[i].us_calculated == 1)
+ return 0;
+ break;
+ }
+ }
+
+ look_up_table->entries[i].us_calculated = 1;
+ look_up_table->entries[i].us_vdd = record->us_vdd;
+ look_up_table->entries[i].us_cac_low = record->us_cac_low;
+ look_up_table->entries[i].us_cac_mid = record->us_cac_mid;
+ look_up_table->entries[i].us_cac_high = record->us_cac_high;
+ /* Only increment the count when we're appending, not replacing duplicate entry. */
+ if (i == look_up_table->count)
+ look_up_table->count++;
+
+ return 0;
+}
+
+
+static int smu7_calc_voltage_dependency_tables(struct pp_hwmgr *hwmgr)
+{
+ uint8_t entry_id;
+ struct phm_ppt_v1_voltage_lookup_record v_record;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ phm_ppt_v1_clock_voltage_dependency_table *sclk_table = pptable_info->vdd_dep_on_sclk;
+ phm_ppt_v1_clock_voltage_dependency_table *mclk_table = pptable_info->vdd_dep_on_mclk;
+
+ if (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) {
+ for (entry_id = 0; entry_id < sclk_table->count; ++entry_id) {
+ if (sclk_table->entries[entry_id].vdd_offset & (1 << 15))
+ v_record.us_vdd = sclk_table->entries[entry_id].vddgfx +
+ sclk_table->entries[entry_id].vdd_offset - 0xFFFF;
+ else
+ v_record.us_vdd = sclk_table->entries[entry_id].vddgfx +
+ sclk_table->entries[entry_id].vdd_offset;
+
+ sclk_table->entries[entry_id].vddc =
+ v_record.us_cac_low = v_record.us_cac_mid =
+ v_record.us_cac_high = v_record.us_vdd;
+
+ phm_add_voltage(hwmgr, pptable_info->vddc_lookup_table, &v_record);
+ }
+
+ for (entry_id = 0; entry_id < mclk_table->count; ++entry_id) {
+ if (mclk_table->entries[entry_id].vdd_offset & (1 << 15))
+ v_record.us_vdd = mclk_table->entries[entry_id].vddc +
+ mclk_table->entries[entry_id].vdd_offset - 0xFFFF;
+ else
+ v_record.us_vdd = mclk_table->entries[entry_id].vddc +
+ mclk_table->entries[entry_id].vdd_offset;
+
+ mclk_table->entries[entry_id].vddgfx = v_record.us_cac_low =
+ v_record.us_cac_mid = v_record.us_cac_high = v_record.us_vdd;
+ phm_add_voltage(hwmgr, pptable_info->vddgfx_lookup_table, &v_record);
+ }
+ }
+ return 0;
+}
+
+static int smu7_calc_mm_voltage_dependency_table(struct pp_hwmgr *hwmgr)
+{
+ uint8_t entry_id;
+ struct phm_ppt_v1_voltage_lookup_record v_record;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table = pptable_info->mm_dep_table;
+
+ if (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) {
+ for (entry_id = 0; entry_id < mm_table->count; entry_id++) {
+ if (mm_table->entries[entry_id].vddgfx_offset & (1 << 15))
+ v_record.us_vdd = mm_table->entries[entry_id].vddc +
+ mm_table->entries[entry_id].vddgfx_offset - 0xFFFF;
+ else
+ v_record.us_vdd = mm_table->entries[entry_id].vddc +
+ mm_table->entries[entry_id].vddgfx_offset;
+
+ /* Add the calculated VDDGFX to the VDDGFX lookup table */
+ mm_table->entries[entry_id].vddgfx = v_record.us_cac_low =
+ v_record.us_cac_mid = v_record.us_cac_high = v_record.us_vdd;
+ phm_add_voltage(hwmgr, pptable_info->vddgfx_lookup_table, &v_record);
+ }
+ }
+ return 0;
+}
+
+static int smu7_sort_lookup_table(struct pp_hwmgr *hwmgr,
+ struct phm_ppt_v1_voltage_lookup_table *lookup_table)
+{
+ uint32_t table_size, i, j;
+ table_size = lookup_table->count;
+
+ PP_ASSERT_WITH_CODE(0 != lookup_table->count,
+ "Lookup table is empty", return -EINVAL);
+
+ /* Sorting voltages */
+ for (i = 0; i < table_size - 1; i++) {
+ for (j = i + 1; j > 0; j--) {
+ if (lookup_table->entries[j].us_vdd <
+ lookup_table->entries[j - 1].us_vdd) {
+ swap(lookup_table->entries[j - 1],
+ lookup_table->entries[j]);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int smu7_complete_dependency_tables(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+ int tmp_result;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ if (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) {
+ tmp_result = smu7_patch_lookup_table_with_leakage(hwmgr,
+ table_info->vddgfx_lookup_table, &(data->vddcgfx_leakage));
+ if (tmp_result != 0)
+ result = tmp_result;
+
+ smu7_patch_ppt_v1_with_vdd_leakage(hwmgr,
+ &table_info->max_clock_voltage_on_dc.vddgfx, &(data->vddcgfx_leakage));
+ } else {
+
+ tmp_result = smu7_patch_lookup_table_with_leakage(hwmgr,
+ table_info->vddc_lookup_table, &(data->vddc_leakage));
+ if (tmp_result)
+ result = tmp_result;
+
+ tmp_result = smu7_patch_clock_voltage_limits_with_vddc_leakage(hwmgr,
+ &(data->vddc_leakage), &table_info->max_clock_voltage_on_dc.vddc);
+ if (tmp_result)
+ result = tmp_result;
+ }
+
+ tmp_result = smu7_patch_voltage_dependency_tables_with_lookup_table(hwmgr);
+ if (tmp_result)
+ result = tmp_result;
+
+ tmp_result = smu7_calc_voltage_dependency_tables(hwmgr);
+ if (tmp_result)
+ result = tmp_result;
+
+ tmp_result = smu7_calc_mm_voltage_dependency_table(hwmgr);
+ if (tmp_result)
+ result = tmp_result;
+
+ tmp_result = smu7_sort_lookup_table(hwmgr, table_info->vddgfx_lookup_table);
+ if (tmp_result)
+ result = tmp_result;
+
+ tmp_result = smu7_sort_lookup_table(hwmgr, table_info->vddc_lookup_table);
+ if (tmp_result)
+ result = tmp_result;
+
+ return result;
+}
+
+static int smu7_set_private_data_based_on_pptable_v1(struct pp_hwmgr *hwmgr)
+{
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ struct phm_ppt_v1_clock_voltage_dependency_table *allowed_sclk_vdd_table =
+ table_info->vdd_dep_on_sclk;
+ struct phm_ppt_v1_clock_voltage_dependency_table *allowed_mclk_vdd_table =
+ table_info->vdd_dep_on_mclk;
+
+ PP_ASSERT_WITH_CODE(allowed_sclk_vdd_table != NULL,
+ "VDD dependency on SCLK table is missing.",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(allowed_sclk_vdd_table->count >= 1,
+ "VDD dependency on SCLK table has to have is missing.",
+ return -EINVAL);
+
+ PP_ASSERT_WITH_CODE(allowed_mclk_vdd_table != NULL,
+ "VDD dependency on MCLK table is missing",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(allowed_mclk_vdd_table->count >= 1,
+ "VDD dependency on MCLK table has to have is missing.",
+ return -EINVAL);
+
+ table_info->max_clock_voltage_on_ac.sclk =
+ allowed_sclk_vdd_table->entries[allowed_sclk_vdd_table->count - 1].clk;
+ table_info->max_clock_voltage_on_ac.mclk =
+ allowed_mclk_vdd_table->entries[allowed_mclk_vdd_table->count - 1].clk;
+ table_info->max_clock_voltage_on_ac.vddc =
+ allowed_sclk_vdd_table->entries[allowed_sclk_vdd_table->count - 1].vddc;
+ table_info->max_clock_voltage_on_ac.vddci =
+ allowed_mclk_vdd_table->entries[allowed_mclk_vdd_table->count - 1].vddci;
+
+ hwmgr->dyn_state.max_clock_voltage_on_ac.sclk = table_info->max_clock_voltage_on_ac.sclk;
+ hwmgr->dyn_state.max_clock_voltage_on_ac.mclk = table_info->max_clock_voltage_on_ac.mclk;
+ hwmgr->dyn_state.max_clock_voltage_on_ac.vddc = table_info->max_clock_voltage_on_ac.vddc;
+ hwmgr->dyn_state.max_clock_voltage_on_ac.vddci = table_info->max_clock_voltage_on_ac.vddci;
+
+ return 0;
+}
+
+static int smu7_patch_voltage_workaround(struct pp_hwmgr *hwmgr)
+{
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table;
+ struct phm_ppt_v1_voltage_lookup_table *lookup_table;
+ uint32_t i;
+ uint32_t hw_revision, sub_vendor_id, sub_sys_id;
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ if (table_info != NULL) {
+ dep_mclk_table = table_info->vdd_dep_on_mclk;
+ lookup_table = table_info->vddc_lookup_table;
+ } else
+ return 0;
+
+ hw_revision = adev->pdev->revision;
+ sub_sys_id = adev->pdev->subsystem_device;
+ sub_vendor_id = adev->pdev->subsystem_vendor;
+
+ if (hwmgr->chip_id == CHIP_POLARIS10 && hw_revision == 0xC7 &&
+ ((sub_sys_id == 0xb37 && sub_vendor_id == 0x1002) ||
+ (sub_sys_id == 0x4a8 && sub_vendor_id == 0x1043) ||
+ (sub_sys_id == 0x9480 && sub_vendor_id == 0x1682))) {
+ if (lookup_table->entries[dep_mclk_table->entries[dep_mclk_table->count-1].vddInd].us_vdd >= 1000)
+ return 0;
+
+ for (i = 0; i < lookup_table->count; i++) {
+ if (lookup_table->entries[i].us_vdd < 0xff01 && lookup_table->entries[i].us_vdd >= 1000) {
+ dep_mclk_table->entries[dep_mclk_table->count-1].vddInd = (uint8_t) i;
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+static int smu7_thermal_parameter_init(struct pp_hwmgr *hwmgr)
+{
+ struct pp_atomctrl_gpio_pin_assignment gpio_pin_assignment;
+ uint32_t temp_reg;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+
+ if (atomctrl_get_pp_assign_pin(hwmgr, VDDC_PCC_GPIO_PINID, &gpio_pin_assignment)) {
+ temp_reg = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCNB_PWRMGT_CNTL);
+ switch (gpio_pin_assignment.uc_gpio_pin_bit_shift) {
+ case 0:
+ temp_reg = PHM_SET_FIELD(temp_reg, CNB_PWRMGT_CNTL, GNB_SLOW_MODE, 0x1);
+ break;
+ case 1:
+ temp_reg = PHM_SET_FIELD(temp_reg, CNB_PWRMGT_CNTL, GNB_SLOW_MODE, 0x2);
+ break;
+ case 2:
+ temp_reg = PHM_SET_FIELD(temp_reg, CNB_PWRMGT_CNTL, GNB_SLOW, 0x1);
+ break;
+ case 3:
+ temp_reg = PHM_SET_FIELD(temp_reg, CNB_PWRMGT_CNTL, FORCE_NB_PS1, 0x1);
+ break;
+ case 4:
+ temp_reg = PHM_SET_FIELD(temp_reg, CNB_PWRMGT_CNTL, DPM_ENABLED, 0x1);
+ break;
+ default:
+ break;
+ }
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCNB_PWRMGT_CNTL, temp_reg);
+ }
+
+ if (table_info == NULL)
+ return 0;
+
+ if (table_info->cac_dtp_table->usDefaultTargetOperatingTemp != 0 &&
+ hwmgr->thermal_controller.advanceFanControlParameters.ucFanControlMode) {
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanPWMMinLimit =
+ (uint16_t)hwmgr->thermal_controller.advanceFanControlParameters.ucMinimumPWMLimit;
+
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanPWMMaxLimit =
+ (uint16_t)hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanPWM;
+
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanPWMStep = 1;
+
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanRPMMaxLimit = 100;
+
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanRPMMinLimit =
+ (uint16_t)hwmgr->thermal_controller.advanceFanControlParameters.ucMinimumPWMLimit;
+
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanRPMStep = 1;
+
+ table_info->cac_dtp_table->usDefaultTargetOperatingTemp = (table_info->cac_dtp_table->usDefaultTargetOperatingTemp >= 50) ?
+ (table_info->cac_dtp_table->usDefaultTargetOperatingTemp - 50) : 0;
+
+ table_info->cac_dtp_table->usOperatingTempMaxLimit = table_info->cac_dtp_table->usDefaultTargetOperatingTemp;
+ table_info->cac_dtp_table->usOperatingTempStep = 1;
+ table_info->cac_dtp_table->usOperatingTempHyst = 1;
+
+ hwmgr->thermal_controller.advanceFanControlParameters.usMaxFanPWM =
+ hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanPWM;
+
+ hwmgr->thermal_controller.advanceFanControlParameters.usMaxFanRPM =
+ hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanRPM;
+
+ hwmgr->dyn_state.cac_dtp_table->usOperatingTempMinLimit =
+ table_info->cac_dtp_table->usOperatingTempMinLimit;
+
+ hwmgr->dyn_state.cac_dtp_table->usOperatingTempMaxLimit =
+ table_info->cac_dtp_table->usOperatingTempMaxLimit;
+
+ hwmgr->dyn_state.cac_dtp_table->usDefaultTargetOperatingTemp =
+ table_info->cac_dtp_table->usDefaultTargetOperatingTemp;
+
+ hwmgr->dyn_state.cac_dtp_table->usOperatingTempStep =
+ table_info->cac_dtp_table->usOperatingTempStep;
+
+ hwmgr->dyn_state.cac_dtp_table->usTargetOperatingTemp =
+ table_info->cac_dtp_table->usTargetOperatingTemp;
+ if (hwmgr->feature_mask & PP_OD_FUZZY_FAN_CONTROL_MASK)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ODFuzzyFanControlSupport);
+ }
+
+ return 0;
+}
+
+/**
+ * Change virtual leakage voltage to actual value.
+ *
+ * @param hwmgr the address of the powerplay hardware manager.
+ * @param pointer to changing voltage
+ * @param pointer to leakage table
+ */
+static void smu7_patch_ppt_v0_with_vdd_leakage(struct pp_hwmgr *hwmgr,
+ uint32_t *voltage, struct smu7_leakage_voltage *leakage_table)
+{
+ uint32_t index;
+
+ /* search for leakage voltage ID 0xff01 ~ 0xff08 */
+ for (index = 0; index < leakage_table->count; index++) {
+ /* if this voltage matches a leakage voltage ID */
+ /* patch with actual leakage voltage */
+ if (leakage_table->leakage_id[index] == *voltage) {
+ *voltage = leakage_table->actual_voltage[index];
+ break;
+ }
+ }
+
+ if (*voltage > ATOM_VIRTUAL_VOLTAGE_ID0)
+ pr_err("Voltage value looks like a Leakage ID but it's not patched \n");
+}
+
+
+static int smu7_patch_vddc(struct pp_hwmgr *hwmgr,
+ struct phm_clock_voltage_dependency_table *tab)
+{
+ uint16_t i;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (tab)
+ for (i = 0; i < tab->count; i++)
+ smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &tab->entries[i].v,
+ &data->vddc_leakage);
+
+ return 0;
+}
+
+static int smu7_patch_vddci(struct pp_hwmgr *hwmgr,
+ struct phm_clock_voltage_dependency_table *tab)
+{
+ uint16_t i;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (tab)
+ for (i = 0; i < tab->count; i++)
+ smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &tab->entries[i].v,
+ &data->vddci_leakage);
+
+ return 0;
+}
+
+static int smu7_patch_vce_vddc(struct pp_hwmgr *hwmgr,
+ struct phm_vce_clock_voltage_dependency_table *tab)
+{
+ uint16_t i;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (tab)
+ for (i = 0; i < tab->count; i++)
+ smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &tab->entries[i].v,
+ &data->vddc_leakage);
+
+ return 0;
+}
+
+
+static int smu7_patch_uvd_vddc(struct pp_hwmgr *hwmgr,
+ struct phm_uvd_clock_voltage_dependency_table *tab)
+{
+ uint16_t i;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (tab)
+ for (i = 0; i < tab->count; i++)
+ smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &tab->entries[i].v,
+ &data->vddc_leakage);
+
+ return 0;
+}
+
+static int smu7_patch_vddc_shed_limit(struct pp_hwmgr *hwmgr,
+ struct phm_phase_shedding_limits_table *tab)
+{
+ uint16_t i;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (tab)
+ for (i = 0; i < tab->count; i++)
+ smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &tab->entries[i].Voltage,
+ &data->vddc_leakage);
+
+ return 0;
+}
+
+static int smu7_patch_samu_vddc(struct pp_hwmgr *hwmgr,
+ struct phm_samu_clock_voltage_dependency_table *tab)
+{
+ uint16_t i;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (tab)
+ for (i = 0; i < tab->count; i++)
+ smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &tab->entries[i].v,
+ &data->vddc_leakage);
+
+ return 0;
+}
+
+static int smu7_patch_acp_vddc(struct pp_hwmgr *hwmgr,
+ struct phm_acp_clock_voltage_dependency_table *tab)
+{
+ uint16_t i;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (tab)
+ for (i = 0; i < tab->count; i++)
+ smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &tab->entries[i].v,
+ &data->vddc_leakage);
+
+ return 0;
+}
+
+static int smu7_patch_limits_vddc(struct pp_hwmgr *hwmgr,
+ struct phm_clock_and_voltage_limits *tab)
+{
+ uint32_t vddc, vddci;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (tab) {
+ vddc = tab->vddc;
+ smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &vddc,
+ &data->vddc_leakage);
+ tab->vddc = vddc;
+ vddci = tab->vddci;
+ smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &vddci,
+ &data->vddci_leakage);
+ tab->vddci = vddci;
+ }
+
+ return 0;
+}
+
+static int smu7_patch_cac_vddc(struct pp_hwmgr *hwmgr, struct phm_cac_leakage_table *tab)
+{
+ uint32_t i;
+ uint32_t vddc;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (tab) {
+ for (i = 0; i < tab->count; i++) {
+ vddc = (uint32_t)(tab->entries[i].Vddc);
+ smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &vddc, &data->vddc_leakage);
+ tab->entries[i].Vddc = (uint16_t)vddc;
+ }
+ }
+
+ return 0;
+}
+
+static int smu7_patch_dependency_tables_with_leakage(struct pp_hwmgr *hwmgr)
+{
+ int tmp;
+
+ tmp = smu7_patch_vddc(hwmgr, hwmgr->dyn_state.vddc_dependency_on_sclk);
+ if (tmp)
+ return -EINVAL;
+
+ tmp = smu7_patch_vddc(hwmgr, hwmgr->dyn_state.vddc_dependency_on_mclk);
+ if (tmp)
+ return -EINVAL;
+
+ tmp = smu7_patch_vddc(hwmgr, hwmgr->dyn_state.vddc_dep_on_dal_pwrl);
+ if (tmp)
+ return -EINVAL;
+
+ tmp = smu7_patch_vddci(hwmgr, hwmgr->dyn_state.vddci_dependency_on_mclk);
+ if (tmp)
+ return -EINVAL;
+
+ tmp = smu7_patch_vce_vddc(hwmgr, hwmgr->dyn_state.vce_clock_voltage_dependency_table);
+ if (tmp)
+ return -EINVAL;
+
+ tmp = smu7_patch_uvd_vddc(hwmgr, hwmgr->dyn_state.uvd_clock_voltage_dependency_table);
+ if (tmp)
+ return -EINVAL;
+
+ tmp = smu7_patch_samu_vddc(hwmgr, hwmgr->dyn_state.samu_clock_voltage_dependency_table);
+ if (tmp)
+ return -EINVAL;
+
+ tmp = smu7_patch_acp_vddc(hwmgr, hwmgr->dyn_state.acp_clock_voltage_dependency_table);
+ if (tmp)
+ return -EINVAL;
+
+ tmp = smu7_patch_vddc_shed_limit(hwmgr, hwmgr->dyn_state.vddc_phase_shed_limits_table);
+ if (tmp)
+ return -EINVAL;
+
+ tmp = smu7_patch_limits_vddc(hwmgr, &hwmgr->dyn_state.max_clock_voltage_on_ac);
+ if (tmp)
+ return -EINVAL;
+
+ tmp = smu7_patch_limits_vddc(hwmgr, &hwmgr->dyn_state.max_clock_voltage_on_dc);
+ if (tmp)
+ return -EINVAL;
+
+ tmp = smu7_patch_cac_vddc(hwmgr, hwmgr->dyn_state.cac_leakage_table);
+ if (tmp)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+static int smu7_set_private_data_based_on_pptable_v0(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ struct phm_clock_voltage_dependency_table *allowed_sclk_vddc_table = hwmgr->dyn_state.vddc_dependency_on_sclk;
+ struct phm_clock_voltage_dependency_table *allowed_mclk_vddc_table = hwmgr->dyn_state.vddc_dependency_on_mclk;
+ struct phm_clock_voltage_dependency_table *allowed_mclk_vddci_table = hwmgr->dyn_state.vddci_dependency_on_mclk;
+
+ PP_ASSERT_WITH_CODE(allowed_sclk_vddc_table != NULL,
+ "VDDC dependency on SCLK table is missing. This table is mandatory",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(allowed_sclk_vddc_table->count >= 1,
+ "VDDC dependency on SCLK table has to have is missing. This table is mandatory",
+ return -EINVAL);
+
+ PP_ASSERT_WITH_CODE(allowed_mclk_vddc_table != NULL,
+ "VDDC dependency on MCLK table is missing. This table is mandatory",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(allowed_mclk_vddc_table->count >= 1,
+ "VDD dependency on MCLK table has to have is missing. This table is mandatory",
+ return -EINVAL);
+
+ data->min_vddc_in_pptable = (uint16_t)allowed_sclk_vddc_table->entries[0].v;
+ data->max_vddc_in_pptable = (uint16_t)allowed_sclk_vddc_table->entries[allowed_sclk_vddc_table->count - 1].v;
+
+ hwmgr->dyn_state.max_clock_voltage_on_ac.sclk =
+ allowed_sclk_vddc_table->entries[allowed_sclk_vddc_table->count - 1].clk;
+ hwmgr->dyn_state.max_clock_voltage_on_ac.mclk =
+ allowed_mclk_vddc_table->entries[allowed_mclk_vddc_table->count - 1].clk;
+ hwmgr->dyn_state.max_clock_voltage_on_ac.vddc =
+ allowed_sclk_vddc_table->entries[allowed_sclk_vddc_table->count - 1].v;
+
+ if (allowed_mclk_vddci_table != NULL && allowed_mclk_vddci_table->count >= 1) {
+ data->min_vddci_in_pptable = (uint16_t)allowed_mclk_vddci_table->entries[0].v;
+ data->max_vddci_in_pptable = (uint16_t)allowed_mclk_vddci_table->entries[allowed_mclk_vddci_table->count - 1].v;
+ }
+
+ if (hwmgr->dyn_state.vddci_dependency_on_mclk != NULL && hwmgr->dyn_state.vddci_dependency_on_mclk->count >= 1)
+ hwmgr->dyn_state.max_clock_voltage_on_ac.vddci = hwmgr->dyn_state.vddci_dependency_on_mclk->entries[hwmgr->dyn_state.vddci_dependency_on_mclk->count - 1].v;
+
+ return 0;
+}
+
+static int smu7_hwmgr_backend_fini(struct pp_hwmgr *hwmgr)
+{
+ kfree(hwmgr->dyn_state.vddc_dep_on_dal_pwrl);
+ hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL;
+ kfree(hwmgr->backend);
+ hwmgr->backend = NULL;
+
+ return 0;
+}
+
+static int smu7_get_elb_voltages(struct pp_hwmgr *hwmgr)
+{
+ uint16_t virtual_voltage_id, vddc, vddci, efuse_voltage_id;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ int i;
+
+ if (atomctrl_get_leakage_id_from_efuse(hwmgr, &efuse_voltage_id) == 0) {
+ for (i = 0; i < SMU7_MAX_LEAKAGE_COUNT; i++) {
+ virtual_voltage_id = ATOM_VIRTUAL_VOLTAGE_ID0 + i;
+ if (atomctrl_get_leakage_vddc_base_on_leakage(hwmgr, &vddc, &vddci,
+ virtual_voltage_id,
+ efuse_voltage_id) == 0) {
+ if (vddc != 0 && vddc != virtual_voltage_id) {
+ data->vddc_leakage.actual_voltage[data->vddc_leakage.count] = vddc;
+ data->vddc_leakage.leakage_id[data->vddc_leakage.count] = virtual_voltage_id;
+ data->vddc_leakage.count++;
+ }
+ if (vddci != 0 && vddci != virtual_voltage_id) {
+ data->vddci_leakage.actual_voltage[data->vddci_leakage.count] = vddci;
+ data->vddci_leakage.leakage_id[data->vddci_leakage.count] = virtual_voltage_id;
+ data->vddci_leakage.count++;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int smu7_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data;
+ int result = 0;
+
+ data = kzalloc(sizeof(struct smu7_hwmgr), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ hwmgr->backend = data;
+ smu7_patch_voltage_workaround(hwmgr);
+ smu7_init_dpm_defaults(hwmgr);
+
+ /* Get leakage voltage based on leakage ID. */
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EVV)) {
+ result = smu7_get_evv_voltages(hwmgr);
+ if (result) {
+ pr_info("Get EVV Voltage Failed. Abort Driver loading!\n");
+ return -EINVAL;
+ }
+ } else {
+ smu7_get_elb_voltages(hwmgr);
+ }
+
+ if (hwmgr->pp_table_version == PP_TABLE_V1) {
+ smu7_complete_dependency_tables(hwmgr);
+ smu7_set_private_data_based_on_pptable_v1(hwmgr);
+ } else if (hwmgr->pp_table_version == PP_TABLE_V0) {
+ smu7_patch_dependency_tables_with_leakage(hwmgr);
+ smu7_set_private_data_based_on_pptable_v0(hwmgr);
+ }
+
+ /* Initalize Dynamic State Adjustment Rule Settings */
+ result = phm_initializa_dynamic_state_adjustment_rule_settings(hwmgr);
+
+ if (0 == result) {
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ data->is_tlu_enabled = false;
+
+ hwmgr->platform_descriptor.hardwareActivityPerformanceLevels =
+ SMU7_MAX_HARDWARE_POWERLEVELS;
+ hwmgr->platform_descriptor.hardwarePerformanceLevels = 2;
+ hwmgr->platform_descriptor.minimumClocksReductionPercentage = 50;
+
+ data->pcie_gen_cap = adev->pm.pcie_gen_mask;
+ if (data->pcie_gen_cap & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN3)
+ data->pcie_spc_cap = 20;
+ data->pcie_lane_cap = adev->pm.pcie_mlw_mask;
+
+ hwmgr->platform_descriptor.vbiosInterruptId = 0x20000400; /* IRQ_SOURCE1_SW_INT */
+/* The true clock step depends on the frequency, typically 4.5 or 9 MHz. Here we use 5. */
+ hwmgr->platform_descriptor.clockStep.engineClock = 500;
+ hwmgr->platform_descriptor.clockStep.memoryClock = 500;
+ smu7_thermal_parameter_init(hwmgr);
+ } else {
+ /* Ignore return value in here, we are cleaning up a mess. */
+ smu7_hwmgr_backend_fini(hwmgr);
+ }
+
+ return 0;
+}
+
+static int smu7_force_dpm_highest(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t level, tmp;
+
+ if (!data->pcie_dpm_key_disabled) {
+ if (data->dpm_level_enable_mask.pcie_dpm_enable_mask) {
+ level = 0;
+ tmp = data->dpm_level_enable_mask.pcie_dpm_enable_mask;
+ while (tmp >>= 1)
+ level++;
+
+ if (level)
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_PCIeDPM_ForceLevel, level,
+ NULL);
+ }
+ }
+
+ if (!data->sclk_dpm_key_disabled) {
+ if (data->dpm_level_enable_mask.sclk_dpm_enable_mask) {
+ level = 0;
+ tmp = data->dpm_level_enable_mask.sclk_dpm_enable_mask;
+ while (tmp >>= 1)
+ level++;
+
+ if (level)
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SCLKDPM_SetEnabledMask,
+ (1 << level),
+ NULL);
+ }
+ }
+
+ if (!data->mclk_dpm_key_disabled) {
+ if (data->dpm_level_enable_mask.mclk_dpm_enable_mask) {
+ level = 0;
+ tmp = data->dpm_level_enable_mask.mclk_dpm_enable_mask;
+ while (tmp >>= 1)
+ level++;
+
+ if (level)
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_MCLKDPM_SetEnabledMask,
+ (1 << level),
+ NULL);
+ }
+ }
+
+ return 0;
+}
+
+static int smu7_upload_dpm_level_enable_mask(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (hwmgr->pp_table_version == PP_TABLE_V1)
+ phm_apply_dal_min_voltage_request(hwmgr);
+/* TO DO for v0 iceland and Ci*/
+
+ if (!data->sclk_dpm_key_disabled) {
+ if (data->dpm_level_enable_mask.sclk_dpm_enable_mask)
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SCLKDPM_SetEnabledMask,
+ data->dpm_level_enable_mask.sclk_dpm_enable_mask,
+ NULL);
+ }
+
+ if (!data->mclk_dpm_key_disabled) {
+ if (data->dpm_level_enable_mask.mclk_dpm_enable_mask)
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_MCLKDPM_SetEnabledMask,
+ data->dpm_level_enable_mask.mclk_dpm_enable_mask,
+ NULL);
+ }
+
+ return 0;
+}
+
+static int smu7_unforce_dpm_levels(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (!smum_is_dpm_running(hwmgr))
+ return -EINVAL;
+
+ if (!data->pcie_dpm_key_disabled) {
+ smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_PCIeDPM_UnForceLevel,
+ NULL);
+ }
+
+ return smu7_upload_dpm_level_enable_mask(hwmgr);
+}
+
+static int smu7_force_dpm_lowest(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data =
+ (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t level;
+
+ if (!data->sclk_dpm_key_disabled)
+ if (data->dpm_level_enable_mask.sclk_dpm_enable_mask) {
+ level = phm_get_lowest_enabled_level(hwmgr,
+ data->dpm_level_enable_mask.sclk_dpm_enable_mask);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SCLKDPM_SetEnabledMask,
+ (1 << level),
+ NULL);
+
+ }
+
+ if (!data->mclk_dpm_key_disabled) {
+ if (data->dpm_level_enable_mask.mclk_dpm_enable_mask) {
+ level = phm_get_lowest_enabled_level(hwmgr,
+ data->dpm_level_enable_mask.mclk_dpm_enable_mask);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_MCLKDPM_SetEnabledMask,
+ (1 << level),
+ NULL);
+ }
+ }
+
+ if (!data->pcie_dpm_key_disabled) {
+ if (data->dpm_level_enable_mask.pcie_dpm_enable_mask) {
+ level = phm_get_lowest_enabled_level(hwmgr,
+ data->dpm_level_enable_mask.pcie_dpm_enable_mask);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_PCIeDPM_ForceLevel,
+ (level),
+ NULL);
+ }
+ }
+
+ return 0;
+}
+
+static int smu7_get_profiling_clk(struct pp_hwmgr *hwmgr, enum amd_dpm_forced_level level,
+ uint32_t *sclk_mask, uint32_t *mclk_mask, uint32_t *pcie_mask)
+{
+ uint32_t percentage;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_dpm_table *golden_dpm_table = &data->golden_dpm_table;
+ int32_t tmp_mclk;
+ int32_t tmp_sclk;
+ int32_t count;
+
+ if (golden_dpm_table->mclk_table.count < 1)
+ return -EINVAL;
+
+ percentage = 100 * golden_dpm_table->sclk_table.dpm_levels[golden_dpm_table->sclk_table.count - 1].value /
+ golden_dpm_table->mclk_table.dpm_levels[golden_dpm_table->mclk_table.count - 1].value;
+
+ if (golden_dpm_table->mclk_table.count == 1) {
+ percentage = 70;
+ tmp_mclk = golden_dpm_table->mclk_table.dpm_levels[golden_dpm_table->mclk_table.count - 1].value;
+ *mclk_mask = golden_dpm_table->mclk_table.count - 1;
+ } else {
+ tmp_mclk = golden_dpm_table->mclk_table.dpm_levels[golden_dpm_table->mclk_table.count - 2].value;
+ *mclk_mask = golden_dpm_table->mclk_table.count - 2;
+ }
+
+ tmp_sclk = tmp_mclk * percentage / 100;
+
+ if (hwmgr->pp_table_version == PP_TABLE_V0) {
+ for (count = hwmgr->dyn_state.vddc_dependency_on_sclk->count-1;
+ count >= 0; count--) {
+ if (tmp_sclk >= hwmgr->dyn_state.vddc_dependency_on_sclk->entries[count].clk) {
+ tmp_sclk = hwmgr->dyn_state.vddc_dependency_on_sclk->entries[count].clk;
+ *sclk_mask = count;
+ break;
+ }
+ }
+ if (count < 0 || level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK) {
+ *sclk_mask = 0;
+ tmp_sclk = hwmgr->dyn_state.vddc_dependency_on_sclk->entries[0].clk;
+ }
+
+ if (level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK)
+ *sclk_mask = hwmgr->dyn_state.vddc_dependency_on_sclk->count-1;
+ } else if (hwmgr->pp_table_version == PP_TABLE_V1) {
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ for (count = table_info->vdd_dep_on_sclk->count-1; count >= 0; count--) {
+ if (tmp_sclk >= table_info->vdd_dep_on_sclk->entries[count].clk) {
+ tmp_sclk = table_info->vdd_dep_on_sclk->entries[count].clk;
+ *sclk_mask = count;
+ break;
+ }
+ }
+ if (count < 0 || level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK) {
+ *sclk_mask = 0;
+ tmp_sclk = table_info->vdd_dep_on_sclk->entries[0].clk;
+ }
+
+ if (level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK)
+ *sclk_mask = table_info->vdd_dep_on_sclk->count - 1;
+ }
+
+ if (level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK)
+ *mclk_mask = 0;
+ else if (level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK)
+ *mclk_mask = golden_dpm_table->mclk_table.count - 1;
+
+ *pcie_mask = data->dpm_table.pcie_speed_table.count - 1;
+ hwmgr->pstate_sclk = tmp_sclk;
+ hwmgr->pstate_mclk = tmp_mclk;
+
+ return 0;
+}
+
+static int smu7_force_dpm_level(struct pp_hwmgr *hwmgr,
+ enum amd_dpm_forced_level level)
+{
+ int ret = 0;
+ uint32_t sclk_mask = 0;
+ uint32_t mclk_mask = 0;
+ uint32_t pcie_mask = 0;
+
+ if (hwmgr->pstate_sclk == 0)
+ smu7_get_profiling_clk(hwmgr, level, &sclk_mask, &mclk_mask, &pcie_mask);
+
+ switch (level) {
+ case AMD_DPM_FORCED_LEVEL_HIGH:
+ ret = smu7_force_dpm_highest(hwmgr);
+ break;
+ case AMD_DPM_FORCED_LEVEL_LOW:
+ ret = smu7_force_dpm_lowest(hwmgr);
+ break;
+ case AMD_DPM_FORCED_LEVEL_AUTO:
+ ret = smu7_unforce_dpm_levels(hwmgr);
+ break;
+ case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+ ret = smu7_get_profiling_clk(hwmgr, level, &sclk_mask, &mclk_mask, &pcie_mask);
+ if (ret)
+ return ret;
+ smu7_force_clock_level(hwmgr, PP_SCLK, 1<<sclk_mask);
+ smu7_force_clock_level(hwmgr, PP_MCLK, 1<<mclk_mask);
+ smu7_force_clock_level(hwmgr, PP_PCIE, 1<<pcie_mask);
+ break;
+ case AMD_DPM_FORCED_LEVEL_MANUAL:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_EXIT:
+ default:
+ break;
+ }
+
+ if (!ret) {
+ if (level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK && hwmgr->dpm_level != AMD_DPM_FORCED_LEVEL_PROFILE_PEAK)
+ smu7_fan_ctrl_set_fan_speed_percent(hwmgr, 100);
+ else if (level != AMD_DPM_FORCED_LEVEL_PROFILE_PEAK && hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK)
+ smu7_fan_ctrl_reset_fan_speed_to_default(hwmgr);
+ }
+ return ret;
+}
+
+static int smu7_get_power_state_size(struct pp_hwmgr *hwmgr)
+{
+ return sizeof(struct smu7_power_state);
+}
+
+static int smu7_vblank_too_short(struct pp_hwmgr *hwmgr,
+ uint32_t vblank_time_us)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t switch_limit_us;
+
+ switch (hwmgr->chip_id) {
+ case CHIP_POLARIS10:
+ case CHIP_POLARIS11:
+ case CHIP_POLARIS12:
+ if (hwmgr->is_kicker)
+ switch_limit_us = data->is_memory_gddr5 ? 450 : 150;
+ else
+ switch_limit_us = data->is_memory_gddr5 ? 200 : 150;
+ break;
+ case CHIP_VEGAM:
+ switch_limit_us = 30;
+ break;
+ default:
+ switch_limit_us = data->is_memory_gddr5 ? 450 : 150;
+ break;
+ }
+
+ if (vblank_time_us < switch_limit_us)
+ return true;
+ else
+ return false;
+}
+
+static int smu7_apply_state_adjust_rules(struct pp_hwmgr *hwmgr,
+ struct pp_power_state *request_ps,
+ const struct pp_power_state *current_ps)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ struct smu7_power_state *smu7_ps =
+ cast_phw_smu7_power_state(&request_ps->hardware);
+ uint32_t sclk;
+ uint32_t mclk;
+ struct PP_Clocks minimum_clocks = {0};
+ bool disable_mclk_switching;
+ bool disable_mclk_switching_for_frame_lock;
+ const struct phm_clock_and_voltage_limits *max_limits;
+ uint32_t i;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ int32_t count;
+ int32_t stable_pstate_sclk = 0, stable_pstate_mclk = 0;
+
+ data->battery_state = (PP_StateUILabel_Battery ==
+ request_ps->classification.ui_label);
+
+ PP_ASSERT_WITH_CODE(smu7_ps->performance_level_count == 2,
+ "VI should always have 2 performance levels",
+ );
+
+ max_limits = adev->pm.ac_power ?
+ &(hwmgr->dyn_state.max_clock_voltage_on_ac) :
+ &(hwmgr->dyn_state.max_clock_voltage_on_dc);
+
+ /* Cap clock DPM tables at DC MAX if it is in DC. */
+ if (!adev->pm.ac_power) {
+ for (i = 0; i < smu7_ps->performance_level_count; i++) {
+ if (smu7_ps->performance_levels[i].memory_clock > max_limits->mclk)
+ smu7_ps->performance_levels[i].memory_clock = max_limits->mclk;
+ if (smu7_ps->performance_levels[i].engine_clock > max_limits->sclk)
+ smu7_ps->performance_levels[i].engine_clock = max_limits->sclk;
+ }
+ }
+
+ minimum_clocks.engineClock = hwmgr->display_config->min_core_set_clock;
+ minimum_clocks.memoryClock = hwmgr->display_config->min_mem_set_clock;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_StablePState)) {
+ max_limits = &(hwmgr->dyn_state.max_clock_voltage_on_ac);
+ stable_pstate_sclk = (max_limits->sclk * 75) / 100;
+
+ for (count = table_info->vdd_dep_on_sclk->count - 1;
+ count >= 0; count--) {
+ if (stable_pstate_sclk >=
+ table_info->vdd_dep_on_sclk->entries[count].clk) {
+ stable_pstate_sclk =
+ table_info->vdd_dep_on_sclk->entries[count].clk;
+ break;
+ }
+ }
+
+ if (count < 0)
+ stable_pstate_sclk = table_info->vdd_dep_on_sclk->entries[0].clk;
+
+ stable_pstate_mclk = max_limits->mclk;
+
+ minimum_clocks.engineClock = stable_pstate_sclk;
+ minimum_clocks.memoryClock = stable_pstate_mclk;
+ }
+
+ disable_mclk_switching_for_frame_lock = phm_cap_enabled(
+ hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DisableMclkSwitchingForFrameLock);
+
+
+ if (hwmgr->display_config->num_display == 0)
+ disable_mclk_switching = false;
+ else
+ disable_mclk_switching = ((1 < hwmgr->display_config->num_display) &&
+ !hwmgr->display_config->multi_monitor_in_sync) ||
+ disable_mclk_switching_for_frame_lock ||
+ smu7_vblank_too_short(hwmgr, hwmgr->display_config->min_vblank_time);
+
+ sclk = smu7_ps->performance_levels[0].engine_clock;
+ mclk = smu7_ps->performance_levels[0].memory_clock;
+
+ if (disable_mclk_switching)
+ mclk = smu7_ps->performance_levels
+ [smu7_ps->performance_level_count - 1].memory_clock;
+
+ if (sclk < minimum_clocks.engineClock)
+ sclk = (minimum_clocks.engineClock > max_limits->sclk) ?
+ max_limits->sclk : minimum_clocks.engineClock;
+
+ if (mclk < minimum_clocks.memoryClock)
+ mclk = (minimum_clocks.memoryClock > max_limits->mclk) ?
+ max_limits->mclk : minimum_clocks.memoryClock;
+
+ smu7_ps->performance_levels[0].engine_clock = sclk;
+ smu7_ps->performance_levels[0].memory_clock = mclk;
+
+ smu7_ps->performance_levels[1].engine_clock =
+ (smu7_ps->performance_levels[1].engine_clock >=
+ smu7_ps->performance_levels[0].engine_clock) ?
+ smu7_ps->performance_levels[1].engine_clock :
+ smu7_ps->performance_levels[0].engine_clock;
+
+ if (disable_mclk_switching) {
+ if (mclk < smu7_ps->performance_levels[1].memory_clock)
+ mclk = smu7_ps->performance_levels[1].memory_clock;
+
+ smu7_ps->performance_levels[0].memory_clock = mclk;
+ smu7_ps->performance_levels[1].memory_clock = mclk;
+ } else {
+ if (smu7_ps->performance_levels[1].memory_clock <
+ smu7_ps->performance_levels[0].memory_clock)
+ smu7_ps->performance_levels[1].memory_clock =
+ smu7_ps->performance_levels[0].memory_clock;
+ }
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_StablePState)) {
+ for (i = 0; i < smu7_ps->performance_level_count; i++) {
+ smu7_ps->performance_levels[i].engine_clock = stable_pstate_sclk;
+ smu7_ps->performance_levels[i].memory_clock = stable_pstate_mclk;
+ smu7_ps->performance_levels[i].pcie_gen = data->pcie_gen_performance.max;
+ smu7_ps->performance_levels[i].pcie_lane = data->pcie_gen_performance.max;
+ }
+ }
+ return 0;
+}
+
+
+static uint32_t smu7_dpm_get_mclk(struct pp_hwmgr *hwmgr, bool low)
+{
+ struct pp_power_state *ps;
+ struct smu7_power_state *smu7_ps;
+
+ if (hwmgr == NULL)
+ return -EINVAL;
+
+ ps = hwmgr->request_ps;
+
+ if (ps == NULL)
+ return -EINVAL;
+
+ smu7_ps = cast_phw_smu7_power_state(&ps->hardware);
+
+ if (low)
+ return smu7_ps->performance_levels[0].memory_clock;
+ else
+ return smu7_ps->performance_levels
+ [smu7_ps->performance_level_count-1].memory_clock;
+}
+
+static uint32_t smu7_dpm_get_sclk(struct pp_hwmgr *hwmgr, bool low)
+{
+ struct pp_power_state *ps;
+ struct smu7_power_state *smu7_ps;
+
+ if (hwmgr == NULL)
+ return -EINVAL;
+
+ ps = hwmgr->request_ps;
+
+ if (ps == NULL)
+ return -EINVAL;
+
+ smu7_ps = cast_phw_smu7_power_state(&ps->hardware);
+
+ if (low)
+ return smu7_ps->performance_levels[0].engine_clock;
+ else
+ return smu7_ps->performance_levels
+ [smu7_ps->performance_level_count-1].engine_clock;
+}
+
+static int smu7_dpm_patch_boot_state(struct pp_hwmgr *hwmgr,
+ struct pp_hw_power_state *hw_ps)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_power_state *ps = (struct smu7_power_state *)hw_ps;
+ ATOM_FIRMWARE_INFO_V2_2 *fw_info;
+ uint16_t size;
+ uint8_t frev, crev;
+ int index = GetIndexIntoMasterTable(DATA, FirmwareInfo);
+
+ /* First retrieve the Boot clocks and VDDC from the firmware info table.
+ * We assume here that fw_info is unchanged if this call fails.
+ */
+ fw_info = (ATOM_FIRMWARE_INFO_V2_2 *)smu_atom_get_data_table(hwmgr->adev, index,
+ &size, &frev, &crev);
+ if (!fw_info)
+ /* During a test, there is no firmware info table. */
+ return 0;
+
+ /* Patch the state. */
+ data->vbios_boot_state.sclk_bootup_value =
+ le32_to_cpu(fw_info->ulDefaultEngineClock);
+ data->vbios_boot_state.mclk_bootup_value =
+ le32_to_cpu(fw_info->ulDefaultMemoryClock);
+ data->vbios_boot_state.mvdd_bootup_value =
+ le16_to_cpu(fw_info->usBootUpMVDDCVoltage);
+ data->vbios_boot_state.vddc_bootup_value =
+ le16_to_cpu(fw_info->usBootUpVDDCVoltage);
+ data->vbios_boot_state.vddci_bootup_value =
+ le16_to_cpu(fw_info->usBootUpVDDCIVoltage);
+ data->vbios_boot_state.pcie_gen_bootup_value =
+ smu7_get_current_pcie_speed(hwmgr);
+
+ data->vbios_boot_state.pcie_lane_bootup_value =
+ (uint16_t)smu7_get_current_pcie_lane_number(hwmgr);
+
+ /* set boot power state */
+ ps->performance_levels[0].memory_clock = data->vbios_boot_state.mclk_bootup_value;
+ ps->performance_levels[0].engine_clock = data->vbios_boot_state.sclk_bootup_value;
+ ps->performance_levels[0].pcie_gen = data->vbios_boot_state.pcie_gen_bootup_value;
+ ps->performance_levels[0].pcie_lane = data->vbios_boot_state.pcie_lane_bootup_value;
+
+ return 0;
+}
+
+static int smu7_get_number_of_powerplay_table_entries(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ unsigned long ret = 0;
+
+ if (hwmgr->pp_table_version == PP_TABLE_V0) {
+ result = pp_tables_get_num_of_entries(hwmgr, &ret);
+ return result ? 0 : ret;
+ } else if (hwmgr->pp_table_version == PP_TABLE_V1) {
+ result = get_number_of_powerplay_table_entries_v1_0(hwmgr);
+ return result;
+ }
+ return 0;
+}
+
+static int smu7_get_pp_table_entry_callback_func_v1(struct pp_hwmgr *hwmgr,
+ void *state, struct pp_power_state *power_state,
+ void *pp_table, uint32_t classification_flag)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_power_state *smu7_power_state =
+ (struct smu7_power_state *)(&(power_state->hardware));
+ struct smu7_performance_level *performance_level;
+ ATOM_Tonga_State *state_entry = (ATOM_Tonga_State *)state;
+ ATOM_Tonga_POWERPLAYTABLE *powerplay_table =
+ (ATOM_Tonga_POWERPLAYTABLE *)pp_table;
+ PPTable_Generic_SubTable_Header *sclk_dep_table =
+ (PPTable_Generic_SubTable_Header *)
+ (((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usSclkDependencyTableOffset));
+
+ ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table =
+ (ATOM_Tonga_MCLK_Dependency_Table *)
+ (((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usMclkDependencyTableOffset));
+
+ /* The following fields are not initialized here: id orderedList allStatesList */
+ power_state->classification.ui_label =
+ (le16_to_cpu(state_entry->usClassification) &
+ ATOM_PPLIB_CLASSIFICATION_UI_MASK) >>
+ ATOM_PPLIB_CLASSIFICATION_UI_SHIFT;
+ power_state->classification.flags = classification_flag;
+ /* NOTE: There is a classification2 flag in BIOS that is not being used right now */
+
+ power_state->classification.temporary_state = false;
+ power_state->classification.to_be_deleted = false;
+
+ power_state->validation.disallowOnDC =
+ (0 != (le32_to_cpu(state_entry->ulCapsAndSettings) &
+ ATOM_Tonga_DISALLOW_ON_DC));
+
+ power_state->pcie.lanes = 0;
+
+ power_state->display.disableFrameModulation = false;
+ power_state->display.limitRefreshrate = false;
+ power_state->display.enableVariBright =
+ (0 != (le32_to_cpu(state_entry->ulCapsAndSettings) &
+ ATOM_Tonga_ENABLE_VARIBRIGHT));
+
+ power_state->validation.supportedPowerLevels = 0;
+ power_state->uvd_clocks.VCLK = 0;
+ power_state->uvd_clocks.DCLK = 0;
+ power_state->temperatures.min = 0;
+ power_state->temperatures.max = 0;
+
+ performance_level = &(smu7_power_state->performance_levels
+ [smu7_power_state->performance_level_count++]);
+
+ PP_ASSERT_WITH_CODE(
+ (smu7_power_state->performance_level_count < smum_get_mac_definition(hwmgr, SMU_MAX_LEVELS_GRAPHICS)),
+ "Performance levels exceeds SMC limit!",
+ return -EINVAL);
+
+ PP_ASSERT_WITH_CODE(
+ (smu7_power_state->performance_level_count <=
+ hwmgr->platform_descriptor.hardwareActivityPerformanceLevels),
+ "Performance levels exceeds Driver limit!",
+ return -EINVAL);
+
+ /* Performance levels are arranged from low to high. */
+ performance_level->memory_clock = mclk_dep_table->entries
+ [state_entry->ucMemoryClockIndexLow].ulMclk;
+ if (sclk_dep_table->ucRevId == 0)
+ performance_level->engine_clock = ((ATOM_Tonga_SCLK_Dependency_Table *)sclk_dep_table)->entries
+ [state_entry->ucEngineClockIndexLow].ulSclk;
+ else if (sclk_dep_table->ucRevId == 1)
+ performance_level->engine_clock = ((ATOM_Polaris_SCLK_Dependency_Table *)sclk_dep_table)->entries
+ [state_entry->ucEngineClockIndexLow].ulSclk;
+ performance_level->pcie_gen = get_pcie_gen_support(data->pcie_gen_cap,
+ state_entry->ucPCIEGenLow);
+ performance_level->pcie_lane = get_pcie_lane_support(data->pcie_lane_cap,
+ state_entry->ucPCIELaneLow);
+
+ performance_level = &(smu7_power_state->performance_levels
+ [smu7_power_state->performance_level_count++]);
+ performance_level->memory_clock = mclk_dep_table->entries
+ [state_entry->ucMemoryClockIndexHigh].ulMclk;
+
+ if (sclk_dep_table->ucRevId == 0)
+ performance_level->engine_clock = ((ATOM_Tonga_SCLK_Dependency_Table *)sclk_dep_table)->entries
+ [state_entry->ucEngineClockIndexHigh].ulSclk;
+ else if (sclk_dep_table->ucRevId == 1)
+ performance_level->engine_clock = ((ATOM_Polaris_SCLK_Dependency_Table *)sclk_dep_table)->entries
+ [state_entry->ucEngineClockIndexHigh].ulSclk;
+
+ performance_level->pcie_gen = get_pcie_gen_support(data->pcie_gen_cap,
+ state_entry->ucPCIEGenHigh);
+ performance_level->pcie_lane = get_pcie_lane_support(data->pcie_lane_cap,
+ state_entry->ucPCIELaneHigh);
+
+ return 0;
+}
+
+static int smu7_get_pp_table_entry_v1(struct pp_hwmgr *hwmgr,
+ unsigned long entry_index, struct pp_power_state *state)
+{
+ int result;
+ struct smu7_power_state *ps;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table =
+ table_info->vdd_dep_on_mclk;
+
+ state->hardware.magic = PHM_VIslands_Magic;
+
+ ps = (struct smu7_power_state *)(&state->hardware);
+
+ result = get_powerplay_table_entry_v1_0(hwmgr, entry_index, state,
+ smu7_get_pp_table_entry_callback_func_v1);
+
+ /* This is the earliest time we have all the dependency table and the VBIOS boot state
+ * as PP_Tables_GetPowerPlayTableEntry retrieves the VBIOS boot state
+ * if there is only one VDDCI/MCLK level, check if it's the same as VBIOS boot state
+ */
+ if (dep_mclk_table != NULL && dep_mclk_table->count == 1) {
+ if (dep_mclk_table->entries[0].clk !=
+ data->vbios_boot_state.mclk_bootup_value)
+ pr_debug("Single MCLK entry VDDCI/MCLK dependency table "
+ "does not match VBIOS boot MCLK level");
+ if (dep_mclk_table->entries[0].vddci !=
+ data->vbios_boot_state.vddci_bootup_value)
+ pr_debug("Single VDDCI entry VDDCI/MCLK dependency table "
+ "does not match VBIOS boot VDDCI level");
+ }
+
+ /* set DC compatible flag if this state supports DC */
+ if (!state->validation.disallowOnDC)
+ ps->dc_compatible = true;
+
+ if (state->classification.flags & PP_StateClassificationFlag_ACPI)
+ data->acpi_pcie_gen = ps->performance_levels[0].pcie_gen;
+
+ ps->uvd_clks.vclk = state->uvd_clocks.VCLK;
+ ps->uvd_clks.dclk = state->uvd_clocks.DCLK;
+
+ if (!result) {
+ uint32_t i;
+
+ switch (state->classification.ui_label) {
+ case PP_StateUILabel_Performance:
+ data->use_pcie_performance_levels = true;
+ for (i = 0; i < ps->performance_level_count; i++) {
+ if (data->pcie_gen_performance.max <
+ ps->performance_levels[i].pcie_gen)
+ data->pcie_gen_performance.max =
+ ps->performance_levels[i].pcie_gen;
+
+ if (data->pcie_gen_performance.min >
+ ps->performance_levels[i].pcie_gen)
+ data->pcie_gen_performance.min =
+ ps->performance_levels[i].pcie_gen;
+
+ if (data->pcie_lane_performance.max <
+ ps->performance_levels[i].pcie_lane)
+ data->pcie_lane_performance.max =
+ ps->performance_levels[i].pcie_lane;
+ if (data->pcie_lane_performance.min >
+ ps->performance_levels[i].pcie_lane)
+ data->pcie_lane_performance.min =
+ ps->performance_levels[i].pcie_lane;
+ }
+ break;
+ case PP_StateUILabel_Battery:
+ data->use_pcie_power_saving_levels = true;
+
+ for (i = 0; i < ps->performance_level_count; i++) {
+ if (data->pcie_gen_power_saving.max <
+ ps->performance_levels[i].pcie_gen)
+ data->pcie_gen_power_saving.max =
+ ps->performance_levels[i].pcie_gen;
+
+ if (data->pcie_gen_power_saving.min >
+ ps->performance_levels[i].pcie_gen)
+ data->pcie_gen_power_saving.min =
+ ps->performance_levels[i].pcie_gen;
+
+ if (data->pcie_lane_power_saving.max <
+ ps->performance_levels[i].pcie_lane)
+ data->pcie_lane_power_saving.max =
+ ps->performance_levels[i].pcie_lane;
+
+ if (data->pcie_lane_power_saving.min >
+ ps->performance_levels[i].pcie_lane)
+ data->pcie_lane_power_saving.min =
+ ps->performance_levels[i].pcie_lane;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+static int smu7_get_pp_table_entry_callback_func_v0(struct pp_hwmgr *hwmgr,
+ struct pp_hw_power_state *power_state,
+ unsigned int index, const void *clock_info)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_power_state *ps = cast_phw_smu7_power_state(power_state);
+ const ATOM_PPLIB_CI_CLOCK_INFO *visland_clk_info = clock_info;
+ struct smu7_performance_level *performance_level;
+ uint32_t engine_clock, memory_clock;
+ uint16_t pcie_gen_from_bios;
+
+ engine_clock = visland_clk_info->ucEngineClockHigh << 16 | visland_clk_info->usEngineClockLow;
+ memory_clock = visland_clk_info->ucMemoryClockHigh << 16 | visland_clk_info->usMemoryClockLow;
+
+ if (!(data->mc_micro_code_feature & DISABLE_MC_LOADMICROCODE) && memory_clock > data->highest_mclk)
+ data->highest_mclk = memory_clock;
+
+ PP_ASSERT_WITH_CODE(
+ (ps->performance_level_count < smum_get_mac_definition(hwmgr, SMU_MAX_LEVELS_GRAPHICS)),
+ "Performance levels exceeds SMC limit!",
+ return -EINVAL);
+
+ PP_ASSERT_WITH_CODE(
+ (ps->performance_level_count <
+ hwmgr->platform_descriptor.hardwareActivityPerformanceLevels),
+ "Performance levels exceeds Driver limit, Skip!",
+ return 0);
+
+ performance_level = &(ps->performance_levels
+ [ps->performance_level_count++]);
+
+ /* Performance levels are arranged from low to high. */
+ performance_level->memory_clock = memory_clock;
+ performance_level->engine_clock = engine_clock;
+
+ pcie_gen_from_bios = visland_clk_info->ucPCIEGen;
+
+ performance_level->pcie_gen = get_pcie_gen_support(data->pcie_gen_cap, pcie_gen_from_bios);
+ performance_level->pcie_lane = get_pcie_lane_support(data->pcie_lane_cap, visland_clk_info->usPCIELane);
+
+ return 0;
+}
+
+static int smu7_get_pp_table_entry_v0(struct pp_hwmgr *hwmgr,
+ unsigned long entry_index, struct pp_power_state *state)
+{
+ int result;
+ struct smu7_power_state *ps;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_clock_voltage_dependency_table *dep_mclk_table =
+ hwmgr->dyn_state.vddci_dependency_on_mclk;
+
+ memset(&state->hardware, 0x00, sizeof(struct pp_hw_power_state));
+
+ state->hardware.magic = PHM_VIslands_Magic;
+
+ ps = (struct smu7_power_state *)(&state->hardware);
+
+ result = pp_tables_get_entry(hwmgr, entry_index, state,
+ smu7_get_pp_table_entry_callback_func_v0);
+
+ /*
+ * This is the earliest time we have all the dependency table
+ * and the VBIOS boot state as
+ * PP_Tables_GetPowerPlayTableEntry retrieves the VBIOS boot
+ * state if there is only one VDDCI/MCLK level, check if it's
+ * the same as VBIOS boot state
+ */
+ if (dep_mclk_table != NULL && dep_mclk_table->count == 1) {
+ if (dep_mclk_table->entries[0].clk !=
+ data->vbios_boot_state.mclk_bootup_value)
+ pr_debug("Single MCLK entry VDDCI/MCLK dependency table "
+ "does not match VBIOS boot MCLK level");
+ if (dep_mclk_table->entries[0].v !=
+ data->vbios_boot_state.vddci_bootup_value)
+ pr_debug("Single VDDCI entry VDDCI/MCLK dependency table "
+ "does not match VBIOS boot VDDCI level");
+ }
+
+ /* set DC compatible flag if this state supports DC */
+ if (!state->validation.disallowOnDC)
+ ps->dc_compatible = true;
+
+ if (state->classification.flags & PP_StateClassificationFlag_ACPI)
+ data->acpi_pcie_gen = ps->performance_levels[0].pcie_gen;
+
+ ps->uvd_clks.vclk = state->uvd_clocks.VCLK;
+ ps->uvd_clks.dclk = state->uvd_clocks.DCLK;
+
+ if (!result) {
+ uint32_t i;
+
+ switch (state->classification.ui_label) {
+ case PP_StateUILabel_Performance:
+ data->use_pcie_performance_levels = true;
+
+ for (i = 0; i < ps->performance_level_count; i++) {
+ if (data->pcie_gen_performance.max <
+ ps->performance_levels[i].pcie_gen)
+ data->pcie_gen_performance.max =
+ ps->performance_levels[i].pcie_gen;
+
+ if (data->pcie_gen_performance.min >
+ ps->performance_levels[i].pcie_gen)
+ data->pcie_gen_performance.min =
+ ps->performance_levels[i].pcie_gen;
+
+ if (data->pcie_lane_performance.max <
+ ps->performance_levels[i].pcie_lane)
+ data->pcie_lane_performance.max =
+ ps->performance_levels[i].pcie_lane;
+
+ if (data->pcie_lane_performance.min >
+ ps->performance_levels[i].pcie_lane)
+ data->pcie_lane_performance.min =
+ ps->performance_levels[i].pcie_lane;
+ }
+ break;
+ case PP_StateUILabel_Battery:
+ data->use_pcie_power_saving_levels = true;
+
+ for (i = 0; i < ps->performance_level_count; i++) {
+ if (data->pcie_gen_power_saving.max <
+ ps->performance_levels[i].pcie_gen)
+ data->pcie_gen_power_saving.max =
+ ps->performance_levels[i].pcie_gen;
+
+ if (data->pcie_gen_power_saving.min >
+ ps->performance_levels[i].pcie_gen)
+ data->pcie_gen_power_saving.min =
+ ps->performance_levels[i].pcie_gen;
+
+ if (data->pcie_lane_power_saving.max <
+ ps->performance_levels[i].pcie_lane)
+ data->pcie_lane_power_saving.max =
+ ps->performance_levels[i].pcie_lane;
+
+ if (data->pcie_lane_power_saving.min >
+ ps->performance_levels[i].pcie_lane)
+ data->pcie_lane_power_saving.min =
+ ps->performance_levels[i].pcie_lane;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+static int smu7_get_pp_table_entry(struct pp_hwmgr *hwmgr,
+ unsigned long entry_index, struct pp_power_state *state)
+{
+ if (hwmgr->pp_table_version == PP_TABLE_V0)
+ return smu7_get_pp_table_entry_v0(hwmgr, entry_index, state);
+ else if (hwmgr->pp_table_version == PP_TABLE_V1)
+ return smu7_get_pp_table_entry_v1(hwmgr, entry_index, state);
+
+ return 0;
+}
+
+static int smu7_get_gpu_power(struct pp_hwmgr *hwmgr, u32 *query)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ int i;
+ u32 tmp = 0;
+
+ if (!query)
+ return -EINVAL;
+
+ /*
+ * PPSMC_MSG_GetCurrPkgPwr is not supported on:
+ * - Hawaii
+ * - Bonaire
+ * - Fiji
+ * - Tonga
+ */
+ if ((adev->asic_type != CHIP_HAWAII) &&
+ (adev->asic_type != CHIP_BONAIRE) &&
+ (adev->asic_type != CHIP_FIJI) &&
+ (adev->asic_type != CHIP_TONGA)) {
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_GetCurrPkgPwr, 0, &tmp);
+ *query = tmp;
+
+ if (tmp != 0)
+ return 0;
+ }
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_PmStatusLogStart, NULL);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixSMU_PM_STATUS_95, 0);
+
+ for (i = 0; i < 10; i++) {
+ msleep(500);
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_PmStatusLogSample, NULL);
+ tmp = cgs_read_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC,
+ ixSMU_PM_STATUS_95);
+ if (tmp != 0)
+ break;
+ }
+ *query = tmp;
+
+ return 0;
+}
+
+static int smu7_read_sensor(struct pp_hwmgr *hwmgr, int idx,
+ void *value, int *size)
+{
+ uint32_t sclk, mclk, activity_percent;
+ uint32_t offset, val_vid;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ /* size must be at least 4 bytes for all sensors */
+ if (*size < 4)
+ return -EINVAL;
+
+ switch (idx) {
+ case AMDGPU_PP_SENSOR_GFX_SCLK:
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_API_GetSclkFrequency, &sclk);
+ *((uint32_t *)value) = sclk;
+ *size = 4;
+ return 0;
+ case AMDGPU_PP_SENSOR_GFX_MCLK:
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_API_GetMclkFrequency, &mclk);
+ *((uint32_t *)value) = mclk;
+ *size = 4;
+ return 0;
+ case AMDGPU_PP_SENSOR_GPU_LOAD:
+ case AMDGPU_PP_SENSOR_MEM_LOAD:
+ offset = data->soft_regs_start + smum_get_offsetof(hwmgr,
+ SMU_SoftRegisters,
+ (idx == AMDGPU_PP_SENSOR_GPU_LOAD) ?
+ AverageGraphicsActivity:
+ AverageMemoryActivity);
+
+ activity_percent = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset);
+ activity_percent += 0x80;
+ activity_percent >>= 8;
+ *((uint32_t *)value) = activity_percent > 100 ? 100 : activity_percent;
+ *size = 4;
+ return 0;
+ case AMDGPU_PP_SENSOR_GPU_TEMP:
+ *((uint32_t *)value) = smu7_thermal_get_temperature(hwmgr);
+ *size = 4;
+ return 0;
+ case AMDGPU_PP_SENSOR_UVD_POWER:
+ *((uint32_t *)value) = data->uvd_power_gated ? 0 : 1;
+ *size = 4;
+ return 0;
+ case AMDGPU_PP_SENSOR_VCE_POWER:
+ *((uint32_t *)value) = data->vce_power_gated ? 0 : 1;
+ *size = 4;
+ return 0;
+ case AMDGPU_PP_SENSOR_GPU_POWER:
+ return smu7_get_gpu_power(hwmgr, (uint32_t *)value);
+ case AMDGPU_PP_SENSOR_VDDGFX:
+ if ((data->vr_config & VRCONF_VDDGFX_MASK) ==
+ (VR_SVI2_PLANE_2 << VRCONF_VDDGFX_SHIFT))
+ val_vid = PHM_READ_INDIRECT_FIELD(hwmgr->device,
+ CGS_IND_REG__SMC, PWR_SVI2_STATUS, PLANE2_VID);
+ else
+ val_vid = PHM_READ_INDIRECT_FIELD(hwmgr->device,
+ CGS_IND_REG__SMC, PWR_SVI2_STATUS, PLANE1_VID);
+
+ *((uint32_t *)value) = (uint32_t)convert_to_vddc(val_vid);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int smu7_find_dpm_states_clocks_in_dpm_table(struct pp_hwmgr *hwmgr, const void *input)
+{
+ const struct phm_set_power_state_input *states =
+ (const struct phm_set_power_state_input *)input;
+ const struct smu7_power_state *smu7_ps =
+ cast_const_phw_smu7_power_state(states->pnew_state);
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_single_dpm_table *sclk_table = &(data->dpm_table.sclk_table);
+ uint32_t sclk = smu7_ps->performance_levels
+ [smu7_ps->performance_level_count - 1].engine_clock;
+ struct smu7_single_dpm_table *mclk_table = &(data->dpm_table.mclk_table);
+ uint32_t mclk = smu7_ps->performance_levels
+ [smu7_ps->performance_level_count - 1].memory_clock;
+ struct PP_Clocks min_clocks = {0};
+ uint32_t i;
+
+ for (i = 0; i < sclk_table->count; i++) {
+ if (sclk == sclk_table->dpm_levels[i].value)
+ break;
+ }
+
+ if (i >= sclk_table->count) {
+ if (sclk > sclk_table->dpm_levels[i-1].value) {
+ data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_SCLK;
+ sclk_table->dpm_levels[i-1].value = sclk;
+ }
+ } else {
+ /* TODO: Check SCLK in DAL's minimum clocks
+ * in case DeepSleep divider update is required.
+ */
+ if (data->display_timing.min_clock_in_sr != min_clocks.engineClockInSR &&
+ (min_clocks.engineClockInSR >= SMU7_MINIMUM_ENGINE_CLOCK ||
+ data->display_timing.min_clock_in_sr >= SMU7_MINIMUM_ENGINE_CLOCK))
+ data->need_update_smu7_dpm_table |= DPMTABLE_UPDATE_SCLK;
+ }
+
+ for (i = 0; i < mclk_table->count; i++) {
+ if (mclk == mclk_table->dpm_levels[i].value)
+ break;
+ }
+
+ if (i >= mclk_table->count) {
+ if (mclk > mclk_table->dpm_levels[i-1].value) {
+ data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_MCLK;
+ mclk_table->dpm_levels[i-1].value = mclk;
+ }
+ }
+
+ if (data->display_timing.num_existing_displays != hwmgr->display_config->num_display)
+ data->need_update_smu7_dpm_table |= DPMTABLE_UPDATE_MCLK;
+
+ return 0;
+}
+
+static uint16_t smu7_get_maximum_link_speed(struct pp_hwmgr *hwmgr,
+ const struct smu7_power_state *smu7_ps)
+{
+ uint32_t i;
+ uint32_t sclk, max_sclk = 0;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_dpm_table *dpm_table = &data->dpm_table;
+
+ for (i = 0; i < smu7_ps->performance_level_count; i++) {
+ sclk = smu7_ps->performance_levels[i].engine_clock;
+ if (max_sclk < sclk)
+ max_sclk = sclk;
+ }
+
+ for (i = 0; i < dpm_table->sclk_table.count; i++) {
+ if (dpm_table->sclk_table.dpm_levels[i].value == max_sclk)
+ return (uint16_t) ((i >= dpm_table->pcie_speed_table.count) ?
+ dpm_table->pcie_speed_table.dpm_levels
+ [dpm_table->pcie_speed_table.count - 1].value :
+ dpm_table->pcie_speed_table.dpm_levels[i].value);
+ }
+
+ return 0;
+}
+
+static int smu7_request_link_speed_change_before_state_change(
+ struct pp_hwmgr *hwmgr, const void *input)
+{
+ const struct phm_set_power_state_input *states =
+ (const struct phm_set_power_state_input *)input;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ const struct smu7_power_state *smu7_nps =
+ cast_const_phw_smu7_power_state(states->pnew_state);
+ const struct smu7_power_state *polaris10_cps =
+ cast_const_phw_smu7_power_state(states->pcurrent_state);
+
+ uint16_t target_link_speed = smu7_get_maximum_link_speed(hwmgr, smu7_nps);
+ uint16_t current_link_speed;
+
+ if (data->force_pcie_gen == PP_PCIEGenInvalid)
+ current_link_speed = smu7_get_maximum_link_speed(hwmgr, polaris10_cps);
+ else
+ current_link_speed = data->force_pcie_gen;
+
+ data->force_pcie_gen = PP_PCIEGenInvalid;
+ data->pspp_notify_required = false;
+
+ if (target_link_speed > current_link_speed) {
+ switch (target_link_speed) {
+#ifdef CONFIG_ACPI
+ case PP_PCIEGen3:
+ if (0 == amdgpu_acpi_pcie_performance_request(hwmgr->adev, PCIE_PERF_REQ_GEN3, false))
+ break;
+ data->force_pcie_gen = PP_PCIEGen2;
+ if (current_link_speed == PP_PCIEGen2)
+ break;
+ fallthrough;
+ case PP_PCIEGen2:
+ if (0 == amdgpu_acpi_pcie_performance_request(hwmgr->adev, PCIE_PERF_REQ_GEN2, false))
+ break;
+ fallthrough;
+#endif
+ default:
+ data->force_pcie_gen = smu7_get_current_pcie_speed(hwmgr);
+ break;
+ }
+ } else {
+ if (target_link_speed < current_link_speed)
+ data->pspp_notify_required = true;
+ }
+
+ return 0;
+}
+
+static int smu7_freeze_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (0 == data->need_update_smu7_dpm_table)
+ return 0;
+
+ if ((0 == data->sclk_dpm_key_disabled) &&
+ (data->need_update_smu7_dpm_table &
+ (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_UPDATE_SCLK))) {
+ PP_ASSERT_WITH_CODE(true == smum_is_dpm_running(hwmgr),
+ "Trying to freeze SCLK DPM when DPM is disabled",
+ );
+ PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_SCLKDPM_FreezeLevel,
+ NULL),
+ "Failed to freeze SCLK DPM during FreezeSclkMclkDPM Function!",
+ return -EINVAL);
+ }
+
+ if ((0 == data->mclk_dpm_key_disabled) &&
+ (data->need_update_smu7_dpm_table &
+ DPMTABLE_OD_UPDATE_MCLK)) {
+ PP_ASSERT_WITH_CODE(true == smum_is_dpm_running(hwmgr),
+ "Trying to freeze MCLK DPM when DPM is disabled",
+ );
+ PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_MCLKDPM_FreezeLevel,
+ NULL),
+ "Failed to freeze MCLK DPM during FreezeSclkMclkDPM Function!",
+ return -EINVAL);
+ }
+
+ return 0;
+}
+
+static int smu7_populate_and_upload_sclk_mclk_dpm_levels(
+ struct pp_hwmgr *hwmgr, const void *input)
+{
+ int result = 0;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_dpm_table *dpm_table = &data->dpm_table;
+ uint32_t count;
+ struct smu7_odn_dpm_table *odn_table = &(data->odn_dpm_table);
+ struct phm_odn_clock_levels *odn_sclk_table = &(odn_table->odn_core_clock_dpm_levels);
+ struct phm_odn_clock_levels *odn_mclk_table = &(odn_table->odn_memory_clock_dpm_levels);
+
+ if (0 == data->need_update_smu7_dpm_table)
+ return 0;
+
+ if (hwmgr->od_enabled && data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_SCLK) {
+ for (count = 0; count < dpm_table->sclk_table.count; count++) {
+ dpm_table->sclk_table.dpm_levels[count].enabled = odn_sclk_table->entries[count].enabled;
+ dpm_table->sclk_table.dpm_levels[count].value = odn_sclk_table->entries[count].clock;
+ }
+ }
+
+ if (hwmgr->od_enabled && data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK) {
+ for (count = 0; count < dpm_table->mclk_table.count; count++) {
+ dpm_table->mclk_table.dpm_levels[count].enabled = odn_mclk_table->entries[count].enabled;
+ dpm_table->mclk_table.dpm_levels[count].value = odn_mclk_table->entries[count].clock;
+ }
+ }
+
+ if (data->need_update_smu7_dpm_table &
+ (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_UPDATE_SCLK)) {
+ result = smum_populate_all_graphic_levels(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Failed to populate SCLK during PopulateNewDPMClocksStates Function!",
+ return result);
+ }
+
+ if (data->need_update_smu7_dpm_table &
+ (DPMTABLE_OD_UPDATE_MCLK + DPMTABLE_UPDATE_MCLK)) {
+ /*populate MCLK dpm table to SMU7 */
+ result = smum_populate_all_memory_levels(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Failed to populate MCLK during PopulateNewDPMClocksStates Function!",
+ return result);
+ }
+
+ return result;
+}
+
+static int smu7_trim_single_dpm_states(struct pp_hwmgr *hwmgr,
+ struct smu7_single_dpm_table *dpm_table,
+ uint32_t low_limit, uint32_t high_limit)
+{
+ uint32_t i;
+
+ /* force the trim if mclk_switching is disabled to prevent flicker */
+ bool force_trim = (low_limit == high_limit);
+ for (i = 0; i < dpm_table->count; i++) {
+ /*skip the trim if od is enabled*/
+ if ((!hwmgr->od_enabled || force_trim)
+ && (dpm_table->dpm_levels[i].value < low_limit
+ || dpm_table->dpm_levels[i].value > high_limit))
+ dpm_table->dpm_levels[i].enabled = false;
+ else
+ dpm_table->dpm_levels[i].enabled = true;
+ }
+
+ return 0;
+}
+
+static int smu7_trim_dpm_states(struct pp_hwmgr *hwmgr,
+ const struct smu7_power_state *smu7_ps)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t high_limit_count;
+
+ PP_ASSERT_WITH_CODE((smu7_ps->performance_level_count >= 1),
+ "power state did not have any performance level",
+ return -EINVAL);
+
+ high_limit_count = (1 == smu7_ps->performance_level_count) ? 0 : 1;
+
+ smu7_trim_single_dpm_states(hwmgr,
+ &(data->dpm_table.sclk_table),
+ smu7_ps->performance_levels[0].engine_clock,
+ smu7_ps->performance_levels[high_limit_count].engine_clock);
+
+ smu7_trim_single_dpm_states(hwmgr,
+ &(data->dpm_table.mclk_table),
+ smu7_ps->performance_levels[0].memory_clock,
+ smu7_ps->performance_levels[high_limit_count].memory_clock);
+
+ return 0;
+}
+
+static int smu7_generate_dpm_level_enable_mask(
+ struct pp_hwmgr *hwmgr, const void *input)
+{
+ int result = 0;
+ const struct phm_set_power_state_input *states =
+ (const struct phm_set_power_state_input *)input;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ const struct smu7_power_state *smu7_ps =
+ cast_const_phw_smu7_power_state(states->pnew_state);
+
+
+ result = smu7_trim_dpm_states(hwmgr, smu7_ps);
+ if (result)
+ return result;
+
+ data->dpm_level_enable_mask.sclk_dpm_enable_mask =
+ phm_get_dpm_level_enable_mask_value(&data->dpm_table.sclk_table);
+ data->dpm_level_enable_mask.mclk_dpm_enable_mask =
+ phm_get_dpm_level_enable_mask_value(&data->dpm_table.mclk_table);
+ data->dpm_level_enable_mask.pcie_dpm_enable_mask =
+ phm_get_dpm_level_enable_mask_value(&data->dpm_table.pcie_speed_table);
+
+ return 0;
+}
+
+static int smu7_unfreeze_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (0 == data->need_update_smu7_dpm_table)
+ return 0;
+
+ if ((0 == data->sclk_dpm_key_disabled) &&
+ (data->need_update_smu7_dpm_table &
+ (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_UPDATE_SCLK))) {
+
+ PP_ASSERT_WITH_CODE(true == smum_is_dpm_running(hwmgr),
+ "Trying to Unfreeze SCLK DPM when DPM is disabled",
+ );
+ PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_SCLKDPM_UnfreezeLevel,
+ NULL),
+ "Failed to unfreeze SCLK DPM during UnFreezeSclkMclkDPM Function!",
+ return -EINVAL);
+ }
+
+ if ((0 == data->mclk_dpm_key_disabled) &&
+ (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK)) {
+
+ PP_ASSERT_WITH_CODE(true == smum_is_dpm_running(hwmgr),
+ "Trying to Unfreeze MCLK DPM when DPM is disabled",
+ );
+ PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_MCLKDPM_UnfreezeLevel,
+ NULL),
+ "Failed to unfreeze MCLK DPM during UnFreezeSclkMclkDPM Function!",
+ return -EINVAL);
+ }
+
+ data->need_update_smu7_dpm_table &= DPMTABLE_OD_UPDATE_VDDC;
+
+ return 0;
+}
+
+static int smu7_notify_link_speed_change_after_state_change(
+ struct pp_hwmgr *hwmgr, const void *input)
+{
+ const struct phm_set_power_state_input *states =
+ (const struct phm_set_power_state_input *)input;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ const struct smu7_power_state *smu7_ps =
+ cast_const_phw_smu7_power_state(states->pnew_state);
+ uint16_t target_link_speed = smu7_get_maximum_link_speed(hwmgr, smu7_ps);
+ uint8_t request;
+
+ if (data->pspp_notify_required) {
+ if (target_link_speed == PP_PCIEGen3)
+ request = PCIE_PERF_REQ_GEN3;
+ else if (target_link_speed == PP_PCIEGen2)
+ request = PCIE_PERF_REQ_GEN2;
+ else
+ request = PCIE_PERF_REQ_GEN1;
+
+ if (request == PCIE_PERF_REQ_GEN1 &&
+ smu7_get_current_pcie_speed(hwmgr) > 0)
+ return 0;
+
+#ifdef CONFIG_ACPI
+ if (amdgpu_acpi_pcie_performance_request(hwmgr->adev, request, false)) {
+ if (PP_PCIEGen2 == target_link_speed)
+ pr_info("PSPP request to switch to Gen2 from Gen3 Failed!");
+ else
+ pr_info("PSPP request to switch to Gen1 from Gen2 Failed!");
+ }
+#endif
+ }
+
+ return 0;
+}
+
+static int smu7_notify_smc_display(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (hwmgr->feature_mask & PP_VBI_TIME_SUPPORT_MASK) {
+ if (hwmgr->chip_id == CHIP_VEGAM)
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ (PPSMC_Msg)PPSMC_MSG_SetVBITimeout_VEGAM, data->frame_time_x2,
+ NULL);
+ else
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ (PPSMC_Msg)PPSMC_MSG_SetVBITimeout, data->frame_time_x2,
+ NULL);
+ }
+ return (smum_send_msg_to_smc(hwmgr, (PPSMC_Msg)PPSMC_HasDisplay, NULL) == 0) ? 0 : -EINVAL;
+}
+
+static int smu7_set_power_state_tasks(struct pp_hwmgr *hwmgr, const void *input)
+{
+ int tmp_result, result = 0;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ tmp_result = smu7_find_dpm_states_clocks_in_dpm_table(hwmgr, input);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to find DPM states clocks in DPM table!",
+ result = tmp_result);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PCIEPerformanceRequest)) {
+ tmp_result =
+ smu7_request_link_speed_change_before_state_change(hwmgr, input);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to request link speed change before state change!",
+ result = tmp_result);
+ }
+
+ tmp_result = smu7_freeze_sclk_mclk_dpm(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to freeze SCLK MCLK DPM!", result = tmp_result);
+
+ tmp_result = smu7_populate_and_upload_sclk_mclk_dpm_levels(hwmgr, input);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to populate and upload SCLK MCLK DPM levels!",
+ result = tmp_result);
+
+ /*
+ * If a custom pp table is loaded, set DPMTABLE_OD_UPDATE_VDDC flag.
+ * That effectively disables AVFS feature.
+ */
+ if (hwmgr->hardcode_pp_table != NULL)
+ data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_VDDC;
+
+ tmp_result = smu7_update_avfs(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to update avfs voltages!",
+ result = tmp_result);
+
+ tmp_result = smu7_generate_dpm_level_enable_mask(hwmgr, input);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to generate DPM level enabled mask!",
+ result = tmp_result);
+
+ tmp_result = smum_update_sclk_threshold(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to update SCLK threshold!",
+ result = tmp_result);
+
+ tmp_result = smu7_notify_smc_display(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to notify smc display settings!",
+ result = tmp_result);
+
+ tmp_result = smu7_unfreeze_sclk_mclk_dpm(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to unfreeze SCLK MCLK DPM!",
+ result = tmp_result);
+
+ tmp_result = smu7_upload_dpm_level_enable_mask(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to upload DPM level enabled mask!",
+ result = tmp_result);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PCIEPerformanceRequest)) {
+ tmp_result =
+ smu7_notify_link_speed_change_after_state_change(hwmgr, input);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to notify link speed change after state change!",
+ result = tmp_result);
+ }
+ data->apply_optimized_settings = false;
+ return result;
+}
+
+static int smu7_set_max_fan_pwm_output(struct pp_hwmgr *hwmgr, uint16_t us_max_fan_pwm)
+{
+ hwmgr->thermal_controller.
+ advanceFanControlParameters.usMaxFanPWM = us_max_fan_pwm;
+
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetFanPwmMax, us_max_fan_pwm,
+ NULL);
+}
+
+static int
+smu7_notify_smc_display_change(struct pp_hwmgr *hwmgr, bool has_display)
+{
+ PPSMC_Msg msg = has_display ? (PPSMC_Msg)PPSMC_HasDisplay : (PPSMC_Msg)PPSMC_NoDisplay;
+
+ return (smum_send_msg_to_smc(hwmgr, msg, NULL) == 0) ? 0 : -1;
+}
+
+static int
+smu7_notify_smc_display_config_after_ps_adjustment(struct pp_hwmgr *hwmgr)
+{
+ if (hwmgr->display_config->num_display > 1 &&
+ !hwmgr->display_config->multi_monitor_in_sync)
+ smu7_notify_smc_display_change(hwmgr, false);
+
+ return 0;
+}
+
+/**
+* Programs the display gap
+*
+* @param hwmgr the address of the powerplay hardware manager.
+* @return always OK
+*/
+static int smu7_program_display_gap(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t display_gap = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_DISPLAY_GAP_CNTL);
+ uint32_t display_gap2;
+ uint32_t pre_vbi_time_in_us;
+ uint32_t frame_time_in_us;
+ uint32_t ref_clock, refresh_rate;
+
+ display_gap = PHM_SET_FIELD(display_gap, CG_DISPLAY_GAP_CNTL, DISP_GAP, (hwmgr->display_config->num_display > 0) ? DISPLAY_GAP_VBLANK_OR_WM : DISPLAY_GAP_IGNORE);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_DISPLAY_GAP_CNTL, display_gap);
+
+ ref_clock = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev);
+ refresh_rate = hwmgr->display_config->vrefresh;
+
+ if (0 == refresh_rate)
+ refresh_rate = 60;
+
+ frame_time_in_us = 1000000 / refresh_rate;
+
+ pre_vbi_time_in_us = frame_time_in_us - 200 - hwmgr->display_config->min_vblank_time;
+
+ data->frame_time_x2 = frame_time_in_us * 2 / 100;
+
+ if (data->frame_time_x2 < 280) {
+ pr_debug("%s: enforce minimal VBITimeout: %d -> 280\n", __func__, data->frame_time_x2);
+ data->frame_time_x2 = 280;
+ }
+
+ display_gap2 = pre_vbi_time_in_us * (ref_clock / 100);
+
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_DISPLAY_GAP_CNTL2, display_gap2);
+
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ data->soft_regs_start + smum_get_offsetof(hwmgr,
+ SMU_SoftRegisters,
+ PreVBlankGap), 0x64);
+
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ data->soft_regs_start + smum_get_offsetof(hwmgr,
+ SMU_SoftRegisters,
+ VBlankTimeout),
+ (frame_time_in_us - pre_vbi_time_in_us));
+
+ return 0;
+}
+
+static int smu7_display_configuration_changed_task(struct pp_hwmgr *hwmgr)
+{
+ return smu7_program_display_gap(hwmgr);
+}
+
+/**
+* Set maximum target operating fan output RPM
+*
+* @param hwmgr: the address of the powerplay hardware manager.
+* @param usMaxFanRpm: max operating fan RPM value.
+* @return The response that came from the SMC.
+*/
+static int smu7_set_max_fan_rpm_output(struct pp_hwmgr *hwmgr, uint16_t us_max_fan_rpm)
+{
+ hwmgr->thermal_controller.
+ advanceFanControlParameters.usMaxFanRPM = us_max_fan_rpm;
+
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetFanRpmMax, us_max_fan_rpm,
+ NULL);
+}
+
+static const struct amdgpu_irq_src_funcs smu7_irq_funcs = {
+ .process = phm_irq_process,
+};
+
+static int smu7_register_irq_handlers(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_irq_src *source =
+ kzalloc(sizeof(struct amdgpu_irq_src), GFP_KERNEL);
+
+ if (!source)
+ return -ENOMEM;
+
+ source->funcs = &smu7_irq_funcs;
+
+ amdgpu_irq_add_id((struct amdgpu_device *)(hwmgr->adev),
+ AMDGPU_IRQ_CLIENTID_LEGACY,
+ VISLANDS30_IV_SRCID_CG_TSS_THERMAL_LOW_TO_HIGH,
+ source);
+ amdgpu_irq_add_id((struct amdgpu_device *)(hwmgr->adev),
+ AMDGPU_IRQ_CLIENTID_LEGACY,
+ VISLANDS30_IV_SRCID_CG_TSS_THERMAL_HIGH_TO_LOW,
+ source);
+
+ /* Register CTF(GPIO_19) interrupt */
+ amdgpu_irq_add_id((struct amdgpu_device *)(hwmgr->adev),
+ AMDGPU_IRQ_CLIENTID_LEGACY,
+ VISLANDS30_IV_SRCID_GPIO_19,
+ source);
+
+ return 0;
+}
+
+static bool
+smu7_check_smc_update_required_for_display_configuration(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ bool is_update_required = false;
+
+ if (data->display_timing.num_existing_displays != hwmgr->display_config->num_display)
+ is_update_required = true;
+
+ if (data->display_timing.vrefresh != hwmgr->display_config->vrefresh)
+ is_update_required = true;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SclkDeepSleep)) {
+ if (data->display_timing.min_clock_in_sr != hwmgr->display_config->min_core_set_clock_in_sr &&
+ (data->display_timing.min_clock_in_sr >= SMU7_MINIMUM_ENGINE_CLOCK ||
+ hwmgr->display_config->min_core_set_clock_in_sr >= SMU7_MINIMUM_ENGINE_CLOCK))
+ is_update_required = true;
+ }
+ return is_update_required;
+}
+
+static inline bool smu7_are_power_levels_equal(const struct smu7_performance_level *pl1,
+ const struct smu7_performance_level *pl2)
+{
+ return ((pl1->memory_clock == pl2->memory_clock) &&
+ (pl1->engine_clock == pl2->engine_clock) &&
+ (pl1->pcie_gen == pl2->pcie_gen) &&
+ (pl1->pcie_lane == pl2->pcie_lane));
+}
+
+static int smu7_check_states_equal(struct pp_hwmgr *hwmgr,
+ const struct pp_hw_power_state *pstate1,
+ const struct pp_hw_power_state *pstate2, bool *equal)
+{
+ const struct smu7_power_state *psa;
+ const struct smu7_power_state *psb;
+ int i;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (pstate1 == NULL || pstate2 == NULL || equal == NULL)
+ return -EINVAL;
+
+ psa = cast_const_phw_smu7_power_state(pstate1);
+ psb = cast_const_phw_smu7_power_state(pstate2);
+ /* If the two states don't even have the same number of performance levels they cannot be the same state. */
+ if (psa->performance_level_count != psb->performance_level_count) {
+ *equal = false;
+ return 0;
+ }
+
+ for (i = 0; i < psa->performance_level_count; i++) {
+ if (!smu7_are_power_levels_equal(&(psa->performance_levels[i]), &(psb->performance_levels[i]))) {
+ /* If we have found even one performance level pair that is different the states are different. */
+ *equal = false;
+ return 0;
+ }
+ }
+
+ /* If all performance levels are the same try to use the UVD clocks to break the tie.*/
+ *equal = ((psa->uvd_clks.vclk == psb->uvd_clks.vclk) && (psa->uvd_clks.dclk == psb->uvd_clks.dclk));
+ *equal &= ((psa->vce_clks.evclk == psb->vce_clks.evclk) && (psa->vce_clks.ecclk == psb->vce_clks.ecclk));
+ *equal &= (psa->sclk_threshold == psb->sclk_threshold);
+ /* For OD call, set value based on flag */
+ *equal &= !(data->need_update_smu7_dpm_table & (DPMTABLE_OD_UPDATE_SCLK |
+ DPMTABLE_OD_UPDATE_MCLK |
+ DPMTABLE_OD_UPDATE_VDDC));
+
+ return 0;
+}
+
+static int smu7_check_mc_firmware(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ uint32_t tmp;
+
+ /* Read MC indirect register offset 0x9F bits [3:0] to see
+ * if VBIOS has already loaded a full version of MC ucode
+ * or not.
+ */
+
+ smu7_get_mc_microcode_version(hwmgr);
+
+ data->need_long_memory_training = false;
+
+ cgs_write_register(hwmgr->device, mmMC_SEQ_IO_DEBUG_INDEX,
+ ixMC_IO_DEBUG_UP_13);
+ tmp = cgs_read_register(hwmgr->device, mmMC_SEQ_IO_DEBUG_DATA);
+
+ if (tmp & (1 << 23)) {
+ data->mem_latency_high = MEM_LATENCY_HIGH;
+ data->mem_latency_low = MEM_LATENCY_LOW;
+ if ((hwmgr->chip_id == CHIP_POLARIS10) ||
+ (hwmgr->chip_id == CHIP_POLARIS11) ||
+ (hwmgr->chip_id == CHIP_POLARIS12))
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_EnableFFC, NULL);
+ } else {
+ data->mem_latency_high = 330;
+ data->mem_latency_low = 330;
+ if ((hwmgr->chip_id == CHIP_POLARIS10) ||
+ (hwmgr->chip_id == CHIP_POLARIS11) ||
+ (hwmgr->chip_id == CHIP_POLARIS12))
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_DisableFFC, NULL);
+ }
+
+ return 0;
+}
+
+static int smu7_read_clock_registers(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ data->clock_registers.vCG_SPLL_FUNC_CNTL =
+ cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_SPLL_FUNC_CNTL);
+ data->clock_registers.vCG_SPLL_FUNC_CNTL_2 =
+ cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_SPLL_FUNC_CNTL_2);
+ data->clock_registers.vCG_SPLL_FUNC_CNTL_3 =
+ cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_SPLL_FUNC_CNTL_3);
+ data->clock_registers.vCG_SPLL_FUNC_CNTL_4 =
+ cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_SPLL_FUNC_CNTL_4);
+ data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM =
+ cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_SPLL_SPREAD_SPECTRUM);
+ data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2 =
+ cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_SPLL_SPREAD_SPECTRUM_2);
+ data->clock_registers.vDLL_CNTL =
+ cgs_read_register(hwmgr->device, mmDLL_CNTL);
+ data->clock_registers.vMCLK_PWRMGT_CNTL =
+ cgs_read_register(hwmgr->device, mmMCLK_PWRMGT_CNTL);
+ data->clock_registers.vMPLL_AD_FUNC_CNTL =
+ cgs_read_register(hwmgr->device, mmMPLL_AD_FUNC_CNTL);
+ data->clock_registers.vMPLL_DQ_FUNC_CNTL =
+ cgs_read_register(hwmgr->device, mmMPLL_DQ_FUNC_CNTL);
+ data->clock_registers.vMPLL_FUNC_CNTL =
+ cgs_read_register(hwmgr->device, mmMPLL_FUNC_CNTL);
+ data->clock_registers.vMPLL_FUNC_CNTL_1 =
+ cgs_read_register(hwmgr->device, mmMPLL_FUNC_CNTL_1);
+ data->clock_registers.vMPLL_FUNC_CNTL_2 =
+ cgs_read_register(hwmgr->device, mmMPLL_FUNC_CNTL_2);
+ data->clock_registers.vMPLL_SS1 =
+ cgs_read_register(hwmgr->device, mmMPLL_SS1);
+ data->clock_registers.vMPLL_SS2 =
+ cgs_read_register(hwmgr->device, mmMPLL_SS2);
+ return 0;
+
+}
+
+/**
+ * Find out if memory is GDDR5.
+ *
+ * @param hwmgr the address of the powerplay hardware manager.
+ * @return always 0
+ */
+static int smu7_get_memory_type(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ data->is_memory_gddr5 = (adev->gmc.vram_type == AMDGPU_VRAM_TYPE_GDDR5);
+
+ return 0;
+}
+
+/**
+ * Enables Dynamic Power Management by SMC
+ *
+ * @param hwmgr the address of the powerplay hardware manager.
+ * @return always 0
+ */
+static int smu7_enable_acpi_power_management(struct pp_hwmgr *hwmgr)
+{
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ GENERAL_PWRMGT, STATIC_PM_EN, 1);
+
+ return 0;
+}
+
+/**
+ * Initialize PowerGating States for different engines
+ *
+ * @param hwmgr the address of the powerplay hardware manager.
+ * @return always 0
+ */
+static int smu7_init_power_gate_state(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ data->uvd_power_gated = false;
+ data->vce_power_gated = false;
+
+ return 0;
+}
+
+static int smu7_init_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ data->low_sclk_interrupt_threshold = 0;
+ return 0;
+}
+
+static int smu7_setup_asic_task(struct pp_hwmgr *hwmgr)
+{
+ int tmp_result, result = 0;
+
+ smu7_check_mc_firmware(hwmgr);
+
+ tmp_result = smu7_read_clock_registers(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to read clock registers!", result = tmp_result);
+
+ tmp_result = smu7_get_memory_type(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to get memory type!", result = tmp_result);
+
+ tmp_result = smu7_enable_acpi_power_management(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to enable ACPI power management!", result = tmp_result);
+
+ tmp_result = smu7_init_power_gate_state(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to init power gate state!", result = tmp_result);
+
+ tmp_result = smu7_get_mc_microcode_version(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to get MC microcode version!", result = tmp_result);
+
+ tmp_result = smu7_init_sclk_threshold(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == tmp_result),
+ "Failed to init sclk threshold!", result = tmp_result);
+
+ return result;
+}
+
+static int smu7_force_clock_level(struct pp_hwmgr *hwmgr,
+ enum pp_clock_type type, uint32_t mask)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (mask == 0)
+ return -EINVAL;
+
+ switch (type) {
+ case PP_SCLK:
+ if (!data->sclk_dpm_key_disabled)
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SCLKDPM_SetEnabledMask,
+ data->dpm_level_enable_mask.sclk_dpm_enable_mask & mask,
+ NULL);
+ break;
+ case PP_MCLK:
+ if (!data->mclk_dpm_key_disabled)
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_MCLKDPM_SetEnabledMask,
+ data->dpm_level_enable_mask.mclk_dpm_enable_mask & mask,
+ NULL);
+ break;
+ case PP_PCIE:
+ {
+ uint32_t tmp = mask & data->dpm_level_enable_mask.pcie_dpm_enable_mask;
+
+ if (!data->pcie_dpm_key_disabled) {
+ if (fls(tmp) != ffs(tmp))
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_PCIeDPM_UnForceLevel,
+ NULL);
+ else
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_PCIeDPM_ForceLevel,
+ fls(tmp) - 1,
+ NULL);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int smu7_print_clock_levels(struct pp_hwmgr *hwmgr,
+ enum pp_clock_type type, char *buf)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_single_dpm_table *sclk_table = &(data->dpm_table.sclk_table);
+ struct smu7_single_dpm_table *mclk_table = &(data->dpm_table.mclk_table);
+ struct smu7_single_dpm_table *pcie_table = &(data->dpm_table.pcie_speed_table);
+ struct smu7_odn_dpm_table *odn_table = &(data->odn_dpm_table);
+ struct phm_odn_clock_levels *odn_sclk_table = &(odn_table->odn_core_clock_dpm_levels);
+ struct phm_odn_clock_levels *odn_mclk_table = &(odn_table->odn_memory_clock_dpm_levels);
+ int i, now, size = 0;
+ uint32_t clock, pcie_speed;
+
+ switch (type) {
+ case PP_SCLK:
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_API_GetSclkFrequency, &clock);
+
+ for (i = 0; i < sclk_table->count; i++) {
+ if (clock > sclk_table->dpm_levels[i].value)
+ continue;
+ break;
+ }
+ now = i;
+
+ for (i = 0; i < sclk_table->count; i++)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ i, sclk_table->dpm_levels[i].value / 100,
+ (i == now) ? "*" : "");
+ break;
+ case PP_MCLK:
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_API_GetMclkFrequency, &clock);
+
+ for (i = 0; i < mclk_table->count; i++) {
+ if (clock > mclk_table->dpm_levels[i].value)
+ continue;
+ break;
+ }
+ now = i;
+
+ for (i = 0; i < mclk_table->count; i++)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ i, mclk_table->dpm_levels[i].value / 100,
+ (i == now) ? "*" : "");
+ break;
+ case PP_PCIE:
+ pcie_speed = smu7_get_current_pcie_speed(hwmgr);
+ for (i = 0; i < pcie_table->count; i++) {
+ if (pcie_speed != pcie_table->dpm_levels[i].value)
+ continue;
+ break;
+ }
+ now = i;
+
+ for (i = 0; i < pcie_table->count; i++)
+ size += sprintf(buf + size, "%d: %s %s\n", i,
+ (pcie_table->dpm_levels[i].value == 0) ? "2.5GT/s, x8" :
+ (pcie_table->dpm_levels[i].value == 1) ? "5.0GT/s, x16" :
+ (pcie_table->dpm_levels[i].value == 2) ? "8.0GT/s, x16" : "",
+ (i == now) ? "*" : "");
+ break;
+ case OD_SCLK:
+ if (hwmgr->od_enabled) {
+ size = sprintf(buf, "%s:\n", "OD_SCLK");
+ for (i = 0; i < odn_sclk_table->num_of_pl; i++)
+ size += sprintf(buf + size, "%d: %10uMHz %10umV\n",
+ i, odn_sclk_table->entries[i].clock/100,
+ odn_sclk_table->entries[i].vddc);
+ }
+ break;
+ case OD_MCLK:
+ if (hwmgr->od_enabled) {
+ size = sprintf(buf, "%s:\n", "OD_MCLK");
+ for (i = 0; i < odn_mclk_table->num_of_pl; i++)
+ size += sprintf(buf + size, "%d: %10uMHz %10umV\n",
+ i, odn_mclk_table->entries[i].clock/100,
+ odn_mclk_table->entries[i].vddc);
+ }
+ break;
+ case OD_RANGE:
+ if (hwmgr->od_enabled) {
+ size = sprintf(buf, "%s:\n", "OD_RANGE");
+ size += sprintf(buf + size, "SCLK: %7uMHz %10uMHz\n",
+ data->golden_dpm_table.sclk_table.dpm_levels[0].value/100,
+ hwmgr->platform_descriptor.overdriveLimit.engineClock/100);
+ size += sprintf(buf + size, "MCLK: %7uMHz %10uMHz\n",
+ data->golden_dpm_table.mclk_table.dpm_levels[0].value/100,
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock/100);
+ size += sprintf(buf + size, "VDDC: %7umV %11umV\n",
+ data->odn_dpm_table.min_vddc,
+ data->odn_dpm_table.max_vddc);
+ }
+ break;
+ default:
+ break;
+ }
+ return size;
+}
+
+static void smu7_set_fan_control_mode(struct pp_hwmgr *hwmgr, uint32_t mode)
+{
+ switch (mode) {
+ case AMD_FAN_CTRL_NONE:
+ smu7_fan_ctrl_set_fan_speed_percent(hwmgr, 100);
+ break;
+ case AMD_FAN_CTRL_MANUAL:
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl))
+ smu7_fan_ctrl_stop_smc_fan_control(hwmgr);
+ break;
+ case AMD_FAN_CTRL_AUTO:
+ if (!smu7_fan_ctrl_set_static_mode(hwmgr, mode))
+ smu7_fan_ctrl_start_smc_fan_control(hwmgr);
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t smu7_get_fan_control_mode(struct pp_hwmgr *hwmgr)
+{
+ return hwmgr->fan_ctrl_enabled ? AMD_FAN_CTRL_AUTO : AMD_FAN_CTRL_MANUAL;
+}
+
+static int smu7_get_sclk_od(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_single_dpm_table *sclk_table = &(data->dpm_table.sclk_table);
+ struct smu7_single_dpm_table *golden_sclk_table =
+ &(data->golden_dpm_table.sclk_table);
+ int value = sclk_table->dpm_levels[sclk_table->count - 1].value;
+ int golden_value = golden_sclk_table->dpm_levels
+ [golden_sclk_table->count - 1].value;
+
+ value -= golden_value;
+ value = DIV_ROUND_UP(value * 100, golden_value);
+
+ return value;
+}
+
+static int smu7_set_sclk_od(struct pp_hwmgr *hwmgr, uint32_t value)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_single_dpm_table *golden_sclk_table =
+ &(data->golden_dpm_table.sclk_table);
+ struct pp_power_state *ps;
+ struct smu7_power_state *smu7_ps;
+
+ if (value > 20)
+ value = 20;
+
+ ps = hwmgr->request_ps;
+
+ if (ps == NULL)
+ return -EINVAL;
+
+ smu7_ps = cast_phw_smu7_power_state(&ps->hardware);
+
+ smu7_ps->performance_levels[smu7_ps->performance_level_count - 1].engine_clock =
+ golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value *
+ value / 100 +
+ golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value;
+
+ return 0;
+}
+
+static int smu7_get_mclk_od(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_single_dpm_table *mclk_table = &(data->dpm_table.mclk_table);
+ struct smu7_single_dpm_table *golden_mclk_table =
+ &(data->golden_dpm_table.mclk_table);
+ int value = mclk_table->dpm_levels[mclk_table->count - 1].value;
+ int golden_value = golden_mclk_table->dpm_levels
+ [golden_mclk_table->count - 1].value;
+
+ value -= golden_value;
+ value = DIV_ROUND_UP(value * 100, golden_value);
+
+ return value;
+}
+
+static int smu7_set_mclk_od(struct pp_hwmgr *hwmgr, uint32_t value)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_single_dpm_table *golden_mclk_table =
+ &(data->golden_dpm_table.mclk_table);
+ struct pp_power_state *ps;
+ struct smu7_power_state *smu7_ps;
+
+ if (value > 20)
+ value = 20;
+
+ ps = hwmgr->request_ps;
+
+ if (ps == NULL)
+ return -EINVAL;
+
+ smu7_ps = cast_phw_smu7_power_state(&ps->hardware);
+
+ smu7_ps->performance_levels[smu7_ps->performance_level_count - 1].memory_clock =
+ golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value *
+ value / 100 +
+ golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value;
+
+ return 0;
+}
+
+
+static int smu7_get_sclks(struct pp_hwmgr *hwmgr, struct amd_pp_clocks *clocks)
+{
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)hwmgr->pptable;
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_sclk_table = NULL;
+ struct phm_clock_voltage_dependency_table *sclk_table;
+ int i;
+
+ if (hwmgr->pp_table_version == PP_TABLE_V1) {
+ if (table_info == NULL || table_info->vdd_dep_on_sclk == NULL)
+ return -EINVAL;
+ dep_sclk_table = table_info->vdd_dep_on_sclk;
+ for (i = 0; i < dep_sclk_table->count; i++)
+ clocks->clock[i] = dep_sclk_table->entries[i].clk * 10;
+ clocks->count = dep_sclk_table->count;
+ } else if (hwmgr->pp_table_version == PP_TABLE_V0) {
+ sclk_table = hwmgr->dyn_state.vddc_dependency_on_sclk;
+ for (i = 0; i < sclk_table->count; i++)
+ clocks->clock[i] = sclk_table->entries[i].clk * 10;
+ clocks->count = sclk_table->count;
+ }
+
+ return 0;
+}
+
+static uint32_t smu7_get_mem_latency(struct pp_hwmgr *hwmgr, uint32_t clk)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (clk >= MEM_FREQ_LOW_LATENCY && clk < MEM_FREQ_HIGH_LATENCY)
+ return data->mem_latency_high;
+ else if (clk >= MEM_FREQ_HIGH_LATENCY)
+ return data->mem_latency_low;
+ else
+ return MEM_LATENCY_ERR;
+}
+
+static int smu7_get_mclks(struct pp_hwmgr *hwmgr, struct amd_pp_clocks *clocks)
+{
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)hwmgr->pptable;
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table;
+ int i;
+ struct phm_clock_voltage_dependency_table *mclk_table;
+
+ if (hwmgr->pp_table_version == PP_TABLE_V1) {
+ if (table_info == NULL)
+ return -EINVAL;
+ dep_mclk_table = table_info->vdd_dep_on_mclk;
+ for (i = 0; i < dep_mclk_table->count; i++) {
+ clocks->clock[i] = dep_mclk_table->entries[i].clk * 10;
+ clocks->latency[i] = smu7_get_mem_latency(hwmgr,
+ dep_mclk_table->entries[i].clk);
+ }
+ clocks->count = dep_mclk_table->count;
+ } else if (hwmgr->pp_table_version == PP_TABLE_V0) {
+ mclk_table = hwmgr->dyn_state.vddc_dependency_on_mclk;
+ for (i = 0; i < mclk_table->count; i++)
+ clocks->clock[i] = mclk_table->entries[i].clk * 10;
+ clocks->count = mclk_table->count;
+ }
+ return 0;
+}
+
+static int smu7_get_clock_by_type(struct pp_hwmgr *hwmgr, enum amd_pp_clock_type type,
+ struct amd_pp_clocks *clocks)
+{
+ switch (type) {
+ case amd_pp_sys_clock:
+ smu7_get_sclks(hwmgr, clocks);
+ break;
+ case amd_pp_mem_clock:
+ smu7_get_mclks(hwmgr, clocks);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smu7_notify_cac_buffer_info(struct pp_hwmgr *hwmgr,
+ uint32_t virtual_addr_low,
+ uint32_t virtual_addr_hi,
+ uint32_t mc_addr_low,
+ uint32_t mc_addr_hi,
+ uint32_t size)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ data->soft_regs_start +
+ smum_get_offsetof(hwmgr,
+ SMU_SoftRegisters, DRAM_LOG_ADDR_H),
+ mc_addr_hi);
+
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ data->soft_regs_start +
+ smum_get_offsetof(hwmgr,
+ SMU_SoftRegisters, DRAM_LOG_ADDR_L),
+ mc_addr_low);
+
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ data->soft_regs_start +
+ smum_get_offsetof(hwmgr,
+ SMU_SoftRegisters, DRAM_LOG_PHY_ADDR_H),
+ virtual_addr_hi);
+
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ data->soft_regs_start +
+ smum_get_offsetof(hwmgr,
+ SMU_SoftRegisters, DRAM_LOG_PHY_ADDR_L),
+ virtual_addr_low);
+
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ data->soft_regs_start +
+ smum_get_offsetof(hwmgr,
+ SMU_SoftRegisters, DRAM_LOG_BUFF_SIZE),
+ size);
+ return 0;
+}
+
+static int smu7_get_max_high_clocks(struct pp_hwmgr *hwmgr,
+ struct amd_pp_simple_clock_info *clocks)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_single_dpm_table *sclk_table = &(data->dpm_table.sclk_table);
+ struct smu7_single_dpm_table *mclk_table = &(data->dpm_table.mclk_table);
+
+ if (clocks == NULL)
+ return -EINVAL;
+
+ clocks->memory_max_clock = mclk_table->count > 1 ?
+ mclk_table->dpm_levels[mclk_table->count-1].value :
+ mclk_table->dpm_levels[0].value;
+ clocks->engine_max_clock = sclk_table->count > 1 ?
+ sclk_table->dpm_levels[sclk_table->count-1].value :
+ sclk_table->dpm_levels[0].value;
+ return 0;
+}
+
+static int smu7_get_thermal_temperature_range(struct pp_hwmgr *hwmgr,
+ struct PP_TemperatureRange *thermal_data)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)hwmgr->pptable;
+
+ memcpy(thermal_data, &SMU7ThermalPolicy[0], sizeof(struct PP_TemperatureRange));
+
+ if (hwmgr->pp_table_version == PP_TABLE_V1)
+ thermal_data->max = table_info->cac_dtp_table->usSoftwareShutdownTemp *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ else if (hwmgr->pp_table_version == PP_TABLE_V0)
+ thermal_data->max = data->thermal_temp_setting.temperature_shutdown *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+ return 0;
+}
+
+static bool smu7_check_clk_voltage_valid(struct pp_hwmgr *hwmgr,
+ enum PP_OD_DPM_TABLE_COMMAND type,
+ uint32_t clk,
+ uint32_t voltage)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (voltage < data->odn_dpm_table.min_vddc || voltage > data->odn_dpm_table.max_vddc) {
+ pr_info("OD voltage is out of range [%d - %d] mV\n",
+ data->odn_dpm_table.min_vddc,
+ data->odn_dpm_table.max_vddc);
+ return false;
+ }
+
+ if (type == PP_OD_EDIT_SCLK_VDDC_TABLE) {
+ if (data->golden_dpm_table.sclk_table.dpm_levels[0].value > clk ||
+ hwmgr->platform_descriptor.overdriveLimit.engineClock < clk) {
+ pr_info("OD engine clock is out of range [%d - %d] MHz\n",
+ data->golden_dpm_table.sclk_table.dpm_levels[0].value/100,
+ hwmgr->platform_descriptor.overdriveLimit.engineClock/100);
+ return false;
+ }
+ } else if (type == PP_OD_EDIT_MCLK_VDDC_TABLE) {
+ if (data->golden_dpm_table.mclk_table.dpm_levels[0].value > clk ||
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock < clk) {
+ pr_info("OD memory clock is out of range [%d - %d] MHz\n",
+ data->golden_dpm_table.mclk_table.dpm_levels[0].value/100,
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock/100);
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+static int smu7_odn_edit_dpm_table(struct pp_hwmgr *hwmgr,
+ enum PP_OD_DPM_TABLE_COMMAND type,
+ long *input, uint32_t size)
+{
+ uint32_t i;
+ struct phm_odn_clock_levels *podn_dpm_table_in_backend = NULL;
+ struct smu7_odn_clock_voltage_dependency_table *podn_vdd_dep_in_backend = NULL;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ uint32_t input_clk;
+ uint32_t input_vol;
+ uint32_t input_level;
+
+ PP_ASSERT_WITH_CODE(input, "NULL user input for clock and voltage",
+ return -EINVAL);
+
+ if (!hwmgr->od_enabled) {
+ pr_info("OverDrive feature not enabled\n");
+ return -EINVAL;
+ }
+
+ if (PP_OD_EDIT_SCLK_VDDC_TABLE == type) {
+ podn_dpm_table_in_backend = &data->odn_dpm_table.odn_core_clock_dpm_levels;
+ podn_vdd_dep_in_backend = &data->odn_dpm_table.vdd_dependency_on_sclk;
+ PP_ASSERT_WITH_CODE((podn_dpm_table_in_backend && podn_vdd_dep_in_backend),
+ "Failed to get ODN SCLK and Voltage tables",
+ return -EINVAL);
+ } else if (PP_OD_EDIT_MCLK_VDDC_TABLE == type) {
+ podn_dpm_table_in_backend = &data->odn_dpm_table.odn_memory_clock_dpm_levels;
+ podn_vdd_dep_in_backend = &data->odn_dpm_table.vdd_dependency_on_mclk;
+
+ PP_ASSERT_WITH_CODE((podn_dpm_table_in_backend && podn_vdd_dep_in_backend),
+ "Failed to get ODN MCLK and Voltage tables",
+ return -EINVAL);
+ } else if (PP_OD_RESTORE_DEFAULT_TABLE == type) {
+ smu7_odn_initial_default_setting(hwmgr);
+ return 0;
+ } else if (PP_OD_COMMIT_DPM_TABLE == type) {
+ smu7_check_dpm_table_updated(hwmgr);
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+
+ for (i = 0; i < size; i += 3) {
+ if (i + 3 > size || input[i] >= podn_dpm_table_in_backend->num_of_pl) {
+ pr_info("invalid clock voltage input \n");
+ return 0;
+ }
+ input_level = input[i];
+ input_clk = input[i+1] * 100;
+ input_vol = input[i+2];
+
+ if (smu7_check_clk_voltage_valid(hwmgr, type, input_clk, input_vol)) {
+ podn_dpm_table_in_backend->entries[input_level].clock = input_clk;
+ podn_vdd_dep_in_backend->entries[input_level].clk = input_clk;
+ podn_dpm_table_in_backend->entries[input_level].vddc = input_vol;
+ podn_vdd_dep_in_backend->entries[input_level].vddc = input_vol;
+ podn_vdd_dep_in_backend->entries[input_level].vddgfx = input_vol;
+ } else {
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int smu7_get_power_profile_mode(struct pp_hwmgr *hwmgr, char *buf)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t i, size = 0;
+ uint32_t len;
+
+ static const char *profile_name[7] = {"BOOTUP_DEFAULT",
+ "3D_FULL_SCREEN",
+ "POWER_SAVING",
+ "VIDEO",
+ "VR",
+ "COMPUTE",
+ "CUSTOM"};
+
+ static const char *title[8] = {"NUM",
+ "MODE_NAME",
+ "SCLK_UP_HYST",
+ "SCLK_DOWN_HYST",
+ "SCLK_ACTIVE_LEVEL",
+ "MCLK_UP_HYST",
+ "MCLK_DOWN_HYST",
+ "MCLK_ACTIVE_LEVEL"};
+
+ if (!buf)
+ return -EINVAL;
+
+ size += sprintf(buf + size, "%s %16s %16s %16s %16s %16s %16s %16s\n",
+ title[0], title[1], title[2], title[3],
+ title[4], title[5], title[6], title[7]);
+
+ len = ARRAY_SIZE(smu7_profiling);
+
+ for (i = 0; i < len; i++) {
+ if (i == hwmgr->power_profile_mode) {
+ size += sprintf(buf + size, "%3d %14s %s: %8d %16d %16d %16d %16d %16d\n",
+ i, profile_name[i], "*",
+ data->current_profile_setting.sclk_up_hyst,
+ data->current_profile_setting.sclk_down_hyst,
+ data->current_profile_setting.sclk_activity,
+ data->current_profile_setting.mclk_up_hyst,
+ data->current_profile_setting.mclk_down_hyst,
+ data->current_profile_setting.mclk_activity);
+ continue;
+ }
+ if (smu7_profiling[i].bupdate_sclk)
+ size += sprintf(buf + size, "%3d %16s: %8d %16d %16d ",
+ i, profile_name[i], smu7_profiling[i].sclk_up_hyst,
+ smu7_profiling[i].sclk_down_hyst,
+ smu7_profiling[i].sclk_activity);
+ else
+ size += sprintf(buf + size, "%3d %16s: %8s %16s %16s ",
+ i, profile_name[i], "-", "-", "-");
+
+ if (smu7_profiling[i].bupdate_mclk)
+ size += sprintf(buf + size, "%16d %16d %16d\n",
+ smu7_profiling[i].mclk_up_hyst,
+ smu7_profiling[i].mclk_down_hyst,
+ smu7_profiling[i].mclk_activity);
+ else
+ size += sprintf(buf + size, "%16s %16s %16s\n",
+ "-", "-", "-");
+ }
+
+ return size;
+}
+
+static void smu7_patch_compute_profile_mode(struct pp_hwmgr *hwmgr,
+ enum PP_SMC_POWER_PROFILE requst)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t tmp, level;
+
+ if (requst == PP_SMC_POWER_PROFILE_COMPUTE) {
+ if (data->dpm_level_enable_mask.sclk_dpm_enable_mask) {
+ level = 0;
+ tmp = data->dpm_level_enable_mask.sclk_dpm_enable_mask;
+ while (tmp >>= 1)
+ level++;
+ if (level > 0)
+ smu7_force_clock_level(hwmgr, PP_SCLK, 3 << (level-1));
+ }
+ } else if (hwmgr->power_profile_mode == PP_SMC_POWER_PROFILE_COMPUTE) {
+ smu7_force_clock_level(hwmgr, PP_SCLK, data->dpm_level_enable_mask.sclk_dpm_enable_mask);
+ }
+}
+
+static int smu7_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, uint32_t size)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct profile_mode_setting tmp;
+ enum PP_SMC_POWER_PROFILE mode;
+
+ if (input == NULL)
+ return -EINVAL;
+
+ mode = input[size];
+ switch (mode) {
+ case PP_SMC_POWER_PROFILE_CUSTOM:
+ if (size < 8 && size != 0)
+ return -EINVAL;
+ /* If only CUSTOM is passed in, use the saved values. Check
+ * that we actually have a CUSTOM profile by ensuring that
+ * the "use sclk" or the "use mclk" bits are set
+ */
+ tmp = smu7_profiling[PP_SMC_POWER_PROFILE_CUSTOM];
+ if (size == 0) {
+ if (tmp.bupdate_sclk == 0 && tmp.bupdate_mclk == 0)
+ return -EINVAL;
+ } else {
+ tmp.bupdate_sclk = input[0];
+ tmp.sclk_up_hyst = input[1];
+ tmp.sclk_down_hyst = input[2];
+ tmp.sclk_activity = input[3];
+ tmp.bupdate_mclk = input[4];
+ tmp.mclk_up_hyst = input[5];
+ tmp.mclk_down_hyst = input[6];
+ tmp.mclk_activity = input[7];
+ smu7_profiling[PP_SMC_POWER_PROFILE_CUSTOM] = tmp;
+ }
+ if (!smum_update_dpm_settings(hwmgr, &tmp)) {
+ memcpy(&data->current_profile_setting, &tmp, sizeof(struct profile_mode_setting));
+ hwmgr->power_profile_mode = mode;
+ }
+ break;
+ case PP_SMC_POWER_PROFILE_FULLSCREEN3D:
+ case PP_SMC_POWER_PROFILE_POWERSAVING:
+ case PP_SMC_POWER_PROFILE_VIDEO:
+ case PP_SMC_POWER_PROFILE_VR:
+ case PP_SMC_POWER_PROFILE_COMPUTE:
+ if (mode == hwmgr->power_profile_mode)
+ return 0;
+
+ memcpy(&tmp, &smu7_profiling[mode], sizeof(struct profile_mode_setting));
+ if (!smum_update_dpm_settings(hwmgr, &tmp)) {
+ if (tmp.bupdate_sclk) {
+ data->current_profile_setting.bupdate_sclk = tmp.bupdate_sclk;
+ data->current_profile_setting.sclk_up_hyst = tmp.sclk_up_hyst;
+ data->current_profile_setting.sclk_down_hyst = tmp.sclk_down_hyst;
+ data->current_profile_setting.sclk_activity = tmp.sclk_activity;
+ }
+ if (tmp.bupdate_mclk) {
+ data->current_profile_setting.bupdate_mclk = tmp.bupdate_mclk;
+ data->current_profile_setting.mclk_up_hyst = tmp.mclk_up_hyst;
+ data->current_profile_setting.mclk_down_hyst = tmp.mclk_down_hyst;
+ data->current_profile_setting.mclk_activity = tmp.mclk_activity;
+ }
+ smu7_patch_compute_profile_mode(hwmgr, mode);
+ hwmgr->power_profile_mode = mode;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smu7_get_performance_level(struct pp_hwmgr *hwmgr, const struct pp_hw_power_state *state,
+ PHM_PerformanceLevelDesignation designation, uint32_t index,
+ PHM_PerformanceLevel *level)
+{
+ const struct smu7_power_state *ps;
+ uint32_t i;
+
+ if (level == NULL || hwmgr == NULL || state == NULL)
+ return -EINVAL;
+
+ ps = cast_const_phw_smu7_power_state(state);
+
+ i = index > ps->performance_level_count - 1 ?
+ ps->performance_level_count - 1 : index;
+
+ level->coreClock = ps->performance_levels[i].engine_clock;
+ level->memory_clock = ps->performance_levels[i].memory_clock;
+
+ return 0;
+}
+
+static int smu7_power_off_asic(struct pp_hwmgr *hwmgr)
+{
+ int result;
+
+ result = smu7_disable_dpm_tasks(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "[disable_dpm_tasks] Failed to disable DPM!",
+ );
+
+ return result;
+}
+
+static const struct pp_hwmgr_func smu7_hwmgr_funcs = {
+ .backend_init = &smu7_hwmgr_backend_init,
+ .backend_fini = &smu7_hwmgr_backend_fini,
+ .asic_setup = &smu7_setup_asic_task,
+ .dynamic_state_management_enable = &smu7_enable_dpm_tasks,
+ .apply_state_adjust_rules = smu7_apply_state_adjust_rules,
+ .force_dpm_level = &smu7_force_dpm_level,
+ .power_state_set = smu7_set_power_state_tasks,
+ .get_power_state_size = smu7_get_power_state_size,
+ .get_mclk = smu7_dpm_get_mclk,
+ .get_sclk = smu7_dpm_get_sclk,
+ .patch_boot_state = smu7_dpm_patch_boot_state,
+ .get_pp_table_entry = smu7_get_pp_table_entry,
+ .get_num_of_pp_table_entries = smu7_get_number_of_powerplay_table_entries,
+ .powerdown_uvd = smu7_powerdown_uvd,
+ .powergate_uvd = smu7_powergate_uvd,
+ .powergate_vce = smu7_powergate_vce,
+ .disable_clock_power_gating = smu7_disable_clock_power_gating,
+ .update_clock_gatings = smu7_update_clock_gatings,
+ .notify_smc_display_config_after_ps_adjustment = smu7_notify_smc_display_config_after_ps_adjustment,
+ .display_config_changed = smu7_display_configuration_changed_task,
+ .set_max_fan_pwm_output = smu7_set_max_fan_pwm_output,
+ .set_max_fan_rpm_output = smu7_set_max_fan_rpm_output,
+ .stop_thermal_controller = smu7_thermal_stop_thermal_controller,
+ .get_fan_speed_info = smu7_fan_ctrl_get_fan_speed_info,
+ .get_fan_speed_percent = smu7_fan_ctrl_get_fan_speed_percent,
+ .set_fan_speed_percent = smu7_fan_ctrl_set_fan_speed_percent,
+ .reset_fan_speed_to_default = smu7_fan_ctrl_reset_fan_speed_to_default,
+ .get_fan_speed_rpm = smu7_fan_ctrl_get_fan_speed_rpm,
+ .set_fan_speed_rpm = smu7_fan_ctrl_set_fan_speed_rpm,
+ .uninitialize_thermal_controller = smu7_thermal_ctrl_uninitialize_thermal_controller,
+ .register_irq_handlers = smu7_register_irq_handlers,
+ .check_smc_update_required_for_display_configuration = smu7_check_smc_update_required_for_display_configuration,
+ .check_states_equal = smu7_check_states_equal,
+ .set_fan_control_mode = smu7_set_fan_control_mode,
+ .get_fan_control_mode = smu7_get_fan_control_mode,
+ .force_clock_level = smu7_force_clock_level,
+ .print_clock_levels = smu7_print_clock_levels,
+ .powergate_gfx = smu7_powergate_gfx,
+ .get_sclk_od = smu7_get_sclk_od,
+ .set_sclk_od = smu7_set_sclk_od,
+ .get_mclk_od = smu7_get_mclk_od,
+ .set_mclk_od = smu7_set_mclk_od,
+ .get_clock_by_type = smu7_get_clock_by_type,
+ .read_sensor = smu7_read_sensor,
+ .dynamic_state_management_disable = smu7_disable_dpm_tasks,
+ .avfs_control = smu7_avfs_control,
+ .disable_smc_firmware_ctf = smu7_thermal_disable_alert,
+ .start_thermal_controller = smu7_start_thermal_controller,
+ .notify_cac_buffer_info = smu7_notify_cac_buffer_info,
+ .get_max_high_clocks = smu7_get_max_high_clocks,
+ .get_thermal_temperature_range = smu7_get_thermal_temperature_range,
+ .odn_edit_dpm_table = smu7_odn_edit_dpm_table,
+ .set_power_limit = smu7_set_power_limit,
+ .get_power_profile_mode = smu7_get_power_profile_mode,
+ .set_power_profile_mode = smu7_set_power_profile_mode,
+ .get_performance_level = smu7_get_performance_level,
+ .get_asic_baco_capability = smu7_baco_get_capability,
+ .get_asic_baco_state = smu7_baco_get_state,
+ .set_asic_baco_state = smu7_baco_set_state,
+ .power_off_asic = smu7_power_off_asic,
+};
+
+uint8_t smu7_get_sleep_divider_id_from_clock(uint32_t clock,
+ uint32_t clock_insr)
+{
+ uint8_t i;
+ uint32_t temp;
+ uint32_t min = max(clock_insr, (uint32_t)SMU7_MINIMUM_ENGINE_CLOCK);
+
+ PP_ASSERT_WITH_CODE((clock >= min), "Engine clock can't satisfy stutter requirement!", return 0);
+ for (i = SMU7_MAX_DEEPSLEEP_DIVIDER_ID; ; i--) {
+ temp = clock >> i;
+
+ if (temp >= min || i == 0)
+ break;
+ }
+ return i;
+}
+
+int smu7_init_function_pointers(struct pp_hwmgr *hwmgr)
+{
+ hwmgr->hwmgr_func = &smu7_hwmgr_funcs;
+ if (hwmgr->pp_table_version == PP_TABLE_V0)
+ hwmgr->pptable_func = &pptable_funcs;
+ else if (hwmgr->pp_table_version == PP_TABLE_V1)
+ hwmgr->pptable_func = &pptable_v1_0_funcs;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.h
new file mode 100644
index 000000000000..69d361f8dfca
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.h
@@ -0,0 +1,368 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _SMU7_HWMGR_H
+#define _SMU7_HWMGR_H
+
+#include "hwmgr.h"
+#include "ppatomctrl.h"
+
+#define SMU7_MAX_HARDWARE_POWERLEVELS 2
+
+#define SMU7_VOLTAGE_CONTROL_NONE 0x0
+#define SMU7_VOLTAGE_CONTROL_BY_GPIO 0x1
+#define SMU7_VOLTAGE_CONTROL_BY_SVID2 0x2
+#define SMU7_VOLTAGE_CONTROL_MERGED 0x3
+
+enum gpu_pt_config_reg_type {
+ GPU_CONFIGREG_MMR = 0,
+ GPU_CONFIGREG_SMC_IND,
+ GPU_CONFIGREG_DIDT_IND,
+ GPU_CONFIGREG_GC_CAC_IND,
+ GPU_CONFIGREG_CACHE,
+ GPU_CONFIGREG_MAX
+};
+
+struct gpu_pt_config_reg {
+ uint32_t offset;
+ uint32_t mask;
+ uint32_t shift;
+ uint32_t value;
+ enum gpu_pt_config_reg_type type;
+};
+
+struct smu7_performance_level {
+ uint32_t memory_clock;
+ uint32_t engine_clock;
+ uint16_t pcie_gen;
+ uint16_t pcie_lane;
+};
+
+struct smu7_thermal_temperature_setting {
+ long temperature_low;
+ long temperature_high;
+ long temperature_shutdown;
+};
+
+struct smu7_uvd_clocks {
+ uint32_t vclk;
+ uint32_t dclk;
+};
+
+struct smu7_vce_clocks {
+ uint32_t evclk;
+ uint32_t ecclk;
+};
+
+struct smu7_power_state {
+ uint32_t magic;
+ struct smu7_uvd_clocks uvd_clks;
+ struct smu7_vce_clocks vce_clks;
+ uint32_t sam_clk;
+ uint16_t performance_level_count;
+ bool dc_compatible;
+ uint32_t sclk_threshold;
+ struct smu7_performance_level performance_levels[SMU7_MAX_HARDWARE_POWERLEVELS];
+};
+
+struct smu7_dpm_level {
+ bool enabled;
+ uint32_t value;
+ uint32_t param1;
+};
+
+#define SMU7_MAX_DEEPSLEEP_DIVIDER_ID 5
+#define MAX_REGULAR_DPM_NUMBER 8
+#define SMU7_MINIMUM_ENGINE_CLOCK 2500
+
+struct smu7_single_dpm_table {
+ uint32_t count;
+ struct smu7_dpm_level dpm_levels[MAX_REGULAR_DPM_NUMBER];
+};
+
+struct smu7_dpm_table {
+ struct smu7_single_dpm_table sclk_table;
+ struct smu7_single_dpm_table mclk_table;
+ struct smu7_single_dpm_table pcie_speed_table;
+ struct smu7_single_dpm_table vddc_table;
+ struct smu7_single_dpm_table vddci_table;
+ struct smu7_single_dpm_table mvdd_table;
+};
+
+struct smu7_clock_registers {
+ uint32_t vCG_SPLL_FUNC_CNTL;
+ uint32_t vCG_SPLL_FUNC_CNTL_2;
+ uint32_t vCG_SPLL_FUNC_CNTL_3;
+ uint32_t vCG_SPLL_FUNC_CNTL_4;
+ uint32_t vCG_SPLL_SPREAD_SPECTRUM;
+ uint32_t vCG_SPLL_SPREAD_SPECTRUM_2;
+ uint32_t vDLL_CNTL;
+ uint32_t vMCLK_PWRMGT_CNTL;
+ uint32_t vMPLL_AD_FUNC_CNTL;
+ uint32_t vMPLL_DQ_FUNC_CNTL;
+ uint32_t vMPLL_FUNC_CNTL;
+ uint32_t vMPLL_FUNC_CNTL_1;
+ uint32_t vMPLL_FUNC_CNTL_2;
+ uint32_t vMPLL_SS1;
+ uint32_t vMPLL_SS2;
+};
+
+#define DISABLE_MC_LOADMICROCODE 1
+#define DISABLE_MC_CFGPROGRAMMING 2
+
+struct smu7_voltage_smio_registers {
+ uint32_t vS0_VID_LOWER_SMIO_CNTL;
+};
+
+#define SMU7_MAX_LEAKAGE_COUNT 8
+
+struct smu7_leakage_voltage {
+ uint16_t count;
+ uint16_t leakage_id[SMU7_MAX_LEAKAGE_COUNT];
+ uint16_t actual_voltage[SMU7_MAX_LEAKAGE_COUNT];
+};
+
+struct smu7_vbios_boot_state {
+ uint16_t mvdd_bootup_value;
+ uint16_t vddc_bootup_value;
+ uint16_t vddci_bootup_value;
+ uint16_t vddgfx_bootup_value;
+ uint32_t sclk_bootup_value;
+ uint32_t mclk_bootup_value;
+ uint16_t pcie_gen_bootup_value;
+ uint16_t pcie_lane_bootup_value;
+};
+
+struct smu7_display_timing {
+ uint32_t min_clock_in_sr;
+ uint32_t num_existing_displays;
+ uint32_t vrefresh;
+};
+
+struct smu7_dpmlevel_enable_mask {
+ uint32_t uvd_dpm_enable_mask;
+ uint32_t vce_dpm_enable_mask;
+ uint32_t acp_dpm_enable_mask;
+ uint32_t samu_dpm_enable_mask;
+ uint32_t sclk_dpm_enable_mask;
+ uint32_t mclk_dpm_enable_mask;
+ uint32_t pcie_dpm_enable_mask;
+};
+
+struct smu7_pcie_perf_range {
+ uint16_t max;
+ uint16_t min;
+};
+
+struct smu7_odn_clock_voltage_dependency_table {
+ uint32_t count;
+ phm_ppt_v1_clock_voltage_dependency_record entries[MAX_REGULAR_DPM_NUMBER];
+};
+
+struct smu7_odn_dpm_table {
+ struct phm_odn_clock_levels odn_core_clock_dpm_levels;
+ struct phm_odn_clock_levels odn_memory_clock_dpm_levels;
+ struct smu7_odn_clock_voltage_dependency_table vdd_dependency_on_sclk;
+ struct smu7_odn_clock_voltage_dependency_table vdd_dependency_on_mclk;
+ uint32_t odn_mclk_min_limit;
+ uint32_t min_vddc;
+ uint32_t max_vddc;
+};
+
+struct profile_mode_setting {
+ uint8_t bupdate_sclk;
+ uint8_t sclk_up_hyst;
+ uint8_t sclk_down_hyst;
+ uint16_t sclk_activity;
+ uint8_t bupdate_mclk;
+ uint8_t mclk_up_hyst;
+ uint8_t mclk_down_hyst;
+ uint16_t mclk_activity;
+};
+
+struct smu7_hwmgr {
+ struct smu7_dpm_table dpm_table;
+ struct smu7_dpm_table golden_dpm_table;
+ struct smu7_odn_dpm_table odn_dpm_table;
+
+ uint32_t voting_rights_clients[8];
+ uint32_t static_screen_threshold_unit;
+ uint32_t static_screen_threshold;
+ uint32_t voltage_control;
+ uint32_t vdd_gfx_control;
+ uint32_t vddc_vddgfx_delta;
+ uint32_t active_auto_throttle_sources;
+
+ struct smu7_clock_registers clock_registers;
+
+ bool is_memory_gddr5;
+ uint16_t acpi_vddc;
+ bool pspp_notify_required;
+ uint16_t force_pcie_gen;
+ uint16_t acpi_pcie_gen;
+ uint32_t pcie_gen_cap;
+ uint32_t pcie_lane_cap;
+ uint32_t pcie_spc_cap;
+ struct smu7_leakage_voltage vddc_leakage;
+ struct smu7_leakage_voltage vddci_leakage;
+ struct smu7_leakage_voltage vddcgfx_leakage;
+
+ uint32_t mvdd_control;
+ uint32_t vddc_mask_low;
+ uint32_t mvdd_mask_low;
+ uint16_t max_vddc_in_pptable;
+ uint16_t min_vddc_in_pptable;
+ uint16_t max_vddci_in_pptable;
+ uint16_t min_vddci_in_pptable;
+ bool is_uvd_enabled;
+ struct smu7_vbios_boot_state vbios_boot_state;
+
+ bool pcie_performance_request;
+ bool battery_state;
+ bool is_tlu_enabled;
+ bool disable_handshake;
+ bool smc_voltage_control_enabled;
+ bool vbi_time_out_support;
+
+ uint32_t soft_regs_start;
+ /* ---- Stuff originally coming from Evergreen ---- */
+ uint32_t vddci_control;
+ struct pp_atomctrl_voltage_table vddc_voltage_table;
+ struct pp_atomctrl_voltage_table vddci_voltage_table;
+ struct pp_atomctrl_voltage_table mvdd_voltage_table;
+ struct pp_atomctrl_voltage_table vddgfx_voltage_table;
+
+ uint32_t mgcg_cgtt_local2;
+ uint32_t mgcg_cgtt_local3;
+ uint32_t gpio_debug;
+ uint32_t mc_micro_code_feature;
+ uint32_t highest_mclk;
+ uint16_t acpi_vddci;
+ uint8_t mvdd_high_index;
+ uint8_t mvdd_low_index;
+ bool dll_default_on;
+ bool performance_request_registered;
+
+ /* ---- Low Power Features ---- */
+ bool ulv_supported;
+
+ /* ---- CAC Stuff ---- */
+ uint32_t cac_table_start;
+ bool cac_configuration_required;
+ bool driver_calculate_cac_leakage;
+ bool cac_enabled;
+
+ /* ---- DPM2 Parameters ---- */
+ uint32_t power_containment_features;
+ bool enable_dte_feature;
+ bool enable_tdc_limit_feature;
+ bool enable_pkg_pwr_tracking_feature;
+ bool disable_uvd_power_tune_feature;
+
+
+ uint32_t dte_tj_offset;
+ uint32_t fast_watermark_threshold;
+
+ /* ---- Phase Shedding ---- */
+ uint8_t vddc_phase_shed_control;
+
+ /* ---- DI/DT ---- */
+ struct smu7_display_timing display_timing;
+
+ /* ---- Thermal Temperature Setting ---- */
+ struct smu7_thermal_temperature_setting thermal_temp_setting;
+ struct smu7_dpmlevel_enable_mask dpm_level_enable_mask;
+ uint32_t need_update_smu7_dpm_table;
+ uint32_t sclk_dpm_key_disabled;
+ uint32_t mclk_dpm_key_disabled;
+ uint32_t pcie_dpm_key_disabled;
+ uint32_t min_engine_clocks;
+ struct smu7_pcie_perf_range pcie_gen_performance;
+ struct smu7_pcie_perf_range pcie_lane_performance;
+ struct smu7_pcie_perf_range pcie_gen_power_saving;
+ struct smu7_pcie_perf_range pcie_lane_power_saving;
+ bool use_pcie_performance_levels;
+ bool use_pcie_power_saving_levels;
+ uint32_t mclk_dpm0_activity_target;
+ uint32_t low_sclk_interrupt_threshold;
+ uint32_t last_mclk_dpm_enable_mask;
+ bool uvd_enabled;
+
+ /* ---- Power Gating States ---- */
+ bool uvd_power_gated;
+ bool vce_power_gated;
+ bool need_long_memory_training;
+
+ /* Application power optimization parameters */
+ bool update_up_hyst;
+ bool update_down_hyst;
+ uint32_t down_hyst;
+ uint32_t up_hyst;
+ uint32_t disable_dpm_mask;
+ bool apply_optimized_settings;
+
+ uint32_t avfs_vdroop_override_setting;
+ bool apply_avfs_cks_off_voltage;
+ uint32_t frame_time_x2;
+ uint16_t mem_latency_high;
+ uint16_t mem_latency_low;
+ uint32_t vr_config;
+ struct profile_mode_setting current_profile_setting;
+};
+
+/* To convert to Q8.8 format for firmware */
+#define SMU7_Q88_FORMAT_CONVERSION_UNIT 256
+
+enum SMU7_I2CLineID {
+ SMU7_I2CLineID_DDC1 = 0x90,
+ SMU7_I2CLineID_DDC2 = 0x91,
+ SMU7_I2CLineID_DDC3 = 0x92,
+ SMU7_I2CLineID_DDC4 = 0x93,
+ SMU7_I2CLineID_DDC5 = 0x94,
+ SMU7_I2CLineID_DDC6 = 0x95,
+ SMU7_I2CLineID_SCLSDA = 0x96,
+ SMU7_I2CLineID_DDCVGA = 0x97
+};
+
+#define SMU7_I2C_DDC1DATA 0
+#define SMU7_I2C_DDC1CLK 1
+#define SMU7_I2C_DDC2DATA 2
+#define SMU7_I2C_DDC2CLK 3
+#define SMU7_I2C_DDC3DATA 4
+#define SMU7_I2C_DDC3CLK 5
+#define SMU7_I2C_SDA 40
+#define SMU7_I2C_SCL 41
+#define SMU7_I2C_DDC4DATA 65
+#define SMU7_I2C_DDC4CLK 66
+#define SMU7_I2C_DDC5DATA 0x48
+#define SMU7_I2C_DDC5CLK 0x49
+#define SMU7_I2C_DDC6DATA 0x4a
+#define SMU7_I2C_DDC6CLK 0x4b
+#define SMU7_I2C_DDCVGADATA 0x4c
+#define SMU7_I2C_DDCVGACLK 0x4d
+
+#define SMU7_UNUSED_GPIO_PIN 0x7F
+uint8_t smu7_get_sleep_divider_id_from_clock(uint32_t clock,
+ uint32_t clock_insr);
+#endif
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_powertune.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_powertune.c
new file mode 100644
index 000000000000..5d4971576111
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_powertune.c
@@ -0,0 +1,1239 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "pp_debug.h"
+#include "hwmgr.h"
+#include "smumgr.h"
+#include "smu7_hwmgr.h"
+#include "smu7_powertune.h"
+#include "smu7_common.h"
+
+#define VOLTAGE_SCALE 4
+
+static uint32_t DIDTBlock_Info = SQ_IR_MASK | TCP_IR_MASK | TD_PCC_MASK;
+
+static uint32_t Polaris11_DIDTBlock_Info = SQ_PCC_MASK | TCP_IR_MASK | TD_PCC_MASK;
+
+static const struct gpu_pt_config_reg GCCACConfig_Polaris10[] = {
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value Type
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x00060013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x00860013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x01060013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x01860013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x02060013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x02860013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x03060013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x03860013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x04060013, GPU_CONFIGREG_GC_CAC_IND },
+
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x000E0013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x008E0013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x010E0013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x018E0013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x020E0013, GPU_CONFIGREG_GC_CAC_IND },
+
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x00100013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x00900013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x01100013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x01900013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x02100013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x02900013, GPU_CONFIGREG_GC_CAC_IND },
+
+ { 0xFFFFFFFF }
+};
+
+static const struct gpu_pt_config_reg GCCACConfig_Polaris11[] = {
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value Type
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x00060011, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x00860011, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x01060011, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x01860011, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x02060011, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x02860011, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x03060011, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x03860011, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x04060011, GPU_CONFIGREG_GC_CAC_IND },
+
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x000E0011, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x008E0011, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x010E0011, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x018E0011, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x020E0011, GPU_CONFIGREG_GC_CAC_IND },
+
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x00100011, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x00900011, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x01100011, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x01900011, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x02100011, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x02900011, GPU_CONFIGREG_GC_CAC_IND },
+
+ { 0xFFFFFFFF }
+};
+
+static const struct gpu_pt_config_reg DIDTConfig_Polaris10[] = {
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value Type
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT0_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT0__SHIFT, 0x0073, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT1_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT1__SHIFT, 0x00ab, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT2_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT2__SHIFT, 0x0084, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT3_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT3__SHIFT, 0x005a, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT4_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT4__SHIFT, 0x0067, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT5_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT5__SHIFT, 0x0084, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT6_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT6__SHIFT, 0x0027, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT7_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT7__SHIFT, 0x0046, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT8_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT8__SHIFT, 0x00aa, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT9_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT9__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT10_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT10__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT11_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT11__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL1, DIDT_SQ_CTRL1__MIN_POWER_MASK, DIDT_SQ_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL1, DIDT_SQ_CTRL1__MAX_POWER_MASK, DIDT_SQ_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL_OCP, DIDT_SQ_CTRL_OCP__UNUSED_0_MASK, DIDT_SQ_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL_OCP, DIDT_SQ_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_SQ_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__MAX_POWER_DELTA_MASK, DIDT_SQ_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3853, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_0_MASK, DIDT_SQ_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x005a, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_1_MASK, DIDT_SQ_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_2_MASK, DIDT_SQ_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x0ebb, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__UNUSED_0_MASK, DIDT_SQ_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x3853, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x3153, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__UNUSED_0_MASK, DIDT_SQ_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK, DIDT_SQ_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__USE_REF_CLOCK_MASK, DIDT_SQ_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__PHASE_OFFSET_MASK, DIDT_SQ_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CTRL_RST_MASK, DIDT_SQ_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__UNUSED_0_MASK, DIDT_SQ_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT0_MASK, DIDT_TD_WEIGHT0_3__WEIGHT0__SHIFT, 0x000a, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT1_MASK, DIDT_TD_WEIGHT0_3__WEIGHT1__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT2_MASK, DIDT_TD_WEIGHT0_3__WEIGHT2__SHIFT, 0x0017, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT3_MASK, DIDT_TD_WEIGHT0_3__WEIGHT3__SHIFT, 0x002f, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT4_MASK, DIDT_TD_WEIGHT4_7__WEIGHT4__SHIFT, 0x0046, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT5_MASK, DIDT_TD_WEIGHT4_7__WEIGHT5__SHIFT, 0x005d, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT6_MASK, DIDT_TD_WEIGHT4_7__WEIGHT6__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT7_MASK, DIDT_TD_WEIGHT4_7__WEIGHT7__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL1, DIDT_TD_CTRL1__MIN_POWER_MASK, DIDT_TD_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL1, DIDT_TD_CTRL1__MAX_POWER_MASK, DIDT_TD_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL_OCP, DIDT_TD_CTRL_OCP__UNUSED_0_MASK, DIDT_TD_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL_OCP, DIDT_TD_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_TD_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0x00ff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__MAX_POWER_DELTA_MASK, DIDT_TD_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3fff, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_0_MASK, DIDT_TD_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x000f, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_1_MASK, DIDT_TD_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_2_MASK, DIDT_TD_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__UNUSED_0_MASK, DIDT_TD_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__UNUSED_0_MASK, DIDT_TD_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK, DIDT_TD_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__USE_REF_CLOCK_MASK, DIDT_TD_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__PHASE_OFFSET_MASK, DIDT_TD_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CTRL_RST_MASK, DIDT_TD_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0009, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0009, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__UNUSED_0_MASK, DIDT_TD_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT0_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT0__SHIFT, 0x0004, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT1_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT1__SHIFT, 0x0037, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT2_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT2__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT3_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT3__SHIFT, 0x00ff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT4_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT4__SHIFT, 0x0054, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT5_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT5__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT6_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT6__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT7_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT7__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL1, DIDT_TCP_CTRL1__MIN_POWER_MASK, DIDT_TCP_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL1, DIDT_TCP_CTRL1__MAX_POWER_MASK, DIDT_TCP_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL_OCP, DIDT_TCP_CTRL_OCP__UNUSED_0_MASK, DIDT_TCP_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL_OCP, DIDT_TCP_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_TCP_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__MAX_POWER_DELTA_MASK, DIDT_TCP_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_0_MASK, DIDT_TCP_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x0032, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_1_MASK, DIDT_TCP_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_2_MASK, DIDT_TCP_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__UNUSED_0_MASK, DIDT_TCP_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__UNUSED_0_MASK, DIDT_TCP_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK, DIDT_TCP_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__USE_REF_CLOCK_MASK, DIDT_TCP_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__PHASE_OFFSET_MASK, DIDT_TCP_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CTRL_RST_MASK, DIDT_TCP_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__UNUSED_0_MASK, DIDT_TCP_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { 0xFFFFFFFF }
+};
+
+static const struct gpu_pt_config_reg DIDTConfig_Polaris11[] = {
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value Type
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT0_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT0__SHIFT, 0x0073, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT1_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT1__SHIFT, 0x00ab, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT2_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT2__SHIFT, 0x0084, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT3_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT3__SHIFT, 0x005a, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT4_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT4__SHIFT, 0x0067, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT5_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT5__SHIFT, 0x0084, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT6_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT6__SHIFT, 0x0027, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT7_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT7__SHIFT, 0x0046, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT8_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT8__SHIFT, 0x00aa, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT9_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT9__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT10_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT10__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT11_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT11__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL1, DIDT_SQ_CTRL1__MIN_POWER_MASK, DIDT_SQ_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL1, DIDT_SQ_CTRL1__MAX_POWER_MASK, DIDT_SQ_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL_OCP, DIDT_SQ_CTRL_OCP__UNUSED_0_MASK, DIDT_SQ_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL_OCP, DIDT_SQ_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_SQ_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__MAX_POWER_DELTA_MASK, DIDT_SQ_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3fff, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_0_MASK, DIDT_SQ_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x000f, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_1_MASK, DIDT_SQ_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_2_MASK, DIDT_SQ_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__UNUSED_0_MASK, DIDT_SQ_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__UNUSED_0_MASK, DIDT_SQ_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK, DIDT_SQ_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__USE_REF_CLOCK_MASK, DIDT_SQ_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__PHASE_OFFSET_MASK, DIDT_SQ_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CTRL_RST_MASK, DIDT_SQ_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0008, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0008, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__UNUSED_0_MASK, DIDT_SQ_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT0_MASK, DIDT_TD_WEIGHT0_3__WEIGHT0__SHIFT, 0x000a, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT1_MASK, DIDT_TD_WEIGHT0_3__WEIGHT1__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT2_MASK, DIDT_TD_WEIGHT0_3__WEIGHT2__SHIFT, 0x0017, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT3_MASK, DIDT_TD_WEIGHT0_3__WEIGHT3__SHIFT, 0x002f, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT4_MASK, DIDT_TD_WEIGHT4_7__WEIGHT4__SHIFT, 0x0046, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT5_MASK, DIDT_TD_WEIGHT4_7__WEIGHT5__SHIFT, 0x005d, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT6_MASK, DIDT_TD_WEIGHT4_7__WEIGHT6__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT7_MASK, DIDT_TD_WEIGHT4_7__WEIGHT7__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL1, DIDT_TD_CTRL1__MIN_POWER_MASK, DIDT_TD_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL1, DIDT_TD_CTRL1__MAX_POWER_MASK, DIDT_TD_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL_OCP, DIDT_TD_CTRL_OCP__UNUSED_0_MASK, DIDT_TD_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL_OCP, DIDT_TD_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_TD_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0x00ff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__MAX_POWER_DELTA_MASK, DIDT_TD_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3fff, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_0_MASK, DIDT_TD_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x000f, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_1_MASK, DIDT_TD_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_2_MASK, DIDT_TD_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__UNUSED_0_MASK, DIDT_TD_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__UNUSED_0_MASK, DIDT_TD_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK, DIDT_TD_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__USE_REF_CLOCK_MASK, DIDT_TD_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__PHASE_OFFSET_MASK, DIDT_TD_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CTRL_RST_MASK, DIDT_TD_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0008, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0008, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__UNUSED_0_MASK, DIDT_TD_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT0_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT0__SHIFT, 0x0004, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT1_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT1__SHIFT, 0x0037, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT2_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT2__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT3_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT3__SHIFT, 0x00ff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT4_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT4__SHIFT, 0x0054, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT5_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT5__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT6_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT6__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT7_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT7__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL1, DIDT_TCP_CTRL1__MIN_POWER_MASK, DIDT_TCP_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL1, DIDT_TCP_CTRL1__MAX_POWER_MASK, DIDT_TCP_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL_OCP, DIDT_TCP_CTRL_OCP__UNUSED_0_MASK, DIDT_TCP_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL_OCP, DIDT_TCP_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_TCP_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__MAX_POWER_DELTA_MASK, DIDT_TCP_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_0_MASK, DIDT_TCP_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x0032, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_1_MASK, DIDT_TCP_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_2_MASK, DIDT_TCP_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__UNUSED_0_MASK, DIDT_TCP_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__UNUSED_0_MASK, DIDT_TCP_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK, DIDT_TCP_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__USE_REF_CLOCK_MASK, DIDT_TCP_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__PHASE_OFFSET_MASK, DIDT_TCP_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CTRL_RST_MASK, DIDT_TCP_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__UNUSED_0_MASK, DIDT_TCP_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { 0xFFFFFFFF }
+};
+
+static const struct gpu_pt_config_reg DIDTConfig_Polaris12[] = {
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value Type
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT0_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT0__SHIFT, 0x0073, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT1_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT1__SHIFT, 0x00ab, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT2_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT2__SHIFT, 0x0084, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT3_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT3__SHIFT, 0x005a, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT4_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT4__SHIFT, 0x0067, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT5_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT5__SHIFT, 0x0084, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT6_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT6__SHIFT, 0x0027, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT7_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT7__SHIFT, 0x0046, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT8_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT8__SHIFT, 0x00aa, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT9_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT9__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT10_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT10__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT11_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT11__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL1, DIDT_SQ_CTRL1__MIN_POWER_MASK, DIDT_SQ_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL1, DIDT_SQ_CTRL1__MAX_POWER_MASK, DIDT_SQ_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL_OCP, DIDT_SQ_CTRL_OCP__UNUSED_0_MASK, DIDT_SQ_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL_OCP, DIDT_SQ_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_SQ_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__MAX_POWER_DELTA_MASK, DIDT_SQ_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3853, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_0_MASK, DIDT_SQ_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x005a, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_1_MASK, DIDT_SQ_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_2_MASK, DIDT_SQ_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x0ebb, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__UNUSED_0_MASK, DIDT_SQ_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x3853, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x3153, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__UNUSED_0_MASK, DIDT_SQ_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK, DIDT_SQ_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__USE_REF_CLOCK_MASK, DIDT_SQ_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__PHASE_OFFSET_MASK, DIDT_SQ_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CTRL_RST_MASK, DIDT_SQ_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__UNUSED_0_MASK, DIDT_SQ_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT0_MASK, DIDT_TD_WEIGHT0_3__WEIGHT0__SHIFT, 0x000a, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT1_MASK, DIDT_TD_WEIGHT0_3__WEIGHT1__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT2_MASK, DIDT_TD_WEIGHT0_3__WEIGHT2__SHIFT, 0x0017, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT3_MASK, DIDT_TD_WEIGHT0_3__WEIGHT3__SHIFT, 0x002f, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT4_MASK, DIDT_TD_WEIGHT4_7__WEIGHT4__SHIFT, 0x0046, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT5_MASK, DIDT_TD_WEIGHT4_7__WEIGHT5__SHIFT, 0x005d, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT6_MASK, DIDT_TD_WEIGHT4_7__WEIGHT6__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT7_MASK, DIDT_TD_WEIGHT4_7__WEIGHT7__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL1, DIDT_TD_CTRL1__MIN_POWER_MASK, DIDT_TD_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL1, DIDT_TD_CTRL1__MAX_POWER_MASK, DIDT_TD_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL_OCP, DIDT_TD_CTRL_OCP__UNUSED_0_MASK, DIDT_TD_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL_OCP, DIDT_TD_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_TD_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0x00ff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__MAX_POWER_DELTA_MASK, DIDT_TD_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3fff, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_0_MASK, DIDT_TD_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x000f, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_1_MASK, DIDT_TD_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_2_MASK, DIDT_TD_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__UNUSED_0_MASK, DIDT_TD_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__UNUSED_0_MASK, DIDT_TD_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK, DIDT_TD_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__USE_REF_CLOCK_MASK, DIDT_TD_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__PHASE_OFFSET_MASK, DIDT_TD_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CTRL_RST_MASK, DIDT_TD_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0008, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0008, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__UNUSED_0_MASK, DIDT_TD_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT0_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT0__SHIFT, 0x0004, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT1_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT1__SHIFT, 0x0037, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT2_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT2__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT3_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT3__SHIFT, 0x00ff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT4_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT4__SHIFT, 0x0054, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT5_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT5__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT6_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT6__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT7_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT7__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL1, DIDT_TCP_CTRL1__MIN_POWER_MASK, DIDT_TCP_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL1, DIDT_TCP_CTRL1__MAX_POWER_MASK, DIDT_TCP_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL_OCP, DIDT_TCP_CTRL_OCP__UNUSED_0_MASK, DIDT_TCP_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL_OCP, DIDT_TCP_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_TCP_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__MAX_POWER_DELTA_MASK, DIDT_TCP_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_0_MASK, DIDT_TCP_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x0032, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_1_MASK, DIDT_TCP_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_2_MASK, DIDT_TCP_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__UNUSED_0_MASK, DIDT_TCP_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__UNUSED_0_MASK, DIDT_TCP_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK, DIDT_TCP_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__USE_REF_CLOCK_MASK, DIDT_TCP_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__PHASE_OFFSET_MASK, DIDT_TCP_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CTRL_RST_MASK, DIDT_TCP_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__UNUSED_0_MASK, DIDT_TCP_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { 0xFFFFFFFF }
+};
+
+static const struct gpu_pt_config_reg DIDTConfig_Polaris11_Kicker[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value Type
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* DIDT_SQ */
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT0_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT0__SHIFT, 0x004c, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT1_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT1__SHIFT, 0x00d0, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT2_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT2__SHIFT, 0x0069, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT3_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT3__SHIFT, 0x0048, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT4_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT4__SHIFT, 0x005f, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT5_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT5__SHIFT, 0x007a, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT6_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT6__SHIFT, 0x001f, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT7_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT7__SHIFT, 0x002d, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT8_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT8__SHIFT, 0x0088, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT9_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT9__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT10_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT10__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT11_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT11__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL1, DIDT_SQ_CTRL1__MIN_POWER_MASK, DIDT_SQ_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL1, DIDT_SQ_CTRL1__MAX_POWER_MASK, DIDT_SQ_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL_OCP, DIDT_SQ_CTRL_OCP__UNUSED_0_MASK, DIDT_SQ_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL_OCP, DIDT_SQ_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_SQ_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0x00ff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__MAX_POWER_DELTA_MASK, DIDT_SQ_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3fff, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_0_MASK, DIDT_SQ_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x000f, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_1_MASK, DIDT_SQ_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_2_MASK, DIDT_SQ_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__UNUSED_0_MASK, DIDT_SQ_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__UNUSED_0_MASK, DIDT_SQ_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK, DIDT_SQ_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__USE_REF_CLOCK_MASK, DIDT_SQ_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__PHASE_OFFSET_MASK, DIDT_SQ_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CTRL_RST_MASK, DIDT_SQ_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0008, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0008, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__UNUSED_0_MASK, DIDT_SQ_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ /* DIDT_TD */
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT0_MASK, DIDT_TD_WEIGHT0_3__WEIGHT0__SHIFT, 0x000a, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT1_MASK, DIDT_TD_WEIGHT0_3__WEIGHT1__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT2_MASK, DIDT_TD_WEIGHT0_3__WEIGHT2__SHIFT, 0x0017, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT3_MASK, DIDT_TD_WEIGHT0_3__WEIGHT3__SHIFT, 0x002f, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT4_MASK, DIDT_TD_WEIGHT4_7__WEIGHT4__SHIFT, 0x0046, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT5_MASK, DIDT_TD_WEIGHT4_7__WEIGHT5__SHIFT, 0x005d, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT6_MASK, DIDT_TD_WEIGHT4_7__WEIGHT6__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT7_MASK, DIDT_TD_WEIGHT4_7__WEIGHT7__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL1, DIDT_TD_CTRL1__MIN_POWER_MASK, DIDT_TD_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL1, DIDT_TD_CTRL1__MAX_POWER_MASK, DIDT_TD_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL_OCP, DIDT_TD_CTRL_OCP__UNUSED_0_MASK, DIDT_TD_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL_OCP, DIDT_TD_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_TD_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0x00ff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__MAX_POWER_DELTA_MASK, DIDT_TD_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3fff, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_0_MASK, DIDT_TD_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x000f, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_1_MASK, DIDT_TD_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_2_MASK, DIDT_TD_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__UNUSED_0_MASK, DIDT_TD_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__UNUSED_0_MASK, DIDT_TD_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK, DIDT_TD_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__USE_REF_CLOCK_MASK, DIDT_TD_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__PHASE_OFFSET_MASK, DIDT_TD_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CTRL_RST_MASK, DIDT_TD_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0008, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0008, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__UNUSED_0_MASK, DIDT_TD_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ /* DIDT_TCP */
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT0_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT0__SHIFT, 0x0004, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT1_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT1__SHIFT, 0x0037, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT2_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT2__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT3_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT3__SHIFT, 0x00ff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT4_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT4__SHIFT, 0x0054, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT5_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT5__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT6_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT6__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT7_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT7__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL1, DIDT_TCP_CTRL1__MIN_POWER_MASK, DIDT_TCP_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL1, DIDT_TCP_CTRL1__MAX_POWER_MASK, DIDT_TCP_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL_OCP, DIDT_TCP_CTRL_OCP__UNUSED_0_MASK, DIDT_TCP_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL_OCP, DIDT_TCP_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_TCP_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__MAX_POWER_DELTA_MASK, DIDT_TCP_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_0_MASK, DIDT_TCP_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x0032, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_1_MASK, DIDT_TCP_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_2_MASK, DIDT_TCP_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT,0x01aa, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__UNUSED_0_MASK, DIDT_TCP_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__UNUSED_0_MASK, DIDT_TCP_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK, DIDT_TCP_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__USE_REF_CLOCK_MASK, DIDT_TCP_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__PHASE_OFFSET_MASK, DIDT_TCP_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CTRL_RST_MASK, DIDT_TCP_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__UNUSED_0_MASK, DIDT_TCP_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct gpu_pt_config_reg GCCACConfig_VegaM[] =
+{
+// ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+// Offset Mask Shift Value Type
+// ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ // DIDT_SQ
+ //
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x00060013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x00860013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x01060013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x01860013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x02060013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x02860013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x03060013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x03860013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x04060013, GPU_CONFIGREG_GC_CAC_IND },
+
+ // DIDT_TD
+ //
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x000E0013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x008E0013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x010E0013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x018E0013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x020E0013, GPU_CONFIGREG_GC_CAC_IND },
+
+ // DIDT_TCP
+ //
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x00100013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x00900013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x01100013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x01900013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x02100013, GPU_CONFIGREG_GC_CAC_IND },
+ { ixGC_CAC_CNTL, 0xFFFFFFFF, 0, 0x02900013, GPU_CONFIGREG_GC_CAC_IND },
+
+ { 0xFFFFFFFF } // End of list
+};
+
+static const struct gpu_pt_config_reg DIDTConfig_VegaM[] =
+{
+// ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+// Offset Mask Shift Value Type
+// ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ // DIDT_SQ
+ //
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT0_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT0__SHIFT, 0x0073, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT1_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT1__SHIFT, 0x00ab, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT2_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT2__SHIFT, 0x0084, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT3_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT3__SHIFT, 0x005a, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT4_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT4__SHIFT, 0x0067, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT5_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT5__SHIFT, 0x0084, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT6_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT6__SHIFT, 0x0027, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT7_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT7__SHIFT, 0x0046, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT8_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT8__SHIFT, 0x00aa, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT9_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT9__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT10_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT10__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT11_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT11__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL1, DIDT_SQ_CTRL1__MIN_POWER_MASK, DIDT_SQ_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL1, DIDT_SQ_CTRL1__MAX_POWER_MASK, DIDT_SQ_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL_OCP, DIDT_SQ_CTRL_OCP__UNUSED_0_MASK, DIDT_SQ_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL_OCP, DIDT_SQ_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_SQ_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__MAX_POWER_DELTA_MASK, DIDT_SQ_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3853, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_0_MASK, DIDT_SQ_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x005a, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_1_MASK, DIDT_SQ_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_2_MASK, DIDT_SQ_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x0ebb, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__UNUSED_0_MASK, DIDT_SQ_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x3853, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x3153, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__UNUSED_0_MASK, DIDT_SQ_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK, DIDT_SQ_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__USE_REF_CLOCK_MASK, DIDT_SQ_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__PHASE_OFFSET_MASK, DIDT_SQ_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CTRL_RST_MASK, DIDT_SQ_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__UNUSED_0_MASK, DIDT_SQ_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ // DIDT_TD
+ //
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT0_MASK, DIDT_TD_WEIGHT0_3__WEIGHT0__SHIFT, 0x000a, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT1_MASK, DIDT_TD_WEIGHT0_3__WEIGHT1__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT2_MASK, DIDT_TD_WEIGHT0_3__WEIGHT2__SHIFT, 0x0017, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT3_MASK, DIDT_TD_WEIGHT0_3__WEIGHT3__SHIFT, 0x002f, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT4_MASK, DIDT_TD_WEIGHT4_7__WEIGHT4__SHIFT, 0x0046, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT5_MASK, DIDT_TD_WEIGHT4_7__WEIGHT5__SHIFT, 0x005d, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT6_MASK, DIDT_TD_WEIGHT4_7__WEIGHT6__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT7_MASK, DIDT_TD_WEIGHT4_7__WEIGHT7__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL1, DIDT_TD_CTRL1__MIN_POWER_MASK, DIDT_TD_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL1, DIDT_TD_CTRL1__MAX_POWER_MASK, DIDT_TD_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL_OCP, DIDT_TD_CTRL_OCP__UNUSED_0_MASK, DIDT_TD_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL_OCP, DIDT_TD_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_TD_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0x00ff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__MAX_POWER_DELTA_MASK, DIDT_TD_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3fff, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_0_MASK, DIDT_TD_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x000f, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_1_MASK, DIDT_TD_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_2_MASK, DIDT_TD_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__UNUSED_0_MASK, DIDT_TD_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__UNUSED_0_MASK, DIDT_TD_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK, DIDT_TD_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__USE_REF_CLOCK_MASK, DIDT_TD_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__PHASE_OFFSET_MASK, DIDT_TD_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CTRL_RST_MASK, DIDT_TD_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0009, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0009, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__UNUSED_0_MASK, DIDT_TD_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ // DIDT_TCP
+ //
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT0_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT0__SHIFT, 0x0004, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT1_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT1__SHIFT, 0x0037, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT2_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT2__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT3_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT3__SHIFT, 0x00ff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT4_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT4__SHIFT, 0x0054, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT5_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT5__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT6_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT6__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT7_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT7__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL1, DIDT_TCP_CTRL1__MIN_POWER_MASK, DIDT_TCP_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL1, DIDT_TCP_CTRL1__MAX_POWER_MASK, DIDT_TCP_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL_OCP, DIDT_TCP_CTRL_OCP__UNUSED_0_MASK, DIDT_TCP_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL_OCP, DIDT_TCP_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_TCP_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__MAX_POWER_DELTA_MASK, DIDT_TCP_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_0_MASK, DIDT_TCP_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x0032, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_1_MASK, DIDT_TCP_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_2_MASK, DIDT_TCP_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT,0x01aa, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__UNUSED_0_MASK, DIDT_TCP_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__UNUSED_0_MASK, DIDT_TCP_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK, DIDT_TCP_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__USE_REF_CLOCK_MASK, DIDT_TCP_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__PHASE_OFFSET_MASK, DIDT_TCP_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CTRL_RST_MASK, DIDT_TCP_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__UNUSED_0_MASK, DIDT_TCP_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND },
+
+ { 0xFFFFFFFF } // End of list
+};
+static int smu7_enable_didt(struct pp_hwmgr *hwmgr, const bool enable)
+{
+ uint32_t en = enable ? 1 : 0;
+ uint32_t block_en = 0;
+ int32_t result = 0;
+ uint32_t didt_block;
+
+ if (hwmgr->chip_id == CHIP_POLARIS11)
+ didt_block = Polaris11_DIDTBlock_Info;
+ else
+ didt_block = DIDTBlock_Info;
+
+ block_en = PP_CAP(PHM_PlatformCaps_SQRamping) ? en : 0;
+ CGS_WREG32_FIELD_IND(hwmgr->device, CGS_IND_REG__DIDT,
+ DIDT_SQ_CTRL0, DIDT_CTRL_EN, block_en);
+ didt_block &= ~SQ_Enable_MASK;
+ didt_block |= block_en << SQ_Enable_SHIFT;
+
+ block_en = PP_CAP(PHM_PlatformCaps_DBRamping) ? en : 0;
+ CGS_WREG32_FIELD_IND(hwmgr->device, CGS_IND_REG__DIDT,
+ DIDT_DB_CTRL0, DIDT_CTRL_EN, block_en);
+ didt_block &= ~DB_Enable_MASK;
+ didt_block |= block_en << DB_Enable_SHIFT;
+
+ block_en = PP_CAP(PHM_PlatformCaps_TDRamping) ? en : 0;
+ CGS_WREG32_FIELD_IND(hwmgr->device, CGS_IND_REG__DIDT,
+ DIDT_TD_CTRL0, DIDT_CTRL_EN, block_en);
+ didt_block &= ~TD_Enable_MASK;
+ didt_block |= block_en << TD_Enable_SHIFT;
+
+ block_en = PP_CAP(PHM_PlatformCaps_TCPRamping) ? en : 0;
+ CGS_WREG32_FIELD_IND(hwmgr->device, CGS_IND_REG__DIDT,
+ DIDT_TCP_CTRL0, DIDT_CTRL_EN, block_en);
+ didt_block &= ~TCP_Enable_MASK;
+ didt_block |= block_en << TCP_Enable_SHIFT;
+
+ if (enable)
+ result = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_Didt_Block_Function,
+ didt_block,
+ NULL);
+
+ return result;
+}
+
+static int smu7_program_pt_config_registers(struct pp_hwmgr *hwmgr,
+ const struct gpu_pt_config_reg *cac_config_regs)
+{
+ const struct gpu_pt_config_reg *config_regs = cac_config_regs;
+ uint32_t cache = 0;
+ uint32_t data = 0;
+
+ PP_ASSERT_WITH_CODE((config_regs != NULL), "Invalid config register table.", return -EINVAL);
+
+ while (config_regs->offset != 0xFFFFFFFF) {
+ if (config_regs->type == GPU_CONFIGREG_CACHE)
+ cache |= ((config_regs->value << config_regs->shift) & config_regs->mask);
+ else {
+ switch (config_regs->type) {
+ case GPU_CONFIGREG_SMC_IND:
+ data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, config_regs->offset);
+ break;
+
+ case GPU_CONFIGREG_DIDT_IND:
+ data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, config_regs->offset);
+ break;
+
+ case GPU_CONFIGREG_GC_CAC_IND:
+ data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG_GC_CAC, config_regs->offset);
+ break;
+
+ default:
+ data = cgs_read_register(hwmgr->device, config_regs->offset);
+ break;
+ }
+
+ data &= ~config_regs->mask;
+ data |= ((config_regs->value << config_regs->shift) & config_regs->mask);
+ data |= cache;
+
+ switch (config_regs->type) {
+ case GPU_CONFIGREG_SMC_IND:
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, config_regs->offset, data);
+ break;
+
+ case GPU_CONFIGREG_DIDT_IND:
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, config_regs->offset, data);
+ break;
+
+ case GPU_CONFIGREG_GC_CAC_IND:
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG_GC_CAC, config_regs->offset, data);
+ break;
+
+ default:
+ cgs_write_register(hwmgr->device, config_regs->offset, data);
+ break;
+ }
+ cache = 0;
+ }
+
+ config_regs++;
+ }
+
+ return 0;
+}
+
+int smu7_enable_didt_config(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ uint32_t num_se = 0;
+ uint32_t count, value, value2;
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ num_se = adev->gfx.config.max_shader_engines;
+
+ if (PP_CAP(PHM_PlatformCaps_SQRamping) ||
+ PP_CAP(PHM_PlatformCaps_DBRamping) ||
+ PP_CAP(PHM_PlatformCaps_TDRamping) ||
+ PP_CAP(PHM_PlatformCaps_TCPRamping)) {
+
+ amdgpu_gfx_rlc_enter_safe_mode(adev);
+ mutex_lock(&adev->grbm_idx_mutex);
+ value = 0;
+ value2 = cgs_read_register(hwmgr->device, mmGRBM_GFX_INDEX);
+ for (count = 0; count < num_se; count++) {
+ value = SYS_GRBM_GFX_INDEX_DATA__INSTANCE_BROADCAST_WRITES_MASK
+ | SYS_GRBM_GFX_INDEX_DATA__SH_BROADCAST_WRITES_MASK
+ | (count << SYS_GRBM_GFX_INDEX_DATA__SE_INDEX__SHIFT);
+ cgs_write_register(hwmgr->device, mmGRBM_GFX_INDEX, value);
+
+ if (hwmgr->chip_id == CHIP_POLARIS10) {
+ result = smu7_program_pt_config_registers(hwmgr, GCCACConfig_Polaris10);
+ PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", goto error);
+ result = smu7_program_pt_config_registers(hwmgr, DIDTConfig_Polaris10);
+ PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", goto error);
+ } else if (hwmgr->chip_id == CHIP_POLARIS11) {
+ result = smu7_program_pt_config_registers(hwmgr, GCCACConfig_Polaris11);
+ PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", goto error);
+ if (hwmgr->is_kicker)
+ result = smu7_program_pt_config_registers(hwmgr, DIDTConfig_Polaris11_Kicker);
+ else
+ result = smu7_program_pt_config_registers(hwmgr, DIDTConfig_Polaris11);
+ PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", goto error);
+ } else if (hwmgr->chip_id == CHIP_POLARIS12) {
+ result = smu7_program_pt_config_registers(hwmgr, GCCACConfig_Polaris11);
+ PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", goto error);
+ result = smu7_program_pt_config_registers(hwmgr, DIDTConfig_Polaris12);
+ PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", goto error);
+ } else if (hwmgr->chip_id == CHIP_VEGAM) {
+ result = smu7_program_pt_config_registers(hwmgr, GCCACConfig_VegaM);
+ PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", goto error);
+ result = smu7_program_pt_config_registers(hwmgr, DIDTConfig_VegaM);
+ PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", goto error);
+ }
+ }
+ cgs_write_register(hwmgr->device, mmGRBM_GFX_INDEX, value2);
+
+ result = smu7_enable_didt(hwmgr, true);
+ PP_ASSERT_WITH_CODE((result == 0), "EnableDiDt failed.", goto error);
+
+ if (hwmgr->chip_id == CHIP_POLARIS11) {
+ result = smum_send_msg_to_smc(hwmgr,
+ (uint16_t)(PPSMC_MSG_EnableDpmDidt),
+ NULL);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Failed to enable DPM DIDT.", goto error);
+ }
+ mutex_unlock(&adev->grbm_idx_mutex);
+ amdgpu_gfx_rlc_exit_safe_mode(adev);
+ }
+
+ return 0;
+error:
+ mutex_unlock(&adev->grbm_idx_mutex);
+ amdgpu_gfx_rlc_exit_safe_mode(adev);
+ return result;
+}
+
+int smu7_disable_didt_config(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ if (PP_CAP(PHM_PlatformCaps_SQRamping) ||
+ PP_CAP(PHM_PlatformCaps_DBRamping) ||
+ PP_CAP(PHM_PlatformCaps_TDRamping) ||
+ PP_CAP(PHM_PlatformCaps_TCPRamping)) {
+
+ amdgpu_gfx_rlc_enter_safe_mode(adev);
+
+ result = smu7_enable_didt(hwmgr, false);
+ PP_ASSERT_WITH_CODE((result == 0),
+ "Post DIDT enable clock gating failed.",
+ goto error);
+ if (hwmgr->chip_id == CHIP_POLARIS11) {
+ result = smum_send_msg_to_smc(hwmgr,
+ (uint16_t)(PPSMC_MSG_DisableDpmDidt),
+ NULL);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Failed to disable DPM DIDT.", goto error);
+ }
+ amdgpu_gfx_rlc_exit_safe_mode(adev);
+ }
+
+ return 0;
+error:
+ amdgpu_gfx_rlc_exit_safe_mode(adev);
+ return result;
+}
+
+int smu7_enable_smc_cac(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ int result = 0;
+
+ if (PP_CAP(PHM_PlatformCaps_CAC)) {
+ int smc_result;
+ smc_result = smum_send_msg_to_smc(hwmgr,
+ (uint16_t)(PPSMC_MSG_EnableCac),
+ NULL);
+ PP_ASSERT_WITH_CODE((0 == smc_result),
+ "Failed to enable CAC in SMC.", result = -1);
+
+ data->cac_enabled = (0 == smc_result) ? true : false;
+ }
+ return result;
+}
+
+int smu7_disable_smc_cac(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ int result = 0;
+
+ if (PP_CAP(PHM_PlatformCaps_CAC) && data->cac_enabled) {
+ int smc_result = smum_send_msg_to_smc(hwmgr,
+ (uint16_t)(PPSMC_MSG_DisableCac),
+ NULL);
+ PP_ASSERT_WITH_CODE((smc_result == 0),
+ "Failed to disable CAC in SMC.", result = -1);
+
+ data->cac_enabled = false;
+ }
+ return result;
+}
+
+int smu7_set_power_limit(struct pp_hwmgr *hwmgr, uint32_t n)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (data->power_containment_features &
+ POWERCONTAINMENT_FEATURE_PkgPwrLimit)
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_PkgPwrSetLimit,
+ n<<8,
+ NULL);
+ return 0;
+}
+
+static int smu7_set_overdriver_target_tdp(struct pp_hwmgr *hwmgr,
+ uint32_t target_tdp)
+{
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_OverDriveSetTargetTdp,
+ target_tdp,
+ NULL);
+}
+
+int smu7_enable_power_containment(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ int smc_result;
+ int result = 0;
+ struct phm_cac_tdp_table *cac_table;
+
+ data->power_containment_features = 0;
+ if (hwmgr->pp_table_version == PP_TABLE_V1)
+ cac_table = table_info->cac_dtp_table;
+ else
+ cac_table = hwmgr->dyn_state.cac_dtp_table;
+
+ if (PP_CAP(PHM_PlatformCaps_PowerContainment)) {
+ if (data->enable_tdc_limit_feature) {
+ smc_result = smum_send_msg_to_smc(hwmgr,
+ (uint16_t)(PPSMC_MSG_TDCLimitEnable),
+ NULL);
+ PP_ASSERT_WITH_CODE((0 == smc_result),
+ "Failed to enable TDCLimit in SMC.", result = -1;);
+ if (0 == smc_result)
+ data->power_containment_features |=
+ POWERCONTAINMENT_FEATURE_TDCLimit;
+ }
+
+ if (data->enable_pkg_pwr_tracking_feature) {
+ smc_result = smum_send_msg_to_smc(hwmgr,
+ (uint16_t)(PPSMC_MSG_PkgPwrLimitEnable),
+ NULL);
+ PP_ASSERT_WITH_CODE((0 == smc_result),
+ "Failed to enable PkgPwrTracking in SMC.", result = -1;);
+ if (0 == smc_result) {
+ hwmgr->default_power_limit = hwmgr->power_limit =
+ cac_table->usMaximumPowerDeliveryLimit;
+ data->power_containment_features |=
+ POWERCONTAINMENT_FEATURE_PkgPwrLimit;
+
+ if (smu7_set_power_limit(hwmgr, hwmgr->power_limit))
+ pr_err("Failed to set Default Power Limit in SMC!");
+ }
+ }
+ }
+ return result;
+}
+
+int smu7_disable_power_containment(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ int result = 0;
+
+ if (PP_CAP(PHM_PlatformCaps_PowerContainment) &&
+ data->power_containment_features) {
+ int smc_result;
+
+ if (data->power_containment_features &
+ POWERCONTAINMENT_FEATURE_TDCLimit) {
+ smc_result = smum_send_msg_to_smc(hwmgr,
+ (uint16_t)(PPSMC_MSG_TDCLimitDisable),
+ NULL);
+ PP_ASSERT_WITH_CODE((smc_result == 0),
+ "Failed to disable TDCLimit in SMC.",
+ result = smc_result);
+ }
+
+ if (data->power_containment_features &
+ POWERCONTAINMENT_FEATURE_DTE) {
+ smc_result = smum_send_msg_to_smc(hwmgr,
+ (uint16_t)(PPSMC_MSG_DisableDTE),
+ NULL);
+ PP_ASSERT_WITH_CODE((smc_result == 0),
+ "Failed to disable DTE in SMC.",
+ result = smc_result);
+ }
+
+ if (data->power_containment_features &
+ POWERCONTAINMENT_FEATURE_PkgPwrLimit) {
+ smc_result = smum_send_msg_to_smc(hwmgr,
+ (uint16_t)(PPSMC_MSG_PkgPwrLimitDisable),
+ NULL);
+ PP_ASSERT_WITH_CODE((smc_result == 0),
+ "Failed to disable PkgPwrTracking in SMC.",
+ result = smc_result);
+ }
+ data->power_containment_features = 0;
+ }
+
+ return result;
+}
+
+int smu7_power_control_set_level(struct pp_hwmgr *hwmgr)
+{
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_cac_tdp_table *cac_table;
+
+ int adjust_percent, target_tdp;
+ int result = 0;
+
+ if (hwmgr->pp_table_version == PP_TABLE_V1)
+ cac_table = table_info->cac_dtp_table;
+ else
+ cac_table = hwmgr->dyn_state.cac_dtp_table;
+ if (PP_CAP(PHM_PlatformCaps_PowerContainment)) {
+ /* adjustment percentage has already been validated */
+ adjust_percent = hwmgr->platform_descriptor.TDPAdjustmentPolarity ?
+ hwmgr->platform_descriptor.TDPAdjustment :
+ (-1 * hwmgr->platform_descriptor.TDPAdjustment);
+
+ if (hwmgr->chip_id > CHIP_TONGA)
+ target_tdp = ((100 + adjust_percent) * (int)(cac_table->usTDP * 256)) / 100;
+ else
+ target_tdp = ((100 + adjust_percent) * (int)(cac_table->usConfigurableTDP * 256)) / 100;
+
+ result = smu7_set_overdriver_target_tdp(hwmgr, (uint32_t)target_tdp);
+ }
+
+ return result;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_powertune.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_powertune.h
new file mode 100644
index 000000000000..22f86b6bf1be
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_powertune.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _SMU7_POWERTUNE_H
+#define _SMU7_POWERTUNE_H
+
+#define DIDT_SQ_CTRL0__UNUSED_0_MASK 0xfffc0000
+#define DIDT_SQ_CTRL0__UNUSED_0__SHIFT 0x12
+#define DIDT_TD_CTRL0__UNUSED_0_MASK 0xfffc0000
+#define DIDT_TD_CTRL0__UNUSED_0__SHIFT 0x12
+#define DIDT_TCP_CTRL0__UNUSED_0_MASK 0xfffc0000
+#define DIDT_TCP_CTRL0__UNUSED_0__SHIFT 0x12
+#define DIDT_SQ_TUNING_CTRL__UNUSED_0_MASK 0xc0000000
+#define DIDT_SQ_TUNING_CTRL__UNUSED_0__SHIFT 0x0000001e
+#define DIDT_TD_TUNING_CTRL__UNUSED_0_MASK 0xc0000000
+#define DIDT_TD_TUNING_CTRL__UNUSED_0__SHIFT 0x0000001e
+#define DIDT_TCP_TUNING_CTRL__UNUSED_0_MASK 0xc0000000
+#define DIDT_TCP_TUNING_CTRL__UNUSED_0__SHIFT 0x0000001e
+
+/* PowerContainment Features */
+#define POWERCONTAINMENT_FEATURE_DTE 0x00000001
+#define POWERCONTAINMENT_FEATURE_TDCLimit 0x00000002
+#define POWERCONTAINMENT_FEATURE_PkgPwrLimit 0x00000004
+
+#define ixGC_CAC_CNTL 0x0000
+#define ixDIDT_SQ_STALL_CTRL 0x0004
+#define ixDIDT_SQ_TUNING_CTRL 0x0005
+#define ixDIDT_TD_STALL_CTRL 0x0044
+#define ixDIDT_TD_TUNING_CTRL 0x0045
+#define ixDIDT_TCP_STALL_CTRL 0x0064
+#define ixDIDT_TCP_TUNING_CTRL 0x0065
+
+
+int smu7_enable_smc_cac(struct pp_hwmgr *hwmgr);
+int smu7_disable_smc_cac(struct pp_hwmgr *hwmgr);
+int smu7_enable_power_containment(struct pp_hwmgr *hwmgr);
+int smu7_disable_power_containment(struct pp_hwmgr *hwmgr);
+int smu7_set_power_limit(struct pp_hwmgr *hwmgr, uint32_t n);
+int smu7_power_control_set_level(struct pp_hwmgr *hwmgr);
+int smu7_enable_didt_config(struct pp_hwmgr *hwmgr);
+int smu7_disable_didt_config(struct pp_hwmgr *hwmgr);
+#endif /* DGPU_POWERTUNE_H */
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_thermal.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_thermal.c
new file mode 100644
index 000000000000..0b30f73649a8
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_thermal.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <asm/div64.h>
+#include "smu7_thermal.h"
+#include "smu7_hwmgr.h"
+#include "smu7_common.h"
+
+int smu7_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr,
+ struct phm_fan_speed_info *fan_speed_info)
+{
+ if (hwmgr->thermal_controller.fanInfo.bNoFan)
+ return -ENODEV;
+
+ fan_speed_info->supports_percent_read = true;
+ fan_speed_info->supports_percent_write = true;
+ fan_speed_info->min_percent = 0;
+ fan_speed_info->max_percent = 100;
+
+ if (PP_CAP(PHM_PlatformCaps_FanSpeedInTableIsRPM) &&
+ hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution) {
+ fan_speed_info->supports_rpm_read = true;
+ fan_speed_info->supports_rpm_write = true;
+ fan_speed_info->min_rpm = hwmgr->thermal_controller.fanInfo.ulMinRPM;
+ fan_speed_info->max_rpm = hwmgr->thermal_controller.fanInfo.ulMaxRPM;
+ } else {
+ fan_speed_info->min_rpm = 0;
+ fan_speed_info->max_rpm = 0;
+ }
+
+ return 0;
+}
+
+int smu7_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr,
+ uint32_t *speed)
+{
+ uint32_t duty100;
+ uint32_t duty;
+ uint64_t tmp64;
+
+ if (hwmgr->thermal_controller.fanInfo.bNoFan)
+ return -ENODEV;
+
+ duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_FDO_CTRL1, FMAX_DUTY100);
+ duty = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_THERMAL_STATUS, FDO_PWM_DUTY);
+
+ if (duty100 == 0)
+ return -EINVAL;
+
+
+ tmp64 = (uint64_t)duty * 100;
+ do_div(tmp64, duty100);
+ *speed = (uint32_t)tmp64;
+
+ if (*speed > 100)
+ *speed = 100;
+
+ return 0;
+}
+
+int smu7_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed)
+{
+ uint32_t tach_period;
+ uint32_t crystal_clock_freq;
+
+ if (hwmgr->thermal_controller.fanInfo.bNoFan ||
+ !hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution)
+ return -ENODEV;
+
+ tach_period = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_TACH_STATUS, TACH_PERIOD);
+
+ if (tach_period == 0)
+ return -EINVAL;
+
+ crystal_clock_freq = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev);
+
+ *speed = 60 * crystal_clock_freq * 10000 / tach_period;
+
+ return 0;
+}
+
+/**
+* Set Fan Speed Control to static mode, so that the user can decide what speed to use.
+* @param hwmgr the address of the powerplay hardware manager.
+* mode the fan control mode, 0 default, 1 by percent, 5, by RPM
+* @exception Should always succeed.
+*/
+int smu7_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr, uint32_t mode)
+{
+ if (hwmgr->fan_ctrl_is_in_default_mode) {
+ hwmgr->fan_ctrl_default_mode =
+ PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_FDO_CTRL2, FDO_PWM_MODE);
+ hwmgr->tmin =
+ PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_FDO_CTRL2, TMIN);
+ hwmgr->fan_ctrl_is_in_default_mode = false;
+ }
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_FDO_CTRL2, TMIN, 0);
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_FDO_CTRL2, FDO_PWM_MODE, mode);
+
+ return 0;
+}
+
+/**
+* Reset Fan Speed Control to default mode.
+* @param hwmgr the address of the powerplay hardware manager.
+* @exception Should always succeed.
+*/
+int smu7_fan_ctrl_set_default_mode(struct pp_hwmgr *hwmgr)
+{
+ if (!hwmgr->fan_ctrl_is_in_default_mode) {
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_FDO_CTRL2, FDO_PWM_MODE, hwmgr->fan_ctrl_default_mode);
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_FDO_CTRL2, TMIN, hwmgr->tmin);
+ hwmgr->fan_ctrl_is_in_default_mode = true;
+ }
+
+ return 0;
+}
+
+int smu7_fan_ctrl_start_smc_fan_control(struct pp_hwmgr *hwmgr)
+{
+ int result;
+
+ if (PP_CAP(PHM_PlatformCaps_ODFuzzyFanControlSupport)) {
+ result = smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_StartFanControl,
+ FAN_CONTROL_FUZZY, NULL);
+
+ if (PP_CAP(PHM_PlatformCaps_FanSpeedInTableIsRPM))
+ hwmgr->hwmgr_func->set_max_fan_rpm_output(hwmgr,
+ hwmgr->thermal_controller.
+ advanceFanControlParameters.usMaxFanRPM);
+ else
+ hwmgr->hwmgr_func->set_max_fan_pwm_output(hwmgr,
+ hwmgr->thermal_controller.
+ advanceFanControlParameters.usMaxFanPWM);
+
+ } else {
+ result = smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_StartFanControl,
+ FAN_CONTROL_TABLE, NULL);
+ }
+
+ if (!result && hwmgr->thermal_controller.
+ advanceFanControlParameters.ucTargetTemperature)
+ result = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetFanTemperatureTarget,
+ hwmgr->thermal_controller.
+ advanceFanControlParameters.ucTargetTemperature,
+ NULL);
+ hwmgr->fan_ctrl_enabled = true;
+
+ return result;
+}
+
+
+int smu7_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr)
+{
+ hwmgr->fan_ctrl_enabled = false;
+ return smum_send_msg_to_smc(hwmgr, PPSMC_StopFanControl, NULL);
+}
+
+/**
+* Set Fan Speed in percent.
+* @param hwmgr the address of the powerplay hardware manager.
+* @param speed is the percentage value (0% - 100%) to be set.
+* @exception Fails is the 100% setting appears to be 0.
+*/
+int smu7_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr,
+ uint32_t speed)
+{
+ uint32_t duty100;
+ uint32_t duty;
+ uint64_t tmp64;
+
+ if (hwmgr->thermal_controller.fanInfo.bNoFan)
+ return 0;
+
+ if (speed > 100)
+ speed = 100;
+
+ if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl))
+ smu7_fan_ctrl_stop_smc_fan_control(hwmgr);
+
+ duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_FDO_CTRL1, FMAX_DUTY100);
+
+ if (duty100 == 0)
+ return -EINVAL;
+
+ tmp64 = (uint64_t)speed * duty100;
+ do_div(tmp64, 100);
+ duty = (uint32_t)tmp64;
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_FDO_CTRL0, FDO_STATIC_DUTY, duty);
+
+ return smu7_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
+}
+
+/**
+* Reset Fan Speed to default.
+* @param hwmgr the address of the powerplay hardware manager.
+* @exception Always succeeds.
+*/
+int smu7_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr)
+{
+ int result;
+
+ if (hwmgr->thermal_controller.fanInfo.bNoFan)
+ return 0;
+
+ if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl)) {
+ result = smu7_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
+ if (!result)
+ result = smu7_fan_ctrl_start_smc_fan_control(hwmgr);
+ } else
+ result = smu7_fan_ctrl_set_default_mode(hwmgr);
+
+ return result;
+}
+
+/**
+* Set Fan Speed in RPM.
+* @param hwmgr the address of the powerplay hardware manager.
+* @param speed is the percentage value (min - max) to be set.
+* @exception Fails is the speed not lie between min and max.
+*/
+int smu7_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed)
+{
+ uint32_t tach_period;
+ uint32_t crystal_clock_freq;
+
+ if (hwmgr->thermal_controller.fanInfo.bNoFan ||
+ (hwmgr->thermal_controller.fanInfo.
+ ucTachometerPulsesPerRevolution == 0) ||
+ speed == 0 ||
+ (speed < hwmgr->thermal_controller.fanInfo.ulMinRPM) ||
+ (speed > hwmgr->thermal_controller.fanInfo.ulMaxRPM))
+ return 0;
+
+ if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl))
+ smu7_fan_ctrl_stop_smc_fan_control(hwmgr);
+
+ crystal_clock_freq = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev);
+
+ tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed);
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_TACH_CTRL, TARGET_PERIOD, tach_period);
+
+ return smu7_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC_RPM);
+}
+
+/**
+* Reads the remote temperature from the SIslands thermal controller.
+*
+* @param hwmgr The address of the hardware manager.
+*/
+int smu7_thermal_get_temperature(struct pp_hwmgr *hwmgr)
+{
+ int temp;
+
+ temp = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_MULT_THERMAL_STATUS, CTF_TEMP);
+
+ /* Bit 9 means the reading is lower than the lowest usable value. */
+ if (temp & 0x200)
+ temp = SMU7_THERMAL_MAXIMUM_TEMP_READING;
+ else
+ temp = temp & 0x1ff;
+
+ temp *= PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+ return temp;
+}
+
+/**
+* Set the requested temperature range for high and low alert signals
+*
+* @param hwmgr The address of the hardware manager.
+* @param range Temperature range to be programmed for high and low alert signals
+* @exception PP_Result_BadInput if the input data is not valid.
+*/
+static int smu7_thermal_set_temperature_range(struct pp_hwmgr *hwmgr,
+ int low_temp, int high_temp)
+{
+ int low = SMU7_THERMAL_MINIMUM_ALERT_TEMP *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ int high = SMU7_THERMAL_MAXIMUM_ALERT_TEMP *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+ if (low < low_temp)
+ low = low_temp;
+ if (high > high_temp)
+ high = high_temp;
+
+ if (low > high)
+ return -EINVAL;
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_THERMAL_INT, DIG_THERM_INTH,
+ (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_THERMAL_INT, DIG_THERM_INTL,
+ (low / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_THERMAL_CTRL, DIG_THERM_DPM,
+ (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
+
+ return 0;
+}
+
+/**
+* Programs thermal controller one-time setting registers
+*
+* @param hwmgr The address of the hardware manager.
+*/
+static int smu7_thermal_initialize(struct pp_hwmgr *hwmgr)
+{
+ if (hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution)
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_TACH_CTRL, EDGE_PER_REV,
+ hwmgr->thermal_controller.fanInfo.
+ ucTachometerPulsesPerRevolution - 1);
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_FDO_CTRL2, TACH_PWM_RESP_RATE, 0x28);
+
+ return 0;
+}
+
+/**
+* Enable thermal alerts on the RV770 thermal controller.
+*
+* @param hwmgr The address of the hardware manager.
+*/
+static void smu7_thermal_enable_alert(struct pp_hwmgr *hwmgr)
+{
+ uint32_t alert;
+
+ alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_THERMAL_INT, THERM_INT_MASK);
+ alert &= ~(SMU7_THERMAL_HIGH_ALERT_MASK | SMU7_THERMAL_LOW_ALERT_MASK);
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_THERMAL_INT, THERM_INT_MASK, alert);
+
+ /* send message to SMU to enable internal thermal interrupts */
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_Thermal_Cntl_Enable, NULL);
+}
+
+/**
+* Disable thermal alerts on the RV770 thermal controller.
+* @param hwmgr The address of the hardware manager.
+*/
+int smu7_thermal_disable_alert(struct pp_hwmgr *hwmgr)
+{
+ uint32_t alert;
+
+ alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_THERMAL_INT, THERM_INT_MASK);
+ alert |= (SMU7_THERMAL_HIGH_ALERT_MASK | SMU7_THERMAL_LOW_ALERT_MASK);
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_THERMAL_INT, THERM_INT_MASK, alert);
+
+ /* send message to SMU to disable internal thermal interrupts */
+ return smum_send_msg_to_smc(hwmgr, PPSMC_MSG_Thermal_Cntl_Disable, NULL);
+}
+
+/**
+* Uninitialize the thermal controller.
+* Currently just disables alerts.
+* @param hwmgr The address of the hardware manager.
+*/
+int smu7_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr)
+{
+ int result = smu7_thermal_disable_alert(hwmgr);
+
+ if (!hwmgr->thermal_controller.fanInfo.bNoFan)
+ smu7_fan_ctrl_set_default_mode(hwmgr);
+
+ return result;
+}
+
+/**
+* Start the fan control on the SMC.
+* @param hwmgr the address of the powerplay hardware manager.
+* @param pInput the pointer to input data
+* @param pOutput the pointer to output data
+* @param pStorage the pointer to temporary storage
+* @param Result the last failure code
+* @return result from set temperature range routine
+*/
+static int smu7_thermal_start_smc_fan_control(struct pp_hwmgr *hwmgr)
+{
+/* If the fantable setup has failed we could have disabled
+ * PHM_PlatformCaps_MicrocodeFanControl even after
+ * this function was included in the table.
+ * Make sure that we still think controlling the fan is OK.
+*/
+ if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl)) {
+ smu7_fan_ctrl_start_smc_fan_control(hwmgr);
+ smu7_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
+ }
+
+ return 0;
+}
+
+int smu7_start_thermal_controller(struct pp_hwmgr *hwmgr,
+ struct PP_TemperatureRange *range)
+{
+ int ret = 0;
+
+ if (range == NULL)
+ return -EINVAL;
+
+ smu7_thermal_initialize(hwmgr);
+ ret = smu7_thermal_set_temperature_range(hwmgr, range->min, range->max);
+ if (ret)
+ return -EINVAL;
+ smu7_thermal_enable_alert(hwmgr);
+ ret = smum_thermal_avfs_enable(hwmgr);
+ if (ret)
+ return -EINVAL;
+
+/* We should restrict performance levels to low before we halt the SMC.
+ * On the other hand we are still in boot state when we do this
+ * so it would be pointless.
+ * If this assumption changes we have to revisit this table.
+ */
+ smum_thermal_setup_fan_table(hwmgr);
+ smu7_thermal_start_smc_fan_control(hwmgr);
+ return 0;
+}
+
+
+
+int smu7_thermal_ctrl_uninitialize_thermal_controller(struct pp_hwmgr *hwmgr)
+{
+ if (!hwmgr->thermal_controller.fanInfo.bNoFan)
+ smu7_fan_ctrl_set_default_mode(hwmgr);
+ return 0;
+}
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_thermal.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_thermal.h
new file mode 100644
index 000000000000..42c1ba0fad78
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_thermal.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _SMU7_THERMAL_H_
+#define _SMU7_THERMAL_H_
+
+#include "hwmgr.h"
+
+#define SMU7_THERMAL_HIGH_ALERT_MASK 0x1
+#define SMU7_THERMAL_LOW_ALERT_MASK 0x2
+
+#define SMU7_THERMAL_MINIMUM_TEMP_READING -256
+#define SMU7_THERMAL_MAXIMUM_TEMP_READING 255
+
+#define SMU7_THERMAL_MINIMUM_ALERT_TEMP 0
+#define SMU7_THERMAL_MAXIMUM_ALERT_TEMP 255
+
+#define FDO_PWM_MODE_STATIC 1
+#define FDO_PWM_MODE_STATIC_RPM 5
+
+extern int smu7_thermal_get_temperature(struct pp_hwmgr *hwmgr);
+extern int smu7_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr);
+extern int smu7_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr, struct phm_fan_speed_info *fan_speed_info);
+extern int smu7_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr, uint32_t *speed);
+extern int smu7_fan_ctrl_set_default_mode(struct pp_hwmgr *hwmgr);
+extern int smu7_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr, uint32_t mode);
+extern int smu7_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr, uint32_t speed);
+extern int smu7_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr);
+extern int smu7_thermal_ctrl_uninitialize_thermal_controller(struct pp_hwmgr *hwmgr);
+extern int smu7_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed);
+extern int smu7_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed);
+extern int smu7_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr);
+extern int smu7_thermal_disable_alert(struct pp_hwmgr *hwmgr);
+extern int smu7_fan_ctrl_start_smc_fan_control(struct pp_hwmgr *hwmgr);
+extern int smu7_start_thermal_controller(struct pp_hwmgr *hwmgr,
+ struct PP_TemperatureRange *temperature_range);
+#endif
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu8_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu8_hwmgr.c
new file mode 100644
index 000000000000..35ed47ebaf09
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu8_hwmgr.c
@@ -0,0 +1,2049 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "pp_debug.h"
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include "atom-types.h"
+#include "atombios.h"
+#include "processpptables.h"
+#include "cgs_common.h"
+#include "smu/smu_8_0_d.h"
+#include "smu8_fusion.h"
+#include "smu/smu_8_0_sh_mask.h"
+#include "smumgr.h"
+#include "hwmgr.h"
+#include "hardwaremanager.h"
+#include "cz_ppsmc.h"
+#include "smu8_hwmgr.h"
+#include "power_state.h"
+#include "pp_thermal.h"
+
+#define ixSMUSVI_NB_CURRENTVID 0xD8230044
+#define CURRENT_NB_VID_MASK 0xff000000
+#define CURRENT_NB_VID__SHIFT 24
+#define ixSMUSVI_GFX_CURRENTVID 0xD8230048
+#define CURRENT_GFX_VID_MASK 0xff000000
+#define CURRENT_GFX_VID__SHIFT 24
+
+static const unsigned long smu8_magic = (unsigned long) PHM_Cz_Magic;
+
+static struct smu8_power_state *cast_smu8_power_state(struct pp_hw_power_state *hw_ps)
+{
+ if (smu8_magic != hw_ps->magic)
+ return NULL;
+
+ return (struct smu8_power_state *)hw_ps;
+}
+
+static const struct smu8_power_state *cast_const_smu8_power_state(
+ const struct pp_hw_power_state *hw_ps)
+{
+ if (smu8_magic != hw_ps->magic)
+ return NULL;
+
+ return (struct smu8_power_state *)hw_ps;
+}
+
+static uint32_t smu8_get_eclk_level(struct pp_hwmgr *hwmgr,
+ uint32_t clock, uint32_t msg)
+{
+ int i = 0;
+ struct phm_vce_clock_voltage_dependency_table *ptable =
+ hwmgr->dyn_state.vce_clock_voltage_dependency_table;
+
+ switch (msg) {
+ case PPSMC_MSG_SetEclkSoftMin:
+ case PPSMC_MSG_SetEclkHardMin:
+ for (i = 0; i < (int)ptable->count; i++) {
+ if (clock <= ptable->entries[i].ecclk)
+ break;
+ }
+ break;
+
+ case PPSMC_MSG_SetEclkSoftMax:
+ case PPSMC_MSG_SetEclkHardMax:
+ for (i = ptable->count - 1; i >= 0; i--) {
+ if (clock >= ptable->entries[i].ecclk)
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return i;
+}
+
+static uint32_t smu8_get_sclk_level(struct pp_hwmgr *hwmgr,
+ uint32_t clock, uint32_t msg)
+{
+ int i = 0;
+ struct phm_clock_voltage_dependency_table *table =
+ hwmgr->dyn_state.vddc_dependency_on_sclk;
+
+ switch (msg) {
+ case PPSMC_MSG_SetSclkSoftMin:
+ case PPSMC_MSG_SetSclkHardMin:
+ for (i = 0; i < (int)table->count; i++) {
+ if (clock <= table->entries[i].clk)
+ break;
+ }
+ break;
+
+ case PPSMC_MSG_SetSclkSoftMax:
+ case PPSMC_MSG_SetSclkHardMax:
+ for (i = table->count - 1; i >= 0; i--) {
+ if (clock >= table->entries[i].clk)
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return i;
+}
+
+static uint32_t smu8_get_uvd_level(struct pp_hwmgr *hwmgr,
+ uint32_t clock, uint32_t msg)
+{
+ int i = 0;
+ struct phm_uvd_clock_voltage_dependency_table *ptable =
+ hwmgr->dyn_state.uvd_clock_voltage_dependency_table;
+
+ switch (msg) {
+ case PPSMC_MSG_SetUvdSoftMin:
+ case PPSMC_MSG_SetUvdHardMin:
+ for (i = 0; i < (int)ptable->count; i++) {
+ if (clock <= ptable->entries[i].vclk)
+ break;
+ }
+ break;
+
+ case PPSMC_MSG_SetUvdSoftMax:
+ case PPSMC_MSG_SetUvdHardMax:
+ for (i = ptable->count - 1; i >= 0; i--) {
+ if (clock >= ptable->entries[i].vclk)
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return i;
+}
+
+static uint32_t smu8_get_max_sclk_level(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+
+ if (data->max_sclk_level == 0) {
+ smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_GetMaxSclkLevel,
+ &data->max_sclk_level);
+ data->max_sclk_level += 1;
+ }
+
+ return data->max_sclk_level;
+}
+
+static int smu8_initialize_dpm_defaults(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ data->gfx_ramp_step = 256*25/100;
+ data->gfx_ramp_delay = 1; /* by default, we delay 1us */
+
+ data->mgcg_cgtt_local0 = 0x00000000;
+ data->mgcg_cgtt_local1 = 0x00000000;
+ data->clock_slow_down_freq = 25000;
+ data->skip_clock_slow_down = 1;
+ data->enable_nb_ps_policy = 1; /* disable until UNB is ready, Enabled */
+ data->voltage_drop_in_dce_power_gating = 0; /* disable until fully verified */
+ data->voting_rights_clients = 0x00C00033;
+ data->static_screen_threshold = 8;
+ data->ddi_power_gating_disabled = 0;
+ data->bapm_enabled = 1;
+ data->voltage_drop_threshold = 0;
+ data->gfx_power_gating_threshold = 500;
+ data->vce_slow_sclk_threshold = 20000;
+ data->dce_slow_sclk_threshold = 30000;
+ data->disable_driver_thermal_policy = 1;
+ data->disable_nb_ps3_in_battery = 0;
+
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ABM);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_NonABMSupportInPPLib);
+
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DynamicM3Arbiter);
+
+ data->override_dynamic_mgpg = 1;
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DynamicPatchPowerState);
+
+ data->thermal_auto_throttling_treshold = 0;
+ data->tdr_clock = 0;
+ data->disable_gfx_power_gating_in_uvd = 0;
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DynamicUVDState);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UVDDPM);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_VCEDPM);
+
+ data->cc6_settings.cpu_cc6_disable = false;
+ data->cc6_settings.cpu_pstate_disable = false;
+ data->cc6_settings.nb_pstate_switch_disable = false;
+ data->cc6_settings.cpu_pstate_separation_time = 0;
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DisableVoltageIsland);
+
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UVDPowerGating);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_VCEPowerGating);
+
+ if (adev->pg_flags & AMD_PG_SUPPORT_UVD)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UVDPowerGating);
+ if (adev->pg_flags & AMD_PG_SUPPORT_VCE)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_VCEPowerGating);
+
+
+ return 0;
+}
+
+/* convert form 8bit vid to real voltage in mV*4 */
+static uint32_t smu8_convert_8Bit_index_to_voltage(
+ struct pp_hwmgr *hwmgr, uint16_t voltage)
+{
+ return 6200 - (voltage * 25);
+}
+
+static int smu8_construct_max_power_limits_table(struct pp_hwmgr *hwmgr,
+ struct phm_clock_and_voltage_limits *table)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+ struct smu8_sys_info *sys_info = &data->sys_info;
+ struct phm_clock_voltage_dependency_table *dep_table =
+ hwmgr->dyn_state.vddc_dependency_on_sclk;
+
+ if (dep_table->count > 0) {
+ table->sclk = dep_table->entries[dep_table->count-1].clk;
+ table->vddc = smu8_convert_8Bit_index_to_voltage(hwmgr,
+ (uint16_t)dep_table->entries[dep_table->count-1].v);
+ }
+ table->mclk = sys_info->nbp_memory_clock[0];
+ return 0;
+}
+
+static int smu8_init_dynamic_state_adjustment_rule_settings(
+ struct pp_hwmgr *hwmgr,
+ ATOM_CLK_VOLT_CAPABILITY *disp_voltage_table)
+{
+ struct phm_clock_voltage_dependency_table *table_clk_vlt;
+
+ table_clk_vlt = kzalloc(struct_size(table_clk_vlt, entries, 7),
+ GFP_KERNEL);
+
+ if (NULL == table_clk_vlt) {
+ pr_err("Can not allocate memory!\n");
+ return -ENOMEM;
+ }
+
+ table_clk_vlt->count = 8;
+ table_clk_vlt->entries[0].clk = PP_DAL_POWERLEVEL_0;
+ table_clk_vlt->entries[0].v = 0;
+ table_clk_vlt->entries[1].clk = PP_DAL_POWERLEVEL_1;
+ table_clk_vlt->entries[1].v = 1;
+ table_clk_vlt->entries[2].clk = PP_DAL_POWERLEVEL_2;
+ table_clk_vlt->entries[2].v = 2;
+ table_clk_vlt->entries[3].clk = PP_DAL_POWERLEVEL_3;
+ table_clk_vlt->entries[3].v = 3;
+ table_clk_vlt->entries[4].clk = PP_DAL_POWERLEVEL_4;
+ table_clk_vlt->entries[4].v = 4;
+ table_clk_vlt->entries[5].clk = PP_DAL_POWERLEVEL_5;
+ table_clk_vlt->entries[5].v = 5;
+ table_clk_vlt->entries[6].clk = PP_DAL_POWERLEVEL_6;
+ table_clk_vlt->entries[6].v = 6;
+ table_clk_vlt->entries[7].clk = PP_DAL_POWERLEVEL_7;
+ table_clk_vlt->entries[7].v = 7;
+ hwmgr->dyn_state.vddc_dep_on_dal_pwrl = table_clk_vlt;
+
+ return 0;
+}
+
+static int smu8_get_system_info_data(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+ ATOM_INTEGRATED_SYSTEM_INFO_V1_9 *info = NULL;
+ uint32_t i;
+ int result = 0;
+ uint8_t frev, crev;
+ uint16_t size;
+
+ info = (ATOM_INTEGRATED_SYSTEM_INFO_V1_9 *)smu_atom_get_data_table(hwmgr->adev,
+ GetIndexIntoMasterTable(DATA, IntegratedSystemInfo),
+ &size, &frev, &crev);
+
+ if (info == NULL) {
+ pr_err("Could not retrieve the Integrated System Info Table!\n");
+ return -EINVAL;
+ }
+
+ if (crev != 9) {
+ pr_err("Unsupported IGP table: %d %d\n", frev, crev);
+ return -EINVAL;
+ }
+
+ data->sys_info.bootup_uma_clock =
+ le32_to_cpu(info->ulBootUpUMAClock);
+
+ data->sys_info.bootup_engine_clock =
+ le32_to_cpu(info->ulBootUpEngineClock);
+
+ data->sys_info.dentist_vco_freq =
+ le32_to_cpu(info->ulDentistVCOFreq);
+
+ data->sys_info.system_config =
+ le32_to_cpu(info->ulSystemConfig);
+
+ data->sys_info.bootup_nb_voltage_index =
+ le16_to_cpu(info->usBootUpNBVoltage);
+
+ data->sys_info.htc_hyst_lmt =
+ (info->ucHtcHystLmt == 0) ? 5 : info->ucHtcHystLmt;
+
+ data->sys_info.htc_tmp_lmt =
+ (info->ucHtcTmpLmt == 0) ? 203 : info->ucHtcTmpLmt;
+
+ if (data->sys_info.htc_tmp_lmt <=
+ data->sys_info.htc_hyst_lmt) {
+ pr_err("The htcTmpLmt should be larger than htcHystLmt.\n");
+ return -EINVAL;
+ }
+
+ data->sys_info.nb_dpm_enable =
+ data->enable_nb_ps_policy &&
+ (le32_to_cpu(info->ulSystemConfig) >> 3 & 0x1);
+
+ for (i = 0; i < SMU8_NUM_NBPSTATES; i++) {
+ if (i < SMU8_NUM_NBPMEMORYCLOCK) {
+ data->sys_info.nbp_memory_clock[i] =
+ le32_to_cpu(info->ulNbpStateMemclkFreq[i]);
+ }
+ data->sys_info.nbp_n_clock[i] =
+ le32_to_cpu(info->ulNbpStateNClkFreq[i]);
+ }
+
+ for (i = 0; i < MAX_DISPLAY_CLOCK_LEVEL; i++) {
+ data->sys_info.display_clock[i] =
+ le32_to_cpu(info->sDispClkVoltageMapping[i].ulMaximumSupportedCLK);
+ }
+
+ /* Here use 4 levels, make sure not exceed */
+ for (i = 0; i < SMU8_NUM_NBPSTATES; i++) {
+ data->sys_info.nbp_voltage_index[i] =
+ le16_to_cpu(info->usNBPStateVoltage[i]);
+ }
+
+ if (!data->sys_info.nb_dpm_enable) {
+ for (i = 1; i < SMU8_NUM_NBPSTATES; i++) {
+ if (i < SMU8_NUM_NBPMEMORYCLOCK) {
+ data->sys_info.nbp_memory_clock[i] =
+ data->sys_info.nbp_memory_clock[0];
+ }
+ data->sys_info.nbp_n_clock[i] =
+ data->sys_info.nbp_n_clock[0];
+ data->sys_info.nbp_voltage_index[i] =
+ data->sys_info.nbp_voltage_index[0];
+ }
+ }
+
+ if (le32_to_cpu(info->ulGPUCapInfo) &
+ SYS_INFO_GPUCAPS__ENABEL_DFS_BYPASS) {
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EnableDFSBypass);
+ }
+
+ data->sys_info.uma_channel_number = info->ucUMAChannelNumber;
+
+ smu8_construct_max_power_limits_table (hwmgr,
+ &hwmgr->dyn_state.max_clock_voltage_on_ac);
+
+ smu8_init_dynamic_state_adjustment_rule_settings(hwmgr,
+ &info->sDISPCLK_Voltage[0]);
+
+ return result;
+}
+
+static int smu8_construct_boot_state(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+
+ data->boot_power_level.engineClock =
+ data->sys_info.bootup_engine_clock;
+
+ data->boot_power_level.vddcIndex =
+ (uint8_t)data->sys_info.bootup_nb_voltage_index;
+
+ data->boot_power_level.dsDividerIndex = 0;
+ data->boot_power_level.ssDividerIndex = 0;
+ data->boot_power_level.allowGnbSlow = 1;
+ data->boot_power_level.forceNBPstate = 0;
+ data->boot_power_level.hysteresis_up = 0;
+ data->boot_power_level.numSIMDToPowerDown = 0;
+ data->boot_power_level.display_wm = 0;
+ data->boot_power_level.vce_wm = 0;
+
+ return 0;
+}
+
+static int smu8_upload_pptable_to_smu(struct pp_hwmgr *hwmgr)
+{
+ struct SMU8_Fusion_ClkTable *clock_table;
+ int ret;
+ uint32_t i;
+ void *table = NULL;
+ pp_atomctrl_clock_dividers_kong dividers;
+
+ struct phm_clock_voltage_dependency_table *vddc_table =
+ hwmgr->dyn_state.vddc_dependency_on_sclk;
+ struct phm_clock_voltage_dependency_table *vdd_gfx_table =
+ hwmgr->dyn_state.vdd_gfx_dependency_on_sclk;
+ struct phm_acp_clock_voltage_dependency_table *acp_table =
+ hwmgr->dyn_state.acp_clock_voltage_dependency_table;
+ struct phm_uvd_clock_voltage_dependency_table *uvd_table =
+ hwmgr->dyn_state.uvd_clock_voltage_dependency_table;
+ struct phm_vce_clock_voltage_dependency_table *vce_table =
+ hwmgr->dyn_state.vce_clock_voltage_dependency_table;
+
+ if (!hwmgr->need_pp_table_upload)
+ return 0;
+
+ ret = smum_download_powerplay_table(hwmgr, &table);
+
+ PP_ASSERT_WITH_CODE((0 == ret && NULL != table),
+ "Fail to get clock table from SMU!", return -EINVAL;);
+
+ clock_table = (struct SMU8_Fusion_ClkTable *)table;
+
+ /* patch clock table */
+ PP_ASSERT_WITH_CODE((vddc_table->count <= SMU8_MAX_HARDWARE_POWERLEVELS),
+ "Dependency table entry exceeds max limit!", return -EINVAL;);
+ PP_ASSERT_WITH_CODE((vdd_gfx_table->count <= SMU8_MAX_HARDWARE_POWERLEVELS),
+ "Dependency table entry exceeds max limit!", return -EINVAL;);
+ PP_ASSERT_WITH_CODE((acp_table->count <= SMU8_MAX_HARDWARE_POWERLEVELS),
+ "Dependency table entry exceeds max limit!", return -EINVAL;);
+ PP_ASSERT_WITH_CODE((uvd_table->count <= SMU8_MAX_HARDWARE_POWERLEVELS),
+ "Dependency table entry exceeds max limit!", return -EINVAL;);
+ PP_ASSERT_WITH_CODE((vce_table->count <= SMU8_MAX_HARDWARE_POWERLEVELS),
+ "Dependency table entry exceeds max limit!", return -EINVAL;);
+
+ for (i = 0; i < SMU8_MAX_HARDWARE_POWERLEVELS; i++) {
+
+ /* vddc_sclk */
+ clock_table->SclkBreakdownTable.ClkLevel[i].GnbVid =
+ (i < vddc_table->count) ? (uint8_t)vddc_table->entries[i].v : 0;
+ clock_table->SclkBreakdownTable.ClkLevel[i].Frequency =
+ (i < vddc_table->count) ? vddc_table->entries[i].clk : 0;
+
+ atomctrl_get_engine_pll_dividers_kong(hwmgr,
+ clock_table->SclkBreakdownTable.ClkLevel[i].Frequency,
+ &dividers);
+
+ clock_table->SclkBreakdownTable.ClkLevel[i].DfsDid =
+ (uint8_t)dividers.pll_post_divider;
+
+ /* vddgfx_sclk */
+ clock_table->SclkBreakdownTable.ClkLevel[i].GfxVid =
+ (i < vdd_gfx_table->count) ? (uint8_t)vdd_gfx_table->entries[i].v : 0;
+
+ /* acp breakdown */
+ clock_table->AclkBreakdownTable.ClkLevel[i].GfxVid =
+ (i < acp_table->count) ? (uint8_t)acp_table->entries[i].v : 0;
+ clock_table->AclkBreakdownTable.ClkLevel[i].Frequency =
+ (i < acp_table->count) ? acp_table->entries[i].acpclk : 0;
+
+ atomctrl_get_engine_pll_dividers_kong(hwmgr,
+ clock_table->AclkBreakdownTable.ClkLevel[i].Frequency,
+ &dividers);
+
+ clock_table->AclkBreakdownTable.ClkLevel[i].DfsDid =
+ (uint8_t)dividers.pll_post_divider;
+
+
+ /* uvd breakdown */
+ clock_table->VclkBreakdownTable.ClkLevel[i].GfxVid =
+ (i < uvd_table->count) ? (uint8_t)uvd_table->entries[i].v : 0;
+ clock_table->VclkBreakdownTable.ClkLevel[i].Frequency =
+ (i < uvd_table->count) ? uvd_table->entries[i].vclk : 0;
+
+ atomctrl_get_engine_pll_dividers_kong(hwmgr,
+ clock_table->VclkBreakdownTable.ClkLevel[i].Frequency,
+ &dividers);
+
+ clock_table->VclkBreakdownTable.ClkLevel[i].DfsDid =
+ (uint8_t)dividers.pll_post_divider;
+
+ clock_table->DclkBreakdownTable.ClkLevel[i].GfxVid =
+ (i < uvd_table->count) ? (uint8_t)uvd_table->entries[i].v : 0;
+ clock_table->DclkBreakdownTable.ClkLevel[i].Frequency =
+ (i < uvd_table->count) ? uvd_table->entries[i].dclk : 0;
+
+ atomctrl_get_engine_pll_dividers_kong(hwmgr,
+ clock_table->DclkBreakdownTable.ClkLevel[i].Frequency,
+ &dividers);
+
+ clock_table->DclkBreakdownTable.ClkLevel[i].DfsDid =
+ (uint8_t)dividers.pll_post_divider;
+
+ /* vce breakdown */
+ clock_table->EclkBreakdownTable.ClkLevel[i].GfxVid =
+ (i < vce_table->count) ? (uint8_t)vce_table->entries[i].v : 0;
+ clock_table->EclkBreakdownTable.ClkLevel[i].Frequency =
+ (i < vce_table->count) ? vce_table->entries[i].ecclk : 0;
+
+
+ atomctrl_get_engine_pll_dividers_kong(hwmgr,
+ clock_table->EclkBreakdownTable.ClkLevel[i].Frequency,
+ &dividers);
+
+ clock_table->EclkBreakdownTable.ClkLevel[i].DfsDid =
+ (uint8_t)dividers.pll_post_divider;
+
+ }
+ ret = smum_upload_powerplay_table(hwmgr);
+
+ return ret;
+}
+
+static int smu8_init_sclk_limit(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+ struct phm_clock_voltage_dependency_table *table =
+ hwmgr->dyn_state.vddc_dependency_on_sclk;
+ unsigned long clock = 0, level;
+
+ if (NULL == table || table->count <= 0)
+ return -EINVAL;
+
+ data->sclk_dpm.soft_min_clk = table->entries[0].clk;
+ data->sclk_dpm.hard_min_clk = table->entries[0].clk;
+
+ level = smu8_get_max_sclk_level(hwmgr) - 1;
+
+ if (level < table->count)
+ clock = table->entries[level].clk;
+ else
+ clock = table->entries[table->count - 1].clk;
+
+ data->sclk_dpm.soft_max_clk = clock;
+ data->sclk_dpm.hard_max_clk = clock;
+
+ return 0;
+}
+
+static int smu8_init_uvd_limit(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+ struct phm_uvd_clock_voltage_dependency_table *table =
+ hwmgr->dyn_state.uvd_clock_voltage_dependency_table;
+ unsigned long clock = 0;
+ uint32_t level;
+
+ if (NULL == table || table->count <= 0)
+ return -EINVAL;
+
+ data->uvd_dpm.soft_min_clk = 0;
+ data->uvd_dpm.hard_min_clk = 0;
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMaxUvdLevel, &level);
+
+ if (level < table->count)
+ clock = table->entries[level].vclk;
+ else
+ clock = table->entries[table->count - 1].vclk;
+
+ data->uvd_dpm.soft_max_clk = clock;
+ data->uvd_dpm.hard_max_clk = clock;
+
+ return 0;
+}
+
+static int smu8_init_vce_limit(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+ struct phm_vce_clock_voltage_dependency_table *table =
+ hwmgr->dyn_state.vce_clock_voltage_dependency_table;
+ unsigned long clock = 0;
+ uint32_t level;
+
+ if (NULL == table || table->count <= 0)
+ return -EINVAL;
+
+ data->vce_dpm.soft_min_clk = 0;
+ data->vce_dpm.hard_min_clk = 0;
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMaxEclkLevel, &level);
+
+ if (level < table->count)
+ clock = table->entries[level].ecclk;
+ else
+ clock = table->entries[table->count - 1].ecclk;
+
+ data->vce_dpm.soft_max_clk = clock;
+ data->vce_dpm.hard_max_clk = clock;
+
+ return 0;
+}
+
+static int smu8_init_acp_limit(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+ struct phm_acp_clock_voltage_dependency_table *table =
+ hwmgr->dyn_state.acp_clock_voltage_dependency_table;
+ unsigned long clock = 0;
+ uint32_t level;
+
+ if (NULL == table || table->count <= 0)
+ return -EINVAL;
+
+ data->acp_dpm.soft_min_clk = 0;
+ data->acp_dpm.hard_min_clk = 0;
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMaxAclkLevel, &level);
+
+ if (level < table->count)
+ clock = table->entries[level].acpclk;
+ else
+ clock = table->entries[table->count - 1].acpclk;
+
+ data->acp_dpm.soft_max_clk = clock;
+ data->acp_dpm.hard_max_clk = clock;
+ return 0;
+}
+
+static void smu8_init_power_gate_state(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+
+ data->uvd_power_gated = false;
+ data->vce_power_gated = false;
+ data->samu_power_gated = false;
+#ifdef CONFIG_DRM_AMD_ACP
+ data->acp_power_gated = false;
+#else
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ACPPowerOFF, NULL);
+ data->acp_power_gated = true;
+#endif
+
+}
+
+static void smu8_init_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+
+ data->low_sclk_interrupt_threshold = 0;
+}
+
+static int smu8_update_sclk_limit(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+ struct phm_clock_voltage_dependency_table *table =
+ hwmgr->dyn_state.vddc_dependency_on_sclk;
+
+ unsigned long clock = 0;
+ unsigned long level;
+ unsigned long stable_pstate_sclk;
+ unsigned long percentage;
+
+ data->sclk_dpm.soft_min_clk = table->entries[0].clk;
+ level = smu8_get_max_sclk_level(hwmgr) - 1;
+
+ if (level < table->count)
+ data->sclk_dpm.soft_max_clk = table->entries[level].clk;
+ else
+ data->sclk_dpm.soft_max_clk = table->entries[table->count - 1].clk;
+
+ clock = hwmgr->display_config->min_core_set_clock;
+ if (clock == 0)
+ pr_debug("min_core_set_clock not set\n");
+
+ if (data->sclk_dpm.hard_min_clk != clock) {
+ data->sclk_dpm.hard_min_clk = clock;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSclkHardMin,
+ smu8_get_sclk_level(hwmgr,
+ data->sclk_dpm.hard_min_clk,
+ PPSMC_MSG_SetSclkHardMin),
+ NULL);
+ }
+
+ clock = data->sclk_dpm.soft_min_clk;
+
+ /* update minimum clocks for Stable P-State feature */
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_StablePState)) {
+ percentage = 75;
+ /*Sclk - calculate sclk value based on percentage and find FLOOR sclk from VddcDependencyOnSCLK table */
+ stable_pstate_sclk = (hwmgr->dyn_state.max_clock_voltage_on_ac.mclk *
+ percentage) / 100;
+
+ if (clock < stable_pstate_sclk)
+ clock = stable_pstate_sclk;
+ }
+
+ if (data->sclk_dpm.soft_min_clk != clock) {
+ data->sclk_dpm.soft_min_clk = clock;
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSclkSoftMin,
+ smu8_get_sclk_level(hwmgr,
+ data->sclk_dpm.soft_min_clk,
+ PPSMC_MSG_SetSclkSoftMin),
+ NULL);
+ }
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_StablePState) &&
+ data->sclk_dpm.soft_max_clk != clock) {
+ data->sclk_dpm.soft_max_clk = clock;
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSclkSoftMax,
+ smu8_get_sclk_level(hwmgr,
+ data->sclk_dpm.soft_max_clk,
+ PPSMC_MSG_SetSclkSoftMax),
+ NULL);
+ }
+
+ return 0;
+}
+
+static int smu8_set_deep_sleep_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkDeepSleep)) {
+ uint32_t clks = hwmgr->display_config->min_core_set_clock_in_sr;
+ if (clks == 0)
+ clks = SMU8_MIN_DEEP_SLEEP_SCLK;
+
+ PP_DBG_LOG("Setting Deep Sleep Clock: %d\n", clks);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetMinDeepSleepSclk,
+ clks,
+ NULL);
+ }
+
+ return 0;
+}
+
+static int smu8_set_watermark_threshold(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data =
+ hwmgr->backend;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetWatermarkFrequency,
+ data->sclk_dpm.soft_max_clk,
+ NULL);
+
+ return 0;
+}
+
+static int smu8_nbdpm_pstate_enable_disable(struct pp_hwmgr *hwmgr, bool enable, bool lock)
+{
+ struct smu8_hwmgr *hw_data = hwmgr->backend;
+
+ if (hw_data->is_nb_dpm_enabled) {
+ if (enable) {
+ PP_DBG_LOG("enable Low Memory PState.\n");
+
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_EnableLowMemoryPstate,
+ (lock ? 1 : 0),
+ NULL);
+ } else {
+ PP_DBG_LOG("disable Low Memory PState.\n");
+
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DisableLowMemoryPstate,
+ (lock ? 1 : 0),
+ NULL);
+ }
+ }
+
+ return 0;
+}
+
+static int smu8_disable_nb_dpm(struct pp_hwmgr *hwmgr)
+{
+ int ret = 0;
+
+ struct smu8_hwmgr *data = hwmgr->backend;
+ unsigned long dpm_features = 0;
+
+ if (data->is_nb_dpm_enabled) {
+ smu8_nbdpm_pstate_enable_disable(hwmgr, true, true);
+ dpm_features |= NB_DPM_MASK;
+ ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr,
+ PPSMC_MSG_DisableAllSmuFeatures,
+ dpm_features,
+ NULL);
+ if (ret == 0)
+ data->is_nb_dpm_enabled = false;
+ }
+
+ return ret;
+}
+
+static int smu8_enable_nb_dpm(struct pp_hwmgr *hwmgr)
+{
+ int ret = 0;
+
+ struct smu8_hwmgr *data = hwmgr->backend;
+ unsigned long dpm_features = 0;
+
+ if (!data->is_nb_dpm_enabled) {
+ PP_DBG_LOG("enabling ALL SMU features.\n");
+ dpm_features |= NB_DPM_MASK;
+ ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr,
+ PPSMC_MSG_EnableAllSmuFeatures,
+ dpm_features,
+ NULL);
+ if (ret == 0)
+ data->is_nb_dpm_enabled = true;
+ }
+
+ return ret;
+}
+
+static int smu8_update_low_mem_pstate(struct pp_hwmgr *hwmgr, const void *input)
+{
+ bool disable_switch;
+ bool enable_low_mem_state;
+ struct smu8_hwmgr *hw_data = hwmgr->backend;
+ const struct phm_set_power_state_input *states = (struct phm_set_power_state_input *)input;
+ const struct smu8_power_state *pnew_state = cast_const_smu8_power_state(states->pnew_state);
+
+ if (hw_data->sys_info.nb_dpm_enable) {
+ disable_switch = hw_data->cc6_settings.nb_pstate_switch_disable ? true : false;
+ enable_low_mem_state = hw_data->cc6_settings.nb_pstate_switch_disable ? false : true;
+
+ if (pnew_state->action == FORCE_HIGH)
+ smu8_nbdpm_pstate_enable_disable(hwmgr, false, disable_switch);
+ else if (pnew_state->action == CANCEL_FORCE_HIGH)
+ smu8_nbdpm_pstate_enable_disable(hwmgr, true, disable_switch);
+ else
+ smu8_nbdpm_pstate_enable_disable(hwmgr, enable_low_mem_state, disable_switch);
+ }
+ return 0;
+}
+
+static int smu8_set_power_state_tasks(struct pp_hwmgr *hwmgr, const void *input)
+{
+ int ret = 0;
+
+ smu8_update_sclk_limit(hwmgr);
+ smu8_set_deep_sleep_sclk_threshold(hwmgr);
+ smu8_set_watermark_threshold(hwmgr);
+ ret = smu8_enable_nb_dpm(hwmgr);
+ if (ret)
+ return ret;
+ smu8_update_low_mem_pstate(hwmgr, input);
+
+ return 0;
+}
+
+
+static int smu8_setup_asic_task(struct pp_hwmgr *hwmgr)
+{
+ int ret;
+
+ ret = smu8_upload_pptable_to_smu(hwmgr);
+ if (ret)
+ return ret;
+ ret = smu8_init_sclk_limit(hwmgr);
+ if (ret)
+ return ret;
+ ret = smu8_init_uvd_limit(hwmgr);
+ if (ret)
+ return ret;
+ ret = smu8_init_vce_limit(hwmgr);
+ if (ret)
+ return ret;
+ ret = smu8_init_acp_limit(hwmgr);
+ if (ret)
+ return ret;
+
+ smu8_init_power_gate_state(hwmgr);
+ smu8_init_sclk_threshold(hwmgr);
+
+ return 0;
+}
+
+static void smu8_power_up_display_clock_sys_pll(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *hw_data = hwmgr->backend;
+
+ hw_data->disp_clk_bypass_pending = false;
+ hw_data->disp_clk_bypass = false;
+}
+
+static void smu8_clear_nb_dpm_flag(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *hw_data = hwmgr->backend;
+
+ hw_data->is_nb_dpm_enabled = false;
+}
+
+static void smu8_reset_cc6_data(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *hw_data = hwmgr->backend;
+
+ hw_data->cc6_settings.cc6_setting_changed = false;
+ hw_data->cc6_settings.cpu_pstate_separation_time = 0;
+ hw_data->cc6_settings.cpu_cc6_disable = false;
+ hw_data->cc6_settings.cpu_pstate_disable = false;
+}
+
+static void smu8_program_voting_clients(struct pp_hwmgr *hwmgr)
+{
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixCG_FREQ_TRAN_VOTING_0,
+ SMU8_VOTINGRIGHTSCLIENTS_DFLT0);
+}
+
+static void smu8_clear_voting_clients(struct pp_hwmgr *hwmgr)
+{
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixCG_FREQ_TRAN_VOTING_0, 0);
+}
+
+static int smu8_start_dpm(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+
+ data->dpm_flags |= DPMFlags_SCLK_Enabled;
+
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_EnableAllSmuFeatures,
+ SCLK_DPM_MASK,
+ NULL);
+}
+
+static int smu8_stop_dpm(struct pp_hwmgr *hwmgr)
+{
+ int ret = 0;
+ struct smu8_hwmgr *data = hwmgr->backend;
+ unsigned long dpm_features = 0;
+
+ if (data->dpm_flags & DPMFlags_SCLK_Enabled) {
+ dpm_features |= SCLK_DPM_MASK;
+ data->dpm_flags &= ~DPMFlags_SCLK_Enabled;
+ ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DisableAllSmuFeatures,
+ dpm_features,
+ NULL);
+ }
+ return ret;
+}
+
+static int smu8_program_bootup_state(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+
+ data->sclk_dpm.soft_min_clk = data->sys_info.bootup_engine_clock;
+ data->sclk_dpm.soft_max_clk = data->sys_info.bootup_engine_clock;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSclkSoftMin,
+ smu8_get_sclk_level(hwmgr,
+ data->sclk_dpm.soft_min_clk,
+ PPSMC_MSG_SetSclkSoftMin),
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSclkSoftMax,
+ smu8_get_sclk_level(hwmgr,
+ data->sclk_dpm.soft_max_clk,
+ PPSMC_MSG_SetSclkSoftMax),
+ NULL);
+
+ return 0;
+}
+
+static void smu8_reset_acp_boot_level(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+
+ data->acp_boot_level = 0xff;
+}
+
+static int smu8_enable_dpm_tasks(struct pp_hwmgr *hwmgr)
+{
+ smu8_program_voting_clients(hwmgr);
+ if (smu8_start_dpm(hwmgr))
+ return -EINVAL;
+ smu8_program_bootup_state(hwmgr);
+ smu8_reset_acp_boot_level(hwmgr);
+
+ return 0;
+}
+
+static int smu8_disable_dpm_tasks(struct pp_hwmgr *hwmgr)
+{
+ smu8_disable_nb_dpm(hwmgr);
+
+ smu8_clear_voting_clients(hwmgr);
+ if (smu8_stop_dpm(hwmgr))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int smu8_power_off_asic(struct pp_hwmgr *hwmgr)
+{
+ smu8_disable_dpm_tasks(hwmgr);
+ smu8_power_up_display_clock_sys_pll(hwmgr);
+ smu8_clear_nb_dpm_flag(hwmgr);
+ smu8_reset_cc6_data(hwmgr);
+ return 0;
+}
+
+static int smu8_apply_state_adjust_rules(struct pp_hwmgr *hwmgr,
+ struct pp_power_state *prequest_ps,
+ const struct pp_power_state *pcurrent_ps)
+{
+ struct smu8_power_state *smu8_ps =
+ cast_smu8_power_state(&prequest_ps->hardware);
+
+ const struct smu8_power_state *smu8_current_ps =
+ cast_const_smu8_power_state(&pcurrent_ps->hardware);
+
+ struct smu8_hwmgr *data = hwmgr->backend;
+ struct PP_Clocks clocks = {0, 0, 0, 0};
+ bool force_high;
+
+ smu8_ps->need_dfs_bypass = true;
+
+ data->battery_state = (PP_StateUILabel_Battery == prequest_ps->classification.ui_label);
+
+ clocks.memoryClock = hwmgr->display_config->min_mem_set_clock != 0 ?
+ hwmgr->display_config->min_mem_set_clock :
+ data->sys_info.nbp_memory_clock[1];
+
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_StablePState))
+ clocks.memoryClock = hwmgr->dyn_state.max_clock_voltage_on_ac.mclk;
+
+ force_high = (clocks.memoryClock > data->sys_info.nbp_memory_clock[SMU8_NUM_NBPMEMORYCLOCK - 1])
+ || (hwmgr->display_config->num_display >= 3);
+
+ smu8_ps->action = smu8_current_ps->action;
+
+ if (hwmgr->request_dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK)
+ smu8_nbdpm_pstate_enable_disable(hwmgr, false, false);
+ else if (hwmgr->request_dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD)
+ smu8_nbdpm_pstate_enable_disable(hwmgr, false, true);
+ else if (!force_high && (smu8_ps->action == FORCE_HIGH))
+ smu8_ps->action = CANCEL_FORCE_HIGH;
+ else if (force_high && (smu8_ps->action != FORCE_HIGH))
+ smu8_ps->action = FORCE_HIGH;
+ else
+ smu8_ps->action = DO_NOTHING;
+
+ return 0;
+}
+
+static int smu8_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+ struct smu8_hwmgr *data;
+
+ data = kzalloc(sizeof(struct smu8_hwmgr), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ hwmgr->backend = data;
+
+ result = smu8_initialize_dpm_defaults(hwmgr);
+ if (result != 0) {
+ pr_err("smu8_initialize_dpm_defaults failed\n");
+ return result;
+ }
+
+ result = smu8_get_system_info_data(hwmgr);
+ if (result != 0) {
+ pr_err("smu8_get_system_info_data failed\n");
+ return result;
+ }
+
+ smu8_construct_boot_state(hwmgr);
+
+ hwmgr->platform_descriptor.hardwareActivityPerformanceLevels = SMU8_MAX_HARDWARE_POWERLEVELS;
+
+ return result;
+}
+
+static int smu8_hwmgr_backend_fini(struct pp_hwmgr *hwmgr)
+{
+ if (hwmgr != NULL) {
+ kfree(hwmgr->dyn_state.vddc_dep_on_dal_pwrl);
+ hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL;
+
+ kfree(hwmgr->backend);
+ hwmgr->backend = NULL;
+ }
+ return 0;
+}
+
+static int smu8_phm_force_dpm_highest(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSclkSoftMin,
+ smu8_get_sclk_level(hwmgr,
+ data->sclk_dpm.soft_max_clk,
+ PPSMC_MSG_SetSclkSoftMin),
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSclkSoftMax,
+ smu8_get_sclk_level(hwmgr,
+ data->sclk_dpm.soft_max_clk,
+ PPSMC_MSG_SetSclkSoftMax),
+ NULL);
+
+ return 0;
+}
+
+static int smu8_phm_unforce_dpm_levels(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+ struct phm_clock_voltage_dependency_table *table =
+ hwmgr->dyn_state.vddc_dependency_on_sclk;
+ unsigned long clock = 0, level;
+
+ if (NULL == table || table->count <= 0)
+ return -EINVAL;
+
+ data->sclk_dpm.soft_min_clk = table->entries[0].clk;
+ data->sclk_dpm.hard_min_clk = table->entries[0].clk;
+ hwmgr->pstate_sclk = table->entries[0].clk;
+ hwmgr->pstate_mclk = 0;
+
+ level = smu8_get_max_sclk_level(hwmgr) - 1;
+
+ if (level < table->count)
+ clock = table->entries[level].clk;
+ else
+ clock = table->entries[table->count - 1].clk;
+
+ data->sclk_dpm.soft_max_clk = clock;
+ data->sclk_dpm.hard_max_clk = clock;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSclkSoftMin,
+ smu8_get_sclk_level(hwmgr,
+ data->sclk_dpm.soft_min_clk,
+ PPSMC_MSG_SetSclkSoftMin),
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSclkSoftMax,
+ smu8_get_sclk_level(hwmgr,
+ data->sclk_dpm.soft_max_clk,
+ PPSMC_MSG_SetSclkSoftMax),
+ NULL);
+
+ return 0;
+}
+
+static int smu8_phm_force_dpm_lowest(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSclkSoftMax,
+ smu8_get_sclk_level(hwmgr,
+ data->sclk_dpm.soft_min_clk,
+ PPSMC_MSG_SetSclkSoftMax),
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSclkSoftMin,
+ smu8_get_sclk_level(hwmgr,
+ data->sclk_dpm.soft_min_clk,
+ PPSMC_MSG_SetSclkSoftMin),
+ NULL);
+
+ return 0;
+}
+
+static int smu8_dpm_force_dpm_level(struct pp_hwmgr *hwmgr,
+ enum amd_dpm_forced_level level)
+{
+ int ret = 0;
+
+ switch (level) {
+ case AMD_DPM_FORCED_LEVEL_HIGH:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+ ret = smu8_phm_force_dpm_highest(hwmgr);
+ break;
+ case AMD_DPM_FORCED_LEVEL_LOW:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+ ret = smu8_phm_force_dpm_lowest(hwmgr);
+ break;
+ case AMD_DPM_FORCED_LEVEL_AUTO:
+ ret = smu8_phm_unforce_dpm_levels(hwmgr);
+ break;
+ case AMD_DPM_FORCED_LEVEL_MANUAL:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_EXIT:
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int smu8_dpm_powerdown_uvd(struct pp_hwmgr *hwmgr)
+{
+ if (PP_CAP(PHM_PlatformCaps_UVDPowerGating))
+ return smum_send_msg_to_smc(hwmgr, PPSMC_MSG_UVDPowerOFF, NULL);
+ return 0;
+}
+
+static int smu8_dpm_powerup_uvd(struct pp_hwmgr *hwmgr)
+{
+ if (PP_CAP(PHM_PlatformCaps_UVDPowerGating)) {
+ return smum_send_msg_to_smc_with_parameter(
+ hwmgr,
+ PPSMC_MSG_UVDPowerON,
+ PP_CAP(PHM_PlatformCaps_UVDDynamicPowerGating) ? 1 : 0,
+ NULL);
+ }
+
+ return 0;
+}
+
+static int smu8_dpm_update_vce_dpm(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+ struct phm_vce_clock_voltage_dependency_table *ptable =
+ hwmgr->dyn_state.vce_clock_voltage_dependency_table;
+
+ /* Stable Pstate is enabled and we need to set the VCE DPM to highest level */
+ if (PP_CAP(PHM_PlatformCaps_StablePState) ||
+ hwmgr->en_umd_pstate) {
+ data->vce_dpm.hard_min_clk =
+ ptable->entries[ptable->count - 1].ecclk;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetEclkHardMin,
+ smu8_get_eclk_level(hwmgr,
+ data->vce_dpm.hard_min_clk,
+ PPSMC_MSG_SetEclkHardMin),
+ NULL);
+ } else {
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetEclkHardMin,
+ 0,
+ NULL);
+ /* disable ECLK DPM 0. Otherwise VCE could hang if
+ * switching SCLK from DPM 0 to 6/7 */
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetEclkSoftMin,
+ 1,
+ NULL);
+ }
+ return 0;
+}
+
+static int smu8_dpm_powerdown_vce(struct pp_hwmgr *hwmgr)
+{
+ if (PP_CAP(PHM_PlatformCaps_VCEPowerGating))
+ return smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_VCEPowerOFF,
+ NULL);
+ return 0;
+}
+
+static int smu8_dpm_powerup_vce(struct pp_hwmgr *hwmgr)
+{
+ if (PP_CAP(PHM_PlatformCaps_VCEPowerGating))
+ return smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_VCEPowerON,
+ NULL);
+ return 0;
+}
+
+static uint32_t smu8_dpm_get_mclk(struct pp_hwmgr *hwmgr, bool low)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+
+ return data->sys_info.bootup_uma_clock;
+}
+
+static uint32_t smu8_dpm_get_sclk(struct pp_hwmgr *hwmgr, bool low)
+{
+ struct pp_power_state *ps;
+ struct smu8_power_state *smu8_ps;
+
+ if (hwmgr == NULL)
+ return -EINVAL;
+
+ ps = hwmgr->request_ps;
+
+ if (ps == NULL)
+ return -EINVAL;
+
+ smu8_ps = cast_smu8_power_state(&ps->hardware);
+
+ if (low)
+ return smu8_ps->levels[0].engineClock;
+ else
+ return smu8_ps->levels[smu8_ps->level-1].engineClock;
+}
+
+static int smu8_dpm_patch_boot_state(struct pp_hwmgr *hwmgr,
+ struct pp_hw_power_state *hw_ps)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+ struct smu8_power_state *smu8_ps = cast_smu8_power_state(hw_ps);
+
+ smu8_ps->level = 1;
+ smu8_ps->nbps_flags = 0;
+ smu8_ps->bapm_flags = 0;
+ smu8_ps->levels[0] = data->boot_power_level;
+
+ return 0;
+}
+
+static int smu8_dpm_get_pp_table_entry_callback(
+ struct pp_hwmgr *hwmgr,
+ struct pp_hw_power_state *hw_ps,
+ unsigned int index,
+ const void *clock_info)
+{
+ struct smu8_power_state *smu8_ps = cast_smu8_power_state(hw_ps);
+
+ const ATOM_PPLIB_CZ_CLOCK_INFO *smu8_clock_info = clock_info;
+
+ struct phm_clock_voltage_dependency_table *table =
+ hwmgr->dyn_state.vddc_dependency_on_sclk;
+ uint8_t clock_info_index = smu8_clock_info->index;
+
+ if (clock_info_index > (uint8_t)(hwmgr->platform_descriptor.hardwareActivityPerformanceLevels - 1))
+ clock_info_index = (uint8_t)(hwmgr->platform_descriptor.hardwareActivityPerformanceLevels - 1);
+
+ smu8_ps->levels[index].engineClock = table->entries[clock_info_index].clk;
+ smu8_ps->levels[index].vddcIndex = (uint8_t)table->entries[clock_info_index].v;
+
+ smu8_ps->level = index + 1;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SclkDeepSleep)) {
+ smu8_ps->levels[index].dsDividerIndex = 5;
+ smu8_ps->levels[index].ssDividerIndex = 5;
+ }
+
+ return 0;
+}
+
+static int smu8_dpm_get_num_of_pp_table_entries(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ unsigned long ret = 0;
+
+ result = pp_tables_get_num_of_entries(hwmgr, &ret);
+
+ return result ? 0 : ret;
+}
+
+static int smu8_dpm_get_pp_table_entry(struct pp_hwmgr *hwmgr,
+ unsigned long entry, struct pp_power_state *ps)
+{
+ int result;
+ struct smu8_power_state *smu8_ps;
+
+ ps->hardware.magic = smu8_magic;
+
+ smu8_ps = cast_smu8_power_state(&(ps->hardware));
+
+ result = pp_tables_get_entry(hwmgr, entry, ps,
+ smu8_dpm_get_pp_table_entry_callback);
+
+ smu8_ps->uvd_clocks.vclk = ps->uvd_clocks.VCLK;
+ smu8_ps->uvd_clocks.dclk = ps->uvd_clocks.DCLK;
+
+ return result;
+}
+
+static int smu8_get_power_state_size(struct pp_hwmgr *hwmgr)
+{
+ return sizeof(struct smu8_power_state);
+}
+
+static void smu8_hw_print_display_cfg(
+ const struct cc6_settings *cc6_settings)
+{
+ PP_DBG_LOG("New Display Configuration:\n");
+
+ PP_DBG_LOG(" cpu_cc6_disable: %d\n",
+ cc6_settings->cpu_cc6_disable);
+ PP_DBG_LOG(" cpu_pstate_disable: %d\n",
+ cc6_settings->cpu_pstate_disable);
+ PP_DBG_LOG(" nb_pstate_switch_disable: %d\n",
+ cc6_settings->nb_pstate_switch_disable);
+ PP_DBG_LOG(" cpu_pstate_separation_time: %d\n\n",
+ cc6_settings->cpu_pstate_separation_time);
+}
+
+ static int smu8_set_cpu_power_state(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_hwmgr *hw_data = hwmgr->backend;
+ uint32_t data = 0;
+
+ if (hw_data->cc6_settings.cc6_setting_changed) {
+
+ hw_data->cc6_settings.cc6_setting_changed = false;
+
+ smu8_hw_print_display_cfg(&hw_data->cc6_settings);
+
+ data |= (hw_data->cc6_settings.cpu_pstate_separation_time
+ & PWRMGT_SEPARATION_TIME_MASK)
+ << PWRMGT_SEPARATION_TIME_SHIFT;
+
+ data |= (hw_data->cc6_settings.cpu_cc6_disable ? 0x1 : 0x0)
+ << PWRMGT_DISABLE_CPU_CSTATES_SHIFT;
+
+ data |= (hw_data->cc6_settings.cpu_pstate_disable ? 0x1 : 0x0)
+ << PWRMGT_DISABLE_CPU_PSTATES_SHIFT;
+
+ PP_DBG_LOG("SetDisplaySizePowerParams data: 0x%X\n",
+ data);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDisplaySizePowerParams,
+ data,
+ NULL);
+ }
+
+ return 0;
+}
+
+
+static int smu8_store_cc6_data(struct pp_hwmgr *hwmgr, uint32_t separation_time,
+ bool cc6_disable, bool pstate_disable, bool pstate_switch_disable)
+{
+ struct smu8_hwmgr *hw_data = hwmgr->backend;
+
+ if (separation_time !=
+ hw_data->cc6_settings.cpu_pstate_separation_time ||
+ cc6_disable != hw_data->cc6_settings.cpu_cc6_disable ||
+ pstate_disable != hw_data->cc6_settings.cpu_pstate_disable ||
+ pstate_switch_disable != hw_data->cc6_settings.nb_pstate_switch_disable) {
+
+ hw_data->cc6_settings.cc6_setting_changed = true;
+
+ hw_data->cc6_settings.cpu_pstate_separation_time =
+ separation_time;
+ hw_data->cc6_settings.cpu_cc6_disable =
+ cc6_disable;
+ hw_data->cc6_settings.cpu_pstate_disable =
+ pstate_disable;
+ hw_data->cc6_settings.nb_pstate_switch_disable =
+ pstate_switch_disable;
+
+ }
+
+ return 0;
+}
+
+static int smu8_get_dal_power_level(struct pp_hwmgr *hwmgr,
+ struct amd_pp_simple_clock_info *info)
+{
+ uint32_t i;
+ const struct phm_clock_voltage_dependency_table *table =
+ hwmgr->dyn_state.vddc_dep_on_dal_pwrl;
+ const struct phm_clock_and_voltage_limits *limits =
+ &hwmgr->dyn_state.max_clock_voltage_on_ac;
+
+ info->engine_max_clock = limits->sclk;
+ info->memory_max_clock = limits->mclk;
+
+ for (i = table->count - 1; i > 0; i--) {
+ if (limits->vddc >= table->entries[i].v) {
+ info->level = table->entries[i].clk;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int smu8_force_clock_level(struct pp_hwmgr *hwmgr,
+ enum pp_clock_type type, uint32_t mask)
+{
+ switch (type) {
+ case PP_SCLK:
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSclkSoftMin,
+ mask,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSclkSoftMax,
+ mask,
+ NULL);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int smu8_print_clock_levels(struct pp_hwmgr *hwmgr,
+ enum pp_clock_type type, char *buf)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+ struct phm_clock_voltage_dependency_table *sclk_table =
+ hwmgr->dyn_state.vddc_dependency_on_sclk;
+ int i, now, size = 0;
+
+ switch (type) {
+ case PP_SCLK:
+ now = PHM_GET_FIELD(cgs_read_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC,
+ ixTARGET_AND_CURRENT_PROFILE_INDEX),
+ TARGET_AND_CURRENT_PROFILE_INDEX,
+ CURR_SCLK_INDEX);
+
+ for (i = 0; i < sclk_table->count; i++)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ i, sclk_table->entries[i].clk / 100,
+ (i == now) ? "*" : "");
+ break;
+ case PP_MCLK:
+ now = PHM_GET_FIELD(cgs_read_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC,
+ ixTARGET_AND_CURRENT_PROFILE_INDEX),
+ TARGET_AND_CURRENT_PROFILE_INDEX,
+ CURR_MCLK_INDEX);
+
+ for (i = SMU8_NUM_NBPMEMORYCLOCK; i > 0; i--)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ SMU8_NUM_NBPMEMORYCLOCK-i, data->sys_info.nbp_memory_clock[i-1] / 100,
+ (SMU8_NUM_NBPMEMORYCLOCK-i == now) ? "*" : "");
+ break;
+ default:
+ break;
+ }
+ return size;
+}
+
+static int smu8_get_performance_level(struct pp_hwmgr *hwmgr, const struct pp_hw_power_state *state,
+ PHM_PerformanceLevelDesignation designation, uint32_t index,
+ PHM_PerformanceLevel *level)
+{
+ const struct smu8_power_state *ps;
+ struct smu8_hwmgr *data;
+ uint32_t level_index;
+ uint32_t i;
+
+ if (level == NULL || hwmgr == NULL || state == NULL)
+ return -EINVAL;
+
+ data = hwmgr->backend;
+ ps = cast_const_smu8_power_state(state);
+
+ level_index = index > ps->level - 1 ? ps->level - 1 : index;
+ level->coreClock = ps->levels[level_index].engineClock;
+
+ if (designation == PHM_PerformanceLevelDesignation_PowerContainment) {
+ for (i = 1; i < ps->level; i++) {
+ if (ps->levels[i].engineClock > data->dce_slow_sclk_threshold) {
+ level->coreClock = ps->levels[i].engineClock;
+ break;
+ }
+ }
+ }
+
+ if (level_index == 0)
+ level->memory_clock = data->sys_info.nbp_memory_clock[SMU8_NUM_NBPMEMORYCLOCK - 1];
+ else
+ level->memory_clock = data->sys_info.nbp_memory_clock[0];
+
+ level->vddc = (smu8_convert_8Bit_index_to_voltage(hwmgr, ps->levels[level_index].vddcIndex) + 2) / 4;
+ level->nonLocalMemoryFreq = 0;
+ level->nonLocalMemoryWidth = 0;
+
+ return 0;
+}
+
+static int smu8_get_current_shallow_sleep_clocks(struct pp_hwmgr *hwmgr,
+ const struct pp_hw_power_state *state, struct pp_clock_info *clock_info)
+{
+ const struct smu8_power_state *ps = cast_const_smu8_power_state(state);
+
+ clock_info->min_eng_clk = ps->levels[0].engineClock / (1 << (ps->levels[0].ssDividerIndex));
+ clock_info->max_eng_clk = ps->levels[ps->level - 1].engineClock / (1 << (ps->levels[ps->level - 1].ssDividerIndex));
+
+ return 0;
+}
+
+static int smu8_get_clock_by_type(struct pp_hwmgr *hwmgr, enum amd_pp_clock_type type,
+ struct amd_pp_clocks *clocks)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+ int i;
+ struct phm_clock_voltage_dependency_table *table;
+
+ clocks->count = smu8_get_max_sclk_level(hwmgr);
+ switch (type) {
+ case amd_pp_disp_clock:
+ for (i = 0; i < clocks->count; i++)
+ clocks->clock[i] = data->sys_info.display_clock[i] * 10;
+ break;
+ case amd_pp_sys_clock:
+ table = hwmgr->dyn_state.vddc_dependency_on_sclk;
+ for (i = 0; i < clocks->count; i++)
+ clocks->clock[i] = table->entries[i].clk * 10;
+ break;
+ case amd_pp_mem_clock:
+ clocks->count = SMU8_NUM_NBPMEMORYCLOCK;
+ for (i = 0; i < clocks->count; i++)
+ clocks->clock[i] = data->sys_info.nbp_memory_clock[clocks->count - 1 - i] * 10;
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int smu8_get_max_high_clocks(struct pp_hwmgr *hwmgr, struct amd_pp_simple_clock_info *clocks)
+{
+ struct phm_clock_voltage_dependency_table *table =
+ hwmgr->dyn_state.vddc_dependency_on_sclk;
+ unsigned long level;
+ const struct phm_clock_and_voltage_limits *limits =
+ &hwmgr->dyn_state.max_clock_voltage_on_ac;
+
+ if ((NULL == table) || (table->count <= 0) || (clocks == NULL))
+ return -EINVAL;
+
+ level = smu8_get_max_sclk_level(hwmgr) - 1;
+
+ if (level < table->count)
+ clocks->engine_max_clock = table->entries[level].clk;
+ else
+ clocks->engine_max_clock = table->entries[table->count - 1].clk;
+
+ clocks->memory_max_clock = limits->mclk;
+
+ return 0;
+}
+
+static int smu8_thermal_get_temperature(struct pp_hwmgr *hwmgr)
+{
+ int actual_temp = 0;
+ uint32_t val = cgs_read_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, ixTHM_TCON_CUR_TMP);
+ uint32_t temp = PHM_GET_FIELD(val, THM_TCON_CUR_TMP, CUR_TEMP);
+
+ if (PHM_GET_FIELD(val, THM_TCON_CUR_TMP, CUR_TEMP_RANGE_SEL))
+ actual_temp = ((temp / 8) - 49) * PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ else
+ actual_temp = (temp / 8) * PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+ return actual_temp;
+}
+
+static int smu8_read_sensor(struct pp_hwmgr *hwmgr, int idx,
+ void *value, int *size)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+
+ struct phm_clock_voltage_dependency_table *table =
+ hwmgr->dyn_state.vddc_dependency_on_sclk;
+
+ struct phm_vce_clock_voltage_dependency_table *vce_table =
+ hwmgr->dyn_state.vce_clock_voltage_dependency_table;
+
+ struct phm_uvd_clock_voltage_dependency_table *uvd_table =
+ hwmgr->dyn_state.uvd_clock_voltage_dependency_table;
+
+ uint32_t sclk_index = PHM_GET_FIELD(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixTARGET_AND_CURRENT_PROFILE_INDEX),
+ TARGET_AND_CURRENT_PROFILE_INDEX, CURR_SCLK_INDEX);
+ uint32_t uvd_index = PHM_GET_FIELD(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixTARGET_AND_CURRENT_PROFILE_INDEX_2),
+ TARGET_AND_CURRENT_PROFILE_INDEX_2, CURR_UVD_INDEX);
+ uint32_t vce_index = PHM_GET_FIELD(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixTARGET_AND_CURRENT_PROFILE_INDEX_2),
+ TARGET_AND_CURRENT_PROFILE_INDEX_2, CURR_VCE_INDEX);
+
+ uint32_t sclk, vclk, dclk, ecclk, tmp, activity_percent;
+ uint16_t vddnb, vddgfx;
+ int result;
+
+ /* size must be at least 4 bytes for all sensors */
+ if (*size < 4)
+ return -EINVAL;
+ *size = 4;
+
+ switch (idx) {
+ case AMDGPU_PP_SENSOR_GFX_SCLK:
+ if (sclk_index < NUM_SCLK_LEVELS) {
+ sclk = table->entries[sclk_index].clk;
+ *((uint32_t *)value) = sclk;
+ return 0;
+ }
+ return -EINVAL;
+ case AMDGPU_PP_SENSOR_VDDNB:
+ tmp = (cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixSMUSVI_NB_CURRENTVID) &
+ CURRENT_NB_VID_MASK) >> CURRENT_NB_VID__SHIFT;
+ vddnb = smu8_convert_8Bit_index_to_voltage(hwmgr, tmp) / 4;
+ *((uint32_t *)value) = vddnb;
+ return 0;
+ case AMDGPU_PP_SENSOR_VDDGFX:
+ tmp = (cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixSMUSVI_GFX_CURRENTVID) &
+ CURRENT_GFX_VID_MASK) >> CURRENT_GFX_VID__SHIFT;
+ vddgfx = smu8_convert_8Bit_index_to_voltage(hwmgr, (u16)tmp) / 4;
+ *((uint32_t *)value) = vddgfx;
+ return 0;
+ case AMDGPU_PP_SENSOR_UVD_VCLK:
+ if (!data->uvd_power_gated) {
+ if (uvd_index >= SMU8_MAX_HARDWARE_POWERLEVELS) {
+ return -EINVAL;
+ } else {
+ vclk = uvd_table->entries[uvd_index].vclk;
+ *((uint32_t *)value) = vclk;
+ return 0;
+ }
+ }
+ *((uint32_t *)value) = 0;
+ return 0;
+ case AMDGPU_PP_SENSOR_UVD_DCLK:
+ if (!data->uvd_power_gated) {
+ if (uvd_index >= SMU8_MAX_HARDWARE_POWERLEVELS) {
+ return -EINVAL;
+ } else {
+ dclk = uvd_table->entries[uvd_index].dclk;
+ *((uint32_t *)value) = dclk;
+ return 0;
+ }
+ }
+ *((uint32_t *)value) = 0;
+ return 0;
+ case AMDGPU_PP_SENSOR_VCE_ECCLK:
+ if (!data->vce_power_gated) {
+ if (vce_index >= SMU8_MAX_HARDWARE_POWERLEVELS) {
+ return -EINVAL;
+ } else {
+ ecclk = vce_table->entries[vce_index].ecclk;
+ *((uint32_t *)value) = ecclk;
+ return 0;
+ }
+ }
+ *((uint32_t *)value) = 0;
+ return 0;
+ case AMDGPU_PP_SENSOR_GPU_LOAD:
+ result = smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_GetAverageGraphicsActivity,
+ &activity_percent);
+ if (0 == result) {
+ activity_percent = activity_percent > 100 ? 100 : activity_percent;
+ } else {
+ activity_percent = 50;
+ }
+ *((uint32_t *)value) = activity_percent;
+ return 0;
+ case AMDGPU_PP_SENSOR_UVD_POWER:
+ *((uint32_t *)value) = data->uvd_power_gated ? 0 : 1;
+ return 0;
+ case AMDGPU_PP_SENSOR_VCE_POWER:
+ *((uint32_t *)value) = data->vce_power_gated ? 0 : 1;
+ return 0;
+ case AMDGPU_PP_SENSOR_GPU_TEMP:
+ *((uint32_t *)value) = smu8_thermal_get_temperature(hwmgr);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int smu8_notify_cac_buffer_info(struct pp_hwmgr *hwmgr,
+ uint32_t virtual_addr_low,
+ uint32_t virtual_addr_hi,
+ uint32_t mc_addr_low,
+ uint32_t mc_addr_hi,
+ uint32_t size)
+{
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DramAddrHiVirtual,
+ mc_addr_hi,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DramAddrLoVirtual,
+ mc_addr_low,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DramAddrHiPhysical,
+ virtual_addr_hi,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DramAddrLoPhysical,
+ virtual_addr_low,
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DramBufferSize,
+ size,
+ NULL);
+ return 0;
+}
+
+static int smu8_get_thermal_temperature_range(struct pp_hwmgr *hwmgr,
+ struct PP_TemperatureRange *thermal_data)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+
+ memcpy(thermal_data, &SMU7ThermalPolicy[0], sizeof(struct PP_TemperatureRange));
+
+ thermal_data->max = (data->thermal_auto_throttling_treshold +
+ data->sys_info.htc_hyst_lmt) *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+ return 0;
+}
+
+static int smu8_enable_disable_uvd_dpm(struct pp_hwmgr *hwmgr, bool enable)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+ uint32_t dpm_features = 0;
+
+ if (enable &&
+ phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UVDDPM)) {
+ data->dpm_flags |= DPMFlags_UVD_Enabled;
+ dpm_features |= UVD_DPM_MASK;
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_EnableAllSmuFeatures,
+ dpm_features,
+ NULL);
+ } else {
+ dpm_features |= UVD_DPM_MASK;
+ data->dpm_flags &= ~DPMFlags_UVD_Enabled;
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DisableAllSmuFeatures,
+ dpm_features,
+ NULL);
+ }
+ return 0;
+}
+
+static int smu8_dpm_update_uvd_dpm(struct pp_hwmgr *hwmgr, bool bgate)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+ struct phm_uvd_clock_voltage_dependency_table *ptable =
+ hwmgr->dyn_state.uvd_clock_voltage_dependency_table;
+
+ if (!bgate) {
+ /* Stable Pstate is enabled and we need to set the UVD DPM to highest level */
+ if (PP_CAP(PHM_PlatformCaps_StablePState) ||
+ hwmgr->en_umd_pstate) {
+ data->uvd_dpm.hard_min_clk =
+ ptable->entries[ptable->count - 1].vclk;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetUvdHardMin,
+ smu8_get_uvd_level(hwmgr,
+ data->uvd_dpm.hard_min_clk,
+ PPSMC_MSG_SetUvdHardMin),
+ NULL);
+
+ smu8_enable_disable_uvd_dpm(hwmgr, true);
+ } else {
+ smu8_enable_disable_uvd_dpm(hwmgr, true);
+ }
+ } else {
+ smu8_enable_disable_uvd_dpm(hwmgr, false);
+ }
+
+ return 0;
+}
+
+static int smu8_enable_disable_vce_dpm(struct pp_hwmgr *hwmgr, bool enable)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+ uint32_t dpm_features = 0;
+
+ if (enable && phm_cap_enabled(
+ hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_VCEDPM)) {
+ data->dpm_flags |= DPMFlags_VCE_Enabled;
+ dpm_features |= VCE_DPM_MASK;
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_EnableAllSmuFeatures,
+ dpm_features,
+ NULL);
+ } else {
+ dpm_features |= VCE_DPM_MASK;
+ data->dpm_flags &= ~DPMFlags_VCE_Enabled;
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DisableAllSmuFeatures,
+ dpm_features,
+ NULL);
+ }
+
+ return 0;
+}
+
+
+static void smu8_dpm_powergate_acp(struct pp_hwmgr *hwmgr, bool bgate)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+
+ if (data->acp_power_gated == bgate)
+ return;
+
+ if (bgate)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ACPPowerOFF, NULL);
+ else
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ACPPowerON, NULL);
+}
+
+static void smu8_dpm_powergate_uvd(struct pp_hwmgr *hwmgr, bool bgate)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+
+ data->uvd_power_gated = bgate;
+
+ if (bgate) {
+ amdgpu_device_ip_set_powergating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_UVD,
+ AMD_PG_STATE_GATE);
+ amdgpu_device_ip_set_clockgating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_UVD,
+ AMD_CG_STATE_GATE);
+ smu8_dpm_update_uvd_dpm(hwmgr, true);
+ smu8_dpm_powerdown_uvd(hwmgr);
+ } else {
+ smu8_dpm_powerup_uvd(hwmgr);
+ amdgpu_device_ip_set_clockgating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_UVD,
+ AMD_CG_STATE_UNGATE);
+ amdgpu_device_ip_set_powergating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_UVD,
+ AMD_PG_STATE_UNGATE);
+ smu8_dpm_update_uvd_dpm(hwmgr, false);
+ }
+
+}
+
+static void smu8_dpm_powergate_vce(struct pp_hwmgr *hwmgr, bool bgate)
+{
+ struct smu8_hwmgr *data = hwmgr->backend;
+
+ if (bgate) {
+ amdgpu_device_ip_set_powergating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_VCE,
+ AMD_PG_STATE_GATE);
+ amdgpu_device_ip_set_clockgating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_VCE,
+ AMD_CG_STATE_GATE);
+ smu8_enable_disable_vce_dpm(hwmgr, false);
+ smu8_dpm_powerdown_vce(hwmgr);
+ data->vce_power_gated = true;
+ } else {
+ smu8_dpm_powerup_vce(hwmgr);
+ data->vce_power_gated = false;
+ amdgpu_device_ip_set_clockgating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_VCE,
+ AMD_CG_STATE_UNGATE);
+ amdgpu_device_ip_set_powergating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_VCE,
+ AMD_PG_STATE_UNGATE);
+ smu8_dpm_update_vce_dpm(hwmgr);
+ smu8_enable_disable_vce_dpm(hwmgr, true);
+ }
+}
+
+static const struct pp_hwmgr_func smu8_hwmgr_funcs = {
+ .backend_init = smu8_hwmgr_backend_init,
+ .backend_fini = smu8_hwmgr_backend_fini,
+ .apply_state_adjust_rules = smu8_apply_state_adjust_rules,
+ .force_dpm_level = smu8_dpm_force_dpm_level,
+ .get_power_state_size = smu8_get_power_state_size,
+ .powerdown_uvd = smu8_dpm_powerdown_uvd,
+ .powergate_uvd = smu8_dpm_powergate_uvd,
+ .powergate_vce = smu8_dpm_powergate_vce,
+ .powergate_acp = smu8_dpm_powergate_acp,
+ .get_mclk = smu8_dpm_get_mclk,
+ .get_sclk = smu8_dpm_get_sclk,
+ .patch_boot_state = smu8_dpm_patch_boot_state,
+ .get_pp_table_entry = smu8_dpm_get_pp_table_entry,
+ .get_num_of_pp_table_entries = smu8_dpm_get_num_of_pp_table_entries,
+ .set_cpu_power_state = smu8_set_cpu_power_state,
+ .store_cc6_data = smu8_store_cc6_data,
+ .force_clock_level = smu8_force_clock_level,
+ .print_clock_levels = smu8_print_clock_levels,
+ .get_dal_power_level = smu8_get_dal_power_level,
+ .get_performance_level = smu8_get_performance_level,
+ .get_current_shallow_sleep_clocks = smu8_get_current_shallow_sleep_clocks,
+ .get_clock_by_type = smu8_get_clock_by_type,
+ .get_max_high_clocks = smu8_get_max_high_clocks,
+ .read_sensor = smu8_read_sensor,
+ .power_off_asic = smu8_power_off_asic,
+ .asic_setup = smu8_setup_asic_task,
+ .dynamic_state_management_enable = smu8_enable_dpm_tasks,
+ .power_state_set = smu8_set_power_state_tasks,
+ .dynamic_state_management_disable = smu8_disable_dpm_tasks,
+ .notify_cac_buffer_info = smu8_notify_cac_buffer_info,
+ .update_nbdpm_pstate = smu8_nbdpm_pstate_enable_disable,
+ .get_thermal_temperature_range = smu8_get_thermal_temperature_range,
+};
+
+int smu8_init_function_pointers(struct pp_hwmgr *hwmgr)
+{
+ hwmgr->hwmgr_func = &smu8_hwmgr_funcs;
+ hwmgr->pptable_func = &pptable_funcs;
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu8_hwmgr.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu8_hwmgr.h
new file mode 100644
index 000000000000..05a06083e1b8
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu8_hwmgr.h
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _SMU8_HWMGR_H_
+#define _SMU8_HWMGR_H_
+
+#include "cgs_common.h"
+#include "ppatomctrl.h"
+
+#define SMU8_NUM_NBPSTATES 4
+#define SMU8_NUM_NBPMEMORYCLOCK 2
+#define MAX_DISPLAY_CLOCK_LEVEL 8
+#define SMU8_MAX_HARDWARE_POWERLEVELS 8
+#define SMU8_VOTINGRIGHTSCLIENTS_DFLT0 0x3FFFC102
+#define SMU8_MIN_DEEP_SLEEP_SCLK 800
+
+/* Carrizo device IDs */
+#define DEVICE_ID_CZ_9870 0x9870
+#define DEVICE_ID_CZ_9874 0x9874
+#define DEVICE_ID_CZ_9875 0x9875
+#define DEVICE_ID_CZ_9876 0x9876
+#define DEVICE_ID_CZ_9877 0x9877
+
+struct smu8_dpm_entry {
+ uint32_t soft_min_clk;
+ uint32_t hard_min_clk;
+ uint32_t soft_max_clk;
+ uint32_t hard_max_clk;
+};
+
+struct smu8_sys_info {
+ uint32_t bootup_uma_clock;
+ uint32_t bootup_engine_clock;
+ uint32_t dentist_vco_freq;
+ uint32_t nb_dpm_enable;
+ uint32_t nbp_memory_clock[SMU8_NUM_NBPMEMORYCLOCK];
+ uint32_t nbp_n_clock[SMU8_NUM_NBPSTATES];
+ uint16_t nbp_voltage_index[SMU8_NUM_NBPSTATES];
+ uint32_t display_clock[MAX_DISPLAY_CLOCK_LEVEL];
+ uint16_t bootup_nb_voltage_index;
+ uint8_t htc_tmp_lmt;
+ uint8_t htc_hyst_lmt;
+ uint32_t system_config;
+ uint32_t uma_channel_number;
+};
+
+#define MAX_DISPLAYPHY_IDS 0x8
+#define DISPLAYPHY_LANEMASK 0xF
+#define UNKNOWN_TRANSMITTER_PHY_ID (-1)
+
+#define DISPLAYPHY_PHYID_SHIFT 24
+#define DISPLAYPHY_LANESELECT_SHIFT 16
+
+#define DISPLAYPHY_RX_SELECT 0x1
+#define DISPLAYPHY_TX_SELECT 0x2
+#define DISPLAYPHY_CORE_SELECT 0x4
+
+#define DDI_POWERGATING_ARG(phyID, lanemask, rx, tx, core) \
+ (((uint32_t)(phyID))<<DISPLAYPHY_PHYID_SHIFT | \
+ ((uint32_t)(lanemask))<<DISPLAYPHY_LANESELECT_SHIFT | \
+ ((rx) ? DISPLAYPHY_RX_SELECT : 0) | \
+ ((tx) ? DISPLAYPHY_TX_SELECT : 0) | \
+ ((core) ? DISPLAYPHY_CORE_SELECT : 0))
+
+struct smu8_display_phy_info_entry {
+ uint8_t phy_present;
+ uint8_t active_lane_mapping;
+ uint8_t display_config_type;
+ uint8_t active_number_of_lanes;
+};
+
+#define SMU8_MAX_DISPLAYPHY_IDS 10
+
+struct smu8_display_phy_info {
+ bool display_phy_access_initialized;
+ struct smu8_display_phy_info_entry entries[SMU8_MAX_DISPLAYPHY_IDS];
+};
+
+struct smu8_power_level {
+ uint32_t engineClock;
+ uint8_t vddcIndex;
+ uint8_t dsDividerIndex;
+ uint8_t ssDividerIndex;
+ uint8_t allowGnbSlow;
+ uint8_t forceNBPstate;
+ uint8_t display_wm;
+ uint8_t vce_wm;
+ uint8_t numSIMDToPowerDown;
+ uint8_t hysteresis_up;
+ uint8_t rsv[3];
+};
+
+struct smu8_uvd_clocks {
+ uint32_t vclk;
+ uint32_t dclk;
+ uint32_t vclk_low_divider;
+ uint32_t vclk_high_divider;
+ uint32_t dclk_low_divider;
+ uint32_t dclk_high_divider;
+};
+
+enum smu8_pstate_previous_action {
+ DO_NOTHING = 1,
+ FORCE_HIGH,
+ CANCEL_FORCE_HIGH
+};
+
+struct pp_disable_nb_ps_flags {
+ union {
+ struct {
+ uint32_t entry : 1;
+ uint32_t display : 1;
+ uint32_t driver: 1;
+ uint32_t vce : 1;
+ uint32_t uvd : 1;
+ uint32_t acp : 1;
+ uint32_t reserved: 26;
+ } bits;
+ uint32_t u32All;
+ };
+};
+
+struct smu8_power_state {
+ unsigned int magic;
+ uint32_t level;
+ struct smu8_uvd_clocks uvd_clocks;
+ uint32_t evclk;
+ uint32_t ecclk;
+ uint32_t samclk;
+ uint32_t acpclk;
+ bool need_dfs_bypass;
+ uint32_t nbps_flags;
+ uint32_t bapm_flags;
+ uint8_t dpm_0_pg_nb_ps_low;
+ uint8_t dpm_0_pg_nb_ps_high;
+ uint8_t dpm_x_nb_ps_low;
+ uint8_t dpm_x_nb_ps_high;
+ enum smu8_pstate_previous_action action;
+ struct smu8_power_level levels[SMU8_MAX_HARDWARE_POWERLEVELS];
+ struct pp_disable_nb_ps_flags disable_nb_ps_flag;
+};
+
+#define DPMFlags_SCLK_Enabled 0x00000001
+#define DPMFlags_UVD_Enabled 0x00000002
+#define DPMFlags_VCE_Enabled 0x00000004
+#define DPMFlags_ACP_Enabled 0x00000008
+#define DPMFlags_ForceHighestValid 0x40000000
+#define DPMFlags_Debug 0x80000000
+
+#define SMU_EnabledFeatureScoreboard_AcpDpmOn 0x00000001 /* bit 0 */
+#define SMU_EnabledFeatureScoreboard_UvdDpmOn 0x00800000 /* bit 23 */
+#define SMU_EnabledFeatureScoreboard_VceDpmOn 0x01000000 /* bit 24 */
+
+struct cc6_settings {
+ bool cc6_setting_changed;
+ bool nb_pstate_switch_disable;/* controls NB PState switch */
+ bool cpu_cc6_disable; /* controls CPU CState switch ( on or off) */
+ bool cpu_pstate_disable;
+ uint32_t cpu_pstate_separation_time;
+};
+
+struct smu8_hwmgr {
+ uint32_t dpm_interval;
+
+ uint32_t voltage_drop_threshold;
+
+ uint32_t voting_rights_clients;
+
+ uint32_t disable_driver_thermal_policy;
+
+ uint32_t static_screen_threshold;
+
+ uint32_t gfx_power_gating_threshold;
+
+ uint32_t activity_hysteresis;
+ uint32_t bootup_sclk_divider;
+ uint32_t gfx_ramp_step;
+ uint32_t gfx_ramp_delay; /* in micro-seconds */
+
+ uint32_t thermal_auto_throttling_treshold;
+
+ struct smu8_sys_info sys_info;
+
+ struct smu8_power_level boot_power_level;
+ struct smu8_power_state *smu8_current_ps;
+ struct smu8_power_state *smu8_requested_ps;
+
+ uint32_t mgcg_cgtt_local0;
+ uint32_t mgcg_cgtt_local1;
+
+ uint32_t tdr_clock; /* in 10khz unit */
+
+ uint32_t ddi_power_gating_disabled;
+ uint32_t disable_gfx_power_gating_in_uvd;
+ uint32_t disable_nb_ps3_in_battery;
+
+ uint32_t lock_nb_ps_in_uvd_play_back;
+
+ struct smu8_display_phy_info display_phy_info;
+ uint32_t vce_slow_sclk_threshold; /* default 200mhz */
+ uint32_t dce_slow_sclk_threshold; /* default 300mhz */
+ uint32_t min_sclk_did; /* minimum sclk divider */
+
+ bool disp_clk_bypass;
+ bool disp_clk_bypass_pending;
+ uint32_t bapm_enabled;
+ uint32_t clock_slow_down_freq;
+ uint32_t skip_clock_slow_down;
+ uint32_t enable_nb_ps_policy;
+ uint32_t voltage_drop_in_dce_power_gating;
+ uint32_t uvd_dpm_interval;
+ uint32_t override_dynamic_mgpg;
+ uint32_t lclk_deep_enabled;
+
+ uint32_t uvd_performance;
+
+ bool video_start;
+ bool battery_state;
+ uint32_t lowest_valid;
+ uint32_t highest_valid;
+ uint32_t high_voltage_threshold;
+ uint32_t is_nb_dpm_enabled;
+ struct cc6_settings cc6_settings;
+ uint32_t is_voltage_island_enabled;
+
+ bool pgacpinit;
+
+ uint8_t disp_config;
+
+ /* PowerTune */
+ uint32_t power_containment_features;
+ bool cac_enabled;
+ bool disable_uvd_power_tune_feature;
+ bool enable_ba_pm_feature;
+ bool enable_tdc_limit_feature;
+
+ uint32_t sram_end;
+ uint32_t dpm_table_start;
+ uint32_t soft_regs_start;
+
+ uint8_t uvd_level_count;
+ uint8_t vce_level_count;
+
+ uint8_t acp_level_count;
+ uint8_t samu_level_count;
+ uint32_t fps_high_threshold;
+ uint32_t fps_low_threshold;
+
+ uint32_t dpm_flags;
+ struct smu8_dpm_entry sclk_dpm;
+ struct smu8_dpm_entry uvd_dpm;
+ struct smu8_dpm_entry vce_dpm;
+ struct smu8_dpm_entry acp_dpm;
+
+ uint8_t uvd_boot_level;
+ uint8_t vce_boot_level;
+ uint8_t acp_boot_level;
+ uint8_t samu_boot_level;
+ uint8_t uvd_interval;
+ uint8_t vce_interval;
+ uint8_t acp_interval;
+ uint8_t samu_interval;
+
+ uint8_t graphics_interval;
+ uint8_t graphics_therm_throttle_enable;
+ uint8_t graphics_voltage_change_enable;
+
+ uint8_t graphics_clk_slow_enable;
+ uint8_t graphics_clk_slow_divider;
+
+ uint32_t display_cac;
+ uint32_t low_sclk_interrupt_threshold;
+
+ uint32_t dram_log_addr_h;
+ uint32_t dram_log_addr_l;
+ uint32_t dram_log_phy_addr_h;
+ uint32_t dram_log_phy_addr_l;
+ uint32_t dram_log_buff_size;
+
+ bool uvd_power_gated;
+ bool vce_power_gated;
+ bool samu_power_gated;
+ bool acp_power_gated;
+ bool acp_power_up_no_dsp;
+ uint32_t active_process_mask;
+
+ uint32_t max_sclk_level;
+ uint32_t num_of_clk_entries;
+};
+
+#endif /* _SMU8_HWMGR_H_ */
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu9_baco.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu9_baco.c
new file mode 100644
index 000000000000..de0a37f7c632
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu9_baco.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "amdgpu.h"
+#include "soc15.h"
+#include "soc15_hw_ip.h"
+#include "vega10_ip_offset.h"
+#include "soc15_common.h"
+#include "vega10_inc.h"
+#include "smu9_baco.h"
+
+int smu9_baco_get_capability(struct pp_hwmgr *hwmgr, bool *cap)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
+ uint32_t reg, data;
+
+ *cap = false;
+ if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_BACO))
+ return 0;
+
+ WREG32(0x12074, 0xFFF0003B);
+ data = RREG32(0x12075);
+
+ if (data == 0x1) {
+ reg = RREG32_SOC15(NBIF, 0, mmRCC_BIF_STRAP0);
+
+ if (reg & RCC_BIF_STRAP0__STRAP_PX_CAPABLE_MASK)
+ *cap = true;
+ }
+
+ return 0;
+}
+
+int smu9_baco_get_state(struct pp_hwmgr *hwmgr, enum BACO_STATE *state)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
+ uint32_t reg;
+
+ reg = RREG32_SOC15(NBIF, 0, mmBACO_CNTL);
+
+ if (reg & BACO_CNTL__BACO_MODE_MASK)
+ /* gfx has already entered BACO state */
+ *state = BACO_STATE_IN;
+ else
+ *state = BACO_STATE_OUT;
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu9_baco.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu9_baco.h
new file mode 100644
index 000000000000..84e90f801ac3
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu9_baco.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 __SMU9_BACO_H__
+#define __SMU9_BACO_H__
+#include "hwmgr.h"
+#include "common_baco.h"
+
+extern int smu9_baco_get_capability(struct pp_hwmgr *hwmgr, bool *cap);
+extern int smu9_baco_get_state(struct pp_hwmgr *hwmgr, enum BACO_STATE *state);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.c
new file mode 100644
index 000000000000..60b5ca974356
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.c
@@ -0,0 +1,767 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <linux/pci.h>
+#include <linux/reboot.h>
+
+#include "hwmgr.h"
+#include "pp_debug.h"
+#include "ppatomctrl.h"
+#include "ppsmc.h"
+#include "atom.h"
+#include "ivsrcid/thm/irqsrcs_thm_9_0.h"
+#include "ivsrcid/smuio/irqsrcs_smuio_9_0.h"
+#include "ivsrcid/ivsrcid_vislands30.h"
+
+uint8_t convert_to_vid(uint16_t vddc)
+{
+ return (uint8_t) ((6200 - (vddc * VOLTAGE_SCALE)) / 25);
+}
+
+uint16_t convert_to_vddc(uint8_t vid)
+{
+ return (uint16_t) ((6200 - (vid * 25)) / VOLTAGE_SCALE);
+}
+
+int phm_copy_clock_limits_array(
+ struct pp_hwmgr *hwmgr,
+ uint32_t **pptable_info_array,
+ const uint32_t *pptable_array,
+ uint32_t power_saving_clock_count)
+{
+ uint32_t array_size, i;
+ uint32_t *table;
+
+ array_size = sizeof(uint32_t) * power_saving_clock_count;
+ table = kzalloc(array_size, GFP_KERNEL);
+ if (NULL == table)
+ return -ENOMEM;
+
+ for (i = 0; i < power_saving_clock_count; i++)
+ table[i] = le32_to_cpu(pptable_array[i]);
+
+ *pptable_info_array = table;
+
+ return 0;
+}
+
+int phm_copy_overdrive_settings_limits_array(
+ struct pp_hwmgr *hwmgr,
+ uint32_t **pptable_info_array,
+ const uint32_t *pptable_array,
+ uint32_t od_setting_count)
+{
+ uint32_t array_size, i;
+ uint32_t *table;
+
+ array_size = sizeof(uint32_t) * od_setting_count;
+ table = kzalloc(array_size, GFP_KERNEL);
+ if (NULL == table)
+ return -ENOMEM;
+
+ for (i = 0; i < od_setting_count; i++)
+ table[i] = le32_to_cpu(pptable_array[i]);
+
+ *pptable_info_array = table;
+
+ return 0;
+}
+
+uint32_t phm_set_field_to_u32(u32 offset, u32 original_data, u32 field, u32 size)
+{
+ u32 mask = 0;
+ u32 shift = 0;
+
+ shift = (offset % 4) << 3;
+ if (size == sizeof(uint8_t))
+ mask = 0xFF << shift;
+ else if (size == sizeof(uint16_t))
+ mask = 0xFFFF << shift;
+
+ original_data &= ~mask;
+ original_data |= (field << shift);
+ return original_data;
+}
+
+/**
+ * Returns once the part of the register indicated by the mask has
+ * reached the given value.
+ */
+int phm_wait_on_register(struct pp_hwmgr *hwmgr, uint32_t index,
+ uint32_t value, uint32_t mask)
+{
+ uint32_t i;
+ uint32_t cur_value;
+
+ if (hwmgr == NULL || hwmgr->device == NULL) {
+ pr_err("Invalid Hardware Manager!");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < hwmgr->usec_timeout; i++) {
+ cur_value = cgs_read_register(hwmgr->device, index);
+ if ((cur_value & mask) == (value & mask))
+ break;
+ udelay(1);
+ }
+
+ /* timeout means wrong logic*/
+ if (i == hwmgr->usec_timeout)
+ return -1;
+ return 0;
+}
+
+
+/**
+ * Returns once the part of the register indicated by the mask has
+ * reached the given value.The indirect space is described by giving
+ * the memory-mapped index of the indirect index register.
+ */
+int phm_wait_on_indirect_register(struct pp_hwmgr *hwmgr,
+ uint32_t indirect_port,
+ uint32_t index,
+ uint32_t value,
+ uint32_t mask)
+{
+ if (hwmgr == NULL || hwmgr->device == NULL) {
+ pr_err("Invalid Hardware Manager!");
+ return -EINVAL;
+ }
+
+ cgs_write_register(hwmgr->device, indirect_port, index);
+ return phm_wait_on_register(hwmgr, indirect_port + 1, mask, value);
+}
+
+int phm_wait_for_register_unequal(struct pp_hwmgr *hwmgr,
+ uint32_t index,
+ uint32_t value, uint32_t mask)
+{
+ uint32_t i;
+ uint32_t cur_value;
+
+ if (hwmgr == NULL || hwmgr->device == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < hwmgr->usec_timeout; i++) {
+ cur_value = cgs_read_register(hwmgr->device,
+ index);
+ if ((cur_value & mask) != (value & mask))
+ break;
+ udelay(1);
+ }
+
+ /* timeout means wrong logic */
+ if (i == hwmgr->usec_timeout)
+ return -ETIME;
+ return 0;
+}
+
+int phm_wait_for_indirect_register_unequal(struct pp_hwmgr *hwmgr,
+ uint32_t indirect_port,
+ uint32_t index,
+ uint32_t value,
+ uint32_t mask)
+{
+ if (hwmgr == NULL || hwmgr->device == NULL)
+ return -EINVAL;
+
+ cgs_write_register(hwmgr->device, indirect_port, index);
+ return phm_wait_for_register_unequal(hwmgr, indirect_port + 1,
+ value, mask);
+}
+
+bool phm_cf_want_uvd_power_gating(struct pp_hwmgr *hwmgr)
+{
+ return phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_UVDPowerGating);
+}
+
+bool phm_cf_want_vce_power_gating(struct pp_hwmgr *hwmgr)
+{
+ return phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_VCEPowerGating);
+}
+
+
+int phm_trim_voltage_table(struct pp_atomctrl_voltage_table *vol_table)
+{
+ uint32_t i, j;
+ uint16_t vvalue;
+ bool found = false;
+ struct pp_atomctrl_voltage_table *table;
+
+ PP_ASSERT_WITH_CODE((NULL != vol_table),
+ "Voltage Table empty.", return -EINVAL);
+
+ table = kzalloc(sizeof(struct pp_atomctrl_voltage_table),
+ GFP_KERNEL);
+
+ if (NULL == table)
+ return -EINVAL;
+
+ table->mask_low = vol_table->mask_low;
+ table->phase_delay = vol_table->phase_delay;
+
+ for (i = 0; i < vol_table->count; i++) {
+ vvalue = vol_table->entries[i].value;
+ found = false;
+
+ for (j = 0; j < table->count; j++) {
+ if (vvalue == table->entries[j].value) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ table->entries[table->count].value = vvalue;
+ table->entries[table->count].smio_low =
+ vol_table->entries[i].smio_low;
+ table->count++;
+ }
+ }
+
+ memcpy(vol_table, table, sizeof(struct pp_atomctrl_voltage_table));
+ kfree(table);
+ table = NULL;
+ return 0;
+}
+
+int phm_get_svi2_mvdd_voltage_table(struct pp_atomctrl_voltage_table *vol_table,
+ phm_ppt_v1_clock_voltage_dependency_table *dep_table)
+{
+ uint32_t i;
+ int result;
+
+ PP_ASSERT_WITH_CODE((0 != dep_table->count),
+ "Voltage Dependency Table empty.", return -EINVAL);
+
+ PP_ASSERT_WITH_CODE((NULL != vol_table),
+ "vol_table empty.", return -EINVAL);
+
+ vol_table->mask_low = 0;
+ vol_table->phase_delay = 0;
+ vol_table->count = dep_table->count;
+
+ for (i = 0; i < dep_table->count; i++) {
+ vol_table->entries[i].value = dep_table->entries[i].mvdd;
+ vol_table->entries[i].smio_low = 0;
+ }
+
+ result = phm_trim_voltage_table(vol_table);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Failed to trim MVDD table.", return result);
+
+ return 0;
+}
+
+int phm_get_svi2_vddci_voltage_table(struct pp_atomctrl_voltage_table *vol_table,
+ phm_ppt_v1_clock_voltage_dependency_table *dep_table)
+{
+ uint32_t i;
+ int result;
+
+ PP_ASSERT_WITH_CODE((0 != dep_table->count),
+ "Voltage Dependency Table empty.", return -EINVAL);
+
+ PP_ASSERT_WITH_CODE((NULL != vol_table),
+ "vol_table empty.", return -EINVAL);
+
+ vol_table->mask_low = 0;
+ vol_table->phase_delay = 0;
+ vol_table->count = dep_table->count;
+
+ for (i = 0; i < dep_table->count; i++) {
+ vol_table->entries[i].value = dep_table->entries[i].vddci;
+ vol_table->entries[i].smio_low = 0;
+ }
+
+ result = phm_trim_voltage_table(vol_table);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Failed to trim VDDCI table.", return result);
+
+ return 0;
+}
+
+int phm_get_svi2_vdd_voltage_table(struct pp_atomctrl_voltage_table *vol_table,
+ phm_ppt_v1_voltage_lookup_table *lookup_table)
+{
+ int i = 0;
+
+ PP_ASSERT_WITH_CODE((0 != lookup_table->count),
+ "Voltage Lookup Table empty.", return -EINVAL);
+
+ PP_ASSERT_WITH_CODE((NULL != vol_table),
+ "vol_table empty.", return -EINVAL);
+
+ vol_table->mask_low = 0;
+ vol_table->phase_delay = 0;
+
+ vol_table->count = lookup_table->count;
+
+ for (i = 0; i < vol_table->count; i++) {
+ vol_table->entries[i].value = lookup_table->entries[i].us_vdd;
+ vol_table->entries[i].smio_low = 0;
+ }
+
+ return 0;
+}
+
+void phm_trim_voltage_table_to_fit_state_table(uint32_t max_vol_steps,
+ struct pp_atomctrl_voltage_table *vol_table)
+{
+ unsigned int i, diff;
+
+ if (vol_table->count <= max_vol_steps)
+ return;
+
+ diff = vol_table->count - max_vol_steps;
+
+ for (i = 0; i < max_vol_steps; i++)
+ vol_table->entries[i] = vol_table->entries[i + diff];
+
+ vol_table->count = max_vol_steps;
+
+ return;
+}
+
+int phm_reset_single_dpm_table(void *table,
+ uint32_t count, int max)
+{
+ int i;
+
+ struct vi_dpm_table *dpm_table = (struct vi_dpm_table *)table;
+
+ dpm_table->count = count > max ? max : count;
+
+ for (i = 0; i < dpm_table->count; i++)
+ dpm_table->dpm_level[i].enabled = false;
+
+ return 0;
+}
+
+void phm_setup_pcie_table_entry(
+ void *table,
+ uint32_t index, uint32_t pcie_gen,
+ uint32_t pcie_lanes)
+{
+ struct vi_dpm_table *dpm_table = (struct vi_dpm_table *)table;
+ dpm_table->dpm_level[index].value = pcie_gen;
+ dpm_table->dpm_level[index].param1 = pcie_lanes;
+ dpm_table->dpm_level[index].enabled = 1;
+}
+
+int32_t phm_get_dpm_level_enable_mask_value(void *table)
+{
+ int32_t i;
+ int32_t mask = 0;
+ struct vi_dpm_table *dpm_table = (struct vi_dpm_table *)table;
+
+ for (i = dpm_table->count; i > 0; i--) {
+ mask = mask << 1;
+ if (dpm_table->dpm_level[i - 1].enabled)
+ mask |= 0x1;
+ else
+ mask &= 0xFFFFFFFE;
+ }
+
+ return mask;
+}
+
+uint8_t phm_get_voltage_index(
+ struct phm_ppt_v1_voltage_lookup_table *lookup_table, uint16_t voltage)
+{
+ uint8_t count = (uint8_t) (lookup_table->count);
+ uint8_t i;
+
+ PP_ASSERT_WITH_CODE((NULL != lookup_table),
+ "Lookup Table empty.", return 0);
+ PP_ASSERT_WITH_CODE((0 != count),
+ "Lookup Table empty.", return 0);
+
+ for (i = 0; i < lookup_table->count; i++) {
+ /* find first voltage equal or bigger than requested */
+ if (lookup_table->entries[i].us_vdd >= voltage)
+ return i;
+ }
+ /* voltage is bigger than max voltage in the table */
+ return i - 1;
+}
+
+uint8_t phm_get_voltage_id(pp_atomctrl_voltage_table *voltage_table,
+ uint32_t voltage)
+{
+ uint8_t count = (uint8_t) (voltage_table->count);
+ uint8_t i = 0;
+
+ PP_ASSERT_WITH_CODE((NULL != voltage_table),
+ "Voltage Table empty.", return 0;);
+ PP_ASSERT_WITH_CODE((0 != count),
+ "Voltage Table empty.", return 0;);
+
+ for (i = 0; i < count; i++) {
+ /* find first voltage bigger than requested */
+ if (voltage_table->entries[i].value >= voltage)
+ return i;
+ }
+
+ /* voltage is bigger than max voltage in the table */
+ return i - 1;
+}
+
+uint16_t phm_find_closest_vddci(struct pp_atomctrl_voltage_table *vddci_table, uint16_t vddci)
+{
+ uint32_t i;
+
+ for (i = 0; i < vddci_table->count; i++) {
+ if (vddci_table->entries[i].value >= vddci)
+ return vddci_table->entries[i].value;
+ }
+
+ pr_debug("vddci is larger than max value in vddci_table\n");
+ return vddci_table->entries[i-1].value;
+}
+
+int phm_find_boot_level(void *table,
+ uint32_t value, uint32_t *boot_level)
+{
+ int result = -EINVAL;
+ uint32_t i;
+ struct vi_dpm_table *dpm_table = (struct vi_dpm_table *)table;
+
+ for (i = 0; i < dpm_table->count; i++) {
+ if (value == dpm_table->dpm_level[i].value) {
+ *boot_level = i;
+ result = 0;
+ }
+ }
+
+ return result;
+}
+
+int phm_get_sclk_for_voltage_evv(struct pp_hwmgr *hwmgr,
+ phm_ppt_v1_voltage_lookup_table *lookup_table,
+ uint16_t virtual_voltage_id, int32_t *sclk)
+{
+ uint8_t entry_id;
+ uint8_t voltage_id;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ PP_ASSERT_WITH_CODE(lookup_table->count != 0, "Lookup table is empty", return -EINVAL);
+
+ /* search for leakage voltage ID 0xff01 ~ 0xff08 and sckl */
+ for (entry_id = 0; entry_id < table_info->vdd_dep_on_sclk->count; entry_id++) {
+ voltage_id = table_info->vdd_dep_on_sclk->entries[entry_id].vddInd;
+ if (lookup_table->entries[voltage_id].us_vdd == virtual_voltage_id)
+ break;
+ }
+
+ if (entry_id >= table_info->vdd_dep_on_sclk->count) {
+ pr_debug("Can't find requested voltage id in vdd_dep_on_sclk table\n");
+ return -EINVAL;
+ }
+
+ *sclk = table_info->vdd_dep_on_sclk->entries[entry_id].clk;
+
+ return 0;
+}
+
+/**
+ * Initialize Dynamic State Adjustment Rule Settings
+ *
+ * @param hwmgr the address of the powerplay hardware manager.
+ */
+int phm_initializa_dynamic_state_adjustment_rule_settings(struct pp_hwmgr *hwmgr)
+{
+ uint32_t table_size;
+ struct phm_clock_voltage_dependency_table *table_clk_vlt;
+ struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ /* initialize vddc_dep_on_dal_pwrl table */
+ table_size = sizeof(uint32_t) + 4 * sizeof(struct phm_clock_voltage_dependency_record);
+ table_clk_vlt = kzalloc(table_size, GFP_KERNEL);
+
+ if (NULL == table_clk_vlt) {
+ pr_err("Can not allocate space for vddc_dep_on_dal_pwrl! \n");
+ return -ENOMEM;
+ } else {
+ table_clk_vlt->count = 4;
+ table_clk_vlt->entries[0].clk = PP_DAL_POWERLEVEL_ULTRALOW;
+ table_clk_vlt->entries[0].v = 0;
+ table_clk_vlt->entries[1].clk = PP_DAL_POWERLEVEL_LOW;
+ table_clk_vlt->entries[1].v = 720;
+ table_clk_vlt->entries[2].clk = PP_DAL_POWERLEVEL_NOMINAL;
+ table_clk_vlt->entries[2].v = 810;
+ table_clk_vlt->entries[3].clk = PP_DAL_POWERLEVEL_PERFORMANCE;
+ table_clk_vlt->entries[3].v = 900;
+ if (pptable_info != NULL)
+ pptable_info->vddc_dep_on_dal_pwrl = table_clk_vlt;
+ hwmgr->dyn_state.vddc_dep_on_dal_pwrl = table_clk_vlt;
+ }
+
+ return 0;
+}
+
+uint32_t phm_get_lowest_enabled_level(struct pp_hwmgr *hwmgr, uint32_t mask)
+{
+ uint32_t level = 0;
+
+ while (0 == (mask & (1 << level)))
+ level++;
+
+ return level;
+}
+
+void phm_apply_dal_min_voltage_request(struct pp_hwmgr *hwmgr)
+{
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)hwmgr->pptable;
+ struct phm_clock_voltage_dependency_table *table =
+ table_info->vddc_dep_on_dal_pwrl;
+ struct phm_ppt_v1_clock_voltage_dependency_table *vddc_table;
+ enum PP_DAL_POWERLEVEL dal_power_level = hwmgr->dal_power_level;
+ uint32_t req_vddc = 0, req_volt, i;
+
+ if (!table || table->count <= 0
+ || dal_power_level < PP_DAL_POWERLEVEL_ULTRALOW
+ || dal_power_level > PP_DAL_POWERLEVEL_PERFORMANCE)
+ return;
+
+ for (i = 0; i < table->count; i++) {
+ if (dal_power_level == table->entries[i].clk) {
+ req_vddc = table->entries[i].v;
+ break;
+ }
+ }
+
+ vddc_table = table_info->vdd_dep_on_sclk;
+ for (i = 0; i < vddc_table->count; i++) {
+ if (req_vddc <= vddc_table->entries[i].vddc) {
+ req_volt = (((uint32_t)vddc_table->entries[i].vddc) * VOLTAGE_SCALE);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_VddC_Request,
+ req_volt,
+ NULL);
+ return;
+ }
+ }
+ pr_err("DAL requested level can not"
+ " found a available voltage in VDDC DPM Table \n");
+}
+
+int phm_get_voltage_evv_on_sclk(struct pp_hwmgr *hwmgr, uint8_t voltage_type,
+ uint32_t sclk, uint16_t id, uint16_t *voltage)
+{
+ uint32_t vol;
+ int ret = 0;
+
+ if (hwmgr->chip_id < CHIP_TONGA) {
+ ret = atomctrl_get_voltage_evv(hwmgr, id, voltage);
+ } else if (hwmgr->chip_id < CHIP_POLARIS10) {
+ ret = atomctrl_get_voltage_evv_on_sclk(hwmgr, voltage_type, sclk, id, voltage);
+ if (*voltage >= 2000 || *voltage == 0)
+ *voltage = 1150;
+ } else {
+ ret = atomctrl_get_voltage_evv_on_sclk_ai(hwmgr, voltage_type, sclk, id, &vol);
+ *voltage = (uint16_t)(vol/100);
+ }
+ return ret;
+}
+
+
+int phm_irq_process(struct amdgpu_device *adev,
+ struct amdgpu_irq_src *source,
+ struct amdgpu_iv_entry *entry)
+{
+ uint32_t client_id = entry->client_id;
+ uint32_t src_id = entry->src_id;
+
+ if (client_id == AMDGPU_IRQ_CLIENTID_LEGACY) {
+ if (src_id == VISLANDS30_IV_SRCID_CG_TSS_THERMAL_LOW_TO_HIGH) {
+ dev_emerg(adev->dev, "ERROR: GPU over temperature range(SW CTF) detected!\n");
+ /*
+ * SW CTF just occurred.
+ * Try to do a graceful shutdown to prevent further damage.
+ */
+ dev_emerg(adev->dev, "ERROR: System is going to shutdown due to GPU SW CTF!\n");
+ orderly_poweroff(true);
+ } else if (src_id == VISLANDS30_IV_SRCID_CG_TSS_THERMAL_HIGH_TO_LOW)
+ dev_emerg(adev->dev, "ERROR: GPU under temperature range detected!\n");
+ else if (src_id == VISLANDS30_IV_SRCID_GPIO_19) {
+ dev_emerg(adev->dev, "ERROR: GPU HW Critical Temperature Fault(aka CTF) detected!\n");
+ /*
+ * HW CTF just occurred. Shutdown to prevent further damage.
+ */
+ dev_emerg(adev->dev, "ERROR: System is going to shutdown due to GPU HW CTF!\n");
+ orderly_poweroff(true);
+ }
+ } else if (client_id == SOC15_IH_CLIENTID_THM) {
+ if (src_id == 0) {
+ dev_emerg(adev->dev, "ERROR: GPU over temperature range(SW CTF) detected!\n");
+ /*
+ * SW CTF just occurred.
+ * Try to do a graceful shutdown to prevent further damage.
+ */
+ dev_emerg(adev->dev, "ERROR: System is going to shutdown due to GPU SW CTF!\n");
+ orderly_poweroff(true);
+ } else
+ dev_emerg(adev->dev, "ERROR: GPU under temperature range detected!\n");
+ } else if (client_id == SOC15_IH_CLIENTID_ROM_SMUIO) {
+ dev_emerg(adev->dev, "ERROR: GPU HW Critical Temperature Fault(aka CTF) detected!\n");
+ /*
+ * HW CTF just occurred. Shutdown to prevent further damage.
+ */
+ dev_emerg(adev->dev, "ERROR: System is going to shutdown due to GPU HW CTF!\n");
+ orderly_poweroff(true);
+ }
+
+ return 0;
+}
+
+static const struct amdgpu_irq_src_funcs smu9_irq_funcs = {
+ .process = phm_irq_process,
+};
+
+int smu9_register_irq_handlers(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_irq_src *source =
+ kzalloc(sizeof(struct amdgpu_irq_src), GFP_KERNEL);
+
+ if (!source)
+ return -ENOMEM;
+
+ source->funcs = &smu9_irq_funcs;
+
+ amdgpu_irq_add_id((struct amdgpu_device *)(hwmgr->adev),
+ SOC15_IH_CLIENTID_THM,
+ THM_9_0__SRCID__THM_DIG_THERM_L2H,
+ source);
+ amdgpu_irq_add_id((struct amdgpu_device *)(hwmgr->adev),
+ SOC15_IH_CLIENTID_THM,
+ THM_9_0__SRCID__THM_DIG_THERM_H2L,
+ source);
+
+ /* Register CTF(GPIO_19) interrupt */
+ amdgpu_irq_add_id((struct amdgpu_device *)(hwmgr->adev),
+ SOC15_IH_CLIENTID_ROM_SMUIO,
+ SMUIO_9_0__SRCID__SMUIO_GPIO19,
+ source);
+
+ return 0;
+}
+
+void *smu_atom_get_data_table(void *dev, uint32_t table, uint16_t *size,
+ uint8_t *frev, uint8_t *crev)
+{
+ struct amdgpu_device *adev = dev;
+ uint16_t data_start;
+
+ if (amdgpu_atom_parse_data_header(
+ adev->mode_info.atom_context, table, size,
+ frev, crev, &data_start))
+ return (uint8_t *)adev->mode_info.atom_context->bios +
+ data_start;
+
+ return NULL;
+}
+
+int smu_get_voltage_dependency_table_ppt_v1(
+ const struct phm_ppt_v1_clock_voltage_dependency_table *allowed_dep_table,
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_table)
+{
+ uint8_t i = 0;
+ PP_ASSERT_WITH_CODE((0 != allowed_dep_table->count),
+ "Voltage Lookup Table empty",
+ return -EINVAL);
+
+ dep_table->count = allowed_dep_table->count;
+ for (i=0; i<dep_table->count; i++) {
+ dep_table->entries[i].clk = allowed_dep_table->entries[i].clk;
+ dep_table->entries[i].vddInd = allowed_dep_table->entries[i].vddInd;
+ dep_table->entries[i].vdd_offset = allowed_dep_table->entries[i].vdd_offset;
+ dep_table->entries[i].vddc = allowed_dep_table->entries[i].vddc;
+ dep_table->entries[i].vddgfx = allowed_dep_table->entries[i].vddgfx;
+ dep_table->entries[i].vddci = allowed_dep_table->entries[i].vddci;
+ dep_table->entries[i].mvdd = allowed_dep_table->entries[i].mvdd;
+ dep_table->entries[i].phases = allowed_dep_table->entries[i].phases;
+ dep_table->entries[i].cks_enable = allowed_dep_table->entries[i].cks_enable;
+ dep_table->entries[i].cks_voffset = allowed_dep_table->entries[i].cks_voffset;
+ }
+
+ return 0;
+}
+
+int smu_set_watermarks_for_clocks_ranges(void *wt_table,
+ struct dm_pp_wm_sets_with_clock_ranges_soc15 *wm_with_clock_ranges)
+{
+ uint32_t i;
+ struct watermarks *table = wt_table;
+
+ if (!table || !wm_with_clock_ranges)
+ return -EINVAL;
+
+ if (wm_with_clock_ranges->num_wm_dmif_sets > 4 || wm_with_clock_ranges->num_wm_mcif_sets > 4)
+ return -EINVAL;
+
+ for (i = 0; i < wm_with_clock_ranges->num_wm_dmif_sets; i++) {
+ table->WatermarkRow[1][i].MinClock =
+ cpu_to_le16((uint16_t)
+ (wm_with_clock_ranges->wm_dmif_clocks_ranges[i].wm_min_dcfclk_clk_in_khz /
+ 1000));
+ table->WatermarkRow[1][i].MaxClock =
+ cpu_to_le16((uint16_t)
+ (wm_with_clock_ranges->wm_dmif_clocks_ranges[i].wm_max_dcfclk_clk_in_khz /
+ 1000));
+ table->WatermarkRow[1][i].MinUclk =
+ cpu_to_le16((uint16_t)
+ (wm_with_clock_ranges->wm_dmif_clocks_ranges[i].wm_min_mem_clk_in_khz /
+ 1000));
+ table->WatermarkRow[1][i].MaxUclk =
+ cpu_to_le16((uint16_t)
+ (wm_with_clock_ranges->wm_dmif_clocks_ranges[i].wm_max_mem_clk_in_khz /
+ 1000));
+ table->WatermarkRow[1][i].WmSetting = (uint8_t)
+ wm_with_clock_ranges->wm_dmif_clocks_ranges[i].wm_set_id;
+ }
+
+ for (i = 0; i < wm_with_clock_ranges->num_wm_mcif_sets; i++) {
+ table->WatermarkRow[0][i].MinClock =
+ cpu_to_le16((uint16_t)
+ (wm_with_clock_ranges->wm_mcif_clocks_ranges[i].wm_min_socclk_clk_in_khz /
+ 1000));
+ table->WatermarkRow[0][i].MaxClock =
+ cpu_to_le16((uint16_t)
+ (wm_with_clock_ranges->wm_mcif_clocks_ranges[i].wm_max_socclk_clk_in_khz /
+ 1000));
+ table->WatermarkRow[0][i].MinUclk =
+ cpu_to_le16((uint16_t)
+ (wm_with_clock_ranges->wm_mcif_clocks_ranges[i].wm_min_mem_clk_in_khz /
+ 1000));
+ table->WatermarkRow[0][i].MaxUclk =
+ cpu_to_le16((uint16_t)
+ (wm_with_clock_ranges->wm_mcif_clocks_ranges[i].wm_max_mem_clk_in_khz /
+ 1000));
+ table->WatermarkRow[0][i].WmSetting = (uint8_t)
+ wm_with_clock_ranges->wm_mcif_clocks_ranges[i].wm_set_id;
+ }
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.h
new file mode 100644
index 000000000000..ad33983a8064
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _SMU_HELPER_H_
+#define _SMU_HELPER_H_
+
+struct pp_atomctrl_voltage_table;
+struct pp_hwmgr;
+struct phm_ppt_v1_voltage_lookup_table;
+struct Watermarks_t;
+struct pp_wm_sets_with_clock_ranges_soc15;
+
+uint8_t convert_to_vid(uint16_t vddc);
+uint16_t convert_to_vddc(uint8_t vid);
+
+struct watermark_row_generic_t {
+ uint16_t MinClock;
+ uint16_t MaxClock;
+ uint16_t MinUclk;
+ uint16_t MaxUclk;
+
+ uint8_t WmSetting;
+ uint8_t Padding[3];
+};
+
+struct watermarks {
+ struct watermark_row_generic_t WatermarkRow[2][4];
+ uint32_t padding[7];
+};
+
+int phm_copy_clock_limits_array(
+ struct pp_hwmgr *hwmgr,
+ uint32_t **pptable_info_array,
+ const uint32_t *pptable_array,
+ uint32_t power_saving_clock_count);
+
+int phm_copy_overdrive_settings_limits_array(
+ struct pp_hwmgr *hwmgr,
+ uint32_t **pptable_info_array,
+ const uint32_t *pptable_array,
+ uint32_t od_setting_count);
+
+extern int phm_wait_for_register_unequal(struct pp_hwmgr *hwmgr,
+ uint32_t index,
+ uint32_t value, uint32_t mask);
+extern int phm_wait_for_indirect_register_unequal(
+ struct pp_hwmgr *hwmgr,
+ uint32_t indirect_port, uint32_t index,
+ uint32_t value, uint32_t mask);
+
+
+extern bool phm_cf_want_uvd_power_gating(struct pp_hwmgr *hwmgr);
+extern bool phm_cf_want_vce_power_gating(struct pp_hwmgr *hwmgr);
+extern bool phm_cf_want_microcode_fan_ctrl(struct pp_hwmgr *hwmgr);
+
+extern int phm_trim_voltage_table(struct pp_atomctrl_voltage_table *vol_table);
+extern int phm_get_svi2_mvdd_voltage_table(struct pp_atomctrl_voltage_table *vol_table, phm_ppt_v1_clock_voltage_dependency_table *dep_table);
+extern int phm_get_svi2_vddci_voltage_table(struct pp_atomctrl_voltage_table *vol_table, phm_ppt_v1_clock_voltage_dependency_table *dep_table);
+extern int phm_get_svi2_vdd_voltage_table(struct pp_atomctrl_voltage_table *vol_table, phm_ppt_v1_voltage_lookup_table *lookup_table);
+extern void phm_trim_voltage_table_to_fit_state_table(uint32_t max_vol_steps, struct pp_atomctrl_voltage_table *vol_table);
+extern int phm_reset_single_dpm_table(void *table, uint32_t count, int max);
+extern void phm_setup_pcie_table_entry(void *table, uint32_t index, uint32_t pcie_gen, uint32_t pcie_lanes);
+extern int32_t phm_get_dpm_level_enable_mask_value(void *table);
+extern uint8_t phm_get_voltage_id(struct pp_atomctrl_voltage_table *voltage_table,
+ uint32_t voltage);
+extern uint8_t phm_get_voltage_index(struct phm_ppt_v1_voltage_lookup_table *lookup_table, uint16_t voltage);
+extern uint16_t phm_find_closest_vddci(struct pp_atomctrl_voltage_table *vddci_table, uint16_t vddci);
+extern int phm_find_boot_level(void *table, uint32_t value, uint32_t *boot_level);
+extern int phm_get_sclk_for_voltage_evv(struct pp_hwmgr *hwmgr, phm_ppt_v1_voltage_lookup_table *lookup_table,
+ uint16_t virtual_voltage_id, int32_t *sclk);
+extern int phm_initializa_dynamic_state_adjustment_rule_settings(struct pp_hwmgr *hwmgr);
+extern uint32_t phm_get_lowest_enabled_level(struct pp_hwmgr *hwmgr, uint32_t mask);
+extern void phm_apply_dal_min_voltage_request(struct pp_hwmgr *hwmgr);
+
+extern int phm_get_voltage_evv_on_sclk(struct pp_hwmgr *hwmgr, uint8_t voltage_type,
+ uint32_t sclk, uint16_t id, uint16_t *voltage);
+
+extern uint32_t phm_set_field_to_u32(u32 offset, u32 original_data, u32 field, u32 size);
+
+extern int phm_wait_on_register(struct pp_hwmgr *hwmgr, uint32_t index,
+ uint32_t value, uint32_t mask);
+
+extern int phm_wait_on_indirect_register(struct pp_hwmgr *hwmgr,
+ uint32_t indirect_port,
+ uint32_t index,
+ uint32_t value,
+ uint32_t mask);
+
+int phm_irq_process(struct amdgpu_device *adev,
+ struct amdgpu_irq_src *source,
+ struct amdgpu_iv_entry *entry);
+
+int smu9_register_irq_handlers(struct pp_hwmgr *hwmgr);
+
+void *smu_atom_get_data_table(void *dev, uint32_t table, uint16_t *size,
+ uint8_t *frev, uint8_t *crev);
+
+int smu_get_voltage_dependency_table_ppt_v1(
+ const struct phm_ppt_v1_clock_voltage_dependency_table *allowed_dep_table,
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_table);
+
+int smu_set_watermarks_for_clocks_ranges(void *wt_table,
+ struct dm_pp_wm_sets_with_clock_ranges_soc15 *wm_with_clock_ranges);
+
+#define PHM_FIELD_SHIFT(reg, field) reg##__##field##__SHIFT
+#define PHM_FIELD_MASK(reg, field) reg##__##field##_MASK
+
+#define PHM_SET_FIELD(origval, reg, field, fieldval) \
+ (((origval) & ~PHM_FIELD_MASK(reg, field)) | \
+ (PHM_FIELD_MASK(reg, field) & ((fieldval) << PHM_FIELD_SHIFT(reg, field))))
+
+#define PHM_GET_FIELD(value, reg, field) \
+ (((value) & PHM_FIELD_MASK(reg, field)) >> \
+ PHM_FIELD_SHIFT(reg, field))
+
+
+/* Operations on named fields. */
+
+#define PHM_READ_FIELD(device, reg, field) \
+ PHM_GET_FIELD(cgs_read_register(device, mm##reg), reg, field)
+
+#define PHM_READ_INDIRECT_FIELD(device, port, reg, field) \
+ PHM_GET_FIELD(cgs_read_ind_register(device, port, ix##reg), \
+ reg, field)
+
+#define PHM_READ_VFPF_INDIRECT_FIELD(device, port, reg, field) \
+ PHM_GET_FIELD(cgs_read_ind_register(device, port, ix##reg), \
+ reg, field)
+
+#define PHM_WRITE_FIELD(device, reg, field, fieldval) \
+ cgs_write_register(device, mm##reg, PHM_SET_FIELD( \
+ cgs_read_register(device, mm##reg), reg, field, fieldval))
+
+#define PHM_WRITE_INDIRECT_FIELD(device, port, reg, field, fieldval) \
+ cgs_write_ind_register(device, port, ix##reg, \
+ PHM_SET_FIELD(cgs_read_ind_register(device, port, ix##reg), \
+ reg, field, fieldval))
+
+#define PHM_WRITE_VFPF_INDIRECT_FIELD(device, port, reg, field, fieldval) \
+ cgs_write_ind_register(device, port, ix##reg, \
+ PHM_SET_FIELD(cgs_read_ind_register(device, port, ix##reg), \
+ reg, field, fieldval))
+
+#define PHM_WAIT_INDIRECT_REGISTER_GIVEN_INDEX(hwmgr, port, index, value, mask) \
+ phm_wait_on_indirect_register(hwmgr, mm##port##_INDEX, index, value, mask)
+
+
+#define PHM_WAIT_INDIRECT_REGISTER(hwmgr, port, reg, value, mask) \
+ PHM_WAIT_INDIRECT_REGISTER_GIVEN_INDEX(hwmgr, port, ix##reg, value, mask)
+
+#define PHM_WAIT_INDIRECT_FIELD(hwmgr, port, reg, field, fieldval) \
+ PHM_WAIT_INDIRECT_REGISTER(hwmgr, port, reg, (fieldval) \
+ << PHM_FIELD_SHIFT(reg, field), PHM_FIELD_MASK(reg, field))
+
+#define PHM_WAIT_INDIRECT_REGISTER_UNEQUAL_GIVEN_INDEX(hwmgr, port, index, value, mask) \
+ phm_wait_for_indirect_register_unequal(hwmgr, \
+ mm##port##_INDEX, index, value, mask)
+
+#define PHM_WAIT_INDIRECT_REGISTER_UNEQUAL(hwmgr, port, reg, value, mask) \
+ PHM_WAIT_INDIRECT_REGISTER_UNEQUAL_GIVEN_INDEX(hwmgr, port, ix##reg, value, mask)
+
+#define PHM_WAIT_INDIRECT_FIELD_UNEQUAL(hwmgr, port, reg, field, fieldval) \
+ PHM_WAIT_INDIRECT_REGISTER_UNEQUAL(hwmgr, port, reg, \
+ (fieldval) << PHM_FIELD_SHIFT(reg, field), \
+ PHM_FIELD_MASK(reg, field) )
+
+
+#define PHM_WAIT_VFPF_INDIRECT_REGISTER_UNEQUAL_GIVEN_INDEX(hwmgr, \
+ port, index, value, mask) \
+ phm_wait_for_indirect_register_unequal(hwmgr, \
+ mm##port##_INDEX_11, index, value, mask)
+
+#define PHM_WAIT_VFPF_INDIRECT_REGISTER_UNEQUAL(hwmgr, port, reg, value, mask) \
+ PHM_WAIT_VFPF_INDIRECT_REGISTER_UNEQUAL_GIVEN_INDEX(hwmgr, port, ix##reg, value, mask)
+
+#define PHM_WAIT_VFPF_INDIRECT_FIELD_UNEQUAL(hwmgr, port, reg, field, fieldval) \
+ PHM_WAIT_VFPF_INDIRECT_REGISTER_UNEQUAL(hwmgr, port, reg, \
+ (fieldval) << PHM_FIELD_SHIFT(reg, field), \
+ PHM_FIELD_MASK(reg, field))
+
+
+#define PHM_WAIT_VFPF_INDIRECT_REGISTER_GIVEN_INDEX(hwmgr, \
+ port, index, value, mask) \
+ phm_wait_on_indirect_register(hwmgr, \
+ mm##port##_INDEX_11, index, value, mask)
+
+#define PHM_WAIT_VFPF_INDIRECT_REGISTER(hwmgr, port, reg, value, mask) \
+ PHM_WAIT_VFPF_INDIRECT_REGISTER_GIVEN_INDEX(hwmgr, port, ix##reg, value, mask)
+
+#define PHM_WAIT_VFPF_INDIRECT_FIELD(hwmgr, port, reg, field, fieldval) \
+ PHM_WAIT_VFPF_INDIRECT_REGISTER(hwmgr, port, reg, \
+ (fieldval) << PHM_FIELD_SHIFT(reg, field), \
+ PHM_FIELD_MASK(reg, field))
+
+#define PHM_WAIT_REGISTER_UNEQUAL_GIVEN_INDEX(hwmgr, \
+ index, value, mask) \
+ phm_wait_for_register_unequal(hwmgr, \
+ index, value, mask)
+
+#define PHM_WAIT_REGISTER_UNEQUAL(hwmgr, reg, value, mask) \
+ PHM_WAIT_REGISTER_UNEQUAL_GIVEN_INDEX(hwmgr, \
+ mm##reg, value, mask)
+
+#define PHM_WAIT_FIELD_UNEQUAL(hwmgr, reg, field, fieldval) \
+ PHM_WAIT_REGISTER_UNEQUAL(hwmgr, reg, \
+ (fieldval) << PHM_FIELD_SHIFT(reg, field), \
+ PHM_FIELD_MASK(reg, field))
+
+#endif /* _SMU_HELPER_H_ */
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/tonga_baco.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/tonga_baco.c
new file mode 100644
index 000000000000..ea743bea8e29
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/tonga_baco.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "amdgpu.h"
+#include "tonga_baco.h"
+
+#include "gmc/gmc_8_1_d.h"
+#include "gmc/gmc_8_1_sh_mask.h"
+
+#include "bif/bif_5_0_d.h"
+#include "bif/bif_5_0_sh_mask.h"
+
+#include "dce/dce_10_0_d.h"
+#include "dce/dce_10_0_sh_mask.h"
+
+#include "smu/smu_7_1_2_d.h"
+#include "smu/smu_7_1_2_sh_mask.h"
+
+
+static const struct baco_cmd_entry gpio_tbl[] =
+{
+ { CMD_WRITE, mmGPIOPAD_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmGPIOPAD_PD_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmGPIOPAD_PU_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmGPIOPAD_MASK, 0, 0, 0, 0xff77ffff },
+ { CMD_WRITE, mmDC_GPIO_DVODATA_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmDC_GPIO_DVODATA_MASK, 0, 0, 0, 0xffffffff },
+ { CMD_WRITE, mmDC_GPIO_GENERIC_EN, 0, 0, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmDC_GPIO_GENERIC_MASK, 0, 0, 0, 0x03333333 },
+ { CMD_WRITE, mmDC_GPIO_SYNCA_EN, 0, 0, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmDC_GPIO_SYNCA_MASK, 0, 0, 0, 0x00001111 }
+};
+
+static const struct baco_cmd_entry enable_fb_req_rej_tbl[] =
+{
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, 0xC0300024 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x1, 0x0, 0, 0x1 },
+ { CMD_WRITE, mmBIF_FB_EN, 0, 0, 0, 0x0 }
+};
+
+static const struct baco_cmd_entry use_bclk_tbl[] =
+{
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_FUNC_CNTL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL__SPLL_BYPASS_EN_MASK, CG_SPLL_FUNC_CNTL__SPLL_BYPASS_EN__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_FUNC_CNTL_2 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL_2__SPLL_BYPASS_CHG_MASK, CG_SPLL_FUNC_CNTL_2__SPLL_BYPASS_CHG__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_STATUS },
+ { CMD_WAITFOR, mmGCK_SMC_IND_DATA, CG_SPLL_STATUS__SPLL_CHG_STATUS_MASK, 0, 0xffffffff, 0x2 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_FUNC_CNTL_2 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL_2__SPLL_BYPASS_CHG_MASK, CG_SPLL_FUNC_CNTL_2__SPLL_BYPASS_CHG__SHIFT, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL_2__SPLL_CTLREQ_CHG_MASK, CG_SPLL_FUNC_CNTL_2__SPLL_CTLREQ_CHG__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_STATUS },
+ { CMD_WAITFOR, mmGCK_SMC_IND_DATA, CG_SPLL_STATUS__SPLL_CHG_STATUS_MASK, 0, 0xffffffff, 0x2 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_FUNC_CNTL_2 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL_2__SPLL_CTLREQ_CHG_MASK, CG_SPLL_FUNC_CNTL_2__SPLL_CTLREQ_CHG__SHIFT, 0, 0x0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, 0xC0500170 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x4000000, 0x1a, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixMPLL_BYPASSCLK_SEL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, MPLL_BYPASSCLK_SEL__MPLL_CLKOUT_SEL_MASK, MPLL_BYPASSCLK_SEL__MPLL_CLKOUT_SEL__SHIFT, 0, 0x2 },
+ { CMD_READMODIFYWRITE, mmMPLL_CNTL_MODE, MPLL_CNTL_MODE__MPLL_SW_DIR_CONTROL_MASK, MPLL_CNTL_MODE__MPLL_SW_DIR_CONTROL__SHIFT, 0, 0x1 },
+ { CMD_READMODIFYWRITE, mmMPLL_CNTL_MODE, MPLL_CNTL_MODE__MPLL_MCLK_SEL_MASK, MPLL_CNTL_MODE__MPLL_MCLK_SEL__SHIFT, 0, 0x0 }
+};
+
+static const struct baco_cmd_entry turn_off_plls_tbl[] =
+{
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_SPLL_FUNC_CNTL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL__SPLL_RESET_MASK, CG_SPLL_FUNC_CNTL__SPLL_RESET__SHIFT, 0, 0x1 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_SPLL_FUNC_CNTL__SPLL_PWRON_MASK, CG_SPLL_FUNC_CNTL__SPLL_PWRON__SHIFT, 0, 0x0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, 0xC0500170 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x2000000, 0x19, 0, 0x1 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, 0x8000000, 0x1b, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmMPLL_CNTL_MODE, MPLL_CNTL_MODE__GLOBAL_MPLL_RESET_MASK, MPLL_CNTL_MODE__GLOBAL_MPLL_RESET__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmMPLL_CONTROL, 0, 0, 0, 0x00000006 },
+ { CMD_WRITE, mmMC_IO_RXCNTL_DPHY0_D0, 0, 0, 0, 0x00007740 },
+ { CMD_WRITE, mmMC_IO_RXCNTL_DPHY0_D1, 0, 0, 0, 0x00007740 },
+ { CMD_WRITE, mmMC_IO_RXCNTL_DPHY1_D0, 0, 0, 0, 0x00007740 },
+ { CMD_WRITE, mmMC_IO_RXCNTL_DPHY1_D1, 0, 0, 0, 0x00007740 },
+ { CMD_READMODIFYWRITE, mmMCLK_PWRMGT_CNTL, MCLK_PWRMGT_CNTL__MRDCK0_PDNB_MASK, MCLK_PWRMGT_CNTL__MRDCK0_PDNB__SHIFT, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmMCLK_PWRMGT_CNTL, MCLK_PWRMGT_CNTL__MRDCK1_PDNB_MASK, MCLK_PWRMGT_CNTL__MRDCK1_PDNB__SHIFT, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmMC_SEQ_CNTL_2, MC_SEQ_CNTL_2__DRST_PU_MASK, MC_SEQ_CNTL_2__DRST_PU__SHIFT, 0, 0x0 },
+ { CMD_READMODIFYWRITE, mmMC_SEQ_CNTL_2, MC_SEQ_CNTL_2__DRST_PD_MASK, MC_SEQ_CNTL_2__DRST_PD__SHIFT, 0, 0x0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_CLKPIN_CNTL_2 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_CLKPIN_CNTL_2__FORCE_BIF_REFCLK_EN_MASK, CG_CLKPIN_CNTL_2__FORCE_BIF_REFCLK_EN__SHIFT, 0, 0x0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixMPLL_BYPASSCLK_SEL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, MPLL_BYPASSCLK_SEL__MPLL_CLKOUT_SEL_MASK, MPLL_BYPASSCLK_SEL__MPLL_CLKOUT_SEL__SHIFT, 0, 0x4 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixMISC_CLK_CTRL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, MISC_CLK_CTRL__DEEP_SLEEP_CLK_SEL_MASK, MISC_CLK_CTRL__DEEP_SLEEP_CLK_SEL__SHIFT, 0, 0x1 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, MISC_CLK_CTRL__ZCLK_SEL_MASK, MISC_CLK_CTRL__ZCLK_SEL__SHIFT, 0, 0x1 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixCG_CLKPIN_CNTL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, CG_CLKPIN_CNTL__BCLK_AS_XCLK_MASK, CG_CLKPIN_CNTL__BCLK_AS_XCLK__SHIFT, 0, 0x0 },
+ { CMD_WRITE, mmGCK_SMC_IND_INDEX, 0, 0, 0, ixTHM_CLK_CNTL },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, THM_CLK_CNTL__CMON_CLK_SEL_MASK, THM_CLK_CNTL__CMON_CLK_SEL__SHIFT, 0, 0x1 },
+ { CMD_READMODIFYWRITE, mmGCK_SMC_IND_DATA, THM_CLK_CNTL__TMON_CLK_SEL_MASK, THM_CLK_CNTL__TMON_CLK_SEL__SHIFT, 0, 0x1 }
+};
+
+static const struct baco_cmd_entry enter_baco_tbl[] =
+{
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_EN_MASK, BACO_CNTL__BACO_EN__SHIFT, 0, 0x01 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_BIF_SCLK_SWITCH_MASK, BACO_CNTL__BACO_BIF_SCLK_SWITCH__SHIFT, 0, 0x01 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_BIF_SCLK_SWITCH_MASK, 0, 5, 0x40000 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_BCLK_OFF_MASK, BACO_CNTL__BACO_BCLK_OFF__SHIFT, 0, 0x01 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_BCLK_OFF_MASK, 0, 5, 0x02 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_ISO_DIS_MASK, BACO_CNTL__BACO_ISO_DIS__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_ISO_DIS_MASK, 0, 5, 0x00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_ANA_ISO_DIS_MASK, BACO_CNTL__BACO_ANA_ISO_DIS__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_ANA_ISO_DIS_MASK, 0, 5, 0x00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_POWER_OFF_MASK, BACO_CNTL__BACO_POWER_OFF__SHIFT, 0, 0x01 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_POWER_OFF_MASK, 0, 5, 0x08 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_MODE_MASK, 0, 0xffffffff, 0x40 }
+};
+
+#define BACO_CNTL__PWRGOOD_MASK BACO_CNTL__PWRGOOD_GPIO_MASK+BACO_CNTL__PWRGOOD_MEM_MASK+BACO_CNTL__PWRGOOD_DVO_MASK
+
+static const struct baco_cmd_entry exit_baco_tbl[] =
+{
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_RESET_EN_MASK, BACO_CNTL__BACO_RESET_EN__SHIFT, 0, 0x01 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_BCLK_OFF_MASK, BACO_CNTL__BACO_BCLK_OFF__SHIFT, 0, 0x00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_POWER_OFF_MASK, BACO_CNTL__BACO_POWER_OFF__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__PWRGOOD_BF_MASK, 0, 0xffffffff, 0x200 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_ISO_DIS_MASK, BACO_CNTL__BACO_ISO_DIS__SHIFT, 0, 0x01 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__PWRGOOD_MASK, 0, 5, 0x1c00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_ANA_ISO_DIS_MASK, BACO_CNTL__BACO_ANA_ISO_DIS__SHIFT, 0, 0x01 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_BIF_SCLK_SWITCH_MASK, BACO_CNTL__BACO_BIF_SCLK_SWITCH__SHIFT, 0, 0x00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_RESET_EN_MASK, BACO_CNTL__BACO_RESET_EN__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__RCU_BIF_CONFIG_DONE_MASK, 0, 5, 0x100 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_EN_MASK, BACO_CNTL__BACO_EN__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_MODE_MASK, 0, 0xffffffff, 0x00 }
+};
+
+static const struct baco_cmd_entry clean_baco_tbl[] =
+{
+ { CMD_WRITE, mmBIOS_SCRATCH_6, 0, 0, 0, 0 },
+ { CMD_WRITE, mmBIOS_SCRATCH_7, 0, 0, 0, 0 }
+};
+
+static const struct baco_cmd_entry gpio_tbl_iceland[] =
+{
+ { CMD_WRITE, mmGPIOPAD_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmGPIOPAD_PD_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmGPIOPAD_PU_EN, 0, 0, 0, 0x0 },
+ { CMD_WRITE, mmGPIOPAD_MASK, 0, 0, 0, 0xff77ffff }
+};
+
+static const struct baco_cmd_entry exit_baco_tbl_iceland[] =
+{
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_RESET_EN_MASK, BACO_CNTL__BACO_RESET_EN__SHIFT, 0, 0x01 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_BCLK_OFF_MASK, BACO_CNTL__BACO_BCLK_OFF__SHIFT, 0, 0x00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_POWER_OFF_MASK, BACO_CNTL__BACO_POWER_OFF__SHIFT, 0, 0x00 },
+ { CMD_DELAY_MS, 0, 0, 0, 20, 0 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__PWRGOOD_BF_MASK, 0, 0xffffffff, 0x200 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_ISO_DIS_MASK, BACO_CNTL__BACO_ISO_DIS__SHIFT, 0, 0x01 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__PWRGOOD_MASK, 0, 5, 0x1c00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_ANA_ISO_DIS_MASK, BACO_CNTL__BACO_ANA_ISO_DIS__SHIFT, 0, 0x01 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_BIF_SCLK_SWITCH_MASK, BACO_CNTL__BACO_BIF_SCLK_SWITCH__SHIFT, 0, 0x00 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_RESET_EN_MASK, BACO_CNTL__BACO_RESET_EN__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__RCU_BIF_CONFIG_DONE_MASK, 0, 5, 0x100 },
+ { CMD_READMODIFYWRITE, mmBACO_CNTL, BACO_CNTL__BACO_EN_MASK, BACO_CNTL__BACO_EN__SHIFT, 0, 0x00 },
+ { CMD_WAITFOR, mmBACO_CNTL, BACO_CNTL__BACO_MODE_MASK, 0, 0xffffffff, 0x00 }
+};
+
+static const struct baco_cmd_entry clean_baco_tbl_iceland[] =
+{
+ { CMD_WRITE, mmBIOS_SCRATCH_7, 0, 0, 0, 0 }
+};
+
+int tonga_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state)
+{
+ enum BACO_STATE cur_state;
+
+ smu7_baco_get_state(hwmgr, &cur_state);
+
+ if (cur_state == state)
+ /* aisc already in the target state */
+ return 0;
+
+ if (state == BACO_STATE_IN) {
+ if (hwmgr->chip_id == CHIP_TOPAZ)
+ baco_program_registers(hwmgr, gpio_tbl_iceland, ARRAY_SIZE(gpio_tbl_iceland));
+ else
+ baco_program_registers(hwmgr, gpio_tbl, ARRAY_SIZE(gpio_tbl));
+ baco_program_registers(hwmgr, enable_fb_req_rej_tbl,
+ ARRAY_SIZE(enable_fb_req_rej_tbl));
+ baco_program_registers(hwmgr, use_bclk_tbl, ARRAY_SIZE(use_bclk_tbl));
+ baco_program_registers(hwmgr, turn_off_plls_tbl,
+ ARRAY_SIZE(turn_off_plls_tbl));
+ if (baco_program_registers(hwmgr, enter_baco_tbl,
+ ARRAY_SIZE(enter_baco_tbl)))
+ return 0;
+
+ } else if (state == BACO_STATE_OUT) {
+ /* HW requires at least 20ms between regulator off and on */
+ msleep(20);
+ /* Execute Hardware BACO exit sequence */
+ if (hwmgr->chip_id == CHIP_TOPAZ) {
+ if (baco_program_registers(hwmgr, exit_baco_tbl_iceland,
+ ARRAY_SIZE(exit_baco_tbl_iceland))) {
+ if (baco_program_registers(hwmgr, clean_baco_tbl_iceland,
+ ARRAY_SIZE(clean_baco_tbl_iceland)))
+ return 0;
+ }
+ } else {
+ if (baco_program_registers(hwmgr, exit_baco_tbl,
+ ARRAY_SIZE(exit_baco_tbl))) {
+ if (baco_program_registers(hwmgr, clean_baco_tbl,
+ ARRAY_SIZE(clean_baco_tbl)))
+ return 0;
+ }
+ }
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/tonga_baco.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/tonga_baco.h
new file mode 100644
index 000000000000..5dc16cc8a295
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/tonga_baco.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 __TONGA_BACO_H__
+#define __TONGA_BACO_H__
+#include "smu7_baco.h"
+
+extern int tonga_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_baco.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_baco.c
new file mode 100644
index 000000000000..46bb16c29cf6
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_baco.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "amdgpu.h"
+#include "soc15.h"
+#include "soc15_hw_ip.h"
+#include "vega10_ip_offset.h"
+#include "soc15_common.h"
+#include "vega10_inc.h"
+#include "vega10_ppsmc.h"
+#include "vega10_baco.h"
+
+
+
+static const struct soc15_baco_cmd_entry pre_baco_tbl[] =
+{
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(NBIF, 0, mmBIF_DOORBELL_CNTL), BIF_DOORBELL_CNTL__DOORBELL_MONITOR_EN_MASK, BIF_DOORBELL_CNTL__DOORBELL_MONITOR_EN__SHIFT, 0, 1},
+ {CMD_WRITE, SOC15_REG_ENTRY(NBIF, 0, mmBIF_FB_EN), 0, 0, 0, 0},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(NBIF, 0, mmBACO_CNTL), BACO_CNTL__BACO_DSTATE_BYPASS_MASK, BACO_CNTL__BACO_DSTATE_BYPASS__SHIFT, 0, 1},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(NBIF, 0, mmBACO_CNTL), BACO_CNTL__BACO_RST_INTR_MASK_MASK, BACO_CNTL__BACO_RST_INTR_MASK__SHIFT, 0, 1}
+};
+
+static const struct soc15_baco_cmd_entry enter_baco_tbl[] =
+{
+ {CMD_WAITFOR, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__SOC_DOMAIN_IDLE_MASK, THM_BACO_CNTL__SOC_DOMAIN_IDLE__SHIFT, 0xffffffff, 0x80000000},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(NBIF, 0, mmBACO_CNTL), BACO_CNTL__BACO_EN_MASK, BACO_CNTL__BACO_EN__SHIFT, 0, 1},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(NBIF, 0, mmBACO_CNTL), BACO_CNTL__BACO_BIF_LCLK_SWITCH_MASK, BACO_CNTL__BACO_BIF_LCLK_SWITCH__SHIFT, 0, 1},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(NBIF, 0, mmBACO_CNTL), BACO_CNTL__BACO_DUMMY_EN_MASK, BACO_CNTL__BACO_DUMMY_EN__SHIFT, 0, 1},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_SOC_VDCI_RESET_MASK, THM_BACO_CNTL__BACO_SOC_VDCI_RESET__SHIFT, 0, 1},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_SMNCLK_MUX_MASK, THM_BACO_CNTL__BACO_SMNCLK_MUX__SHIFT,0, 1},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_ISO_EN_MASK, THM_BACO_CNTL__BACO_ISO_EN__SHIFT, 0, 1},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_AEB_ISO_EN_MASK, THM_BACO_CNTL__BACO_AEB_ISO_EN__SHIFT,0, 1},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_ANA_ISO_EN_MASK, THM_BACO_CNTL__BACO_ANA_ISO_EN__SHIFT, 0, 1},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_SOC_REFCLK_OFF_MASK, THM_BACO_CNTL__BACO_SOC_REFCLK_OFF__SHIFT, 0, 1},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(NBIF, 0, mmBACO_CNTL), BACO_CNTL__BACO_POWER_OFF_MASK, BACO_CNTL__BACO_POWER_OFF__SHIFT, 0, 1},
+ {CMD_DELAY_MS, 0, 0, 0, 0, 0, 0, 5, 0},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_RESET_EN_MASK, THM_BACO_CNTL__BACO_RESET_EN__SHIFT, 0, 1},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_PWROKRAW_CNTL_MASK, THM_BACO_CNTL__BACO_PWROKRAW_CNTL__SHIFT, 0, 0},
+ {CMD_WAITFOR, SOC15_REG_ENTRY(NBIF, 0, mmBACO_CNTL), BACO_CNTL__BACO_MODE_MASK, BACO_CNTL__BACO_MODE__SHIFT, 0xffffffff, 0x100}
+};
+
+static const struct soc15_baco_cmd_entry exit_baco_tbl[] =
+{
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(NBIF, 0, mmBACO_CNTL), BACO_CNTL__BACO_POWER_OFF_MASK, BACO_CNTL__BACO_POWER_OFF__SHIFT, 0, 0},
+ {CMD_DELAY_MS, 0, 0, 0, 0, 0, 0, 10,0},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_SOC_REFCLK_OFF_MASK, THM_BACO_CNTL__BACO_SOC_REFCLK_OFF__SHIFT, 0,0},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_ANA_ISO_EN_MASK, THM_BACO_CNTL__BACO_ANA_ISO_EN__SHIFT, 0, 0},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_AEB_ISO_EN_MASK, THM_BACO_CNTL__BACO_AEB_ISO_EN__SHIFT,0, 0},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_ISO_EN_MASK, THM_BACO_CNTL__BACO_ISO_EN__SHIFT, 0, 0},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_PWROKRAW_CNTL_MASK, THM_BACO_CNTL__BACO_PWROKRAW_CNTL__SHIFT, 0, 1},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_SMNCLK_MUX_MASK, THM_BACO_CNTL__BACO_SMNCLK_MUX__SHIFT, 0, 0},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_SOC_VDCI_RESET_MASK, THM_BACO_CNTL__BACO_SOC_VDCI_RESET__SHIFT, 0, 0},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_EXIT_MASK, THM_BACO_CNTL__BACO_EXIT__SHIFT, 0, 1},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_RESET_EN_MASK, THM_BACO_CNTL__BACO_RESET_EN__SHIFT, 0, 0},
+ {CMD_WAITFOR, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_EXIT_MASK, 0, 0xffffffff, 0},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(THM, 0, mmTHM_BACO_CNTL), THM_BACO_CNTL__BACO_SB_AXI_FENCE_MASK, THM_BACO_CNTL__BACO_SB_AXI_FENCE__SHIFT, 0, 0},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(NBIF, 0, mmBACO_CNTL), BACO_CNTL__BACO_DUMMY_EN_MASK, BACO_CNTL__BACO_DUMMY_EN__SHIFT, 0, 0},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(NBIF, 0, mmBACO_CNTL), BACO_CNTL__BACO_BIF_LCLK_SWITCH_MASK ,BACO_CNTL__BACO_BIF_LCLK_SWITCH__SHIFT, 0, 0},
+ {CMD_READMODIFYWRITE, SOC15_REG_ENTRY(NBIF, 0, mmBACO_CNTL), BACO_CNTL__BACO_EN_MASK , BACO_CNTL__BACO_EN__SHIFT, 0,0},
+ {CMD_WAITFOR, SOC15_REG_ENTRY(NBIF, 0, mmBACO_CNTL), BACO_CNTL__BACO_MODE_MASK, 0, 0xffffffff, 0}
+ };
+
+static const struct soc15_baco_cmd_entry clean_baco_tbl[] =
+{
+ {CMD_WRITE, SOC15_REG_ENTRY(NBIF, 0, mmBIOS_SCRATCH_6), 0, 0, 0, 0},
+ {CMD_WRITE, SOC15_REG_ENTRY(NBIF, 0, mmBIOS_SCRATCH_7), 0, 0, 0, 0},
+};
+
+int vega10_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state)
+{
+ enum BACO_STATE cur_state;
+
+ smu9_baco_get_state(hwmgr, &cur_state);
+
+ if (cur_state == state)
+ /* aisc already in the target state */
+ return 0;
+
+ if (state == BACO_STATE_IN) {
+ if (soc15_baco_program_registers(hwmgr, pre_baco_tbl,
+ ARRAY_SIZE(pre_baco_tbl))) {
+ if (smum_send_msg_to_smc(hwmgr, PPSMC_MSG_EnterBaco, NULL))
+ return -EINVAL;
+
+ if (soc15_baco_program_registers(hwmgr, enter_baco_tbl,
+ ARRAY_SIZE(enter_baco_tbl)))
+ return 0;
+ }
+ } else if (state == BACO_STATE_OUT) {
+ /* HW requires at least 20ms between regulator off and on */
+ msleep(20);
+ /* Execute Hardware BACO exit sequence */
+ if (soc15_baco_program_registers(hwmgr, exit_baco_tbl,
+ ARRAY_SIZE(exit_baco_tbl))) {
+ if (soc15_baco_program_registers(hwmgr, clean_baco_tbl,
+ ARRAY_SIZE(clean_baco_tbl)))
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_baco.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_baco.h
new file mode 100644
index 000000000000..96d793f026a5
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_baco.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 __VEGA10_BACO_H__
+#define __VEGA10_BACO_H__
+#include "smu9_baco.h"
+
+extern int vega10_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c
new file mode 100644
index 000000000000..7eada3098ffc
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c
@@ -0,0 +1,5482 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "hwmgr.h"
+#include "amd_powerplay.h"
+#include "hardwaremanager.h"
+#include "ppatomfwctrl.h"
+#include "atomfirmware.h"
+#include "cgs_common.h"
+#include "vega10_powertune.h"
+#include "smu9.h"
+#include "smu9_driver_if.h"
+#include "vega10_inc.h"
+#include "soc15_common.h"
+#include "pppcielanes.h"
+#include "vega10_hwmgr.h"
+#include "vega10_smumgr.h"
+#include "vega10_processpptables.h"
+#include "vega10_pptable.h"
+#include "vega10_thermal.h"
+#include "pp_debug.h"
+#include "amd_pcie_helpers.h"
+#include "ppinterrupt.h"
+#include "pp_overdriver.h"
+#include "pp_thermal.h"
+#include "vega10_baco.h"
+
+#include "smuio/smuio_9_0_offset.h"
+#include "smuio/smuio_9_0_sh_mask.h"
+
+#define HBM_MEMORY_CHANNEL_WIDTH 128
+
+static const uint32_t channel_number[] = {1, 2, 0, 4, 0, 8, 0, 16, 2};
+
+#define mmDF_CS_AON0_DramBaseAddress0 0x0044
+#define mmDF_CS_AON0_DramBaseAddress0_BASE_IDX 0
+
+//DF_CS_AON0_DramBaseAddress0
+#define DF_CS_AON0_DramBaseAddress0__AddrRngVal__SHIFT 0x0
+#define DF_CS_AON0_DramBaseAddress0__LgcyMmioHoleEn__SHIFT 0x1
+#define DF_CS_AON0_DramBaseAddress0__IntLvNumChan__SHIFT 0x4
+#define DF_CS_AON0_DramBaseAddress0__IntLvAddrSel__SHIFT 0x8
+#define DF_CS_AON0_DramBaseAddress0__DramBaseAddr__SHIFT 0xc
+#define DF_CS_AON0_DramBaseAddress0__AddrRngVal_MASK 0x00000001L
+#define DF_CS_AON0_DramBaseAddress0__LgcyMmioHoleEn_MASK 0x00000002L
+#define DF_CS_AON0_DramBaseAddress0__IntLvNumChan_MASK 0x000000F0L
+#define DF_CS_AON0_DramBaseAddress0__IntLvAddrSel_MASK 0x00000700L
+#define DF_CS_AON0_DramBaseAddress0__DramBaseAddr_MASK 0xFFFFF000L
+
+typedef enum {
+ CLK_SMNCLK = 0,
+ CLK_SOCCLK,
+ CLK_MP0CLK,
+ CLK_MP1CLK,
+ CLK_LCLK,
+ CLK_DCEFCLK,
+ CLK_VCLK,
+ CLK_DCLK,
+ CLK_ECLK,
+ CLK_UCLK,
+ CLK_GFXCLK,
+ CLK_COUNT,
+} CLOCK_ID_e;
+
+static const ULONG PhwVega10_Magic = (ULONG)(PHM_VIslands_Magic);
+
+static struct vega10_power_state *cast_phw_vega10_power_state(
+ struct pp_hw_power_state *hw_ps)
+{
+ PP_ASSERT_WITH_CODE((PhwVega10_Magic == hw_ps->magic),
+ "Invalid Powerstate Type!",
+ return NULL;);
+
+ return (struct vega10_power_state *)hw_ps;
+}
+
+static const struct vega10_power_state *cast_const_phw_vega10_power_state(
+ const struct pp_hw_power_state *hw_ps)
+{
+ PP_ASSERT_WITH_CODE((PhwVega10_Magic == hw_ps->magic),
+ "Invalid Powerstate Type!",
+ return NULL;);
+
+ return (const struct vega10_power_state *)hw_ps;
+}
+
+static void vega10_set_default_registry_data(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ data->registry_data.sclk_dpm_key_disabled =
+ hwmgr->feature_mask & PP_SCLK_DPM_MASK ? false : true;
+ data->registry_data.socclk_dpm_key_disabled =
+ hwmgr->feature_mask & PP_SOCCLK_DPM_MASK ? false : true;
+ data->registry_data.mclk_dpm_key_disabled =
+ hwmgr->feature_mask & PP_MCLK_DPM_MASK ? false : true;
+ data->registry_data.pcie_dpm_key_disabled =
+ hwmgr->feature_mask & PP_PCIE_DPM_MASK ? false : true;
+
+ data->registry_data.dcefclk_dpm_key_disabled =
+ hwmgr->feature_mask & PP_DCEFCLK_DPM_MASK ? false : true;
+
+ if (hwmgr->feature_mask & PP_POWER_CONTAINMENT_MASK) {
+ data->registry_data.power_containment_support = 1;
+ data->registry_data.enable_pkg_pwr_tracking_feature = 1;
+ data->registry_data.enable_tdc_limit_feature = 1;
+ }
+
+ data->registry_data.clock_stretcher_support =
+ hwmgr->feature_mask & PP_CLOCK_STRETCH_MASK ? true : false;
+
+ data->registry_data.ulv_support =
+ hwmgr->feature_mask & PP_ULV_MASK ? true : false;
+
+ data->registry_data.sclk_deep_sleep_support =
+ hwmgr->feature_mask & PP_SCLK_DEEP_SLEEP_MASK ? true : false;
+
+ data->registry_data.disable_water_mark = 0;
+
+ data->registry_data.fan_control_support = 1;
+ data->registry_data.thermal_support = 1;
+ data->registry_data.fw_ctf_enabled = 1;
+
+ data->registry_data.avfs_support =
+ hwmgr->feature_mask & PP_AVFS_MASK ? true : false;
+ data->registry_data.led_dpm_enabled = 1;
+
+ data->registry_data.vr0hot_enabled = 1;
+ data->registry_data.vr1hot_enabled = 1;
+ data->registry_data.regulator_hot_gpio_support = 1;
+
+ data->registry_data.didt_support = 1;
+ if (data->registry_data.didt_support) {
+ data->registry_data.didt_mode = 6;
+ data->registry_data.sq_ramping_support = 1;
+ data->registry_data.db_ramping_support = 0;
+ data->registry_data.td_ramping_support = 0;
+ data->registry_data.tcp_ramping_support = 0;
+ data->registry_data.dbr_ramping_support = 0;
+ data->registry_data.edc_didt_support = 1;
+ data->registry_data.gc_didt_support = 0;
+ data->registry_data.psm_didt_support = 0;
+ }
+
+ data->display_voltage_mode = PPVEGA10_VEGA10DISPLAYVOLTAGEMODE_DFLT;
+ data->dcef_clk_quad_eqn_a = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
+ data->dcef_clk_quad_eqn_b = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
+ data->dcef_clk_quad_eqn_c = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
+ data->disp_clk_quad_eqn_a = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
+ data->disp_clk_quad_eqn_b = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
+ data->disp_clk_quad_eqn_c = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
+ data->pixel_clk_quad_eqn_a = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
+ data->pixel_clk_quad_eqn_b = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
+ data->pixel_clk_quad_eqn_c = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
+ data->phy_clk_quad_eqn_a = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
+ data->phy_clk_quad_eqn_b = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
+ data->phy_clk_quad_eqn_c = PPREGKEY_VEGA10QUADRATICEQUATION_DFLT;
+
+ data->gfxclk_average_alpha = PPVEGA10_VEGA10GFXCLKAVERAGEALPHA_DFLT;
+ data->socclk_average_alpha = PPVEGA10_VEGA10SOCCLKAVERAGEALPHA_DFLT;
+ data->uclk_average_alpha = PPVEGA10_VEGA10UCLKCLKAVERAGEALPHA_DFLT;
+ data->gfx_activity_average_alpha = PPVEGA10_VEGA10GFXACTIVITYAVERAGEALPHA_DFLT;
+}
+
+static int vega10_set_features_platform_caps(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)hwmgr->pptable;
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkDeepSleep);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DynamicPatchPowerState);
+
+ if (data->vddci_control == VEGA10_VOLTAGE_CONTROL_NONE)
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ControlVDDCI);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EnableSMU7ThermalManagement);
+
+ if (adev->pg_flags & AMD_PG_SUPPORT_UVD)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UVDPowerGating);
+
+ if (adev->pg_flags & AMD_PG_SUPPORT_VCE)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_VCEPowerGating);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UnTabledHardwareInterface);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_FanSpeedInTableIsRPM);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ODFuzzyFanControlSupport);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DynamicPowerManagement);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SMC);
+
+ /* power tune caps */
+ /* assume disabled */
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PowerContainment);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DiDtSupport);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SQRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DBRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TDRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TCPRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DBRRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DiDtEDCEnable);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_GCEDC);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PSM);
+
+ if (data->registry_data.didt_support) {
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DiDtSupport);
+ if (data->registry_data.sq_ramping_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SQRamping);
+ if (data->registry_data.db_ramping_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DBRamping);
+ if (data->registry_data.td_ramping_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TDRamping);
+ if (data->registry_data.tcp_ramping_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TCPRamping);
+ if (data->registry_data.dbr_ramping_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DBRRamping);
+ if (data->registry_data.edc_didt_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DiDtEDCEnable);
+ if (data->registry_data.gc_didt_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_GCEDC);
+ if (data->registry_data.psm_didt_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_PSM);
+ }
+
+ if (data->registry_data.power_containment_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PowerContainment);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_CAC);
+
+ if (table_info->tdp_table->usClockStretchAmount &&
+ data->registry_data.clock_stretcher_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ClockStretcher);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_RegulatorHot);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UVDDPM);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_VCEDPM);
+
+ return 0;
+}
+
+static int vega10_odn_initial_default_setting(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ struct vega10_odn_dpm_table *odn_table = &(data->odn_dpm_table);
+ struct vega10_odn_vddc_lookup_table *od_lookup_table;
+ struct phm_ppt_v1_voltage_lookup_table *vddc_lookup_table;
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_table[3];
+ struct phm_ppt_v1_clock_voltage_dependency_table *od_table[3];
+ struct pp_atomfwctrl_avfs_parameters avfs_params = {0};
+ uint32_t i;
+ int result;
+
+ result = pp_atomfwctrl_get_avfs_information(hwmgr, &avfs_params);
+ if (!result) {
+ data->odn_dpm_table.max_vddc = avfs_params.ulMaxVddc;
+ data->odn_dpm_table.min_vddc = avfs_params.ulMinVddc;
+ }
+
+ od_lookup_table = &odn_table->vddc_lookup_table;
+ vddc_lookup_table = table_info->vddc_lookup_table;
+
+ for (i = 0; i < vddc_lookup_table->count; i++)
+ od_lookup_table->entries[i].us_vdd = vddc_lookup_table->entries[i].us_vdd;
+
+ od_lookup_table->count = vddc_lookup_table->count;
+
+ dep_table[0] = table_info->vdd_dep_on_sclk;
+ dep_table[1] = table_info->vdd_dep_on_mclk;
+ dep_table[2] = table_info->vdd_dep_on_socclk;
+ od_table[0] = (struct phm_ppt_v1_clock_voltage_dependency_table *)&odn_table->vdd_dep_on_sclk;
+ od_table[1] = (struct phm_ppt_v1_clock_voltage_dependency_table *)&odn_table->vdd_dep_on_mclk;
+ od_table[2] = (struct phm_ppt_v1_clock_voltage_dependency_table *)&odn_table->vdd_dep_on_socclk;
+
+ for (i = 0; i < 3; i++)
+ smu_get_voltage_dependency_table_ppt_v1(dep_table[i], od_table[i]);
+
+ if (odn_table->max_vddc == 0 || odn_table->max_vddc > 2000)
+ odn_table->max_vddc = dep_table[0]->entries[dep_table[0]->count - 1].vddc;
+ if (odn_table->min_vddc == 0 || odn_table->min_vddc > 2000)
+ odn_table->min_vddc = dep_table[0]->entries[0].vddc;
+
+ i = od_table[2]->count - 1;
+ od_table[2]->entries[i].clk = hwmgr->platform_descriptor.overdriveLimit.memoryClock > od_table[2]->entries[i].clk ?
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock :
+ od_table[2]->entries[i].clk;
+ od_table[2]->entries[i].vddc = odn_table->max_vddc > od_table[2]->entries[i].vddc ?
+ odn_table->max_vddc :
+ od_table[2]->entries[i].vddc;
+
+ return 0;
+}
+
+static void vega10_init_dpm_defaults(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ int i;
+ uint32_t sub_vendor_id, hw_revision;
+ uint32_t top32, bottom32;
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ vega10_initialize_power_tune_defaults(hwmgr);
+
+ for (i = 0; i < GNLD_FEATURES_MAX; i++) {
+ data->smu_features[i].smu_feature_id = 0xffff;
+ data->smu_features[i].smu_feature_bitmap = 1 << i;
+ data->smu_features[i].enabled = false;
+ data->smu_features[i].supported = false;
+ }
+
+ data->smu_features[GNLD_DPM_PREFETCHER].smu_feature_id =
+ FEATURE_DPM_PREFETCHER_BIT;
+ data->smu_features[GNLD_DPM_GFXCLK].smu_feature_id =
+ FEATURE_DPM_GFXCLK_BIT;
+ data->smu_features[GNLD_DPM_UCLK].smu_feature_id =
+ FEATURE_DPM_UCLK_BIT;
+ data->smu_features[GNLD_DPM_SOCCLK].smu_feature_id =
+ FEATURE_DPM_SOCCLK_BIT;
+ data->smu_features[GNLD_DPM_UVD].smu_feature_id =
+ FEATURE_DPM_UVD_BIT;
+ data->smu_features[GNLD_DPM_VCE].smu_feature_id =
+ FEATURE_DPM_VCE_BIT;
+ data->smu_features[GNLD_DPM_MP0CLK].smu_feature_id =
+ FEATURE_DPM_MP0CLK_BIT;
+ data->smu_features[GNLD_DPM_LINK].smu_feature_id =
+ FEATURE_DPM_LINK_BIT;
+ data->smu_features[GNLD_DPM_DCEFCLK].smu_feature_id =
+ FEATURE_DPM_DCEFCLK_BIT;
+ data->smu_features[GNLD_ULV].smu_feature_id =
+ FEATURE_ULV_BIT;
+ data->smu_features[GNLD_AVFS].smu_feature_id =
+ FEATURE_AVFS_BIT;
+ data->smu_features[GNLD_DS_GFXCLK].smu_feature_id =
+ FEATURE_DS_GFXCLK_BIT;
+ data->smu_features[GNLD_DS_SOCCLK].smu_feature_id =
+ FEATURE_DS_SOCCLK_BIT;
+ data->smu_features[GNLD_DS_LCLK].smu_feature_id =
+ FEATURE_DS_LCLK_BIT;
+ data->smu_features[GNLD_PPT].smu_feature_id =
+ FEATURE_PPT_BIT;
+ data->smu_features[GNLD_TDC].smu_feature_id =
+ FEATURE_TDC_BIT;
+ data->smu_features[GNLD_THERMAL].smu_feature_id =
+ FEATURE_THERMAL_BIT;
+ data->smu_features[GNLD_GFX_PER_CU_CG].smu_feature_id =
+ FEATURE_GFX_PER_CU_CG_BIT;
+ data->smu_features[GNLD_RM].smu_feature_id =
+ FEATURE_RM_BIT;
+ data->smu_features[GNLD_DS_DCEFCLK].smu_feature_id =
+ FEATURE_DS_DCEFCLK_BIT;
+ data->smu_features[GNLD_ACDC].smu_feature_id =
+ FEATURE_ACDC_BIT;
+ data->smu_features[GNLD_VR0HOT].smu_feature_id =
+ FEATURE_VR0HOT_BIT;
+ data->smu_features[GNLD_VR1HOT].smu_feature_id =
+ FEATURE_VR1HOT_BIT;
+ data->smu_features[GNLD_FW_CTF].smu_feature_id =
+ FEATURE_FW_CTF_BIT;
+ data->smu_features[GNLD_LED_DISPLAY].smu_feature_id =
+ FEATURE_LED_DISPLAY_BIT;
+ data->smu_features[GNLD_FAN_CONTROL].smu_feature_id =
+ FEATURE_FAN_CONTROL_BIT;
+ data->smu_features[GNLD_ACG].smu_feature_id = FEATURE_ACG_BIT;
+ data->smu_features[GNLD_DIDT].smu_feature_id = FEATURE_GFX_EDC_BIT;
+ data->smu_features[GNLD_PCC_LIMIT].smu_feature_id = FEATURE_PCC_LIMIT_CONTROL_BIT;
+
+ if (!data->registry_data.prefetcher_dpm_key_disabled)
+ data->smu_features[GNLD_DPM_PREFETCHER].supported = true;
+
+ if (!data->registry_data.sclk_dpm_key_disabled)
+ data->smu_features[GNLD_DPM_GFXCLK].supported = true;
+
+ if (!data->registry_data.mclk_dpm_key_disabled)
+ data->smu_features[GNLD_DPM_UCLK].supported = true;
+
+ if (!data->registry_data.socclk_dpm_key_disabled)
+ data->smu_features[GNLD_DPM_SOCCLK].supported = true;
+
+ if (PP_CAP(PHM_PlatformCaps_UVDDPM))
+ data->smu_features[GNLD_DPM_UVD].supported = true;
+
+ if (PP_CAP(PHM_PlatformCaps_VCEDPM))
+ data->smu_features[GNLD_DPM_VCE].supported = true;
+
+ if (!data->registry_data.pcie_dpm_key_disabled)
+ data->smu_features[GNLD_DPM_LINK].supported = true;
+
+ if (!data->registry_data.dcefclk_dpm_key_disabled)
+ data->smu_features[GNLD_DPM_DCEFCLK].supported = true;
+
+ if (PP_CAP(PHM_PlatformCaps_SclkDeepSleep) &&
+ data->registry_data.sclk_deep_sleep_support) {
+ data->smu_features[GNLD_DS_GFXCLK].supported = true;
+ data->smu_features[GNLD_DS_SOCCLK].supported = true;
+ data->smu_features[GNLD_DS_LCLK].supported = true;
+ data->smu_features[GNLD_DS_DCEFCLK].supported = true;
+ }
+
+ if (data->registry_data.enable_pkg_pwr_tracking_feature)
+ data->smu_features[GNLD_PPT].supported = true;
+
+ if (data->registry_data.enable_tdc_limit_feature)
+ data->smu_features[GNLD_TDC].supported = true;
+
+ if (data->registry_data.thermal_support)
+ data->smu_features[GNLD_THERMAL].supported = true;
+
+ if (data->registry_data.fan_control_support)
+ data->smu_features[GNLD_FAN_CONTROL].supported = true;
+
+ if (data->registry_data.fw_ctf_enabled)
+ data->smu_features[GNLD_FW_CTF].supported = true;
+
+ if (data->registry_data.avfs_support)
+ data->smu_features[GNLD_AVFS].supported = true;
+
+ if (data->registry_data.led_dpm_enabled)
+ data->smu_features[GNLD_LED_DISPLAY].supported = true;
+
+ if (data->registry_data.vr1hot_enabled)
+ data->smu_features[GNLD_VR1HOT].supported = true;
+
+ if (data->registry_data.vr0hot_enabled)
+ data->smu_features[GNLD_VR0HOT].supported = true;
+
+ smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_GetSmuVersion,
+ &hwmgr->smu_version);
+ /* ACG firmware has major version 5 */
+ if ((hwmgr->smu_version & 0xff000000) == 0x5000000)
+ data->smu_features[GNLD_ACG].supported = true;
+ if (data->registry_data.didt_support)
+ data->smu_features[GNLD_DIDT].supported = true;
+
+ hw_revision = adev->pdev->revision;
+ sub_vendor_id = adev->pdev->subsystem_vendor;
+
+ if ((hwmgr->chip_id == 0x6862 ||
+ hwmgr->chip_id == 0x6861 ||
+ hwmgr->chip_id == 0x6868) &&
+ (hw_revision == 0) &&
+ (sub_vendor_id != 0x1002))
+ data->smu_features[GNLD_PCC_LIMIT].supported = true;
+
+ /* Get the SN to turn into a Unique ID */
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ReadSerialNumTop32, &top32);
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ReadSerialNumBottom32, &bottom32);
+
+ adev->unique_id = ((uint64_t)bottom32 << 32) | top32;
+}
+
+#ifdef PPLIB_VEGA10_EVV_SUPPORT
+static int vega10_get_socclk_for_voltage_evv(struct pp_hwmgr *hwmgr,
+ phm_ppt_v1_voltage_lookup_table *lookup_table,
+ uint16_t virtual_voltage_id, int32_t *socclk)
+{
+ uint8_t entry_id;
+ uint8_t voltage_id;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+
+ PP_ASSERT_WITH_CODE(lookup_table->count != 0,
+ "Lookup table is empty",
+ return -EINVAL);
+
+ /* search for leakage voltage ID 0xff01 ~ 0xff08 and sclk */
+ for (entry_id = 0; entry_id < table_info->vdd_dep_on_sclk->count; entry_id++) {
+ voltage_id = table_info->vdd_dep_on_socclk->entries[entry_id].vddInd;
+ if (lookup_table->entries[voltage_id].us_vdd == virtual_voltage_id)
+ break;
+ }
+
+ PP_ASSERT_WITH_CODE(entry_id < table_info->vdd_dep_on_socclk->count,
+ "Can't find requested voltage id in vdd_dep_on_socclk table!",
+ return -EINVAL);
+
+ *socclk = table_info->vdd_dep_on_socclk->entries[entry_id].clk;
+
+ return 0;
+}
+
+#define ATOM_VIRTUAL_VOLTAGE_ID0 0xff01
+/**
+* Get Leakage VDDC based on leakage ID.
+*
+* @param hwmgr the address of the powerplay hardware manager.
+* @return always 0.
+*/
+static int vega10_get_evv_voltages(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ uint16_t vv_id;
+ uint32_t vddc = 0;
+ uint16_t i, j;
+ uint32_t sclk = 0;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)hwmgr->pptable;
+ struct phm_ppt_v1_clock_voltage_dependency_table *socclk_table =
+ table_info->vdd_dep_on_socclk;
+ int result;
+
+ for (i = 0; i < VEGA10_MAX_LEAKAGE_COUNT; i++) {
+ vv_id = ATOM_VIRTUAL_VOLTAGE_ID0 + i;
+
+ if (!vega10_get_socclk_for_voltage_evv(hwmgr,
+ table_info->vddc_lookup_table, vv_id, &sclk)) {
+ if (PP_CAP(PHM_PlatformCaps_ClockStretcher)) {
+ for (j = 1; j < socclk_table->count; j++) {
+ if (socclk_table->entries[j].clk == sclk &&
+ socclk_table->entries[j].cks_enable == 0) {
+ sclk += 5000;
+ break;
+ }
+ }
+ }
+
+ PP_ASSERT_WITH_CODE(!atomctrl_get_voltage_evv_on_sclk_ai(hwmgr,
+ VOLTAGE_TYPE_VDDC, sclk, vv_id, &vddc),
+ "Error retrieving EVV voltage value!",
+ continue);
+
+
+ /* need to make sure vddc is less than 2v or else, it could burn the ASIC. */
+ PP_ASSERT_WITH_CODE((vddc < 2000 && vddc != 0),
+ "Invalid VDDC value", result = -EINVAL;);
+
+ /* the voltage should not be zero nor equal to leakage ID */
+ if (vddc != 0 && vddc != vv_id) {
+ data->vddc_leakage.actual_voltage[data->vddc_leakage.count] = (uint16_t)(vddc/100);
+ data->vddc_leakage.leakage_id[data->vddc_leakage.count] = vv_id;
+ data->vddc_leakage.count++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Change virtual leakage voltage to actual value.
+ *
+ * @param hwmgr the address of the powerplay hardware manager.
+ * @param pointer to changing voltage
+ * @param pointer to leakage table
+ */
+static void vega10_patch_with_vdd_leakage(struct pp_hwmgr *hwmgr,
+ uint16_t *voltage, struct vega10_leakage_voltage *leakage_table)
+{
+ uint32_t index;
+
+ /* search for leakage voltage ID 0xff01 ~ 0xff08 */
+ for (index = 0; index < leakage_table->count; index++) {
+ /* if this voltage matches a leakage voltage ID */
+ /* patch with actual leakage voltage */
+ if (leakage_table->leakage_id[index] == *voltage) {
+ *voltage = leakage_table->actual_voltage[index];
+ break;
+ }
+ }
+
+ if (*voltage > ATOM_VIRTUAL_VOLTAGE_ID0)
+ pr_info("Voltage value looks like a Leakage ID but it's not patched\n");
+}
+
+/**
+* Patch voltage lookup table by EVV leakages.
+*
+* @param hwmgr the address of the powerplay hardware manager.
+* @param pointer to voltage lookup table
+* @param pointer to leakage table
+* @return always 0
+*/
+static int vega10_patch_lookup_table_with_leakage(struct pp_hwmgr *hwmgr,
+ phm_ppt_v1_voltage_lookup_table *lookup_table,
+ struct vega10_leakage_voltage *leakage_table)
+{
+ uint32_t i;
+
+ for (i = 0; i < lookup_table->count; i++)
+ vega10_patch_with_vdd_leakage(hwmgr,
+ &lookup_table->entries[i].us_vdd, leakage_table);
+
+ return 0;
+}
+
+static int vega10_patch_clock_voltage_limits_with_vddc_leakage(
+ struct pp_hwmgr *hwmgr, struct vega10_leakage_voltage *leakage_table,
+ uint16_t *vddc)
+{
+ vega10_patch_with_vdd_leakage(hwmgr, (uint16_t *)vddc, leakage_table);
+
+ return 0;
+}
+#endif
+
+static int vega10_patch_voltage_dependency_tables_with_lookup_table(
+ struct pp_hwmgr *hwmgr)
+{
+ uint8_t entry_id, voltage_id;
+ unsigned i;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+ table_info->mm_dep_table;
+ struct phm_ppt_v1_clock_voltage_dependency_table *mclk_table =
+ table_info->vdd_dep_on_mclk;
+
+ for (i = 0; i < 6; i++) {
+ struct phm_ppt_v1_clock_voltage_dependency_table *vdt;
+ switch (i) {
+ case 0: vdt = table_info->vdd_dep_on_socclk; break;
+ case 1: vdt = table_info->vdd_dep_on_sclk; break;
+ case 2: vdt = table_info->vdd_dep_on_dcefclk; break;
+ case 3: vdt = table_info->vdd_dep_on_pixclk; break;
+ case 4: vdt = table_info->vdd_dep_on_dispclk; break;
+ case 5: vdt = table_info->vdd_dep_on_phyclk; break;
+ }
+
+ for (entry_id = 0; entry_id < vdt->count; entry_id++) {
+ voltage_id = vdt->entries[entry_id].vddInd;
+ vdt->entries[entry_id].vddc =
+ table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
+ }
+ }
+
+ for (entry_id = 0; entry_id < mm_table->count; ++entry_id) {
+ voltage_id = mm_table->entries[entry_id].vddcInd;
+ mm_table->entries[entry_id].vddc =
+ table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
+ }
+
+ for (entry_id = 0; entry_id < mclk_table->count; ++entry_id) {
+ voltage_id = mclk_table->entries[entry_id].vddInd;
+ mclk_table->entries[entry_id].vddc =
+ table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
+ voltage_id = mclk_table->entries[entry_id].vddciInd;
+ mclk_table->entries[entry_id].vddci =
+ table_info->vddci_lookup_table->entries[voltage_id].us_vdd;
+ voltage_id = mclk_table->entries[entry_id].mvddInd;
+ mclk_table->entries[entry_id].mvdd =
+ table_info->vddmem_lookup_table->entries[voltage_id].us_vdd;
+ }
+
+
+ return 0;
+
+}
+
+static int vega10_sort_lookup_table(struct pp_hwmgr *hwmgr,
+ struct phm_ppt_v1_voltage_lookup_table *lookup_table)
+{
+ uint32_t table_size, i, j;
+
+ PP_ASSERT_WITH_CODE(lookup_table && lookup_table->count,
+ "Lookup table is empty", return -EINVAL);
+
+ table_size = lookup_table->count;
+
+ /* Sorting voltages */
+ for (i = 0; i < table_size - 1; i++) {
+ for (j = i + 1; j > 0; j--) {
+ if (lookup_table->entries[j].us_vdd <
+ lookup_table->entries[j - 1].us_vdd) {
+ swap(lookup_table->entries[j - 1],
+ lookup_table->entries[j]);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int vega10_complete_dependency_tables(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+ int tmp_result;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+#ifdef PPLIB_VEGA10_EVV_SUPPORT
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ tmp_result = vega10_patch_lookup_table_with_leakage(hwmgr,
+ table_info->vddc_lookup_table, &(data->vddc_leakage));
+ if (tmp_result)
+ result = tmp_result;
+
+ tmp_result = vega10_patch_clock_voltage_limits_with_vddc_leakage(hwmgr,
+ &(data->vddc_leakage), &table_info->max_clock_voltage_on_dc.vddc);
+ if (tmp_result)
+ result = tmp_result;
+#endif
+
+ tmp_result = vega10_patch_voltage_dependency_tables_with_lookup_table(hwmgr);
+ if (tmp_result)
+ result = tmp_result;
+
+ tmp_result = vega10_sort_lookup_table(hwmgr, table_info->vddc_lookup_table);
+ if (tmp_result)
+ result = tmp_result;
+
+ return result;
+}
+
+static int vega10_set_private_data_based_on_pptable(struct pp_hwmgr *hwmgr)
+{
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_clock_voltage_dependency_table *allowed_sclk_vdd_table =
+ table_info->vdd_dep_on_socclk;
+ struct phm_ppt_v1_clock_voltage_dependency_table *allowed_mclk_vdd_table =
+ table_info->vdd_dep_on_mclk;
+
+ PP_ASSERT_WITH_CODE(allowed_sclk_vdd_table,
+ "VDD dependency on SCLK table is missing. This table is mandatory", return -EINVAL);
+ PP_ASSERT_WITH_CODE(allowed_sclk_vdd_table->count >= 1,
+ "VDD dependency on SCLK table is empty. This table is mandatory", return -EINVAL);
+
+ PP_ASSERT_WITH_CODE(allowed_mclk_vdd_table,
+ "VDD dependency on MCLK table is missing. This table is mandatory", return -EINVAL);
+ PP_ASSERT_WITH_CODE(allowed_mclk_vdd_table->count >= 1,
+ "VDD dependency on MCLK table is empty. This table is mandatory", return -EINVAL);
+
+ table_info->max_clock_voltage_on_ac.sclk =
+ allowed_sclk_vdd_table->entries[allowed_sclk_vdd_table->count - 1].clk;
+ table_info->max_clock_voltage_on_ac.mclk =
+ allowed_mclk_vdd_table->entries[allowed_mclk_vdd_table->count - 1].clk;
+ table_info->max_clock_voltage_on_ac.vddc =
+ allowed_sclk_vdd_table->entries[allowed_sclk_vdd_table->count - 1].vddc;
+ table_info->max_clock_voltage_on_ac.vddci =
+ allowed_mclk_vdd_table->entries[allowed_mclk_vdd_table->count - 1].vddci;
+
+ hwmgr->dyn_state.max_clock_voltage_on_ac.sclk =
+ table_info->max_clock_voltage_on_ac.sclk;
+ hwmgr->dyn_state.max_clock_voltage_on_ac.mclk =
+ table_info->max_clock_voltage_on_ac.mclk;
+ hwmgr->dyn_state.max_clock_voltage_on_ac.vddc =
+ table_info->max_clock_voltage_on_ac.vddc;
+ hwmgr->dyn_state.max_clock_voltage_on_ac.vddci =
+ table_info->max_clock_voltage_on_ac.vddci;
+
+ return 0;
+}
+
+static int vega10_hwmgr_backend_fini(struct pp_hwmgr *hwmgr)
+{
+ kfree(hwmgr->dyn_state.vddc_dep_on_dal_pwrl);
+ hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL;
+
+ kfree(hwmgr->backend);
+ hwmgr->backend = NULL;
+
+ return 0;
+}
+
+static int vega10_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+ struct vega10_hwmgr *data;
+ uint32_t config_telemetry = 0;
+ struct pp_atomfwctrl_voltage_table vol_table;
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ data = kzalloc(sizeof(struct vega10_hwmgr), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ hwmgr->backend = data;
+
+ hwmgr->workload_mask = 1 << hwmgr->workload_prority[PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT];
+ hwmgr->power_profile_mode = PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT;
+ hwmgr->default_power_profile_mode = PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT;
+
+ vega10_set_default_registry_data(hwmgr);
+ data->disable_dpm_mask = 0xff;
+
+ /* need to set voltage control types before EVV patching */
+ data->vddc_control = VEGA10_VOLTAGE_CONTROL_NONE;
+ data->mvdd_control = VEGA10_VOLTAGE_CONTROL_NONE;
+ data->vddci_control = VEGA10_VOLTAGE_CONTROL_NONE;
+
+ /* VDDCR_SOC */
+ if (pp_atomfwctrl_is_voltage_controlled_by_gpio_v4(hwmgr,
+ VOLTAGE_TYPE_VDDC, VOLTAGE_OBJ_SVID2)) {
+ if (!pp_atomfwctrl_get_voltage_table_v4(hwmgr,
+ VOLTAGE_TYPE_VDDC, VOLTAGE_OBJ_SVID2,
+ &vol_table)) {
+ config_telemetry = ((vol_table.telemetry_slope << 8) & 0xff00) |
+ (vol_table.telemetry_offset & 0xff);
+ data->vddc_control = VEGA10_VOLTAGE_CONTROL_BY_SVID2;
+ }
+ } else {
+ kfree(hwmgr->backend);
+ hwmgr->backend = NULL;
+ PP_ASSERT_WITH_CODE(false,
+ "VDDCR_SOC is not SVID2!",
+ return -1);
+ }
+
+ /* MVDDC */
+ if (pp_atomfwctrl_is_voltage_controlled_by_gpio_v4(hwmgr,
+ VOLTAGE_TYPE_MVDDC, VOLTAGE_OBJ_SVID2)) {
+ if (!pp_atomfwctrl_get_voltage_table_v4(hwmgr,
+ VOLTAGE_TYPE_MVDDC, VOLTAGE_OBJ_SVID2,
+ &vol_table)) {
+ config_telemetry |=
+ ((vol_table.telemetry_slope << 24) & 0xff000000) |
+ ((vol_table.telemetry_offset << 16) & 0xff0000);
+ data->mvdd_control = VEGA10_VOLTAGE_CONTROL_BY_SVID2;
+ }
+ }
+
+ /* VDDCI_MEM */
+ if (PP_CAP(PHM_PlatformCaps_ControlVDDCI)) {
+ if (pp_atomfwctrl_is_voltage_controlled_by_gpio_v4(hwmgr,
+ VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_GPIO_LUT))
+ data->vddci_control = VEGA10_VOLTAGE_CONTROL_BY_GPIO;
+ }
+
+ data->config_telemetry = config_telemetry;
+
+ vega10_set_features_platform_caps(hwmgr);
+
+ vega10_init_dpm_defaults(hwmgr);
+
+#ifdef PPLIB_VEGA10_EVV_SUPPORT
+ /* Get leakage voltage based on leakage ID. */
+ PP_ASSERT_WITH_CODE(!vega10_get_evv_voltages(hwmgr),
+ "Get EVV Voltage Failed. Abort Driver loading!",
+ return -1);
+#endif
+
+ /* Patch our voltage dependency table with actual leakage voltage
+ * We need to perform leakage translation before it's used by other functions
+ */
+ vega10_complete_dependency_tables(hwmgr);
+
+ /* Parse pptable data read from VBIOS */
+ vega10_set_private_data_based_on_pptable(hwmgr);
+
+ data->is_tlu_enabled = false;
+
+ hwmgr->platform_descriptor.hardwareActivityPerformanceLevels =
+ VEGA10_MAX_HARDWARE_POWERLEVELS;
+ hwmgr->platform_descriptor.hardwarePerformanceLevels = 2;
+ hwmgr->platform_descriptor.minimumClocksReductionPercentage = 50;
+
+ hwmgr->platform_descriptor.vbiosInterruptId = 0x20000400; /* IRQ_SOURCE1_SW_INT */
+ /* The true clock step depends on the frequency, typically 4.5 or 9 MHz. Here we use 5. */
+ hwmgr->platform_descriptor.clockStep.engineClock = 500;
+ hwmgr->platform_descriptor.clockStep.memoryClock = 500;
+
+ data->total_active_cus = adev->gfx.cu_info.number;
+ if (!hwmgr->not_vf)
+ return result;
+
+ /* Setup default Overdrive Fan control settings */
+ data->odn_fan_table.target_fan_speed =
+ hwmgr->thermal_controller.advanceFanControlParameters.usMaxFanRPM;
+ data->odn_fan_table.target_temperature =
+ hwmgr->thermal_controller.
+ advanceFanControlParameters.ucTargetTemperature;
+ data->odn_fan_table.min_performance_clock =
+ hwmgr->thermal_controller.advanceFanControlParameters.
+ ulMinFanSCLKAcousticLimit;
+ data->odn_fan_table.min_fan_limit =
+ hwmgr->thermal_controller.
+ advanceFanControlParameters.usFanPWMMinLimit *
+ hwmgr->thermal_controller.fanInfo.ulMaxRPM / 100;
+
+ data->mem_channels = (RREG32_SOC15(DF, 0, mmDF_CS_AON0_DramBaseAddress0) &
+ DF_CS_AON0_DramBaseAddress0__IntLvNumChan_MASK) >>
+ DF_CS_AON0_DramBaseAddress0__IntLvNumChan__SHIFT;
+ PP_ASSERT_WITH_CODE(data->mem_channels < ARRAY_SIZE(channel_number),
+ "Mem Channel Index Exceeded maximum!",
+ return -EINVAL);
+
+ return result;
+}
+
+static int vega10_init_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ data->low_sclk_interrupt_threshold = 0;
+
+ return 0;
+}
+
+static int vega10_setup_dpm_led_config(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+
+ struct pp_atomfwctrl_voltage_table table;
+ uint8_t i, j;
+ uint32_t mask = 0;
+ uint32_t tmp;
+ int32_t ret = 0;
+
+ ret = pp_atomfwctrl_get_voltage_table_v4(hwmgr, VOLTAGE_TYPE_LEDDPM,
+ VOLTAGE_OBJ_GPIO_LUT, &table);
+
+ if (!ret) {
+ tmp = table.mask_low;
+ for (i = 0, j = 0; i < 32; i++) {
+ if (tmp & 1) {
+ mask |= (uint32_t)(i << (8 * j));
+ if (++j >= 3)
+ break;
+ }
+ tmp >>= 1;
+ }
+ }
+
+ pp_table->LedPin0 = (uint8_t)(mask & 0xff);
+ pp_table->LedPin1 = (uint8_t)((mask >> 8) & 0xff);
+ pp_table->LedPin2 = (uint8_t)((mask >> 16) & 0xff);
+ return 0;
+}
+
+static int vega10_setup_asic_task(struct pp_hwmgr *hwmgr)
+{
+ if (!hwmgr->not_vf)
+ return 0;
+
+ PP_ASSERT_WITH_CODE(!vega10_init_sclk_threshold(hwmgr),
+ "Failed to init sclk threshold!",
+ return -EINVAL);
+
+ PP_ASSERT_WITH_CODE(!vega10_setup_dpm_led_config(hwmgr),
+ "Failed to set up led dpm config!",
+ return -EINVAL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_NumOfDisplays,
+ 0,
+ NULL);
+
+ return 0;
+}
+
+/**
+* Remove repeated voltage values and create table with unique values.
+*
+* @param hwmgr the address of the powerplay hardware manager.
+* @param vol_table the pointer to changing voltage table
+* @return 0 in success
+*/
+
+static int vega10_trim_voltage_table(struct pp_hwmgr *hwmgr,
+ struct pp_atomfwctrl_voltage_table *vol_table)
+{
+ uint32_t i, j;
+ uint16_t vvalue;
+ bool found = false;
+ struct pp_atomfwctrl_voltage_table *table;
+
+ PP_ASSERT_WITH_CODE(vol_table,
+ "Voltage Table empty.", return -EINVAL);
+ table = kzalloc(sizeof(struct pp_atomfwctrl_voltage_table),
+ GFP_KERNEL);
+
+ if (!table)
+ return -ENOMEM;
+
+ table->mask_low = vol_table->mask_low;
+ table->phase_delay = vol_table->phase_delay;
+
+ for (i = 0; i < vol_table->count; i++) {
+ vvalue = vol_table->entries[i].value;
+ found = false;
+
+ for (j = 0; j < table->count; j++) {
+ if (vvalue == table->entries[j].value) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ table->entries[table->count].value = vvalue;
+ table->entries[table->count].smio_low =
+ vol_table->entries[i].smio_low;
+ table->count++;
+ }
+ }
+
+ memcpy(vol_table, table, sizeof(struct pp_atomfwctrl_voltage_table));
+ kfree(table);
+
+ return 0;
+}
+
+static int vega10_get_mvdd_voltage_table(struct pp_hwmgr *hwmgr,
+ phm_ppt_v1_clock_voltage_dependency_table *dep_table,
+ struct pp_atomfwctrl_voltage_table *vol_table)
+{
+ int i;
+
+ PP_ASSERT_WITH_CODE(dep_table->count,
+ "Voltage Dependency Table empty.",
+ return -EINVAL);
+
+ vol_table->mask_low = 0;
+ vol_table->phase_delay = 0;
+ vol_table->count = dep_table->count;
+
+ for (i = 0; i < vol_table->count; i++) {
+ vol_table->entries[i].value = dep_table->entries[i].mvdd;
+ vol_table->entries[i].smio_low = 0;
+ }
+
+ PP_ASSERT_WITH_CODE(!vega10_trim_voltage_table(hwmgr,
+ vol_table),
+ "Failed to trim MVDD Table!",
+ return -1);
+
+ return 0;
+}
+
+static int vega10_get_vddci_voltage_table(struct pp_hwmgr *hwmgr,
+ phm_ppt_v1_clock_voltage_dependency_table *dep_table,
+ struct pp_atomfwctrl_voltage_table *vol_table)
+{
+ uint32_t i;
+
+ PP_ASSERT_WITH_CODE(dep_table->count,
+ "Voltage Dependency Table empty.",
+ return -EINVAL);
+
+ vol_table->mask_low = 0;
+ vol_table->phase_delay = 0;
+ vol_table->count = dep_table->count;
+
+ for (i = 0; i < dep_table->count; i++) {
+ vol_table->entries[i].value = dep_table->entries[i].vddci;
+ vol_table->entries[i].smio_low = 0;
+ }
+
+ PP_ASSERT_WITH_CODE(!vega10_trim_voltage_table(hwmgr, vol_table),
+ "Failed to trim VDDCI table.",
+ return -1);
+
+ return 0;
+}
+
+static int vega10_get_vdd_voltage_table(struct pp_hwmgr *hwmgr,
+ phm_ppt_v1_clock_voltage_dependency_table *dep_table,
+ struct pp_atomfwctrl_voltage_table *vol_table)
+{
+ int i;
+
+ PP_ASSERT_WITH_CODE(dep_table->count,
+ "Voltage Dependency Table empty.",
+ return -EINVAL);
+
+ vol_table->mask_low = 0;
+ vol_table->phase_delay = 0;
+ vol_table->count = dep_table->count;
+
+ for (i = 0; i < vol_table->count; i++) {
+ vol_table->entries[i].value = dep_table->entries[i].vddc;
+ vol_table->entries[i].smio_low = 0;
+ }
+
+ return 0;
+}
+
+/* ---- Voltage Tables ----
+ * If the voltage table would be bigger than
+ * what will fit into the state table on
+ * the SMC keep only the higher entries.
+ */
+static void vega10_trim_voltage_table_to_fit_state_table(
+ struct pp_hwmgr *hwmgr,
+ uint32_t max_vol_steps,
+ struct pp_atomfwctrl_voltage_table *vol_table)
+{
+ unsigned int i, diff;
+
+ if (vol_table->count <= max_vol_steps)
+ return;
+
+ diff = vol_table->count - max_vol_steps;
+
+ for (i = 0; i < max_vol_steps; i++)
+ vol_table->entries[i] = vol_table->entries[i + diff];
+
+ vol_table->count = max_vol_steps;
+}
+
+/**
+* Create Voltage Tables.
+*
+* @param hwmgr the address of the powerplay hardware manager.
+* @return always 0
+*/
+static int vega10_construct_voltage_tables(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)hwmgr->pptable;
+ int result;
+
+ if (data->mvdd_control == VEGA10_VOLTAGE_CONTROL_BY_SVID2 ||
+ data->mvdd_control == VEGA10_VOLTAGE_CONTROL_NONE) {
+ result = vega10_get_mvdd_voltage_table(hwmgr,
+ table_info->vdd_dep_on_mclk,
+ &(data->mvdd_voltage_table));
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to retrieve MVDDC table!",
+ return result);
+ }
+
+ if (data->vddci_control == VEGA10_VOLTAGE_CONTROL_NONE) {
+ result = vega10_get_vddci_voltage_table(hwmgr,
+ table_info->vdd_dep_on_mclk,
+ &(data->vddci_voltage_table));
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to retrieve VDDCI_MEM table!",
+ return result);
+ }
+
+ if (data->vddc_control == VEGA10_VOLTAGE_CONTROL_BY_SVID2 ||
+ data->vddc_control == VEGA10_VOLTAGE_CONTROL_NONE) {
+ result = vega10_get_vdd_voltage_table(hwmgr,
+ table_info->vdd_dep_on_sclk,
+ &(data->vddc_voltage_table));
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to retrieve VDDCR_SOC table!",
+ return result);
+ }
+
+ PP_ASSERT_WITH_CODE(data->vddc_voltage_table.count <= 16,
+ "Too many voltage values for VDDC. Trimming to fit state table.",
+ vega10_trim_voltage_table_to_fit_state_table(hwmgr,
+ 16, &(data->vddc_voltage_table)));
+
+ PP_ASSERT_WITH_CODE(data->vddci_voltage_table.count <= 16,
+ "Too many voltage values for VDDCI. Trimming to fit state table.",
+ vega10_trim_voltage_table_to_fit_state_table(hwmgr,
+ 16, &(data->vddci_voltage_table)));
+
+ PP_ASSERT_WITH_CODE(data->mvdd_voltage_table.count <= 16,
+ "Too many voltage values for MVDD. Trimming to fit state table.",
+ vega10_trim_voltage_table_to_fit_state_table(hwmgr,
+ 16, &(data->mvdd_voltage_table)));
+
+
+ return 0;
+}
+
+/*
+ * @fn vega10_init_dpm_state
+ * @brief Function to initialize all Soft Min/Max and Hard Min/Max to 0xff.
+ *
+ * @param dpm_state - the address of the DPM Table to initiailize.
+ * @return None.
+ */
+static void vega10_init_dpm_state(struct vega10_dpm_state *dpm_state)
+{
+ dpm_state->soft_min_level = 0xff;
+ dpm_state->soft_max_level = 0xff;
+ dpm_state->hard_min_level = 0xff;
+ dpm_state->hard_max_level = 0xff;
+}
+
+static void vega10_setup_default_single_dpm_table(struct pp_hwmgr *hwmgr,
+ struct vega10_single_dpm_table *dpm_table,
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_table)
+{
+ int i;
+
+ dpm_table->count = 0;
+
+ for (i = 0; i < dep_table->count; i++) {
+ if (i == 0 || dpm_table->dpm_levels[dpm_table->count - 1].value <=
+ dep_table->entries[i].clk) {
+ dpm_table->dpm_levels[dpm_table->count].value =
+ dep_table->entries[i].clk;
+ dpm_table->dpm_levels[dpm_table->count].enabled = true;
+ dpm_table->count++;
+ }
+ }
+}
+static int vega10_setup_default_pcie_table(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct vega10_pcie_table *pcie_table = &(data->dpm_table.pcie_table);
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_pcie_table *bios_pcie_table =
+ table_info->pcie_table;
+ uint32_t i;
+
+ PP_ASSERT_WITH_CODE(bios_pcie_table->count,
+ "Incorrect number of PCIE States from VBIOS!",
+ return -1);
+
+ for (i = 0; i < NUM_LINK_LEVELS; i++) {
+ if (data->registry_data.pcieSpeedOverride)
+ pcie_table->pcie_gen[i] =
+ data->registry_data.pcieSpeedOverride;
+ else
+ pcie_table->pcie_gen[i] =
+ bios_pcie_table->entries[i].gen_speed;
+
+ if (data->registry_data.pcieLaneOverride)
+ pcie_table->pcie_lane[i] = (uint8_t)encode_pcie_lane_width(
+ data->registry_data.pcieLaneOverride);
+ else
+ pcie_table->pcie_lane[i] = (uint8_t)encode_pcie_lane_width(
+ bios_pcie_table->entries[i].lane_width);
+ if (data->registry_data.pcieClockOverride)
+ pcie_table->lclk[i] =
+ data->registry_data.pcieClockOverride;
+ else
+ pcie_table->lclk[i] =
+ bios_pcie_table->entries[i].pcie_sclk;
+ }
+
+ pcie_table->count = NUM_LINK_LEVELS;
+
+ return 0;
+}
+
+/*
+ * This function is to initialize all DPM state tables
+ * for SMU based on the dependency table.
+ * Dynamic state patching function will then trim these
+ * state tables to the allowed range based
+ * on the power policy or external client requests,
+ * such as UVD request, etc.
+ */
+static int vega10_setup_default_dpm_tables(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ struct vega10_single_dpm_table *dpm_table;
+ uint32_t i;
+
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_soc_table =
+ table_info->vdd_dep_on_socclk;
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_gfx_table =
+ table_info->vdd_dep_on_sclk;
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table =
+ table_info->vdd_dep_on_mclk;
+ struct phm_ppt_v1_mm_clock_voltage_dependency_table *dep_mm_table =
+ table_info->mm_dep_table;
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_dcef_table =
+ table_info->vdd_dep_on_dcefclk;
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_pix_table =
+ table_info->vdd_dep_on_pixclk;
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_disp_table =
+ table_info->vdd_dep_on_dispclk;
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_phy_table =
+ table_info->vdd_dep_on_phyclk;
+
+ PP_ASSERT_WITH_CODE(dep_soc_table,
+ "SOCCLK dependency table is missing. This table is mandatory",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(dep_soc_table->count >= 1,
+ "SOCCLK dependency table is empty. This table is mandatory",
+ return -EINVAL);
+
+ PP_ASSERT_WITH_CODE(dep_gfx_table,
+ "GFXCLK dependency table is missing. This table is mandatory",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(dep_gfx_table->count >= 1,
+ "GFXCLK dependency table is empty. This table is mandatory",
+ return -EINVAL);
+
+ PP_ASSERT_WITH_CODE(dep_mclk_table,
+ "MCLK dependency table is missing. This table is mandatory",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(dep_mclk_table->count >= 1,
+ "MCLK dependency table has to have is missing. This table is mandatory",
+ return -EINVAL);
+
+ /* Initialize Sclk DPM table based on allow Sclk values */
+ dpm_table = &(data->dpm_table.soc_table);
+ vega10_setup_default_single_dpm_table(hwmgr,
+ dpm_table,
+ dep_soc_table);
+
+ vega10_init_dpm_state(&(dpm_table->dpm_state));
+
+ dpm_table = &(data->dpm_table.gfx_table);
+ vega10_setup_default_single_dpm_table(hwmgr,
+ dpm_table,
+ dep_gfx_table);
+ if (hwmgr->platform_descriptor.overdriveLimit.engineClock == 0)
+ hwmgr->platform_descriptor.overdriveLimit.engineClock =
+ dpm_table->dpm_levels[dpm_table->count-1].value;
+ vega10_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* Initialize Mclk DPM table based on allow Mclk values */
+ data->dpm_table.mem_table.count = 0;
+ dpm_table = &(data->dpm_table.mem_table);
+ vega10_setup_default_single_dpm_table(hwmgr,
+ dpm_table,
+ dep_mclk_table);
+ if (hwmgr->platform_descriptor.overdriveLimit.memoryClock == 0)
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock =
+ dpm_table->dpm_levels[dpm_table->count-1].value;
+ vega10_init_dpm_state(&(dpm_table->dpm_state));
+
+ data->dpm_table.eclk_table.count = 0;
+ dpm_table = &(data->dpm_table.eclk_table);
+ for (i = 0; i < dep_mm_table->count; i++) {
+ if (i == 0 || dpm_table->dpm_levels
+ [dpm_table->count - 1].value <=
+ dep_mm_table->entries[i].eclk) {
+ dpm_table->dpm_levels[dpm_table->count].value =
+ dep_mm_table->entries[i].eclk;
+ dpm_table->dpm_levels[dpm_table->count].enabled =
+ (i == 0) ? true : false;
+ dpm_table->count++;
+ }
+ }
+ vega10_init_dpm_state(&(dpm_table->dpm_state));
+
+ data->dpm_table.vclk_table.count = 0;
+ data->dpm_table.dclk_table.count = 0;
+ dpm_table = &(data->dpm_table.vclk_table);
+ for (i = 0; i < dep_mm_table->count; i++) {
+ if (i == 0 || dpm_table->dpm_levels
+ [dpm_table->count - 1].value <=
+ dep_mm_table->entries[i].vclk) {
+ dpm_table->dpm_levels[dpm_table->count].value =
+ dep_mm_table->entries[i].vclk;
+ dpm_table->dpm_levels[dpm_table->count].enabled =
+ (i == 0) ? true : false;
+ dpm_table->count++;
+ }
+ }
+ vega10_init_dpm_state(&(dpm_table->dpm_state));
+
+ dpm_table = &(data->dpm_table.dclk_table);
+ for (i = 0; i < dep_mm_table->count; i++) {
+ if (i == 0 || dpm_table->dpm_levels
+ [dpm_table->count - 1].value <=
+ dep_mm_table->entries[i].dclk) {
+ dpm_table->dpm_levels[dpm_table->count].value =
+ dep_mm_table->entries[i].dclk;
+ dpm_table->dpm_levels[dpm_table->count].enabled =
+ (i == 0) ? true : false;
+ dpm_table->count++;
+ }
+ }
+ vega10_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* Assume there is no headless Vega10 for now */
+ dpm_table = &(data->dpm_table.dcef_table);
+ vega10_setup_default_single_dpm_table(hwmgr,
+ dpm_table,
+ dep_dcef_table);
+
+ vega10_init_dpm_state(&(dpm_table->dpm_state));
+
+ dpm_table = &(data->dpm_table.pixel_table);
+ vega10_setup_default_single_dpm_table(hwmgr,
+ dpm_table,
+ dep_pix_table);
+
+ vega10_init_dpm_state(&(dpm_table->dpm_state));
+
+ dpm_table = &(data->dpm_table.display_table);
+ vega10_setup_default_single_dpm_table(hwmgr,
+ dpm_table,
+ dep_disp_table);
+
+ vega10_init_dpm_state(&(dpm_table->dpm_state));
+
+ dpm_table = &(data->dpm_table.phy_table);
+ vega10_setup_default_single_dpm_table(hwmgr,
+ dpm_table,
+ dep_phy_table);
+
+ vega10_init_dpm_state(&(dpm_table->dpm_state));
+
+ vega10_setup_default_pcie_table(hwmgr);
+
+ /* Zero out the saved copy of the CUSTOM profile
+ * This will be checked when trying to set the profile
+ * and will require that new values be passed in
+ */
+ data->custom_profile_mode[0] = 0;
+ data->custom_profile_mode[1] = 0;
+ data->custom_profile_mode[2] = 0;
+ data->custom_profile_mode[3] = 0;
+
+ /* save a copy of the default DPM table */
+ memcpy(&(data->golden_dpm_table), &(data->dpm_table),
+ sizeof(struct vega10_dpm_table));
+
+ return 0;
+}
+
+/*
+ * @fn vega10_populate_ulv_state
+ * @brief Function to provide parameters for Utral Low Voltage state to SMC.
+ *
+ * @param hwmgr - the address of the hardware manager.
+ * @return Always 0.
+ */
+static int vega10_populate_ulv_state(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+
+ data->smc_state_table.pp_table.UlvOffsetVid =
+ (uint8_t)table_info->us_ulv_voltage_offset;
+
+ data->smc_state_table.pp_table.UlvSmnclkDid =
+ (uint8_t)(table_info->us_ulv_smnclk_did);
+ data->smc_state_table.pp_table.UlvMp1clkDid =
+ (uint8_t)(table_info->us_ulv_mp1clk_did);
+ data->smc_state_table.pp_table.UlvGfxclkBypass =
+ (uint8_t)(table_info->us_ulv_gfxclk_bypass);
+ data->smc_state_table.pp_table.UlvPhaseSheddingPsi0 =
+ (uint8_t)(data->vddc_voltage_table.psi0_enable);
+ data->smc_state_table.pp_table.UlvPhaseSheddingPsi1 =
+ (uint8_t)(data->vddc_voltage_table.psi1_enable);
+
+ return 0;
+}
+
+static int vega10_populate_single_lclk_level(struct pp_hwmgr *hwmgr,
+ uint32_t lclock, uint8_t *curr_lclk_did)
+{
+ struct pp_atomfwctrl_clock_dividers_soc15 dividers;
+
+ PP_ASSERT_WITH_CODE(!pp_atomfwctrl_get_gpu_pll_dividers_vega10(
+ hwmgr,
+ COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
+ lclock, &dividers),
+ "Failed to get LCLK clock settings from VBIOS!",
+ return -1);
+
+ *curr_lclk_did = dividers.ulDid;
+
+ return 0;
+}
+
+static int vega10_populate_smc_link_levels(struct pp_hwmgr *hwmgr)
+{
+ int result = -1;
+ struct vega10_hwmgr *data = hwmgr->backend;
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+ struct vega10_pcie_table *pcie_table =
+ &(data->dpm_table.pcie_table);
+ uint32_t i, j;
+
+ for (i = 0; i < pcie_table->count; i++) {
+ pp_table->PcieGenSpeed[i] = pcie_table->pcie_gen[i];
+ pp_table->PcieLaneCount[i] = pcie_table->pcie_lane[i];
+
+ result = vega10_populate_single_lclk_level(hwmgr,
+ pcie_table->lclk[i], &(pp_table->LclkDid[i]));
+ if (result) {
+ pr_info("Populate LClock Level %d Failed!\n", i);
+ return result;
+ }
+ }
+
+ j = i - 1;
+ while (i < NUM_LINK_LEVELS) {
+ pp_table->PcieGenSpeed[i] = pcie_table->pcie_gen[j];
+ pp_table->PcieLaneCount[i] = pcie_table->pcie_lane[j];
+
+ result = vega10_populate_single_lclk_level(hwmgr,
+ pcie_table->lclk[j], &(pp_table->LclkDid[i]));
+ if (result) {
+ pr_info("Populate LClock Level %d Failed!\n", i);
+ return result;
+ }
+ i++;
+ }
+
+ return result;
+}
+
+/**
+* Populates single SMC GFXSCLK structure using the provided engine clock
+*
+* @param hwmgr the address of the hardware manager
+* @param gfx_clock the GFX clock to use to populate the structure.
+* @param current_gfxclk_level location in PPTable for the SMC GFXCLK structure.
+*/
+
+static int vega10_populate_single_gfx_level(struct pp_hwmgr *hwmgr,
+ uint32_t gfx_clock, PllSetting_t *current_gfxclk_level,
+ uint32_t *acg_freq)
+{
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_on_sclk;
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct pp_atomfwctrl_clock_dividers_soc15 dividers;
+ uint32_t gfx_max_clock =
+ hwmgr->platform_descriptor.overdriveLimit.engineClock;
+ uint32_t i = 0;
+
+ if (hwmgr->od_enabled)
+ dep_on_sclk = (struct phm_ppt_v1_clock_voltage_dependency_table *)
+ &(data->odn_dpm_table.vdd_dep_on_sclk);
+ else
+ dep_on_sclk = table_info->vdd_dep_on_sclk;
+
+ PP_ASSERT_WITH_CODE(dep_on_sclk,
+ "Invalid SOC_VDD-GFX_CLK Dependency Table!",
+ return -EINVAL);
+
+ if (data->need_update_dpm_table & DPMTABLE_OD_UPDATE_SCLK)
+ gfx_clock = gfx_clock > gfx_max_clock ? gfx_max_clock : gfx_clock;
+ else {
+ for (i = 0; i < dep_on_sclk->count; i++) {
+ if (dep_on_sclk->entries[i].clk == gfx_clock)
+ break;
+ }
+ PP_ASSERT_WITH_CODE(dep_on_sclk->count > i,
+ "Cannot find gfx_clk in SOC_VDD-GFX_CLK!",
+ return -EINVAL);
+ }
+
+ PP_ASSERT_WITH_CODE(!pp_atomfwctrl_get_gpu_pll_dividers_vega10(hwmgr,
+ COMPUTE_GPUCLK_INPUT_FLAG_GFXCLK,
+ gfx_clock, &dividers),
+ "Failed to get GFX Clock settings from VBIOS!",
+ return -EINVAL);
+
+ /* Feedback Multiplier: bit 0:8 int, bit 15:12 post_div, bit 31:16 frac */
+ current_gfxclk_level->FbMult =
+ cpu_to_le32(dividers.ulPll_fb_mult);
+ /* Spread FB Multiplier bit: bit 0:8 int, bit 31:16 frac */
+ current_gfxclk_level->SsOn = dividers.ucPll_ss_enable;
+ current_gfxclk_level->SsFbMult =
+ cpu_to_le32(dividers.ulPll_ss_fbsmult);
+ current_gfxclk_level->SsSlewFrac =
+ cpu_to_le16(dividers.usPll_ss_slew_frac);
+ current_gfxclk_level->Did = (uint8_t)(dividers.ulDid);
+
+ *acg_freq = gfx_clock / 100; /* 100 Khz to Mhz conversion */
+
+ return 0;
+}
+
+/**
+ * @brief Populates single SMC SOCCLK structure using the provided clock.
+ *
+ * @param hwmgr - the address of the hardware manager.
+ * @param soc_clock - the SOC clock to use to populate the structure.
+ * @param current_socclk_level - location in PPTable for the SMC SOCCLK structure.
+ * @return 0 on success..
+ */
+static int vega10_populate_single_soc_level(struct pp_hwmgr *hwmgr,
+ uint32_t soc_clock, uint8_t *current_soc_did,
+ uint8_t *current_vol_index)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_on_soc;
+ struct pp_atomfwctrl_clock_dividers_soc15 dividers;
+ uint32_t i;
+
+ if (hwmgr->od_enabled) {
+ dep_on_soc = (struct phm_ppt_v1_clock_voltage_dependency_table *)
+ &data->odn_dpm_table.vdd_dep_on_socclk;
+ for (i = 0; i < dep_on_soc->count; i++) {
+ if (dep_on_soc->entries[i].clk >= soc_clock)
+ break;
+ }
+ } else {
+ dep_on_soc = table_info->vdd_dep_on_socclk;
+ for (i = 0; i < dep_on_soc->count; i++) {
+ if (dep_on_soc->entries[i].clk == soc_clock)
+ break;
+ }
+ }
+
+ PP_ASSERT_WITH_CODE(dep_on_soc->count > i,
+ "Cannot find SOC_CLK in SOC_VDD-SOC_CLK Dependency Table",
+ return -EINVAL);
+
+ PP_ASSERT_WITH_CODE(!pp_atomfwctrl_get_gpu_pll_dividers_vega10(hwmgr,
+ COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
+ soc_clock, &dividers),
+ "Failed to get SOC Clock settings from VBIOS!",
+ return -EINVAL);
+
+ *current_soc_did = (uint8_t)dividers.ulDid;
+ *current_vol_index = (uint8_t)(dep_on_soc->entries[i].vddInd);
+ return 0;
+}
+
+/**
+* Populates all SMC SCLK levels' structure based on the trimmed allowed dpm engine clock states
+*
+* @param hwmgr the address of the hardware manager
+*/
+static int vega10_populate_all_graphic_levels(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+ struct vega10_single_dpm_table *dpm_table = &(data->dpm_table.gfx_table);
+ int result = 0;
+ uint32_t i, j;
+
+ for (i = 0; i < dpm_table->count; i++) {
+ result = vega10_populate_single_gfx_level(hwmgr,
+ dpm_table->dpm_levels[i].value,
+ &(pp_table->GfxclkLevel[i]),
+ &(pp_table->AcgFreqTable[i]));
+ if (result)
+ return result;
+ }
+
+ j = i - 1;
+ while (i < NUM_GFXCLK_DPM_LEVELS) {
+ result = vega10_populate_single_gfx_level(hwmgr,
+ dpm_table->dpm_levels[j].value,
+ &(pp_table->GfxclkLevel[i]),
+ &(pp_table->AcgFreqTable[i]));
+ if (result)
+ return result;
+ i++;
+ }
+
+ pp_table->GfxclkSlewRate =
+ cpu_to_le16(table_info->us_gfxclk_slew_rate);
+
+ dpm_table = &(data->dpm_table.soc_table);
+ for (i = 0; i < dpm_table->count; i++) {
+ result = vega10_populate_single_soc_level(hwmgr,
+ dpm_table->dpm_levels[i].value,
+ &(pp_table->SocclkDid[i]),
+ &(pp_table->SocDpmVoltageIndex[i]));
+ if (result)
+ return result;
+ }
+
+ j = i - 1;
+ while (i < NUM_SOCCLK_DPM_LEVELS) {
+ result = vega10_populate_single_soc_level(hwmgr,
+ dpm_table->dpm_levels[j].value,
+ &(pp_table->SocclkDid[i]),
+ &(pp_table->SocDpmVoltageIndex[i]));
+ if (result)
+ return result;
+ i++;
+ }
+
+ return result;
+}
+
+static void vega10_populate_vddc_soc_levels(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+ struct phm_ppt_v2_information *table_info = hwmgr->pptable;
+ struct phm_ppt_v1_voltage_lookup_table *vddc_lookup_table;
+
+ uint8_t soc_vid = 0;
+ uint32_t i, max_vddc_level;
+
+ if (hwmgr->od_enabled)
+ vddc_lookup_table = (struct phm_ppt_v1_voltage_lookup_table *)&data->odn_dpm_table.vddc_lookup_table;
+ else
+ vddc_lookup_table = table_info->vddc_lookup_table;
+
+ max_vddc_level = vddc_lookup_table->count;
+ for (i = 0; i < max_vddc_level; i++) {
+ soc_vid = (uint8_t)convert_to_vid(vddc_lookup_table->entries[i].us_vdd);
+ pp_table->SocVid[i] = soc_vid;
+ }
+ while (i < MAX_REGULAR_DPM_NUMBER) {
+ pp_table->SocVid[i] = soc_vid;
+ i++;
+ }
+}
+
+/**
+ * @brief Populates single SMC GFXCLK structure using the provided clock.
+ *
+ * @param hwmgr - the address of the hardware manager.
+ * @param mem_clock - the memory clock to use to populate the structure.
+ * @return 0 on success..
+ */
+static int vega10_populate_single_memory_level(struct pp_hwmgr *hwmgr,
+ uint32_t mem_clock, uint8_t *current_mem_vid,
+ PllSetting_t *current_memclk_level, uint8_t *current_mem_soc_vind)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_on_mclk;
+ struct pp_atomfwctrl_clock_dividers_soc15 dividers;
+ uint32_t mem_max_clock =
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock;
+ uint32_t i = 0;
+
+ if (hwmgr->od_enabled)
+ dep_on_mclk = (struct phm_ppt_v1_clock_voltage_dependency_table *)
+ &data->odn_dpm_table.vdd_dep_on_mclk;
+ else
+ dep_on_mclk = table_info->vdd_dep_on_mclk;
+
+ PP_ASSERT_WITH_CODE(dep_on_mclk,
+ "Invalid SOC_VDD-UCLK Dependency Table!",
+ return -EINVAL);
+
+ if (data->need_update_dpm_table & DPMTABLE_OD_UPDATE_MCLK) {
+ mem_clock = mem_clock > mem_max_clock ? mem_max_clock : mem_clock;
+ } else {
+ for (i = 0; i < dep_on_mclk->count; i++) {
+ if (dep_on_mclk->entries[i].clk == mem_clock)
+ break;
+ }
+ PP_ASSERT_WITH_CODE(dep_on_mclk->count > i,
+ "Cannot find UCLK in SOC_VDD-UCLK Dependency Table!",
+ return -EINVAL);
+ }
+
+ PP_ASSERT_WITH_CODE(!pp_atomfwctrl_get_gpu_pll_dividers_vega10(
+ hwmgr, COMPUTE_GPUCLK_INPUT_FLAG_UCLK, mem_clock, &dividers),
+ "Failed to get UCLK settings from VBIOS!",
+ return -1);
+
+ *current_mem_vid =
+ (uint8_t)(convert_to_vid(dep_on_mclk->entries[i].mvdd));
+ *current_mem_soc_vind =
+ (uint8_t)(dep_on_mclk->entries[i].vddInd);
+ current_memclk_level->FbMult = cpu_to_le32(dividers.ulPll_fb_mult);
+ current_memclk_level->Did = (uint8_t)(dividers.ulDid);
+
+ PP_ASSERT_WITH_CODE(current_memclk_level->Did >= 1,
+ "Invalid Divider ID!",
+ return -EINVAL);
+
+ return 0;
+}
+
+/**
+ * @brief Populates all SMC MCLK levels' structure based on the trimmed allowed dpm memory clock states.
+ *
+ * @param pHwMgr - the address of the hardware manager.
+ * @return PP_Result_OK on success.
+ */
+static int vega10_populate_all_memory_levels(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+ struct vega10_single_dpm_table *dpm_table =
+ &(data->dpm_table.mem_table);
+ int result = 0;
+ uint32_t i, j;
+
+ for (i = 0; i < dpm_table->count; i++) {
+ result = vega10_populate_single_memory_level(hwmgr,
+ dpm_table->dpm_levels[i].value,
+ &(pp_table->MemVid[i]),
+ &(pp_table->UclkLevel[i]),
+ &(pp_table->MemSocVoltageIndex[i]));
+ if (result)
+ return result;
+ }
+
+ j = i - 1;
+ while (i < NUM_UCLK_DPM_LEVELS) {
+ result = vega10_populate_single_memory_level(hwmgr,
+ dpm_table->dpm_levels[j].value,
+ &(pp_table->MemVid[i]),
+ &(pp_table->UclkLevel[i]),
+ &(pp_table->MemSocVoltageIndex[i]));
+ if (result)
+ return result;
+ i++;
+ }
+
+ pp_table->NumMemoryChannels = (uint16_t)(data->mem_channels);
+ pp_table->MemoryChannelWidth =
+ (uint16_t)(HBM_MEMORY_CHANNEL_WIDTH *
+ channel_number[data->mem_channels]);
+
+ pp_table->LowestUclkReservedForUlv =
+ (uint8_t)(data->lowest_uclk_reserved_for_ulv);
+
+ return result;
+}
+
+static int vega10_populate_single_display_type(struct pp_hwmgr *hwmgr,
+ DSPCLK_e disp_clock)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)
+ (hwmgr->pptable);
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_table;
+ uint32_t i;
+ uint16_t clk = 0, vddc = 0;
+ uint8_t vid = 0;
+
+ switch (disp_clock) {
+ case DSPCLK_DCEFCLK:
+ dep_table = table_info->vdd_dep_on_dcefclk;
+ break;
+ case DSPCLK_DISPCLK:
+ dep_table = table_info->vdd_dep_on_dispclk;
+ break;
+ case DSPCLK_PIXCLK:
+ dep_table = table_info->vdd_dep_on_pixclk;
+ break;
+ case DSPCLK_PHYCLK:
+ dep_table = table_info->vdd_dep_on_phyclk;
+ break;
+ default:
+ return -1;
+ }
+
+ PP_ASSERT_WITH_CODE(dep_table->count <= NUM_DSPCLK_LEVELS,
+ "Number Of Entries Exceeded maximum!",
+ return -1);
+
+ for (i = 0; i < dep_table->count; i++) {
+ clk = (uint16_t)(dep_table->entries[i].clk / 100);
+ vddc = table_info->vddc_lookup_table->
+ entries[dep_table->entries[i].vddInd].us_vdd;
+ vid = (uint8_t)convert_to_vid(vddc);
+ pp_table->DisplayClockTable[disp_clock][i].Freq =
+ cpu_to_le16(clk);
+ pp_table->DisplayClockTable[disp_clock][i].Vid =
+ cpu_to_le16(vid);
+ }
+
+ while (i < NUM_DSPCLK_LEVELS) {
+ pp_table->DisplayClockTable[disp_clock][i].Freq =
+ cpu_to_le16(clk);
+ pp_table->DisplayClockTable[disp_clock][i].Vid =
+ cpu_to_le16(vid);
+ i++;
+ }
+
+ return 0;
+}
+
+static int vega10_populate_all_display_clock_levels(struct pp_hwmgr *hwmgr)
+{
+ uint32_t i;
+
+ for (i = 0; i < DSPCLK_COUNT; i++) {
+ PP_ASSERT_WITH_CODE(!vega10_populate_single_display_type(hwmgr, i),
+ "Failed to populate Clock in DisplayClockTable!",
+ return -1);
+ }
+
+ return 0;
+}
+
+static int vega10_populate_single_eclock_level(struct pp_hwmgr *hwmgr,
+ uint32_t eclock, uint8_t *current_eclk_did,
+ uint8_t *current_soc_vol)
+{
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_mm_clock_voltage_dependency_table *dep_table =
+ table_info->mm_dep_table;
+ struct pp_atomfwctrl_clock_dividers_soc15 dividers;
+ uint32_t i;
+
+ PP_ASSERT_WITH_CODE(!pp_atomfwctrl_get_gpu_pll_dividers_vega10(hwmgr,
+ COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
+ eclock, &dividers),
+ "Failed to get ECLK clock settings from VBIOS!",
+ return -1);
+
+ *current_eclk_did = (uint8_t)dividers.ulDid;
+
+ for (i = 0; i < dep_table->count; i++) {
+ if (dep_table->entries[i].eclk == eclock)
+ *current_soc_vol = dep_table->entries[i].vddcInd;
+ }
+
+ return 0;
+}
+
+static int vega10_populate_smc_vce_levels(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+ struct vega10_single_dpm_table *dpm_table = &(data->dpm_table.eclk_table);
+ int result = -EINVAL;
+ uint32_t i, j;
+
+ for (i = 0; i < dpm_table->count; i++) {
+ result = vega10_populate_single_eclock_level(hwmgr,
+ dpm_table->dpm_levels[i].value,
+ &(pp_table->EclkDid[i]),
+ &(pp_table->VceDpmVoltageIndex[i]));
+ if (result)
+ return result;
+ }
+
+ j = i - 1;
+ while (i < NUM_VCE_DPM_LEVELS) {
+ result = vega10_populate_single_eclock_level(hwmgr,
+ dpm_table->dpm_levels[j].value,
+ &(pp_table->EclkDid[i]),
+ &(pp_table->VceDpmVoltageIndex[i]));
+ if (result)
+ return result;
+ i++;
+ }
+
+ return result;
+}
+
+static int vega10_populate_single_vclock_level(struct pp_hwmgr *hwmgr,
+ uint32_t vclock, uint8_t *current_vclk_did)
+{
+ struct pp_atomfwctrl_clock_dividers_soc15 dividers;
+
+ PP_ASSERT_WITH_CODE(!pp_atomfwctrl_get_gpu_pll_dividers_vega10(hwmgr,
+ COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
+ vclock, &dividers),
+ "Failed to get VCLK clock settings from VBIOS!",
+ return -EINVAL);
+
+ *current_vclk_did = (uint8_t)dividers.ulDid;
+
+ return 0;
+}
+
+static int vega10_populate_single_dclock_level(struct pp_hwmgr *hwmgr,
+ uint32_t dclock, uint8_t *current_dclk_did)
+{
+ struct pp_atomfwctrl_clock_dividers_soc15 dividers;
+
+ PP_ASSERT_WITH_CODE(!pp_atomfwctrl_get_gpu_pll_dividers_vega10(hwmgr,
+ COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
+ dclock, &dividers),
+ "Failed to get DCLK clock settings from VBIOS!",
+ return -EINVAL);
+
+ *current_dclk_did = (uint8_t)dividers.ulDid;
+
+ return 0;
+}
+
+static int vega10_populate_smc_uvd_levels(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+ struct vega10_single_dpm_table *vclk_dpm_table =
+ &(data->dpm_table.vclk_table);
+ struct vega10_single_dpm_table *dclk_dpm_table =
+ &(data->dpm_table.dclk_table);
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_mm_clock_voltage_dependency_table *dep_table =
+ table_info->mm_dep_table;
+ int result = -EINVAL;
+ uint32_t i, j;
+
+ for (i = 0; i < vclk_dpm_table->count; i++) {
+ result = vega10_populate_single_vclock_level(hwmgr,
+ vclk_dpm_table->dpm_levels[i].value,
+ &(pp_table->VclkDid[i]));
+ if (result)
+ return result;
+ }
+
+ j = i - 1;
+ while (i < NUM_UVD_DPM_LEVELS) {
+ result = vega10_populate_single_vclock_level(hwmgr,
+ vclk_dpm_table->dpm_levels[j].value,
+ &(pp_table->VclkDid[i]));
+ if (result)
+ return result;
+ i++;
+ }
+
+ for (i = 0; i < dclk_dpm_table->count; i++) {
+ result = vega10_populate_single_dclock_level(hwmgr,
+ dclk_dpm_table->dpm_levels[i].value,
+ &(pp_table->DclkDid[i]));
+ if (result)
+ return result;
+ }
+
+ j = i - 1;
+ while (i < NUM_UVD_DPM_LEVELS) {
+ result = vega10_populate_single_dclock_level(hwmgr,
+ dclk_dpm_table->dpm_levels[j].value,
+ &(pp_table->DclkDid[i]));
+ if (result)
+ return result;
+ i++;
+ }
+
+ for (i = 0; i < dep_table->count; i++) {
+ if (dep_table->entries[i].vclk ==
+ vclk_dpm_table->dpm_levels[i].value &&
+ dep_table->entries[i].dclk ==
+ dclk_dpm_table->dpm_levels[i].value)
+ pp_table->UvdDpmVoltageIndex[i] =
+ dep_table->entries[i].vddcInd;
+ else
+ return -1;
+ }
+
+ j = i - 1;
+ while (i < NUM_UVD_DPM_LEVELS) {
+ pp_table->UvdDpmVoltageIndex[i] = dep_table->entries[j].vddcInd;
+ i++;
+ }
+
+ return 0;
+}
+
+static int vega10_populate_clock_stretcher_table(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_table =
+ table_info->vdd_dep_on_sclk;
+ uint32_t i;
+
+ for (i = 0; i < dep_table->count; i++) {
+ pp_table->CksEnable[i] = dep_table->entries[i].cks_enable;
+ pp_table->CksVidOffset[i] = (uint8_t)(dep_table->entries[i].cks_voffset
+ * VOLTAGE_VID_OFFSET_SCALE2 / VOLTAGE_VID_OFFSET_SCALE1);
+ }
+
+ return 0;
+}
+
+static int vega10_populate_avfs_parameters(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_table =
+ table_info->vdd_dep_on_sclk;
+ struct pp_atomfwctrl_avfs_parameters avfs_params = {0};
+ int result = 0;
+ uint32_t i;
+
+ pp_table->MinVoltageVid = (uint8_t)0xff;
+ pp_table->MaxVoltageVid = (uint8_t)0;
+
+ if (data->smu_features[GNLD_AVFS].supported) {
+ result = pp_atomfwctrl_get_avfs_information(hwmgr, &avfs_params);
+ if (!result) {
+ pp_table->MinVoltageVid = (uint8_t)
+ convert_to_vid((uint16_t)(avfs_params.ulMinVddc));
+ pp_table->MaxVoltageVid = (uint8_t)
+ convert_to_vid((uint16_t)(avfs_params.ulMaxVddc));
+
+ pp_table->AConstant[0] = cpu_to_le32(avfs_params.ulMeanNsigmaAcontant0);
+ pp_table->AConstant[1] = cpu_to_le32(avfs_params.ulMeanNsigmaAcontant1);
+ pp_table->AConstant[2] = cpu_to_le32(avfs_params.ulMeanNsigmaAcontant2);
+ pp_table->DC_tol_sigma = cpu_to_le16(avfs_params.usMeanNsigmaDcTolSigma);
+ pp_table->Platform_mean = cpu_to_le16(avfs_params.usMeanNsigmaPlatformMean);
+ pp_table->Platform_sigma = cpu_to_le16(avfs_params.usMeanNsigmaDcTolSigma);
+ pp_table->PSM_Age_CompFactor = cpu_to_le16(avfs_params.usPsmAgeComfactor);
+
+ pp_table->BtcGbVdroopTableCksOff.a0 =
+ cpu_to_le32(avfs_params.ulGbVdroopTableCksoffA0);
+ pp_table->BtcGbVdroopTableCksOff.a0_shift = 20;
+ pp_table->BtcGbVdroopTableCksOff.a1 =
+ cpu_to_le32(avfs_params.ulGbVdroopTableCksoffA1);
+ pp_table->BtcGbVdroopTableCksOff.a1_shift = 20;
+ pp_table->BtcGbVdroopTableCksOff.a2 =
+ cpu_to_le32(avfs_params.ulGbVdroopTableCksoffA2);
+ pp_table->BtcGbVdroopTableCksOff.a2_shift = 20;
+
+ pp_table->OverrideBtcGbCksOn = avfs_params.ucEnableGbVdroopTableCkson;
+ pp_table->BtcGbVdroopTableCksOn.a0 =
+ cpu_to_le32(avfs_params.ulGbVdroopTableCksonA0);
+ pp_table->BtcGbVdroopTableCksOn.a0_shift = 20;
+ pp_table->BtcGbVdroopTableCksOn.a1 =
+ cpu_to_le32(avfs_params.ulGbVdroopTableCksonA1);
+ pp_table->BtcGbVdroopTableCksOn.a1_shift = 20;
+ pp_table->BtcGbVdroopTableCksOn.a2 =
+ cpu_to_le32(avfs_params.ulGbVdroopTableCksonA2);
+ pp_table->BtcGbVdroopTableCksOn.a2_shift = 20;
+
+ pp_table->AvfsGbCksOn.m1 =
+ cpu_to_le32(avfs_params.ulGbFuseTableCksonM1);
+ pp_table->AvfsGbCksOn.m2 =
+ cpu_to_le32(avfs_params.ulGbFuseTableCksonM2);
+ pp_table->AvfsGbCksOn.b =
+ cpu_to_le32(avfs_params.ulGbFuseTableCksonB);
+ pp_table->AvfsGbCksOn.m1_shift = 24;
+ pp_table->AvfsGbCksOn.m2_shift = 12;
+ pp_table->AvfsGbCksOn.b_shift = 0;
+
+ pp_table->OverrideAvfsGbCksOn =
+ avfs_params.ucEnableGbFuseTableCkson;
+ pp_table->AvfsGbCksOff.m1 =
+ cpu_to_le32(avfs_params.ulGbFuseTableCksoffM1);
+ pp_table->AvfsGbCksOff.m2 =
+ cpu_to_le32(avfs_params.ulGbFuseTableCksoffM2);
+ pp_table->AvfsGbCksOff.b =
+ cpu_to_le32(avfs_params.ulGbFuseTableCksoffB);
+ pp_table->AvfsGbCksOff.m1_shift = 24;
+ pp_table->AvfsGbCksOff.m2_shift = 12;
+ pp_table->AvfsGbCksOff.b_shift = 0;
+
+ for (i = 0; i < dep_table->count; i++)
+ pp_table->StaticVoltageOffsetVid[i] =
+ convert_to_vid((uint8_t)(dep_table->entries[i].sclk_offset));
+
+ if ((PPREGKEY_VEGA10QUADRATICEQUATION_DFLT !=
+ data->disp_clk_quad_eqn_a) &&
+ (PPREGKEY_VEGA10QUADRATICEQUATION_DFLT !=
+ data->disp_clk_quad_eqn_b)) {
+ pp_table->DisplayClock2Gfxclk[DSPCLK_DISPCLK].m1 =
+ (int32_t)data->disp_clk_quad_eqn_a;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_DISPCLK].m2 =
+ (int32_t)data->disp_clk_quad_eqn_b;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_DISPCLK].b =
+ (int32_t)data->disp_clk_quad_eqn_c;
+ } else {
+ pp_table->DisplayClock2Gfxclk[DSPCLK_DISPCLK].m1 =
+ (int32_t)avfs_params.ulDispclk2GfxclkM1;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_DISPCLK].m2 =
+ (int32_t)avfs_params.ulDispclk2GfxclkM2;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_DISPCLK].b =
+ (int32_t)avfs_params.ulDispclk2GfxclkB;
+ }
+
+ pp_table->DisplayClock2Gfxclk[DSPCLK_DISPCLK].m1_shift = 24;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_DISPCLK].m2_shift = 12;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_DISPCLK].b_shift = 12;
+
+ if ((PPREGKEY_VEGA10QUADRATICEQUATION_DFLT !=
+ data->dcef_clk_quad_eqn_a) &&
+ (PPREGKEY_VEGA10QUADRATICEQUATION_DFLT !=
+ data->dcef_clk_quad_eqn_b)) {
+ pp_table->DisplayClock2Gfxclk[DSPCLK_DCEFCLK].m1 =
+ (int32_t)data->dcef_clk_quad_eqn_a;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_DCEFCLK].m2 =
+ (int32_t)data->dcef_clk_quad_eqn_b;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_DCEFCLK].b =
+ (int32_t)data->dcef_clk_quad_eqn_c;
+ } else {
+ pp_table->DisplayClock2Gfxclk[DSPCLK_DCEFCLK].m1 =
+ (int32_t)avfs_params.ulDcefclk2GfxclkM1;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_DCEFCLK].m2 =
+ (int32_t)avfs_params.ulDcefclk2GfxclkM2;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_DCEFCLK].b =
+ (int32_t)avfs_params.ulDcefclk2GfxclkB;
+ }
+
+ pp_table->DisplayClock2Gfxclk[DSPCLK_DCEFCLK].m1_shift = 24;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_DCEFCLK].m2_shift = 12;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_DCEFCLK].b_shift = 12;
+
+ if ((PPREGKEY_VEGA10QUADRATICEQUATION_DFLT !=
+ data->pixel_clk_quad_eqn_a) &&
+ (PPREGKEY_VEGA10QUADRATICEQUATION_DFLT !=
+ data->pixel_clk_quad_eqn_b)) {
+ pp_table->DisplayClock2Gfxclk[DSPCLK_PIXCLK].m1 =
+ (int32_t)data->pixel_clk_quad_eqn_a;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_PIXCLK].m2 =
+ (int32_t)data->pixel_clk_quad_eqn_b;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_PIXCLK].b =
+ (int32_t)data->pixel_clk_quad_eqn_c;
+ } else {
+ pp_table->DisplayClock2Gfxclk[DSPCLK_PIXCLK].m1 =
+ (int32_t)avfs_params.ulPixelclk2GfxclkM1;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_PIXCLK].m2 =
+ (int32_t)avfs_params.ulPixelclk2GfxclkM2;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_PIXCLK].b =
+ (int32_t)avfs_params.ulPixelclk2GfxclkB;
+ }
+
+ pp_table->DisplayClock2Gfxclk[DSPCLK_PIXCLK].m1_shift = 24;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_PIXCLK].m2_shift = 12;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_PIXCLK].b_shift = 12;
+ if ((PPREGKEY_VEGA10QUADRATICEQUATION_DFLT !=
+ data->phy_clk_quad_eqn_a) &&
+ (PPREGKEY_VEGA10QUADRATICEQUATION_DFLT !=
+ data->phy_clk_quad_eqn_b)) {
+ pp_table->DisplayClock2Gfxclk[DSPCLK_PHYCLK].m1 =
+ (int32_t)data->phy_clk_quad_eqn_a;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_PHYCLK].m2 =
+ (int32_t)data->phy_clk_quad_eqn_b;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_PHYCLK].b =
+ (int32_t)data->phy_clk_quad_eqn_c;
+ } else {
+ pp_table->DisplayClock2Gfxclk[DSPCLK_PHYCLK].m1 =
+ (int32_t)avfs_params.ulPhyclk2GfxclkM1;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_PHYCLK].m2 =
+ (int32_t)avfs_params.ulPhyclk2GfxclkM2;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_PHYCLK].b =
+ (int32_t)avfs_params.ulPhyclk2GfxclkB;
+ }
+
+ pp_table->DisplayClock2Gfxclk[DSPCLK_PHYCLK].m1_shift = 24;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_PHYCLK].m2_shift = 12;
+ pp_table->DisplayClock2Gfxclk[DSPCLK_PHYCLK].b_shift = 12;
+
+ pp_table->AcgBtcGbVdroopTable.a0 = avfs_params.ulAcgGbVdroopTableA0;
+ pp_table->AcgBtcGbVdroopTable.a0_shift = 20;
+ pp_table->AcgBtcGbVdroopTable.a1 = avfs_params.ulAcgGbVdroopTableA1;
+ pp_table->AcgBtcGbVdroopTable.a1_shift = 20;
+ pp_table->AcgBtcGbVdroopTable.a2 = avfs_params.ulAcgGbVdroopTableA2;
+ pp_table->AcgBtcGbVdroopTable.a2_shift = 20;
+
+ pp_table->AcgAvfsGb.m1 = avfs_params.ulAcgGbFuseTableM1;
+ pp_table->AcgAvfsGb.m2 = avfs_params.ulAcgGbFuseTableM2;
+ pp_table->AcgAvfsGb.b = avfs_params.ulAcgGbFuseTableB;
+ pp_table->AcgAvfsGb.m1_shift = 24;
+ pp_table->AcgAvfsGb.m2_shift = 12;
+ pp_table->AcgAvfsGb.b_shift = 0;
+
+ } else {
+ data->smu_features[GNLD_AVFS].supported = false;
+ }
+ }
+
+ return 0;
+}
+
+static int vega10_acg_enable(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ uint32_t agc_btc_response;
+
+ if (data->smu_features[GNLD_ACG].supported) {
+ if (0 == vega10_enable_smc_features(hwmgr, true,
+ data->smu_features[GNLD_DPM_PREFETCHER].smu_feature_bitmap))
+ data->smu_features[GNLD_DPM_PREFETCHER].enabled = true;
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_InitializeAcg, NULL);
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_RunAcgBtc, &agc_btc_response);
+
+ if (1 == agc_btc_response) {
+ if (1 == data->acg_loop_state)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_RunAcgInClosedLoop, NULL);
+ else if (2 == data->acg_loop_state)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_RunAcgInOpenLoop, NULL);
+ if (0 == vega10_enable_smc_features(hwmgr, true,
+ data->smu_features[GNLD_ACG].smu_feature_bitmap))
+ data->smu_features[GNLD_ACG].enabled = true;
+ } else {
+ pr_info("[ACG_Enable] ACG BTC Returned Failed Status!\n");
+ data->smu_features[GNLD_ACG].enabled = false;
+ }
+ }
+
+ return 0;
+}
+
+static int vega10_acg_disable(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->smu_features[GNLD_ACG].supported &&
+ data->smu_features[GNLD_ACG].enabled)
+ if (!vega10_enable_smc_features(hwmgr, false,
+ data->smu_features[GNLD_ACG].smu_feature_bitmap))
+ data->smu_features[GNLD_ACG].enabled = false;
+
+ return 0;
+}
+
+static int vega10_populate_gpio_parameters(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+ struct pp_atomfwctrl_gpio_parameters gpio_params = {0};
+ int result;
+
+ result = pp_atomfwctrl_get_gpio_information(hwmgr, &gpio_params);
+ if (!result) {
+ if (PP_CAP(PHM_PlatformCaps_RegulatorHot) &&
+ data->registry_data.regulator_hot_gpio_support) {
+ pp_table->VR0HotGpio = gpio_params.ucVR0HotGpio;
+ pp_table->VR0HotPolarity = gpio_params.ucVR0HotPolarity;
+ pp_table->VR1HotGpio = gpio_params.ucVR1HotGpio;
+ pp_table->VR1HotPolarity = gpio_params.ucVR1HotPolarity;
+ } else {
+ pp_table->VR0HotGpio = 0;
+ pp_table->VR0HotPolarity = 0;
+ pp_table->VR1HotGpio = 0;
+ pp_table->VR1HotPolarity = 0;
+ }
+
+ if (PP_CAP(PHM_PlatformCaps_AutomaticDCTransition) &&
+ data->registry_data.ac_dc_switch_gpio_support) {
+ pp_table->AcDcGpio = gpio_params.ucAcDcGpio;
+ pp_table->AcDcPolarity = gpio_params.ucAcDcPolarity;
+ } else {
+ pp_table->AcDcGpio = 0;
+ pp_table->AcDcPolarity = 0;
+ }
+ }
+
+ return result;
+}
+
+static int vega10_avfs_enable(struct pp_hwmgr *hwmgr, bool enable)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->smu_features[GNLD_AVFS].supported) {
+ /* Already enabled or disabled */
+ if (!(enable ^ data->smu_features[GNLD_AVFS].enabled))
+ return 0;
+
+ if (enable) {
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ true,
+ data->smu_features[GNLD_AVFS].smu_feature_bitmap),
+ "[avfs_control] Attempt to Enable AVFS feature Failed!",
+ return -1);
+ data->smu_features[GNLD_AVFS].enabled = true;
+ } else {
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ false,
+ data->smu_features[GNLD_AVFS].smu_feature_bitmap),
+ "[avfs_control] Attempt to Disable AVFS feature Failed!",
+ return -1);
+ data->smu_features[GNLD_AVFS].enabled = false;
+ }
+ }
+
+ return 0;
+}
+
+static int vega10_update_avfs(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->need_update_dpm_table & DPMTABLE_OD_UPDATE_VDDC) {
+ vega10_avfs_enable(hwmgr, false);
+ } else if (data->need_update_dpm_table) {
+ vega10_avfs_enable(hwmgr, false);
+ vega10_avfs_enable(hwmgr, true);
+ } else {
+ vega10_avfs_enable(hwmgr, true);
+ }
+
+ return 0;
+}
+
+static int vega10_populate_and_upload_avfs_fuse_override(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+
+ uint64_t serial_number = 0;
+ uint32_t top32, bottom32;
+ struct phm_fuses_default fuse;
+
+ struct vega10_hwmgr *data = hwmgr->backend;
+ AvfsFuseOverride_t *avfs_fuse_table = &(data->smc_state_table.avfs_fuse_override_table);
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ReadSerialNumTop32, &top32);
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ReadSerialNumBottom32, &bottom32);
+
+ serial_number = ((uint64_t)bottom32 << 32) | top32;
+
+ if (pp_override_get_default_fuse_value(serial_number, &fuse) == 0) {
+ avfs_fuse_table->VFT0_b = fuse.VFT0_b;
+ avfs_fuse_table->VFT0_m1 = fuse.VFT0_m1;
+ avfs_fuse_table->VFT0_m2 = fuse.VFT0_m2;
+ avfs_fuse_table->VFT1_b = fuse.VFT1_b;
+ avfs_fuse_table->VFT1_m1 = fuse.VFT1_m1;
+ avfs_fuse_table->VFT1_m2 = fuse.VFT1_m2;
+ avfs_fuse_table->VFT2_b = fuse.VFT2_b;
+ avfs_fuse_table->VFT2_m1 = fuse.VFT2_m1;
+ avfs_fuse_table->VFT2_m2 = fuse.VFT2_m2;
+ result = smum_smc_table_manager(hwmgr, (uint8_t *)avfs_fuse_table,
+ AVFSFUSETABLE, false);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to upload FuseOVerride!",
+ );
+ }
+
+ return result;
+}
+
+static void vega10_check_dpm_table_updated(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct vega10_odn_dpm_table *odn_table = &(data->odn_dpm_table);
+ struct phm_ppt_v2_information *table_info = hwmgr->pptable;
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_table;
+ struct phm_ppt_v1_clock_voltage_dependency_table *odn_dep_table;
+ uint32_t i;
+
+ dep_table = table_info->vdd_dep_on_mclk;
+ odn_dep_table = (struct phm_ppt_v1_clock_voltage_dependency_table *)&(odn_table->vdd_dep_on_mclk);
+
+ for (i = 0; i < dep_table->count; i++) {
+ if (dep_table->entries[i].vddc != odn_dep_table->entries[i].vddc) {
+ data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_VDDC | DPMTABLE_OD_UPDATE_MCLK;
+ return;
+ }
+ }
+
+ dep_table = table_info->vdd_dep_on_sclk;
+ odn_dep_table = (struct phm_ppt_v1_clock_voltage_dependency_table *)&(odn_table->vdd_dep_on_sclk);
+ for (i = 0; i < dep_table->count; i++) {
+ if (dep_table->entries[i].vddc != odn_dep_table->entries[i].vddc) {
+ data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_VDDC | DPMTABLE_OD_UPDATE_SCLK;
+ return;
+ }
+ }
+}
+
+/**
+* Initializes the SMC table and uploads it
+*
+* @param hwmgr the address of the powerplay hardware manager.
+* @param pInput the pointer to input data (PowerState)
+* @return always 0
+*/
+static int vega10_init_smc_table(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+ struct pp_atomfwctrl_voltage_table voltage_table;
+ struct pp_atomfwctrl_bios_boot_up_values boot_up_values;
+ struct vega10_odn_dpm_table *odn_table = &(data->odn_dpm_table);
+
+ result = vega10_setup_default_dpm_tables(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to setup default DPM tables!",
+ return result);
+
+ if (!hwmgr->not_vf)
+ return 0;
+
+ /* initialize ODN table */
+ if (hwmgr->od_enabled) {
+ if (odn_table->max_vddc) {
+ data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_SCLK | DPMTABLE_OD_UPDATE_MCLK;
+ vega10_check_dpm_table_updated(hwmgr);
+ } else {
+ vega10_odn_initial_default_setting(hwmgr);
+ }
+ }
+
+ pp_atomfwctrl_get_voltage_table_v4(hwmgr, VOLTAGE_TYPE_VDDC,
+ VOLTAGE_OBJ_SVID2, &voltage_table);
+ pp_table->MaxVidStep = voltage_table.max_vid_step;
+
+ pp_table->GfxDpmVoltageMode =
+ (uint8_t)(table_info->uc_gfx_dpm_voltage_mode);
+ pp_table->SocDpmVoltageMode =
+ (uint8_t)(table_info->uc_soc_dpm_voltage_mode);
+ pp_table->UclkDpmVoltageMode =
+ (uint8_t)(table_info->uc_uclk_dpm_voltage_mode);
+ pp_table->UvdDpmVoltageMode =
+ (uint8_t)(table_info->uc_uvd_dpm_voltage_mode);
+ pp_table->VceDpmVoltageMode =
+ (uint8_t)(table_info->uc_vce_dpm_voltage_mode);
+ pp_table->Mp0DpmVoltageMode =
+ (uint8_t)(table_info->uc_mp0_dpm_voltage_mode);
+
+ pp_table->DisplayDpmVoltageMode =
+ (uint8_t)(table_info->uc_dcef_dpm_voltage_mode);
+
+ data->vddc_voltage_table.psi0_enable = voltage_table.psi0_enable;
+ data->vddc_voltage_table.psi1_enable = voltage_table.psi1_enable;
+
+ if (data->registry_data.ulv_support &&
+ table_info->us_ulv_voltage_offset) {
+ result = vega10_populate_ulv_state(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize ULV state!",
+ return result);
+ }
+
+ result = vega10_populate_smc_link_levels(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize Link Level!",
+ return result);
+
+ result = vega10_populate_all_graphic_levels(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize Graphics Level!",
+ return result);
+
+ result = vega10_populate_all_memory_levels(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize Memory Level!",
+ return result);
+
+ vega10_populate_vddc_soc_levels(hwmgr);
+
+ result = vega10_populate_all_display_clock_levels(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize Display Level!",
+ return result);
+
+ result = vega10_populate_smc_vce_levels(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize VCE Level!",
+ return result);
+
+ result = vega10_populate_smc_uvd_levels(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize UVD Level!",
+ return result);
+
+ if (data->registry_data.clock_stretcher_support) {
+ result = vega10_populate_clock_stretcher_table(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to populate Clock Stretcher Table!",
+ return result);
+ }
+
+ result = pp_atomfwctrl_get_vbios_bootup_values(hwmgr, &boot_up_values);
+ if (!result) {
+ data->vbios_boot_state.vddc = boot_up_values.usVddc;
+ data->vbios_boot_state.vddci = boot_up_values.usVddci;
+ data->vbios_boot_state.mvddc = boot_up_values.usMvddc;
+ data->vbios_boot_state.gfx_clock = boot_up_values.ulGfxClk;
+ data->vbios_boot_state.mem_clock = boot_up_values.ulUClk;
+ pp_atomfwctrl_get_clk_information_by_clkid(hwmgr,
+ SMU9_SYSPLL0_SOCCLK_ID, 0, &boot_up_values.ulSocClk);
+
+ pp_atomfwctrl_get_clk_information_by_clkid(hwmgr,
+ SMU9_SYSPLL0_DCEFCLK_ID, 0, &boot_up_values.ulDCEFClk);
+
+ data->vbios_boot_state.soc_clock = boot_up_values.ulSocClk;
+ data->vbios_boot_state.dcef_clock = boot_up_values.ulDCEFClk;
+ if (0 != boot_up_values.usVddc) {
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetFloorSocVoltage,
+ (boot_up_values.usVddc * 4),
+ NULL);
+ data->vbios_boot_state.bsoc_vddc_lock = true;
+ } else {
+ data->vbios_boot_state.bsoc_vddc_lock = false;
+ }
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetMinDeepSleepDcefclk,
+ (uint32_t)(data->vbios_boot_state.dcef_clock / 100),
+ NULL);
+ }
+
+ result = vega10_populate_avfs_parameters(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize AVFS Parameters!",
+ return result);
+
+ result = vega10_populate_gpio_parameters(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize GPIO Parameters!",
+ return result);
+
+ pp_table->GfxclkAverageAlpha = (uint8_t)
+ (data->gfxclk_average_alpha);
+ pp_table->SocclkAverageAlpha = (uint8_t)
+ (data->socclk_average_alpha);
+ pp_table->UclkAverageAlpha = (uint8_t)
+ (data->uclk_average_alpha);
+ pp_table->GfxActivityAverageAlpha = (uint8_t)
+ (data->gfx_activity_average_alpha);
+
+ vega10_populate_and_upload_avfs_fuse_override(hwmgr);
+
+ result = smum_smc_table_manager(hwmgr, (uint8_t *)pp_table, PPTABLE, false);
+
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to upload PPtable!", return result);
+
+ result = vega10_avfs_enable(hwmgr, true);
+ PP_ASSERT_WITH_CODE(!result, "Attempt to enable AVFS feature Failed!",
+ return result);
+ vega10_acg_enable(hwmgr);
+
+ return 0;
+}
+
+static int vega10_enable_thermal_protection(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->smu_features[GNLD_THERMAL].supported) {
+ if (data->smu_features[GNLD_THERMAL].enabled)
+ pr_info("THERMAL Feature Already enabled!");
+
+ PP_ASSERT_WITH_CODE(
+ !vega10_enable_smc_features(hwmgr,
+ true,
+ data->smu_features[GNLD_THERMAL].smu_feature_bitmap),
+ "Enable THERMAL Feature Failed!",
+ return -1);
+ data->smu_features[GNLD_THERMAL].enabled = true;
+ }
+
+ return 0;
+}
+
+static int vega10_disable_thermal_protection(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->smu_features[GNLD_THERMAL].supported) {
+ if (!data->smu_features[GNLD_THERMAL].enabled)
+ pr_info("THERMAL Feature Already disabled!");
+
+ PP_ASSERT_WITH_CODE(
+ !vega10_enable_smc_features(hwmgr,
+ false,
+ data->smu_features[GNLD_THERMAL].smu_feature_bitmap),
+ "disable THERMAL Feature Failed!",
+ return -1);
+ data->smu_features[GNLD_THERMAL].enabled = false;
+ }
+
+ return 0;
+}
+
+static int vega10_enable_vrhot_feature(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (PP_CAP(PHM_PlatformCaps_RegulatorHot)) {
+ if (data->smu_features[GNLD_VR0HOT].supported) {
+ PP_ASSERT_WITH_CODE(
+ !vega10_enable_smc_features(hwmgr,
+ true,
+ data->smu_features[GNLD_VR0HOT].smu_feature_bitmap),
+ "Attempt to Enable VR0 Hot feature Failed!",
+ return -1);
+ data->smu_features[GNLD_VR0HOT].enabled = true;
+ } else {
+ if (data->smu_features[GNLD_VR1HOT].supported) {
+ PP_ASSERT_WITH_CODE(
+ !vega10_enable_smc_features(hwmgr,
+ true,
+ data->smu_features[GNLD_VR1HOT].smu_feature_bitmap),
+ "Attempt to Enable VR0 Hot feature Failed!",
+ return -1);
+ data->smu_features[GNLD_VR1HOT].enabled = true;
+ }
+ }
+ }
+ return 0;
+}
+
+static int vega10_enable_ulv(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->registry_data.ulv_support) {
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ true, data->smu_features[GNLD_ULV].smu_feature_bitmap),
+ "Enable ULV Feature Failed!",
+ return -1);
+ data->smu_features[GNLD_ULV].enabled = true;
+ }
+
+ return 0;
+}
+
+static int vega10_disable_ulv(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->registry_data.ulv_support) {
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ false, data->smu_features[GNLD_ULV].smu_feature_bitmap),
+ "disable ULV Feature Failed!",
+ return -EINVAL);
+ data->smu_features[GNLD_ULV].enabled = false;
+ }
+
+ return 0;
+}
+
+static int vega10_enable_deep_sleep_master_switch(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->smu_features[GNLD_DS_GFXCLK].supported) {
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ true, data->smu_features[GNLD_DS_GFXCLK].smu_feature_bitmap),
+ "Attempt to Enable DS_GFXCLK Feature Failed!",
+ return -EINVAL);
+ data->smu_features[GNLD_DS_GFXCLK].enabled = true;
+ }
+
+ if (data->smu_features[GNLD_DS_SOCCLK].supported) {
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ true, data->smu_features[GNLD_DS_SOCCLK].smu_feature_bitmap),
+ "Attempt to Enable DS_SOCCLK Feature Failed!",
+ return -EINVAL);
+ data->smu_features[GNLD_DS_SOCCLK].enabled = true;
+ }
+
+ if (data->smu_features[GNLD_DS_LCLK].supported) {
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ true, data->smu_features[GNLD_DS_LCLK].smu_feature_bitmap),
+ "Attempt to Enable DS_LCLK Feature Failed!",
+ return -EINVAL);
+ data->smu_features[GNLD_DS_LCLK].enabled = true;
+ }
+
+ if (data->smu_features[GNLD_DS_DCEFCLK].supported) {
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ true, data->smu_features[GNLD_DS_DCEFCLK].smu_feature_bitmap),
+ "Attempt to Enable DS_DCEFCLK Feature Failed!",
+ return -EINVAL);
+ data->smu_features[GNLD_DS_DCEFCLK].enabled = true;
+ }
+
+ return 0;
+}
+
+static int vega10_disable_deep_sleep_master_switch(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->smu_features[GNLD_DS_GFXCLK].supported) {
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ false, data->smu_features[GNLD_DS_GFXCLK].smu_feature_bitmap),
+ "Attempt to disable DS_GFXCLK Feature Failed!",
+ return -EINVAL);
+ data->smu_features[GNLD_DS_GFXCLK].enabled = false;
+ }
+
+ if (data->smu_features[GNLD_DS_SOCCLK].supported) {
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ false, data->smu_features[GNLD_DS_SOCCLK].smu_feature_bitmap),
+ "Attempt to disable DS_ Feature Failed!",
+ return -EINVAL);
+ data->smu_features[GNLD_DS_SOCCLK].enabled = false;
+ }
+
+ if (data->smu_features[GNLD_DS_LCLK].supported) {
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ false, data->smu_features[GNLD_DS_LCLK].smu_feature_bitmap),
+ "Attempt to disable DS_LCLK Feature Failed!",
+ return -EINVAL);
+ data->smu_features[GNLD_DS_LCLK].enabled = false;
+ }
+
+ if (data->smu_features[GNLD_DS_DCEFCLK].supported) {
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ false, data->smu_features[GNLD_DS_DCEFCLK].smu_feature_bitmap),
+ "Attempt to disable DS_DCEFCLK Feature Failed!",
+ return -EINVAL);
+ data->smu_features[GNLD_DS_DCEFCLK].enabled = false;
+ }
+
+ return 0;
+}
+
+static int vega10_stop_dpm(struct pp_hwmgr *hwmgr, uint32_t bitmap)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ uint32_t i, feature_mask = 0;
+
+ if (!hwmgr->not_vf)
+ return 0;
+
+ if(data->smu_features[GNLD_LED_DISPLAY].supported == true){
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ false, data->smu_features[GNLD_LED_DISPLAY].smu_feature_bitmap),
+ "Attempt to disable LED DPM feature failed!", return -EINVAL);
+ data->smu_features[GNLD_LED_DISPLAY].enabled = false;
+ }
+
+ for (i = 0; i < GNLD_DPM_MAX; i++) {
+ if (data->smu_features[i].smu_feature_bitmap & bitmap) {
+ if (data->smu_features[i].supported) {
+ if (data->smu_features[i].enabled) {
+ feature_mask |= data->smu_features[i].
+ smu_feature_bitmap;
+ data->smu_features[i].enabled = false;
+ }
+ }
+ }
+ }
+
+ vega10_enable_smc_features(hwmgr, false, feature_mask);
+
+ return 0;
+}
+
+/**
+ * @brief Tell SMC to enabled the supported DPMs.
+ *
+ * @param hwmgr - the address of the powerplay hardware manager.
+ * @Param bitmap - bitmap for the features to enabled.
+ * @return 0 on at least one DPM is successfully enabled.
+ */
+static int vega10_start_dpm(struct pp_hwmgr *hwmgr, uint32_t bitmap)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ uint32_t i, feature_mask = 0;
+
+ for (i = 0; i < GNLD_DPM_MAX; i++) {
+ if (data->smu_features[i].smu_feature_bitmap & bitmap) {
+ if (data->smu_features[i].supported) {
+ if (!data->smu_features[i].enabled) {
+ feature_mask |= data->smu_features[i].
+ smu_feature_bitmap;
+ data->smu_features[i].enabled = true;
+ }
+ }
+ }
+ }
+
+ if (vega10_enable_smc_features(hwmgr,
+ true, feature_mask)) {
+ for (i = 0; i < GNLD_DPM_MAX; i++) {
+ if (data->smu_features[i].smu_feature_bitmap &
+ feature_mask)
+ data->smu_features[i].enabled = false;
+ }
+ }
+
+ if(data->smu_features[GNLD_LED_DISPLAY].supported == true){
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ true, data->smu_features[GNLD_LED_DISPLAY].smu_feature_bitmap),
+ "Attempt to Enable LED DPM feature Failed!", return -EINVAL);
+ data->smu_features[GNLD_LED_DISPLAY].enabled = true;
+ }
+
+ if (data->vbios_boot_state.bsoc_vddc_lock) {
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetFloorSocVoltage, 0,
+ NULL);
+ data->vbios_boot_state.bsoc_vddc_lock = false;
+ }
+
+ if (PP_CAP(PHM_PlatformCaps_Falcon_QuickTransition)) {
+ if (data->smu_features[GNLD_ACDC].supported) {
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ true, data->smu_features[GNLD_ACDC].smu_feature_bitmap),
+ "Attempt to Enable DS_GFXCLK Feature Failed!",
+ return -1);
+ data->smu_features[GNLD_ACDC].enabled = true;
+ }
+ }
+
+ return 0;
+}
+
+static int vega10_enable_disable_PCC_limit_feature(struct pp_hwmgr *hwmgr, bool enable)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->smu_features[GNLD_PCC_LIMIT].supported) {
+ if (enable == data->smu_features[GNLD_PCC_LIMIT].enabled)
+ pr_info("GNLD_PCC_LIMIT has been %s \n", enable ? "enabled" : "disabled");
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ enable, data->smu_features[GNLD_PCC_LIMIT].smu_feature_bitmap),
+ "Attempt to Enable PCC Limit feature Failed!",
+ return -EINVAL);
+ data->smu_features[GNLD_PCC_LIMIT].enabled = enable;
+ }
+
+ return 0;
+}
+
+static int vega10_enable_dpm_tasks(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ int tmp_result, result = 0;
+
+ if (hwmgr->not_vf) {
+ vega10_enable_disable_PCC_limit_feature(hwmgr, true);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_ConfigureTelemetry, data->config_telemetry,
+ NULL);
+
+ tmp_result = vega10_construct_voltage_tables(hwmgr);
+ PP_ASSERT_WITH_CODE(!tmp_result,
+ "Failed to construct voltage tables!",
+ result = tmp_result);
+ }
+
+ if (hwmgr->not_vf || hwmgr->pp_one_vf) {
+ tmp_result = vega10_init_smc_table(hwmgr);
+ PP_ASSERT_WITH_CODE(!tmp_result,
+ "Failed to initialize SMC table!",
+ result = tmp_result);
+ }
+
+ if (hwmgr->not_vf) {
+ if (PP_CAP(PHM_PlatformCaps_ThermalController)) {
+ tmp_result = vega10_enable_thermal_protection(hwmgr);
+ PP_ASSERT_WITH_CODE(!tmp_result,
+ "Failed to enable thermal protection!",
+ result = tmp_result);
+ }
+
+ tmp_result = vega10_enable_vrhot_feature(hwmgr);
+ PP_ASSERT_WITH_CODE(!tmp_result,
+ "Failed to enable VR hot feature!",
+ result = tmp_result);
+
+ tmp_result = vega10_enable_deep_sleep_master_switch(hwmgr);
+ PP_ASSERT_WITH_CODE(!tmp_result,
+ "Failed to enable deep sleep master switch!",
+ result = tmp_result);
+ }
+
+ if (hwmgr->not_vf) {
+ tmp_result = vega10_start_dpm(hwmgr, SMC_DPM_FEATURES);
+ PP_ASSERT_WITH_CODE(!tmp_result,
+ "Failed to start DPM!", result = tmp_result);
+ }
+
+ if (hwmgr->not_vf) {
+ /* enable didt, do not abort if failed didt */
+ tmp_result = vega10_enable_didt_config(hwmgr);
+ PP_ASSERT(!tmp_result,
+ "Failed to enable didt config!");
+ }
+
+ tmp_result = vega10_enable_power_containment(hwmgr);
+ PP_ASSERT_WITH_CODE(!tmp_result,
+ "Failed to enable power containment!",
+ result = tmp_result);
+
+ if (hwmgr->not_vf) {
+ tmp_result = vega10_power_control_set_level(hwmgr);
+ PP_ASSERT_WITH_CODE(!tmp_result,
+ "Failed to power control set level!",
+ result = tmp_result);
+
+ tmp_result = vega10_enable_ulv(hwmgr);
+ PP_ASSERT_WITH_CODE(!tmp_result,
+ "Failed to enable ULV!",
+ result = tmp_result);
+ }
+
+ return result;
+}
+
+static int vega10_get_power_state_size(struct pp_hwmgr *hwmgr)
+{
+ return sizeof(struct vega10_power_state);
+}
+
+static int vega10_get_pp_table_entry_callback_func(struct pp_hwmgr *hwmgr,
+ void *state, struct pp_power_state *power_state,
+ void *pp_table, uint32_t classification_flag)
+{
+ ATOM_Vega10_GFXCLK_Dependency_Record_V2 *patom_record_V2;
+ struct vega10_power_state *vega10_power_state =
+ cast_phw_vega10_power_state(&(power_state->hardware));
+ struct vega10_performance_level *performance_level;
+ ATOM_Vega10_State *state_entry = (ATOM_Vega10_State *)state;
+ ATOM_Vega10_POWERPLAYTABLE *powerplay_table =
+ (ATOM_Vega10_POWERPLAYTABLE *)pp_table;
+ ATOM_Vega10_SOCCLK_Dependency_Table *socclk_dep_table =
+ (ATOM_Vega10_SOCCLK_Dependency_Table *)
+ (((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usSocclkDependencyTableOffset));
+ ATOM_Vega10_GFXCLK_Dependency_Table *gfxclk_dep_table =
+ (ATOM_Vega10_GFXCLK_Dependency_Table *)
+ (((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usGfxclkDependencyTableOffset));
+ ATOM_Vega10_MCLK_Dependency_Table *mclk_dep_table =
+ (ATOM_Vega10_MCLK_Dependency_Table *)
+ (((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usMclkDependencyTableOffset));
+
+
+ /* The following fields are not initialized here:
+ * id orderedList allStatesList
+ */
+ power_state->classification.ui_label =
+ (le16_to_cpu(state_entry->usClassification) &
+ ATOM_PPLIB_CLASSIFICATION_UI_MASK) >>
+ ATOM_PPLIB_CLASSIFICATION_UI_SHIFT;
+ power_state->classification.flags = classification_flag;
+ /* NOTE: There is a classification2 flag in BIOS
+ * that is not being used right now
+ */
+ power_state->classification.temporary_state = false;
+ power_state->classification.to_be_deleted = false;
+
+ power_state->validation.disallowOnDC =
+ ((le32_to_cpu(state_entry->ulCapsAndSettings) &
+ ATOM_Vega10_DISALLOW_ON_DC) != 0);
+
+ power_state->display.disableFrameModulation = false;
+ power_state->display.limitRefreshrate = false;
+ power_state->display.enableVariBright =
+ ((le32_to_cpu(state_entry->ulCapsAndSettings) &
+ ATOM_Vega10_ENABLE_VARIBRIGHT) != 0);
+
+ power_state->validation.supportedPowerLevels = 0;
+ power_state->uvd_clocks.VCLK = 0;
+ power_state->uvd_clocks.DCLK = 0;
+ power_state->temperatures.min = 0;
+ power_state->temperatures.max = 0;
+
+ performance_level = &(vega10_power_state->performance_levels
+ [vega10_power_state->performance_level_count++]);
+
+ PP_ASSERT_WITH_CODE(
+ (vega10_power_state->performance_level_count <
+ NUM_GFXCLK_DPM_LEVELS),
+ "Performance levels exceeds SMC limit!",
+ return -1);
+
+ PP_ASSERT_WITH_CODE(
+ (vega10_power_state->performance_level_count <=
+ hwmgr->platform_descriptor.
+ hardwareActivityPerformanceLevels),
+ "Performance levels exceeds Driver limit!",
+ return -1);
+
+ /* Performance levels are arranged from low to high. */
+ performance_level->soc_clock = socclk_dep_table->entries
+ [state_entry->ucSocClockIndexLow].ulClk;
+ performance_level->gfx_clock = gfxclk_dep_table->entries
+ [state_entry->ucGfxClockIndexLow].ulClk;
+ performance_level->mem_clock = mclk_dep_table->entries
+ [state_entry->ucMemClockIndexLow].ulMemClk;
+
+ performance_level = &(vega10_power_state->performance_levels
+ [vega10_power_state->performance_level_count++]);
+ performance_level->soc_clock = socclk_dep_table->entries
+ [state_entry->ucSocClockIndexHigh].ulClk;
+ if (gfxclk_dep_table->ucRevId == 0) {
+ /* under vega10 pp one vf mode, the gfx clk dpm need be lower
+ * to level-4 due to the limited 110w-power
+ */
+ if (hwmgr->pp_one_vf && (state_entry->ucGfxClockIndexHigh > 0))
+ performance_level->gfx_clock =
+ gfxclk_dep_table->entries[4].ulClk;
+ else
+ performance_level->gfx_clock = gfxclk_dep_table->entries
+ [state_entry->ucGfxClockIndexHigh].ulClk;
+ } else if (gfxclk_dep_table->ucRevId == 1) {
+ patom_record_V2 = (ATOM_Vega10_GFXCLK_Dependency_Record_V2 *)gfxclk_dep_table->entries;
+ if (hwmgr->pp_one_vf && (state_entry->ucGfxClockIndexHigh > 0))
+ performance_level->gfx_clock = patom_record_V2[4].ulClk;
+ else
+ performance_level->gfx_clock =
+ patom_record_V2[state_entry->ucGfxClockIndexHigh].ulClk;
+ }
+
+ performance_level->mem_clock = mclk_dep_table->entries
+ [state_entry->ucMemClockIndexHigh].ulMemClk;
+ return 0;
+}
+
+static int vega10_get_pp_table_entry(struct pp_hwmgr *hwmgr,
+ unsigned long entry_index, struct pp_power_state *state)
+{
+ int result;
+ struct vega10_power_state *ps;
+
+ state->hardware.magic = PhwVega10_Magic;
+
+ ps = cast_phw_vega10_power_state(&state->hardware);
+
+ result = vega10_get_powerplay_table_entry(hwmgr, entry_index, state,
+ vega10_get_pp_table_entry_callback_func);
+
+ /*
+ * This is the earliest time we have all the dependency table
+ * and the VBIOS boot state
+ */
+ /* set DC compatible flag if this state supports DC */
+ if (!state->validation.disallowOnDC)
+ ps->dc_compatible = true;
+
+ ps->uvd_clks.vclk = state->uvd_clocks.VCLK;
+ ps->uvd_clks.dclk = state->uvd_clocks.DCLK;
+
+ return 0;
+}
+
+static int vega10_patch_boot_state(struct pp_hwmgr *hwmgr,
+ struct pp_hw_power_state *hw_ps)
+{
+ return 0;
+}
+
+static int vega10_apply_state_adjust_rules(struct pp_hwmgr *hwmgr,
+ struct pp_power_state *request_ps,
+ const struct pp_power_state *current_ps)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ struct vega10_power_state *vega10_ps =
+ cast_phw_vega10_power_state(&request_ps->hardware);
+ uint32_t sclk;
+ uint32_t mclk;
+ struct PP_Clocks minimum_clocks = {0};
+ bool disable_mclk_switching;
+ bool disable_mclk_switching_for_frame_lock;
+ bool disable_mclk_switching_for_vr;
+ bool force_mclk_high;
+ const struct phm_clock_and_voltage_limits *max_limits;
+ uint32_t i;
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ int32_t count;
+ uint32_t stable_pstate_sclk_dpm_percentage;
+ uint32_t stable_pstate_sclk = 0, stable_pstate_mclk = 0;
+ uint32_t latency;
+
+ data->battery_state = (PP_StateUILabel_Battery ==
+ request_ps->classification.ui_label);
+
+ if (vega10_ps->performance_level_count != 2)
+ pr_info("VI should always have 2 performance levels");
+
+ max_limits = adev->pm.ac_power ?
+ &(hwmgr->dyn_state.max_clock_voltage_on_ac) :
+ &(hwmgr->dyn_state.max_clock_voltage_on_dc);
+
+ /* Cap clock DPM tables at DC MAX if it is in DC. */
+ if (!adev->pm.ac_power) {
+ for (i = 0; i < vega10_ps->performance_level_count; i++) {
+ if (vega10_ps->performance_levels[i].mem_clock >
+ max_limits->mclk)
+ vega10_ps->performance_levels[i].mem_clock =
+ max_limits->mclk;
+ if (vega10_ps->performance_levels[i].gfx_clock >
+ max_limits->sclk)
+ vega10_ps->performance_levels[i].gfx_clock =
+ max_limits->sclk;
+ }
+ }
+
+ /* result = PHM_CheckVBlankTime(hwmgr, &vblankTooShort);*/
+ minimum_clocks.engineClock = hwmgr->display_config->min_core_set_clock;
+ minimum_clocks.memoryClock = hwmgr->display_config->min_mem_set_clock;
+
+ if (PP_CAP(PHM_PlatformCaps_StablePState)) {
+ stable_pstate_sclk_dpm_percentage =
+ data->registry_data.stable_pstate_sclk_dpm_percentage;
+ PP_ASSERT_WITH_CODE(
+ data->registry_data.stable_pstate_sclk_dpm_percentage >= 1 &&
+ data->registry_data.stable_pstate_sclk_dpm_percentage <= 100,
+ "percent sclk value must range from 1% to 100%, setting default value",
+ stable_pstate_sclk_dpm_percentage = 75);
+
+ max_limits = &(hwmgr->dyn_state.max_clock_voltage_on_ac);
+ stable_pstate_sclk = (max_limits->sclk *
+ stable_pstate_sclk_dpm_percentage) / 100;
+
+ for (count = table_info->vdd_dep_on_sclk->count - 1;
+ count >= 0; count--) {
+ if (stable_pstate_sclk >=
+ table_info->vdd_dep_on_sclk->entries[count].clk) {
+ stable_pstate_sclk =
+ table_info->vdd_dep_on_sclk->entries[count].clk;
+ break;
+ }
+ }
+
+ if (count < 0)
+ stable_pstate_sclk = table_info->vdd_dep_on_sclk->entries[0].clk;
+
+ stable_pstate_mclk = max_limits->mclk;
+
+ minimum_clocks.engineClock = stable_pstate_sclk;
+ minimum_clocks.memoryClock = stable_pstate_mclk;
+ }
+
+ disable_mclk_switching_for_frame_lock =
+ PP_CAP(PHM_PlatformCaps_DisableMclkSwitchingForFrameLock);
+ disable_mclk_switching_for_vr =
+ PP_CAP(PHM_PlatformCaps_DisableMclkSwitchForVR);
+ force_mclk_high = PP_CAP(PHM_PlatformCaps_ForceMclkHigh);
+
+ if (hwmgr->display_config->num_display == 0)
+ disable_mclk_switching = false;
+ else
+ disable_mclk_switching = ((1 < hwmgr->display_config->num_display) &&
+ !hwmgr->display_config->multi_monitor_in_sync) ||
+ disable_mclk_switching_for_frame_lock ||
+ disable_mclk_switching_for_vr ||
+ force_mclk_high;
+
+ sclk = vega10_ps->performance_levels[0].gfx_clock;
+ mclk = vega10_ps->performance_levels[0].mem_clock;
+
+ if (sclk < minimum_clocks.engineClock)
+ sclk = (minimum_clocks.engineClock > max_limits->sclk) ?
+ max_limits->sclk : minimum_clocks.engineClock;
+
+ if (mclk < minimum_clocks.memoryClock)
+ mclk = (minimum_clocks.memoryClock > max_limits->mclk) ?
+ max_limits->mclk : minimum_clocks.memoryClock;
+
+ vega10_ps->performance_levels[0].gfx_clock = sclk;
+ vega10_ps->performance_levels[0].mem_clock = mclk;
+
+ if (vega10_ps->performance_levels[1].gfx_clock <
+ vega10_ps->performance_levels[0].gfx_clock)
+ vega10_ps->performance_levels[0].gfx_clock =
+ vega10_ps->performance_levels[1].gfx_clock;
+
+ if (disable_mclk_switching) {
+ /* Set Mclk the max of level 0 and level 1 */
+ if (mclk < vega10_ps->performance_levels[1].mem_clock)
+ mclk = vega10_ps->performance_levels[1].mem_clock;
+
+ /* Find the lowest MCLK frequency that is within
+ * the tolerable latency defined in DAL
+ */
+ latency = hwmgr->display_config->dce_tolerable_mclk_in_active_latency;
+ for (i = 0; i < data->mclk_latency_table.count; i++) {
+ if ((data->mclk_latency_table.entries[i].latency <= latency) &&
+ (data->mclk_latency_table.entries[i].frequency >=
+ vega10_ps->performance_levels[0].mem_clock) &&
+ (data->mclk_latency_table.entries[i].frequency <=
+ vega10_ps->performance_levels[1].mem_clock))
+ mclk = data->mclk_latency_table.entries[i].frequency;
+ }
+ vega10_ps->performance_levels[0].mem_clock = mclk;
+ } else {
+ if (vega10_ps->performance_levels[1].mem_clock <
+ vega10_ps->performance_levels[0].mem_clock)
+ vega10_ps->performance_levels[0].mem_clock =
+ vega10_ps->performance_levels[1].mem_clock;
+ }
+
+ if (PP_CAP(PHM_PlatformCaps_StablePState)) {
+ for (i = 0; i < vega10_ps->performance_level_count; i++) {
+ vega10_ps->performance_levels[i].gfx_clock = stable_pstate_sclk;
+ vega10_ps->performance_levels[i].mem_clock = stable_pstate_mclk;
+ }
+ }
+
+ return 0;
+}
+
+static int vega10_find_dpm_states_clocks_in_dpm_table(struct pp_hwmgr *hwmgr, const void *input)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ const struct phm_set_power_state_input *states =
+ (const struct phm_set_power_state_input *)input;
+ const struct vega10_power_state *vega10_ps =
+ cast_const_phw_vega10_power_state(states->pnew_state);
+ struct vega10_single_dpm_table *sclk_table = &(data->dpm_table.gfx_table);
+ uint32_t sclk = vega10_ps->performance_levels
+ [vega10_ps->performance_level_count - 1].gfx_clock;
+ struct vega10_single_dpm_table *mclk_table = &(data->dpm_table.mem_table);
+ uint32_t mclk = vega10_ps->performance_levels
+ [vega10_ps->performance_level_count - 1].mem_clock;
+ uint32_t i;
+
+ for (i = 0; i < sclk_table->count; i++) {
+ if (sclk == sclk_table->dpm_levels[i].value)
+ break;
+ }
+
+ if (i >= sclk_table->count) {
+ if (sclk > sclk_table->dpm_levels[i-1].value) {
+ data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_SCLK;
+ sclk_table->dpm_levels[i-1].value = sclk;
+ }
+ }
+
+ for (i = 0; i < mclk_table->count; i++) {
+ if (mclk == mclk_table->dpm_levels[i].value)
+ break;
+ }
+
+ if (i >= mclk_table->count) {
+ if (mclk > mclk_table->dpm_levels[i-1].value) {
+ data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_MCLK;
+ mclk_table->dpm_levels[i-1].value = mclk;
+ }
+ }
+
+ if (data->display_timing.num_existing_displays != hwmgr->display_config->num_display)
+ data->need_update_dpm_table |= DPMTABLE_UPDATE_MCLK;
+
+ return 0;
+}
+
+static int vega10_populate_and_upload_sclk_mclk_dpm_levels(
+ struct pp_hwmgr *hwmgr, const void *input)
+{
+ int result = 0;
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct vega10_dpm_table *dpm_table = &data->dpm_table;
+ struct vega10_odn_dpm_table *odn_table = &data->odn_dpm_table;
+ struct vega10_odn_clock_voltage_dependency_table *odn_clk_table = &odn_table->vdd_dep_on_sclk;
+ int count;
+
+ if (!data->need_update_dpm_table)
+ return 0;
+
+ if (hwmgr->od_enabled && data->need_update_dpm_table & DPMTABLE_OD_UPDATE_SCLK) {
+ for (count = 0; count < dpm_table->gfx_table.count; count++)
+ dpm_table->gfx_table.dpm_levels[count].value = odn_clk_table->entries[count].clk;
+ }
+
+ odn_clk_table = &odn_table->vdd_dep_on_mclk;
+ if (hwmgr->od_enabled && data->need_update_dpm_table & DPMTABLE_OD_UPDATE_MCLK) {
+ for (count = 0; count < dpm_table->mem_table.count; count++)
+ dpm_table->mem_table.dpm_levels[count].value = odn_clk_table->entries[count].clk;
+ }
+
+ if (data->need_update_dpm_table &
+ (DPMTABLE_OD_UPDATE_SCLK | DPMTABLE_UPDATE_SCLK | DPMTABLE_UPDATE_SOCCLK)) {
+ result = vega10_populate_all_graphic_levels(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Failed to populate SCLK during PopulateNewDPMClocksStates Function!",
+ return result);
+ }
+
+ if (data->need_update_dpm_table &
+ (DPMTABLE_OD_UPDATE_MCLK | DPMTABLE_UPDATE_MCLK)) {
+ result = vega10_populate_all_memory_levels(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Failed to populate MCLK during PopulateNewDPMClocksStates Function!",
+ return result);
+ }
+
+ vega10_populate_vddc_soc_levels(hwmgr);
+
+ return result;
+}
+
+static int vega10_trim_single_dpm_states(struct pp_hwmgr *hwmgr,
+ struct vega10_single_dpm_table *dpm_table,
+ uint32_t low_limit, uint32_t high_limit)
+{
+ uint32_t i;
+
+ for (i = 0; i < dpm_table->count; i++) {
+ if ((dpm_table->dpm_levels[i].value < low_limit) ||
+ (dpm_table->dpm_levels[i].value > high_limit))
+ dpm_table->dpm_levels[i].enabled = false;
+ else
+ dpm_table->dpm_levels[i].enabled = true;
+ }
+ return 0;
+}
+
+static int vega10_trim_single_dpm_states_with_mask(struct pp_hwmgr *hwmgr,
+ struct vega10_single_dpm_table *dpm_table,
+ uint32_t low_limit, uint32_t high_limit,
+ uint32_t disable_dpm_mask)
+{
+ uint32_t i;
+
+ for (i = 0; i < dpm_table->count; i++) {
+ if ((dpm_table->dpm_levels[i].value < low_limit) ||
+ (dpm_table->dpm_levels[i].value > high_limit))
+ dpm_table->dpm_levels[i].enabled = false;
+ else if (!((1 << i) & disable_dpm_mask))
+ dpm_table->dpm_levels[i].enabled = false;
+ else
+ dpm_table->dpm_levels[i].enabled = true;
+ }
+ return 0;
+}
+
+static int vega10_trim_dpm_states(struct pp_hwmgr *hwmgr,
+ const struct vega10_power_state *vega10_ps)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ uint32_t high_limit_count;
+
+ PP_ASSERT_WITH_CODE((vega10_ps->performance_level_count >= 1),
+ "power state did not have any performance level",
+ return -1);
+
+ high_limit_count = (vega10_ps->performance_level_count == 1) ? 0 : 1;
+
+ vega10_trim_single_dpm_states(hwmgr,
+ &(data->dpm_table.soc_table),
+ vega10_ps->performance_levels[0].soc_clock,
+ vega10_ps->performance_levels[high_limit_count].soc_clock);
+
+ vega10_trim_single_dpm_states_with_mask(hwmgr,
+ &(data->dpm_table.gfx_table),
+ vega10_ps->performance_levels[0].gfx_clock,
+ vega10_ps->performance_levels[high_limit_count].gfx_clock,
+ data->disable_dpm_mask);
+
+ vega10_trim_single_dpm_states(hwmgr,
+ &(data->dpm_table.mem_table),
+ vega10_ps->performance_levels[0].mem_clock,
+ vega10_ps->performance_levels[high_limit_count].mem_clock);
+
+ return 0;
+}
+
+static uint32_t vega10_find_lowest_dpm_level(
+ struct vega10_single_dpm_table *table)
+{
+ uint32_t i;
+
+ for (i = 0; i < table->count; i++) {
+ if (table->dpm_levels[i].enabled)
+ break;
+ }
+
+ return i;
+}
+
+static uint32_t vega10_find_highest_dpm_level(
+ struct vega10_single_dpm_table *table)
+{
+ uint32_t i = 0;
+
+ if (table->count <= MAX_REGULAR_DPM_NUMBER) {
+ for (i = table->count; i > 0; i--) {
+ if (table->dpm_levels[i - 1].enabled)
+ return i - 1;
+ }
+ } else {
+ pr_info("DPM Table Has Too Many Entries!");
+ return MAX_REGULAR_DPM_NUMBER - 1;
+ }
+
+ return i;
+}
+
+static void vega10_apply_dal_minimum_voltage_request(
+ struct pp_hwmgr *hwmgr)
+{
+ return;
+}
+
+static int vega10_get_soc_index_for_max_uclk(struct pp_hwmgr *hwmgr)
+{
+ struct phm_ppt_v1_clock_voltage_dependency_table *vdd_dep_table_on_mclk;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+
+ vdd_dep_table_on_mclk = table_info->vdd_dep_on_mclk;
+
+ return vdd_dep_table_on_mclk->entries[NUM_UCLK_DPM_LEVELS - 1].vddInd + 1;
+}
+
+static int vega10_upload_dpm_bootup_level(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ uint32_t socclk_idx;
+
+ vega10_apply_dal_minimum_voltage_request(hwmgr);
+
+ if (!data->registry_data.sclk_dpm_key_disabled) {
+ if (data->smc_state_table.gfx_boot_level !=
+ data->dpm_table.gfx_table.dpm_state.soft_min_level) {
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMinGfxclkByIndex,
+ data->smc_state_table.gfx_boot_level,
+ NULL);
+
+ data->dpm_table.gfx_table.dpm_state.soft_min_level =
+ data->smc_state_table.gfx_boot_level;
+ }
+ }
+
+ if (!data->registry_data.mclk_dpm_key_disabled) {
+ if (data->smc_state_table.mem_boot_level !=
+ data->dpm_table.mem_table.dpm_state.soft_min_level) {
+ if ((data->smc_state_table.mem_boot_level == NUM_UCLK_DPM_LEVELS - 1)
+ && hwmgr->not_vf) {
+ socclk_idx = vega10_get_soc_index_for_max_uclk(hwmgr);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMinSocclkByIndex,
+ socclk_idx,
+ NULL);
+ } else {
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMinUclkByIndex,
+ data->smc_state_table.mem_boot_level,
+ NULL);
+ }
+ data->dpm_table.mem_table.dpm_state.soft_min_level =
+ data->smc_state_table.mem_boot_level;
+ }
+ }
+
+ if (!hwmgr->not_vf)
+ return 0;
+
+ if (!data->registry_data.socclk_dpm_key_disabled) {
+ if (data->smc_state_table.soc_boot_level !=
+ data->dpm_table.soc_table.dpm_state.soft_min_level) {
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMinSocclkByIndex,
+ data->smc_state_table.soc_boot_level,
+ NULL);
+ data->dpm_table.soc_table.dpm_state.soft_min_level =
+ data->smc_state_table.soc_boot_level;
+ }
+ }
+
+ return 0;
+}
+
+static int vega10_upload_dpm_max_level(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ vega10_apply_dal_minimum_voltage_request(hwmgr);
+
+ if (!data->registry_data.sclk_dpm_key_disabled) {
+ if (data->smc_state_table.gfx_max_level !=
+ data->dpm_table.gfx_table.dpm_state.soft_max_level) {
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxGfxclkByIndex,
+ data->smc_state_table.gfx_max_level,
+ NULL);
+ data->dpm_table.gfx_table.dpm_state.soft_max_level =
+ data->smc_state_table.gfx_max_level;
+ }
+ }
+
+ if (!data->registry_data.mclk_dpm_key_disabled) {
+ if (data->smc_state_table.mem_max_level !=
+ data->dpm_table.mem_table.dpm_state.soft_max_level) {
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxUclkByIndex,
+ data->smc_state_table.mem_max_level,
+ NULL);
+ data->dpm_table.mem_table.dpm_state.soft_max_level =
+ data->smc_state_table.mem_max_level;
+ }
+ }
+
+ if (!hwmgr->not_vf)
+ return 0;
+
+ if (!data->registry_data.socclk_dpm_key_disabled) {
+ if (data->smc_state_table.soc_max_level !=
+ data->dpm_table.soc_table.dpm_state.soft_max_level) {
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMaxSocclkByIndex,
+ data->smc_state_table.soc_max_level,
+ NULL);
+ data->dpm_table.soc_table.dpm_state.soft_max_level =
+ data->smc_state_table.soc_max_level;
+ }
+ }
+
+ return 0;
+}
+
+static int vega10_generate_dpm_level_enable_mask(
+ struct pp_hwmgr *hwmgr, const void *input)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ const struct phm_set_power_state_input *states =
+ (const struct phm_set_power_state_input *)input;
+ const struct vega10_power_state *vega10_ps =
+ cast_const_phw_vega10_power_state(states->pnew_state);
+ int i;
+
+ PP_ASSERT_WITH_CODE(!vega10_trim_dpm_states(hwmgr, vega10_ps),
+ "Attempt to Trim DPM States Failed!",
+ return -1);
+
+ data->smc_state_table.gfx_boot_level =
+ vega10_find_lowest_dpm_level(&(data->dpm_table.gfx_table));
+ data->smc_state_table.gfx_max_level =
+ vega10_find_highest_dpm_level(&(data->dpm_table.gfx_table));
+ data->smc_state_table.mem_boot_level =
+ vega10_find_lowest_dpm_level(&(data->dpm_table.mem_table));
+ data->smc_state_table.mem_max_level =
+ vega10_find_highest_dpm_level(&(data->dpm_table.mem_table));
+ data->smc_state_table.soc_boot_level =
+ vega10_find_lowest_dpm_level(&(data->dpm_table.soc_table));
+ data->smc_state_table.soc_max_level =
+ vega10_find_highest_dpm_level(&(data->dpm_table.soc_table));
+
+ PP_ASSERT_WITH_CODE(!vega10_upload_dpm_bootup_level(hwmgr),
+ "Attempt to upload DPM Bootup Levels Failed!",
+ return -1);
+ PP_ASSERT_WITH_CODE(!vega10_upload_dpm_max_level(hwmgr),
+ "Attempt to upload DPM Max Levels Failed!",
+ return -1);
+ for(i = data->smc_state_table.gfx_boot_level; i < data->smc_state_table.gfx_max_level; i++)
+ data->dpm_table.gfx_table.dpm_levels[i].enabled = true;
+
+
+ for(i = data->smc_state_table.mem_boot_level; i < data->smc_state_table.mem_max_level; i++)
+ data->dpm_table.mem_table.dpm_levels[i].enabled = true;
+
+ for (i = data->smc_state_table.soc_boot_level; i < data->smc_state_table.soc_max_level; i++)
+ data->dpm_table.soc_table.dpm_levels[i].enabled = true;
+
+ return 0;
+}
+
+int vega10_enable_disable_vce_dpm(struct pp_hwmgr *hwmgr, bool enable)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->smu_features[GNLD_DPM_VCE].supported) {
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ enable,
+ data->smu_features[GNLD_DPM_VCE].smu_feature_bitmap),
+ "Attempt to Enable/Disable DPM VCE Failed!",
+ return -1);
+ data->smu_features[GNLD_DPM_VCE].enabled = enable;
+ }
+
+ return 0;
+}
+
+static int vega10_update_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ uint32_t low_sclk_interrupt_threshold = 0;
+
+ if (PP_CAP(PHM_PlatformCaps_SclkThrottleLowNotification) &&
+ (data->low_sclk_interrupt_threshold != 0)) {
+ low_sclk_interrupt_threshold =
+ data->low_sclk_interrupt_threshold;
+
+ data->smc_state_table.pp_table.LowGfxclkInterruptThreshold =
+ cpu_to_le32(low_sclk_interrupt_threshold);
+
+ /* This message will also enable SmcToHost Interrupt */
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetLowGfxclkInterruptThreshold,
+ (uint32_t)low_sclk_interrupt_threshold,
+ NULL);
+ }
+
+ return 0;
+}
+
+static int vega10_set_power_state_tasks(struct pp_hwmgr *hwmgr,
+ const void *input)
+{
+ int tmp_result, result = 0;
+ struct vega10_hwmgr *data = hwmgr->backend;
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+
+ tmp_result = vega10_find_dpm_states_clocks_in_dpm_table(hwmgr, input);
+ PP_ASSERT_WITH_CODE(!tmp_result,
+ "Failed to find DPM states clocks in DPM table!",
+ result = tmp_result);
+
+ tmp_result = vega10_populate_and_upload_sclk_mclk_dpm_levels(hwmgr, input);
+ PP_ASSERT_WITH_CODE(!tmp_result,
+ "Failed to populate and upload SCLK MCLK DPM levels!",
+ result = tmp_result);
+
+ tmp_result = vega10_generate_dpm_level_enable_mask(hwmgr, input);
+ PP_ASSERT_WITH_CODE(!tmp_result,
+ "Failed to generate DPM level enabled mask!",
+ result = tmp_result);
+
+ tmp_result = vega10_update_sclk_threshold(hwmgr);
+ PP_ASSERT_WITH_CODE(!tmp_result,
+ "Failed to update SCLK threshold!",
+ result = tmp_result);
+
+ result = smum_smc_table_manager(hwmgr, (uint8_t *)pp_table, PPTABLE, false);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to upload PPtable!", return result);
+
+ /*
+ * If a custom pp table is loaded, set DPMTABLE_OD_UPDATE_VDDC flag.
+ * That effectively disables AVFS feature.
+ */
+ if(hwmgr->hardcode_pp_table != NULL)
+ data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_VDDC;
+
+ vega10_update_avfs(hwmgr);
+
+ /*
+ * Clear all OD flags except DPMTABLE_OD_UPDATE_VDDC.
+ * That will help to keep AVFS disabled.
+ */
+ data->need_update_dpm_table &= DPMTABLE_OD_UPDATE_VDDC;
+
+ return 0;
+}
+
+static uint32_t vega10_dpm_get_sclk(struct pp_hwmgr *hwmgr, bool low)
+{
+ struct pp_power_state *ps;
+ struct vega10_power_state *vega10_ps;
+
+ if (hwmgr == NULL)
+ return -EINVAL;
+
+ ps = hwmgr->request_ps;
+
+ if (ps == NULL)
+ return -EINVAL;
+
+ vega10_ps = cast_phw_vega10_power_state(&ps->hardware);
+
+ if (low)
+ return vega10_ps->performance_levels[0].gfx_clock;
+ else
+ return vega10_ps->performance_levels
+ [vega10_ps->performance_level_count - 1].gfx_clock;
+}
+
+static uint32_t vega10_dpm_get_mclk(struct pp_hwmgr *hwmgr, bool low)
+{
+ struct pp_power_state *ps;
+ struct vega10_power_state *vega10_ps;
+
+ if (hwmgr == NULL)
+ return -EINVAL;
+
+ ps = hwmgr->request_ps;
+
+ if (ps == NULL)
+ return -EINVAL;
+
+ vega10_ps = cast_phw_vega10_power_state(&ps->hardware);
+
+ if (low)
+ return vega10_ps->performance_levels[0].mem_clock;
+ else
+ return vega10_ps->performance_levels
+ [vega10_ps->performance_level_count-1].mem_clock;
+}
+
+static int vega10_get_gpu_power(struct pp_hwmgr *hwmgr,
+ uint32_t *query)
+{
+ uint32_t value;
+
+ if (!query)
+ return -EINVAL;
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetCurrPkgPwr, &value);
+
+ /* SMC returning actual watts, keep consistent with legacy asics, low 8 bit as 8 fractional bits */
+ *query = value << 8;
+
+ return 0;
+}
+
+static int vega10_read_sensor(struct pp_hwmgr *hwmgr, int idx,
+ void *value, int *size)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t sclk_mhz, mclk_idx, activity_percent = 0;
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct vega10_dpm_table *dpm_table = &data->dpm_table;
+ int ret = 0;
+ uint32_t val_vid;
+
+ switch (idx) {
+ case AMDGPU_PP_SENSOR_GFX_SCLK:
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetAverageGfxclkActualFrequency, &sclk_mhz);
+ *((uint32_t *)value) = sclk_mhz * 100;
+ break;
+ case AMDGPU_PP_SENSOR_GFX_MCLK:
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetCurrentUclkIndex, &mclk_idx);
+ if (mclk_idx < dpm_table->mem_table.count) {
+ *((uint32_t *)value) = dpm_table->mem_table.dpm_levels[mclk_idx].value;
+ *size = 4;
+ } else {
+ ret = -EINVAL;
+ }
+ break;
+ case AMDGPU_PP_SENSOR_GPU_LOAD:
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_GetAverageGfxActivity, 0,
+ &activity_percent);
+ *((uint32_t *)value) = activity_percent > 100 ? 100 : activity_percent;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GPU_TEMP:
+ *((uint32_t *)value) = vega10_thermal_get_temperature(hwmgr);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_HOTSPOT_TEMP:
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetTemperatureHotspot, (uint32_t *)value);
+ *((uint32_t *)value) = *((uint32_t *)value) *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_MEM_TEMP:
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetTemperatureHBM, (uint32_t *)value);
+ *((uint32_t *)value) = *((uint32_t *)value) *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_UVD_POWER:
+ *((uint32_t *)value) = data->uvd_power_gated ? 0 : 1;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_VCE_POWER:
+ *((uint32_t *)value) = data->vce_power_gated ? 0 : 1;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GPU_POWER:
+ ret = vega10_get_gpu_power(hwmgr, (uint32_t *)value);
+ break;
+ case AMDGPU_PP_SENSOR_VDDGFX:
+ val_vid = (RREG32_SOC15(SMUIO, 0, mmSMUSVI0_PLANE0_CURRENTVID) &
+ SMUSVI0_PLANE0_CURRENTVID__CURRENT_SVI0_PLANE0_VID_MASK) >>
+ SMUSVI0_PLANE0_CURRENTVID__CURRENT_SVI0_PLANE0_VID__SHIFT;
+ *((uint32_t *)value) = (uint32_t)convert_to_vddc((uint8_t)val_vid);
+ return 0;
+ case AMDGPU_PP_SENSOR_ENABLED_SMC_FEATURES_MASK:
+ ret = vega10_get_enabled_smc_features(hwmgr, (uint64_t *)value);
+ if (!ret)
+ *size = 8;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static void vega10_notify_smc_display_change(struct pp_hwmgr *hwmgr,
+ bool has_disp)
+{
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetUclkFastSwitch,
+ has_disp ? 1 : 0,
+ NULL);
+}
+
+static int vega10_display_clock_voltage_request(struct pp_hwmgr *hwmgr,
+ struct pp_display_clock_request *clock_req)
+{
+ int result = 0;
+ enum amd_pp_clock_type clk_type = clock_req->clock_type;
+ uint32_t clk_freq = clock_req->clock_freq_in_khz / 1000;
+ DSPCLK_e clk_select = 0;
+ uint32_t clk_request = 0;
+
+ switch (clk_type) {
+ case amd_pp_dcef_clock:
+ clk_select = DSPCLK_DCEFCLK;
+ break;
+ case amd_pp_disp_clock:
+ clk_select = DSPCLK_DISPCLK;
+ break;
+ case amd_pp_pixel_clock:
+ clk_select = DSPCLK_PIXCLK;
+ break;
+ case amd_pp_phy_clock:
+ clk_select = DSPCLK_PHYCLK;
+ break;
+ default:
+ pr_info("[DisplayClockVoltageRequest]Invalid Clock Type!");
+ result = -1;
+ break;
+ }
+
+ if (!result) {
+ clk_request = (clk_freq << 16) | clk_select;
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_RequestDisplayClockByFreq,
+ clk_request,
+ NULL);
+ }
+
+ return result;
+}
+
+static uint8_t vega10_get_uclk_index(struct pp_hwmgr *hwmgr,
+ struct phm_ppt_v1_clock_voltage_dependency_table *mclk_table,
+ uint32_t frequency)
+{
+ uint8_t count;
+ uint8_t i;
+
+ if (mclk_table == NULL || mclk_table->count == 0)
+ return 0;
+
+ count = (uint8_t)(mclk_table->count);
+
+ for(i = 0; i < count; i++) {
+ if(mclk_table->entries[i].clk >= frequency)
+ return i;
+ }
+
+ return i-1;
+}
+
+static int vega10_notify_smc_display_config_after_ps_adjustment(
+ struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct vega10_single_dpm_table *dpm_table =
+ &data->dpm_table.dcef_table;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)hwmgr->pptable;
+ struct phm_ppt_v1_clock_voltage_dependency_table *mclk_table = table_info->vdd_dep_on_mclk;
+ uint32_t idx;
+ struct PP_Clocks min_clocks = {0};
+ uint32_t i;
+ struct pp_display_clock_request clock_req;
+
+ if ((hwmgr->display_config->num_display > 1) &&
+ !hwmgr->display_config->multi_monitor_in_sync &&
+ !hwmgr->display_config->nb_pstate_switch_disable)
+ vega10_notify_smc_display_change(hwmgr, false);
+ else
+ vega10_notify_smc_display_change(hwmgr, true);
+
+ min_clocks.dcefClock = hwmgr->display_config->min_dcef_set_clk;
+ min_clocks.dcefClockInSR = hwmgr->display_config->min_dcef_deep_sleep_set_clk;
+ min_clocks.memoryClock = hwmgr->display_config->min_mem_set_clock;
+
+ for (i = 0; i < dpm_table->count; i++) {
+ if (dpm_table->dpm_levels[i].value == min_clocks.dcefClock)
+ break;
+ }
+
+ if (i < dpm_table->count) {
+ clock_req.clock_type = amd_pp_dcef_clock;
+ clock_req.clock_freq_in_khz = dpm_table->dpm_levels[i].value * 10;
+ if (!vega10_display_clock_voltage_request(hwmgr, &clock_req)) {
+ smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetMinDeepSleepDcefclk,
+ min_clocks.dcefClockInSR / 100,
+ NULL);
+ } else {
+ pr_info("Attempt to set Hard Min for DCEFCLK Failed!");
+ }
+ } else {
+ pr_debug("Cannot find requested DCEFCLK!");
+ }
+
+ if (min_clocks.memoryClock != 0) {
+ idx = vega10_get_uclk_index(hwmgr, mclk_table, min_clocks.memoryClock);
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_SetSoftMinUclkByIndex, idx,
+ NULL);
+ data->dpm_table.mem_table.dpm_state.soft_min_level= idx;
+ }
+
+ return 0;
+}
+
+static int vega10_force_dpm_highest(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ data->smc_state_table.gfx_boot_level =
+ data->smc_state_table.gfx_max_level =
+ vega10_find_highest_dpm_level(&(data->dpm_table.gfx_table));
+ data->smc_state_table.mem_boot_level =
+ data->smc_state_table.mem_max_level =
+ vega10_find_highest_dpm_level(&(data->dpm_table.mem_table));
+
+ PP_ASSERT_WITH_CODE(!vega10_upload_dpm_bootup_level(hwmgr),
+ "Failed to upload boot level to highest!",
+ return -1);
+
+ PP_ASSERT_WITH_CODE(!vega10_upload_dpm_max_level(hwmgr),
+ "Failed to upload dpm max level to highest!",
+ return -1);
+
+ return 0;
+}
+
+static int vega10_force_dpm_lowest(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ data->smc_state_table.gfx_boot_level =
+ data->smc_state_table.gfx_max_level =
+ vega10_find_lowest_dpm_level(&(data->dpm_table.gfx_table));
+ data->smc_state_table.mem_boot_level =
+ data->smc_state_table.mem_max_level =
+ vega10_find_lowest_dpm_level(&(data->dpm_table.mem_table));
+
+ PP_ASSERT_WITH_CODE(!vega10_upload_dpm_bootup_level(hwmgr),
+ "Failed to upload boot level to highest!",
+ return -1);
+
+ PP_ASSERT_WITH_CODE(!vega10_upload_dpm_max_level(hwmgr),
+ "Failed to upload dpm max level to highest!",
+ return -1);
+
+ return 0;
+
+}
+
+static int vega10_unforce_dpm_levels(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ data->smc_state_table.gfx_boot_level =
+ vega10_find_lowest_dpm_level(&(data->dpm_table.gfx_table));
+ data->smc_state_table.gfx_max_level =
+ vega10_find_highest_dpm_level(&(data->dpm_table.gfx_table));
+ data->smc_state_table.mem_boot_level =
+ vega10_find_lowest_dpm_level(&(data->dpm_table.mem_table));
+ data->smc_state_table.mem_max_level =
+ vega10_find_highest_dpm_level(&(data->dpm_table.mem_table));
+
+ PP_ASSERT_WITH_CODE(!vega10_upload_dpm_bootup_level(hwmgr),
+ "Failed to upload DPM Bootup Levels!",
+ return -1);
+
+ PP_ASSERT_WITH_CODE(!vega10_upload_dpm_max_level(hwmgr),
+ "Failed to upload DPM Max Levels!",
+ return -1);
+ return 0;
+}
+
+static int vega10_get_profiling_clk_mask(struct pp_hwmgr *hwmgr, enum amd_dpm_forced_level level,
+ uint32_t *sclk_mask, uint32_t *mclk_mask, uint32_t *soc_mask)
+{
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+
+ if (table_info->vdd_dep_on_sclk->count > VEGA10_UMD_PSTATE_GFXCLK_LEVEL &&
+ table_info->vdd_dep_on_socclk->count > VEGA10_UMD_PSTATE_SOCCLK_LEVEL &&
+ table_info->vdd_dep_on_mclk->count > VEGA10_UMD_PSTATE_MCLK_LEVEL) {
+ *sclk_mask = VEGA10_UMD_PSTATE_GFXCLK_LEVEL;
+ *soc_mask = VEGA10_UMD_PSTATE_SOCCLK_LEVEL;
+ *mclk_mask = VEGA10_UMD_PSTATE_MCLK_LEVEL;
+ hwmgr->pstate_sclk = table_info->vdd_dep_on_sclk->entries[VEGA10_UMD_PSTATE_GFXCLK_LEVEL].clk;
+ hwmgr->pstate_mclk = table_info->vdd_dep_on_mclk->entries[VEGA10_UMD_PSTATE_MCLK_LEVEL].clk;
+ }
+
+ if (level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK) {
+ *sclk_mask = 0;
+ } else if (level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK) {
+ *mclk_mask = 0;
+ } else if (level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ /* under vega10 pp one vf mode, the gfx clk dpm need be lower
+ * to level-4 due to the limited power
+ */
+ if (hwmgr->pp_one_vf)
+ *sclk_mask = 4;
+ else
+ *sclk_mask = table_info->vdd_dep_on_sclk->count - 1;
+ *soc_mask = table_info->vdd_dep_on_socclk->count - 1;
+ *mclk_mask = table_info->vdd_dep_on_mclk->count - 1;
+ }
+
+ return 0;
+}
+
+static void vega10_set_fan_control_mode(struct pp_hwmgr *hwmgr, uint32_t mode)
+{
+ if (!hwmgr->not_vf)
+ return;
+
+ switch (mode) {
+ case AMD_FAN_CTRL_NONE:
+ vega10_fan_ctrl_set_fan_speed_percent(hwmgr, 100);
+ break;
+ case AMD_FAN_CTRL_MANUAL:
+ if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl))
+ vega10_fan_ctrl_stop_smc_fan_control(hwmgr);
+ break;
+ case AMD_FAN_CTRL_AUTO:
+ if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl))
+ vega10_fan_ctrl_start_smc_fan_control(hwmgr);
+ break;
+ default:
+ break;
+ }
+}
+
+static int vega10_force_clock_level(struct pp_hwmgr *hwmgr,
+ enum pp_clock_type type, uint32_t mask)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ switch (type) {
+ case PP_SCLK:
+ data->smc_state_table.gfx_boot_level = mask ? (ffs(mask) - 1) : 0;
+ data->smc_state_table.gfx_max_level = mask ? (fls(mask) - 1) : 0;
+
+ PP_ASSERT_WITH_CODE(!vega10_upload_dpm_bootup_level(hwmgr),
+ "Failed to upload boot level to lowest!",
+ return -EINVAL);
+
+ PP_ASSERT_WITH_CODE(!vega10_upload_dpm_max_level(hwmgr),
+ "Failed to upload dpm max level to highest!",
+ return -EINVAL);
+ break;
+
+ case PP_MCLK:
+ data->smc_state_table.mem_boot_level = mask ? (ffs(mask) - 1) : 0;
+ data->smc_state_table.mem_max_level = mask ? (fls(mask) - 1) : 0;
+
+ PP_ASSERT_WITH_CODE(!vega10_upload_dpm_bootup_level(hwmgr),
+ "Failed to upload boot level to lowest!",
+ return -EINVAL);
+
+ PP_ASSERT_WITH_CODE(!vega10_upload_dpm_max_level(hwmgr),
+ "Failed to upload dpm max level to highest!",
+ return -EINVAL);
+
+ break;
+
+ case PP_SOCCLK:
+ data->smc_state_table.soc_boot_level = mask ? (ffs(mask) - 1) : 0;
+ data->smc_state_table.soc_max_level = mask ? (fls(mask) - 1) : 0;
+
+ PP_ASSERT_WITH_CODE(!vega10_upload_dpm_bootup_level(hwmgr),
+ "Failed to upload boot level to lowest!",
+ return -EINVAL);
+
+ PP_ASSERT_WITH_CODE(!vega10_upload_dpm_max_level(hwmgr),
+ "Failed to upload dpm max level to highest!",
+ return -EINVAL);
+
+ break;
+
+ case PP_DCEFCLK:
+ pr_info("Setting DCEFCLK min/max dpm level is not supported!\n");
+ break;
+
+ case PP_PCIE:
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int vega10_dpm_force_dpm_level(struct pp_hwmgr *hwmgr,
+ enum amd_dpm_forced_level level)
+{
+ int ret = 0;
+ uint32_t sclk_mask = 0;
+ uint32_t mclk_mask = 0;
+ uint32_t soc_mask = 0;
+
+ if (hwmgr->pstate_sclk == 0)
+ vega10_get_profiling_clk_mask(hwmgr, level, &sclk_mask, &mclk_mask, &soc_mask);
+
+ switch (level) {
+ case AMD_DPM_FORCED_LEVEL_HIGH:
+ ret = vega10_force_dpm_highest(hwmgr);
+ break;
+ case AMD_DPM_FORCED_LEVEL_LOW:
+ ret = vega10_force_dpm_lowest(hwmgr);
+ break;
+ case AMD_DPM_FORCED_LEVEL_AUTO:
+ ret = vega10_unforce_dpm_levels(hwmgr);
+ break;
+ case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+ ret = vega10_get_profiling_clk_mask(hwmgr, level, &sclk_mask, &mclk_mask, &soc_mask);
+ if (ret)
+ return ret;
+ vega10_force_clock_level(hwmgr, PP_SCLK, 1<<sclk_mask);
+ vega10_force_clock_level(hwmgr, PP_MCLK, 1<<mclk_mask);
+ break;
+ case AMD_DPM_FORCED_LEVEL_MANUAL:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_EXIT:
+ default:
+ break;
+ }
+
+ if (!hwmgr->not_vf)
+ return ret;
+
+ if (!ret) {
+ if (level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK && hwmgr->dpm_level != AMD_DPM_FORCED_LEVEL_PROFILE_PEAK)
+ vega10_set_fan_control_mode(hwmgr, AMD_FAN_CTRL_NONE);
+ else if (level != AMD_DPM_FORCED_LEVEL_PROFILE_PEAK && hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK)
+ vega10_set_fan_control_mode(hwmgr, AMD_FAN_CTRL_AUTO);
+ }
+
+ return ret;
+}
+
+static uint32_t vega10_get_fan_control_mode(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->smu_features[GNLD_FAN_CONTROL].enabled == false)
+ return AMD_FAN_CTRL_MANUAL;
+ else
+ return AMD_FAN_CTRL_AUTO;
+}
+
+static int vega10_get_dal_power_level(struct pp_hwmgr *hwmgr,
+ struct amd_pp_simple_clock_info *info)
+{
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)hwmgr->pptable;
+ struct phm_clock_and_voltage_limits *max_limits =
+ &table_info->max_clock_voltage_on_ac;
+
+ info->engine_max_clock = max_limits->sclk;
+ info->memory_max_clock = max_limits->mclk;
+
+ return 0;
+}
+
+static void vega10_get_sclks(struct pp_hwmgr *hwmgr,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)hwmgr->pptable;
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_table =
+ table_info->vdd_dep_on_sclk;
+ uint32_t i;
+
+ clocks->num_levels = 0;
+ for (i = 0; i < dep_table->count; i++) {
+ if (dep_table->entries[i].clk) {
+ clocks->data[clocks->num_levels].clocks_in_khz =
+ dep_table->entries[i].clk * 10;
+ clocks->num_levels++;
+ }
+ }
+
+}
+
+static void vega10_get_memclocks(struct pp_hwmgr *hwmgr,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)hwmgr->pptable;
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_table =
+ table_info->vdd_dep_on_mclk;
+ struct vega10_hwmgr *data = hwmgr->backend;
+ uint32_t j = 0;
+ uint32_t i;
+
+ for (i = 0; i < dep_table->count; i++) {
+ if (dep_table->entries[i].clk) {
+
+ clocks->data[j].clocks_in_khz =
+ dep_table->entries[i].clk * 10;
+ data->mclk_latency_table.entries[j].frequency =
+ dep_table->entries[i].clk;
+ clocks->data[j].latency_in_us =
+ data->mclk_latency_table.entries[j].latency = 25;
+ j++;
+ }
+ }
+ clocks->num_levels = data->mclk_latency_table.count = j;
+}
+
+static void vega10_get_dcefclocks(struct pp_hwmgr *hwmgr,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)hwmgr->pptable;
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_table =
+ table_info->vdd_dep_on_dcefclk;
+ uint32_t i;
+
+ for (i = 0; i < dep_table->count; i++) {
+ clocks->data[i].clocks_in_khz = dep_table->entries[i].clk * 10;
+ clocks->data[i].latency_in_us = 0;
+ clocks->num_levels++;
+ }
+}
+
+static void vega10_get_socclocks(struct pp_hwmgr *hwmgr,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)hwmgr->pptable;
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_table =
+ table_info->vdd_dep_on_socclk;
+ uint32_t i;
+
+ for (i = 0; i < dep_table->count; i++) {
+ clocks->data[i].clocks_in_khz = dep_table->entries[i].clk * 10;
+ clocks->data[i].latency_in_us = 0;
+ clocks->num_levels++;
+ }
+}
+
+static int vega10_get_clock_by_type_with_latency(struct pp_hwmgr *hwmgr,
+ enum amd_pp_clock_type type,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ switch (type) {
+ case amd_pp_sys_clock:
+ vega10_get_sclks(hwmgr, clocks);
+ break;
+ case amd_pp_mem_clock:
+ vega10_get_memclocks(hwmgr, clocks);
+ break;
+ case amd_pp_dcef_clock:
+ vega10_get_dcefclocks(hwmgr, clocks);
+ break;
+ case amd_pp_soc_clock:
+ vega10_get_socclocks(hwmgr, clocks);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int vega10_get_clock_by_type_with_voltage(struct pp_hwmgr *hwmgr,
+ enum amd_pp_clock_type type,
+ struct pp_clock_levels_with_voltage *clocks)
+{
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)hwmgr->pptable;
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_table;
+ uint32_t i;
+
+ switch (type) {
+ case amd_pp_mem_clock:
+ dep_table = table_info->vdd_dep_on_mclk;
+ break;
+ case amd_pp_dcef_clock:
+ dep_table = table_info->vdd_dep_on_dcefclk;
+ break;
+ case amd_pp_disp_clock:
+ dep_table = table_info->vdd_dep_on_dispclk;
+ break;
+ case amd_pp_pixel_clock:
+ dep_table = table_info->vdd_dep_on_pixclk;
+ break;
+ case amd_pp_phy_clock:
+ dep_table = table_info->vdd_dep_on_phyclk;
+ break;
+ default:
+ return -1;
+ }
+
+ for (i = 0; i < dep_table->count; i++) {
+ clocks->data[i].clocks_in_khz = dep_table->entries[i].clk * 10;
+ clocks->data[i].voltage_in_mv = (uint32_t)(table_info->vddc_lookup_table->
+ entries[dep_table->entries[i].vddInd].us_vdd);
+ clocks->num_levels++;
+ }
+
+ if (i < dep_table->count)
+ return -1;
+
+ return 0;
+}
+
+static int vega10_set_watermarks_for_clocks_ranges(struct pp_hwmgr *hwmgr,
+ void *clock_range)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct dm_pp_wm_sets_with_clock_ranges_soc15 *wm_with_clock_ranges = clock_range;
+ Watermarks_t *table = &(data->smc_state_table.water_marks_table);
+
+ if (!data->registry_data.disable_water_mark) {
+ smu_set_watermarks_for_clocks_ranges(table, wm_with_clock_ranges);
+ data->water_marks_bitmap = WaterMarksExist;
+ }
+
+ return 0;
+}
+
+static int vega10_get_ppfeature_status(struct pp_hwmgr *hwmgr, char *buf)
+{
+ static const char *ppfeature_name[] = {
+ "DPM_PREFETCHER",
+ "GFXCLK_DPM",
+ "UCLK_DPM",
+ "SOCCLK_DPM",
+ "UVD_DPM",
+ "VCE_DPM",
+ "ULV",
+ "MP0CLK_DPM",
+ "LINK_DPM",
+ "DCEFCLK_DPM",
+ "AVFS",
+ "GFXCLK_DS",
+ "SOCCLK_DS",
+ "LCLK_DS",
+ "PPT",
+ "TDC",
+ "THERMAL",
+ "GFX_PER_CU_CG",
+ "RM",
+ "DCEFCLK_DS",
+ "ACDC",
+ "VR0HOT",
+ "VR1HOT",
+ "FW_CTF",
+ "LED_DISPLAY",
+ "FAN_CONTROL",
+ "FAST_PPT",
+ "DIDT",
+ "ACG",
+ "PCC_LIMIT"};
+ static const char *output_title[] = {
+ "FEATURES",
+ "BITMASK",
+ "ENABLEMENT"};
+ uint64_t features_enabled;
+ int i;
+ int ret = 0;
+ int size = 0;
+
+ ret = vega10_get_enabled_smc_features(hwmgr, &features_enabled);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[EnableAllSmuFeatures] Failed to get enabled smc features!",
+ return ret);
+
+ size += sprintf(buf + size, "Current ppfeatures: 0x%016llx\n", features_enabled);
+ size += sprintf(buf + size, "%-19s %-22s %s\n",
+ output_title[0],
+ output_title[1],
+ output_title[2]);
+ for (i = 0; i < GNLD_FEATURES_MAX; i++) {
+ size += sprintf(buf + size, "%-19s 0x%016llx %6s\n",
+ ppfeature_name[i],
+ 1ULL << i,
+ (features_enabled & (1ULL << i)) ? "Y" : "N");
+ }
+
+ return size;
+}
+
+static int vega10_set_ppfeature_status(struct pp_hwmgr *hwmgr, uint64_t new_ppfeature_masks)
+{
+ uint64_t features_enabled;
+ uint64_t features_to_enable;
+ uint64_t features_to_disable;
+ int ret = 0;
+
+ if (new_ppfeature_masks >= (1ULL << GNLD_FEATURES_MAX))
+ return -EINVAL;
+
+ ret = vega10_get_enabled_smc_features(hwmgr, &features_enabled);
+ if (ret)
+ return ret;
+
+ features_to_disable =
+ features_enabled & ~new_ppfeature_masks;
+ features_to_enable =
+ ~features_enabled & new_ppfeature_masks;
+
+ pr_debug("features_to_disable 0x%llx\n", features_to_disable);
+ pr_debug("features_to_enable 0x%llx\n", features_to_enable);
+
+ if (features_to_disable) {
+ ret = vega10_enable_smc_features(hwmgr, false, features_to_disable);
+ if (ret)
+ return ret;
+ }
+
+ if (features_to_enable) {
+ ret = vega10_enable_smc_features(hwmgr, true, features_to_enable);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int vega10_print_clock_levels(struct pp_hwmgr *hwmgr,
+ enum pp_clock_type type, char *buf)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct vega10_single_dpm_table *sclk_table = &(data->dpm_table.gfx_table);
+ struct vega10_single_dpm_table *mclk_table = &(data->dpm_table.mem_table);
+ struct vega10_single_dpm_table *soc_table = &(data->dpm_table.soc_table);
+ struct vega10_single_dpm_table *dcef_table = &(data->dpm_table.dcef_table);
+ struct vega10_pcie_table *pcie_table = &(data->dpm_table.pcie_table);
+ struct vega10_odn_clock_voltage_dependency_table *podn_vdd_dep = NULL;
+
+ int i, now, size = 0, count = 0;
+
+ switch (type) {
+ case PP_SCLK:
+ if (data->registry_data.sclk_dpm_key_disabled)
+ break;
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetCurrentGfxclkIndex, &now);
+
+ if (hwmgr->pp_one_vf &&
+ (hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK))
+ count = 5;
+ else
+ count = sclk_table->count;
+ for (i = 0; i < count; i++)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ i, sclk_table->dpm_levels[i].value / 100,
+ (i == now) ? "*" : "");
+ break;
+ case PP_MCLK:
+ if (data->registry_data.mclk_dpm_key_disabled)
+ break;
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetCurrentUclkIndex, &now);
+
+ for (i = 0; i < mclk_table->count; i++)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ i, mclk_table->dpm_levels[i].value / 100,
+ (i == now) ? "*" : "");
+ break;
+ case PP_SOCCLK:
+ if (data->registry_data.socclk_dpm_key_disabled)
+ break;
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetCurrentSocclkIndex, &now);
+
+ for (i = 0; i < soc_table->count; i++)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ i, soc_table->dpm_levels[i].value / 100,
+ (i == now) ? "*" : "");
+ break;
+ case PP_DCEFCLK:
+ if (data->registry_data.dcefclk_dpm_key_disabled)
+ break;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_GetClockFreqMHz, CLK_DCEFCLK, &now);
+
+ for (i = 0; i < dcef_table->count; i++)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ i, dcef_table->dpm_levels[i].value / 100,
+ (dcef_table->dpm_levels[i].value / 100 == now) ?
+ "*" : "");
+ break;
+ case PP_PCIE:
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetCurrentLinkIndex, &now);
+
+ for (i = 0; i < pcie_table->count; i++)
+ size += sprintf(buf + size, "%d: %s %s\n", i,
+ (pcie_table->pcie_gen[i] == 0) ? "2.5GT/s, x1" :
+ (pcie_table->pcie_gen[i] == 1) ? "5.0GT/s, x16" :
+ (pcie_table->pcie_gen[i] == 2) ? "8.0GT/s, x16" : "",
+ (i == now) ? "*" : "");
+ break;
+ case OD_SCLK:
+ if (hwmgr->od_enabled) {
+ size = sprintf(buf, "%s:\n", "OD_SCLK");
+ podn_vdd_dep = &data->odn_dpm_table.vdd_dep_on_sclk;
+ for (i = 0; i < podn_vdd_dep->count; i++)
+ size += sprintf(buf + size, "%d: %10uMhz %10umV\n",
+ i, podn_vdd_dep->entries[i].clk / 100,
+ podn_vdd_dep->entries[i].vddc);
+ }
+ break;
+ case OD_MCLK:
+ if (hwmgr->od_enabled) {
+ size = sprintf(buf, "%s:\n", "OD_MCLK");
+ podn_vdd_dep = &data->odn_dpm_table.vdd_dep_on_mclk;
+ for (i = 0; i < podn_vdd_dep->count; i++)
+ size += sprintf(buf + size, "%d: %10uMhz %10umV\n",
+ i, podn_vdd_dep->entries[i].clk/100,
+ podn_vdd_dep->entries[i].vddc);
+ }
+ break;
+ case OD_RANGE:
+ if (hwmgr->od_enabled) {
+ size = sprintf(buf, "%s:\n", "OD_RANGE");
+ size += sprintf(buf + size, "SCLK: %7uMHz %10uMHz\n",
+ data->golden_dpm_table.gfx_table.dpm_levels[0].value/100,
+ hwmgr->platform_descriptor.overdriveLimit.engineClock/100);
+ size += sprintf(buf + size, "MCLK: %7uMHz %10uMHz\n",
+ data->golden_dpm_table.mem_table.dpm_levels[0].value/100,
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock/100);
+ size += sprintf(buf + size, "VDDC: %7umV %11umV\n",
+ data->odn_dpm_table.min_vddc,
+ data->odn_dpm_table.max_vddc);
+ }
+ break;
+ default:
+ break;
+ }
+ return size;
+}
+
+static int vega10_display_configuration_changed_task(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ Watermarks_t *wm_table = &(data->smc_state_table.water_marks_table);
+ int result = 0;
+
+ if ((data->water_marks_bitmap & WaterMarksExist) &&
+ !(data->water_marks_bitmap & WaterMarksLoaded)) {
+ result = smum_smc_table_manager(hwmgr, (uint8_t *)wm_table, WMTABLE, false);
+ PP_ASSERT_WITH_CODE(result, "Failed to update WMTABLE!", return -EINVAL);
+ data->water_marks_bitmap |= WaterMarksLoaded;
+ }
+
+ if (data->water_marks_bitmap & WaterMarksLoaded) {
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_NumOfDisplays, hwmgr->display_config->num_display,
+ NULL);
+ }
+
+ return result;
+}
+
+static int vega10_enable_disable_uvd_dpm(struct pp_hwmgr *hwmgr, bool enable)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->smu_features[GNLD_DPM_UVD].supported) {
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ enable,
+ data->smu_features[GNLD_DPM_UVD].smu_feature_bitmap),
+ "Attempt to Enable/Disable DPM UVD Failed!",
+ return -1);
+ data->smu_features[GNLD_DPM_UVD].enabled = enable;
+ }
+ return 0;
+}
+
+static void vega10_power_gate_vce(struct pp_hwmgr *hwmgr, bool bgate)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ data->vce_power_gated = bgate;
+ vega10_enable_disable_vce_dpm(hwmgr, !bgate);
+}
+
+static void vega10_power_gate_uvd(struct pp_hwmgr *hwmgr, bool bgate)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ data->uvd_power_gated = bgate;
+ vega10_enable_disable_uvd_dpm(hwmgr, !bgate);
+}
+
+static inline bool vega10_are_power_levels_equal(
+ const struct vega10_performance_level *pl1,
+ const struct vega10_performance_level *pl2)
+{
+ return ((pl1->soc_clock == pl2->soc_clock) &&
+ (pl1->gfx_clock == pl2->gfx_clock) &&
+ (pl1->mem_clock == pl2->mem_clock));
+}
+
+static int vega10_check_states_equal(struct pp_hwmgr *hwmgr,
+ const struct pp_hw_power_state *pstate1,
+ const struct pp_hw_power_state *pstate2, bool *equal)
+{
+ const struct vega10_power_state *psa;
+ const struct vega10_power_state *psb;
+ int i;
+
+ if (pstate1 == NULL || pstate2 == NULL || equal == NULL)
+ return -EINVAL;
+
+ psa = cast_const_phw_vega10_power_state(pstate1);
+ psb = cast_const_phw_vega10_power_state(pstate2);
+ /* If the two states don't even have the same number of performance levels they cannot be the same state. */
+ if (psa->performance_level_count != psb->performance_level_count) {
+ *equal = false;
+ return 0;
+ }
+
+ for (i = 0; i < psa->performance_level_count; i++) {
+ if (!vega10_are_power_levels_equal(&(psa->performance_levels[i]), &(psb->performance_levels[i]))) {
+ /* If we have found even one performance level pair that is different the states are different. */
+ *equal = false;
+ return 0;
+ }
+ }
+
+ /* If all performance levels are the same try to use the UVD clocks to break the tie.*/
+ *equal = ((psa->uvd_clks.vclk == psb->uvd_clks.vclk) && (psa->uvd_clks.dclk == psb->uvd_clks.dclk));
+ *equal &= ((psa->vce_clks.evclk == psb->vce_clks.evclk) && (psa->vce_clks.ecclk == psb->vce_clks.ecclk));
+ *equal &= (psa->sclk_threshold == psb->sclk_threshold);
+
+ return 0;
+}
+
+static bool
+vega10_check_smc_update_required_for_display_configuration(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ bool is_update_required = false;
+
+ if (data->display_timing.num_existing_displays != hwmgr->display_config->num_display)
+ is_update_required = true;
+
+ if (PP_CAP(PHM_PlatformCaps_SclkDeepSleep)) {
+ if (data->display_timing.min_clock_in_sr != hwmgr->display_config->min_core_set_clock_in_sr)
+ is_update_required = true;
+ }
+
+ return is_update_required;
+}
+
+static int vega10_disable_dpm_tasks(struct pp_hwmgr *hwmgr)
+{
+ int tmp_result, result = 0;
+
+ if (!hwmgr->not_vf)
+ return 0;
+
+ if (PP_CAP(PHM_PlatformCaps_ThermalController))
+ vega10_disable_thermal_protection(hwmgr);
+
+ tmp_result = vega10_disable_power_containment(hwmgr);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to disable power containment!", result = tmp_result);
+
+ tmp_result = vega10_disable_didt_config(hwmgr);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to disable didt config!", result = tmp_result);
+
+ tmp_result = vega10_avfs_enable(hwmgr, false);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to disable AVFS!", result = tmp_result);
+
+ tmp_result = vega10_stop_dpm(hwmgr, SMC_DPM_FEATURES);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to stop DPM!", result = tmp_result);
+
+ tmp_result = vega10_disable_deep_sleep_master_switch(hwmgr);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to disable deep sleep!", result = tmp_result);
+
+ tmp_result = vega10_disable_ulv(hwmgr);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to disable ulv!", result = tmp_result);
+
+ tmp_result = vega10_acg_disable(hwmgr);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to disable acg!", result = tmp_result);
+
+ vega10_enable_disable_PCC_limit_feature(hwmgr, false);
+ return result;
+}
+
+static int vega10_power_off_asic(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ int result;
+
+ result = vega10_disable_dpm_tasks(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "[disable_dpm_tasks] Failed to disable DPM!",
+ );
+ data->water_marks_bitmap &= ~(WaterMarksLoaded);
+
+ return result;
+}
+
+static int vega10_get_sclk_od(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct vega10_single_dpm_table *sclk_table = &(data->dpm_table.gfx_table);
+ struct vega10_single_dpm_table *golden_sclk_table =
+ &(data->golden_dpm_table.gfx_table);
+ int value = sclk_table->dpm_levels[sclk_table->count - 1].value;
+ int golden_value = golden_sclk_table->dpm_levels
+ [golden_sclk_table->count - 1].value;
+
+ value -= golden_value;
+ value = DIV_ROUND_UP(value * 100, golden_value);
+
+ return value;
+}
+
+static int vega10_set_sclk_od(struct pp_hwmgr *hwmgr, uint32_t value)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct vega10_single_dpm_table *golden_sclk_table =
+ &(data->golden_dpm_table.gfx_table);
+ struct pp_power_state *ps;
+ struct vega10_power_state *vega10_ps;
+
+ ps = hwmgr->request_ps;
+
+ if (ps == NULL)
+ return -EINVAL;
+
+ vega10_ps = cast_phw_vega10_power_state(&ps->hardware);
+
+ vega10_ps->performance_levels
+ [vega10_ps->performance_level_count - 1].gfx_clock =
+ golden_sclk_table->dpm_levels
+ [golden_sclk_table->count - 1].value *
+ value / 100 +
+ golden_sclk_table->dpm_levels
+ [golden_sclk_table->count - 1].value;
+
+ if (vega10_ps->performance_levels
+ [vega10_ps->performance_level_count - 1].gfx_clock >
+ hwmgr->platform_descriptor.overdriveLimit.engineClock) {
+ vega10_ps->performance_levels
+ [vega10_ps->performance_level_count - 1].gfx_clock =
+ hwmgr->platform_descriptor.overdriveLimit.engineClock;
+ pr_warn("max sclk supported by vbios is %d\n",
+ hwmgr->platform_descriptor.overdriveLimit.engineClock);
+ }
+ return 0;
+}
+
+static int vega10_get_mclk_od(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct vega10_single_dpm_table *mclk_table = &(data->dpm_table.mem_table);
+ struct vega10_single_dpm_table *golden_mclk_table =
+ &(data->golden_dpm_table.mem_table);
+ int value = mclk_table->dpm_levels[mclk_table->count - 1].value;
+ int golden_value = golden_mclk_table->dpm_levels
+ [golden_mclk_table->count - 1].value;
+
+ value -= golden_value;
+ value = DIV_ROUND_UP(value * 100, golden_value);
+
+ return value;
+}
+
+static int vega10_set_mclk_od(struct pp_hwmgr *hwmgr, uint32_t value)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct vega10_single_dpm_table *golden_mclk_table =
+ &(data->golden_dpm_table.mem_table);
+ struct pp_power_state *ps;
+ struct vega10_power_state *vega10_ps;
+
+ ps = hwmgr->request_ps;
+
+ if (ps == NULL)
+ return -EINVAL;
+
+ vega10_ps = cast_phw_vega10_power_state(&ps->hardware);
+
+ vega10_ps->performance_levels
+ [vega10_ps->performance_level_count - 1].mem_clock =
+ golden_mclk_table->dpm_levels
+ [golden_mclk_table->count - 1].value *
+ value / 100 +
+ golden_mclk_table->dpm_levels
+ [golden_mclk_table->count - 1].value;
+
+ if (vega10_ps->performance_levels
+ [vega10_ps->performance_level_count - 1].mem_clock >
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock) {
+ vega10_ps->performance_levels
+ [vega10_ps->performance_level_count - 1].mem_clock =
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock;
+ pr_warn("max mclk supported by vbios is %d\n",
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock);
+ }
+
+ return 0;
+}
+
+static int vega10_notify_cac_buffer_info(struct pp_hwmgr *hwmgr,
+ uint32_t virtual_addr_low,
+ uint32_t virtual_addr_hi,
+ uint32_t mc_addr_low,
+ uint32_t mc_addr_hi,
+ uint32_t size)
+{
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSystemVirtualDramAddrHigh,
+ virtual_addr_hi,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSystemVirtualDramAddrLow,
+ virtual_addr_low,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DramLogSetDramAddrHigh,
+ mc_addr_hi,
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DramLogSetDramAddrLow,
+ mc_addr_low,
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DramLogSetDramSize,
+ size,
+ NULL);
+ return 0;
+}
+
+static int vega10_get_thermal_temperature_range(struct pp_hwmgr *hwmgr,
+ struct PP_TemperatureRange *thermal_data)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+
+ memcpy(thermal_data, &SMU7ThermalWithDelayPolicy[0], sizeof(struct PP_TemperatureRange));
+
+ thermal_data->max = pp_table->TedgeLimit *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ thermal_data->edge_emergency_max = (pp_table->TedgeLimit + CTF_OFFSET_EDGE) *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ thermal_data->hotspot_crit_max = pp_table->ThotspotLimit *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ thermal_data->hotspot_emergency_max = (pp_table->ThotspotLimit + CTF_OFFSET_HOTSPOT) *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ thermal_data->mem_crit_max = pp_table->ThbmLimit *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ thermal_data->mem_emergency_max = (pp_table->ThbmLimit + CTF_OFFSET_HBM)*
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+ return 0;
+}
+
+static int vega10_get_power_profile_mode(struct pp_hwmgr *hwmgr, char *buf)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ uint32_t i, size = 0;
+ static const uint8_t profile_mode_setting[6][4] = {{70, 60, 0, 0,},
+ {70, 60, 1, 3,},
+ {90, 60, 0, 0,},
+ {70, 60, 0, 0,},
+ {70, 90, 0, 0,},
+ {30, 60, 0, 6,},
+ };
+ static const char *profile_name[7] = {"BOOTUP_DEFAULT",
+ "3D_FULL_SCREEN",
+ "POWER_SAVING",
+ "VIDEO",
+ "VR",
+ "COMPUTE",
+ "CUSTOM"};
+ static const char *title[6] = {"NUM",
+ "MODE_NAME",
+ "BUSY_SET_POINT",
+ "FPS",
+ "USE_RLC_BUSY",
+ "MIN_ACTIVE_LEVEL"};
+
+ if (!buf)
+ return -EINVAL;
+
+ size += sprintf(buf + size, "%s %16s %s %s %s %s\n",title[0],
+ title[1], title[2], title[3], title[4], title[5]);
+
+ for (i = 0; i < PP_SMC_POWER_PROFILE_CUSTOM; i++)
+ size += sprintf(buf + size, "%3d %14s%s: %14d %3d %10d %14d\n",
+ i, profile_name[i], (i == hwmgr->power_profile_mode) ? "*" : " ",
+ profile_mode_setting[i][0], profile_mode_setting[i][1],
+ profile_mode_setting[i][2], profile_mode_setting[i][3]);
+ size += sprintf(buf + size, "%3d %14s%s: %14d %3d %10d %14d\n", i,
+ profile_name[i], (i == hwmgr->power_profile_mode) ? "*" : " ",
+ data->custom_profile_mode[0], data->custom_profile_mode[1],
+ data->custom_profile_mode[2], data->custom_profile_mode[3]);
+ return size;
+}
+
+static int vega10_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, uint32_t size)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ uint8_t busy_set_point;
+ uint8_t FPS;
+ uint8_t use_rlc_busy;
+ uint8_t min_active_level;
+ uint32_t power_profile_mode = input[size];
+
+ if (power_profile_mode == PP_SMC_POWER_PROFILE_CUSTOM) {
+ if (size != 0 && size != 4)
+ return -EINVAL;
+
+ /* If size = 0 and the CUSTOM profile has been set already
+ * then just apply the profile. The copy stored in the hwmgr
+ * is zeroed out on init
+ */
+ if (size == 0) {
+ if (data->custom_profile_mode[0] != 0)
+ goto out;
+ else
+ return -EINVAL;
+ }
+
+ data->custom_profile_mode[0] = busy_set_point = input[0];
+ data->custom_profile_mode[1] = FPS = input[1];
+ data->custom_profile_mode[2] = use_rlc_busy = input[2];
+ data->custom_profile_mode[3] = min_active_level = input[3];
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetCustomGfxDpmParameters,
+ busy_set_point | FPS<<8 |
+ use_rlc_busy << 16 | min_active_level<<24,
+ NULL);
+ }
+
+out:
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_SetWorkloadMask,
+ 1 << power_profile_mode,
+ NULL);
+ hwmgr->power_profile_mode = power_profile_mode;
+
+ return 0;
+}
+
+
+static bool vega10_check_clk_voltage_valid(struct pp_hwmgr *hwmgr,
+ enum PP_OD_DPM_TABLE_COMMAND type,
+ uint32_t clk,
+ uint32_t voltage)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct vega10_odn_dpm_table *odn_table = &(data->odn_dpm_table);
+ struct vega10_single_dpm_table *golden_table;
+
+ if (voltage < odn_table->min_vddc || voltage > odn_table->max_vddc) {
+ pr_info("OD voltage is out of range [%d - %d] mV\n", odn_table->min_vddc, odn_table->max_vddc);
+ return false;
+ }
+
+ if (type == PP_OD_EDIT_SCLK_VDDC_TABLE) {
+ golden_table = &(data->golden_dpm_table.gfx_table);
+ if (golden_table->dpm_levels[0].value > clk ||
+ hwmgr->platform_descriptor.overdriveLimit.engineClock < clk) {
+ pr_info("OD engine clock is out of range [%d - %d] MHz\n",
+ golden_table->dpm_levels[0].value/100,
+ hwmgr->platform_descriptor.overdriveLimit.engineClock/100);
+ return false;
+ }
+ } else if (type == PP_OD_EDIT_MCLK_VDDC_TABLE) {
+ golden_table = &(data->golden_dpm_table.mem_table);
+ if (golden_table->dpm_levels[0].value > clk ||
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock < clk) {
+ pr_info("OD memory clock is out of range [%d - %d] MHz\n",
+ golden_table->dpm_levels[0].value/100,
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock/100);
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+static void vega10_odn_update_power_state(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct pp_power_state *ps = hwmgr->request_ps;
+ struct vega10_power_state *vega10_ps;
+ struct vega10_single_dpm_table *gfx_dpm_table =
+ &data->dpm_table.gfx_table;
+ struct vega10_single_dpm_table *soc_dpm_table =
+ &data->dpm_table.soc_table;
+ struct vega10_single_dpm_table *mem_dpm_table =
+ &data->dpm_table.mem_table;
+ int max_level;
+
+ if (!ps)
+ return;
+
+ vega10_ps = cast_phw_vega10_power_state(&ps->hardware);
+ max_level = vega10_ps->performance_level_count - 1;
+
+ if (vega10_ps->performance_levels[max_level].gfx_clock !=
+ gfx_dpm_table->dpm_levels[gfx_dpm_table->count - 1].value)
+ vega10_ps->performance_levels[max_level].gfx_clock =
+ gfx_dpm_table->dpm_levels[gfx_dpm_table->count - 1].value;
+
+ if (vega10_ps->performance_levels[max_level].soc_clock !=
+ soc_dpm_table->dpm_levels[soc_dpm_table->count - 1].value)
+ vega10_ps->performance_levels[max_level].soc_clock =
+ soc_dpm_table->dpm_levels[soc_dpm_table->count - 1].value;
+
+ if (vega10_ps->performance_levels[max_level].mem_clock !=
+ mem_dpm_table->dpm_levels[mem_dpm_table->count - 1].value)
+ vega10_ps->performance_levels[max_level].mem_clock =
+ mem_dpm_table->dpm_levels[mem_dpm_table->count - 1].value;
+
+ if (!hwmgr->ps)
+ return;
+
+ ps = (struct pp_power_state *)((unsigned long)(hwmgr->ps) + hwmgr->ps_size * (hwmgr->num_ps - 1));
+ vega10_ps = cast_phw_vega10_power_state(&ps->hardware);
+ max_level = vega10_ps->performance_level_count - 1;
+
+ if (vega10_ps->performance_levels[max_level].gfx_clock !=
+ gfx_dpm_table->dpm_levels[gfx_dpm_table->count - 1].value)
+ vega10_ps->performance_levels[max_level].gfx_clock =
+ gfx_dpm_table->dpm_levels[gfx_dpm_table->count - 1].value;
+
+ if (vega10_ps->performance_levels[max_level].soc_clock !=
+ soc_dpm_table->dpm_levels[soc_dpm_table->count - 1].value)
+ vega10_ps->performance_levels[max_level].soc_clock =
+ soc_dpm_table->dpm_levels[soc_dpm_table->count - 1].value;
+
+ if (vega10_ps->performance_levels[max_level].mem_clock !=
+ mem_dpm_table->dpm_levels[mem_dpm_table->count - 1].value)
+ vega10_ps->performance_levels[max_level].mem_clock =
+ mem_dpm_table->dpm_levels[mem_dpm_table->count - 1].value;
+}
+
+static void vega10_odn_update_soc_table(struct pp_hwmgr *hwmgr,
+ enum PP_OD_DPM_TABLE_COMMAND type)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct phm_ppt_v2_information *table_info = hwmgr->pptable;
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_table = table_info->vdd_dep_on_socclk;
+ struct vega10_single_dpm_table *dpm_table = &data->golden_dpm_table.mem_table;
+
+ struct vega10_odn_clock_voltage_dependency_table *podn_vdd_dep_on_socclk =
+ &data->odn_dpm_table.vdd_dep_on_socclk;
+ struct vega10_odn_vddc_lookup_table *od_vddc_lookup_table = &data->odn_dpm_table.vddc_lookup_table;
+
+ struct vega10_odn_clock_voltage_dependency_table *podn_vdd_dep;
+ uint8_t i, j;
+
+ if (type == PP_OD_EDIT_SCLK_VDDC_TABLE) {
+ podn_vdd_dep = &data->odn_dpm_table.vdd_dep_on_sclk;
+ for (i = 0; i < podn_vdd_dep->count; i++)
+ od_vddc_lookup_table->entries[i].us_vdd = podn_vdd_dep->entries[i].vddc;
+ } else if (type == PP_OD_EDIT_MCLK_VDDC_TABLE) {
+ podn_vdd_dep = &data->odn_dpm_table.vdd_dep_on_mclk;
+ for (i = 0; i < dpm_table->count; i++) {
+ for (j = 0; j < od_vddc_lookup_table->count; j++) {
+ if (od_vddc_lookup_table->entries[j].us_vdd >
+ podn_vdd_dep->entries[i].vddc)
+ break;
+ }
+ if (j == od_vddc_lookup_table->count) {
+ j = od_vddc_lookup_table->count - 1;
+ od_vddc_lookup_table->entries[j].us_vdd =
+ podn_vdd_dep->entries[i].vddc;
+ data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_VDDC;
+ }
+ podn_vdd_dep->entries[i].vddInd = j;
+ }
+ dpm_table = &data->dpm_table.soc_table;
+ for (i = 0; i < dep_table->count; i++) {
+ if (dep_table->entries[i].vddInd == podn_vdd_dep->entries[podn_vdd_dep->count-1].vddInd &&
+ dep_table->entries[i].clk < podn_vdd_dep->entries[podn_vdd_dep->count-1].clk) {
+ data->need_update_dpm_table |= DPMTABLE_UPDATE_SOCCLK;
+ for (; (i < dep_table->count) &&
+ (dep_table->entries[i].clk < podn_vdd_dep->entries[podn_vdd_dep->count - 1].clk); i++) {
+ podn_vdd_dep_on_socclk->entries[i].clk = podn_vdd_dep->entries[podn_vdd_dep->count-1].clk;
+ dpm_table->dpm_levels[i].value = podn_vdd_dep_on_socclk->entries[i].clk;
+ }
+ break;
+ } else {
+ dpm_table->dpm_levels[i].value = dep_table->entries[i].clk;
+ podn_vdd_dep_on_socclk->entries[i].vddc = dep_table->entries[i].vddc;
+ podn_vdd_dep_on_socclk->entries[i].vddInd = dep_table->entries[i].vddInd;
+ podn_vdd_dep_on_socclk->entries[i].clk = dep_table->entries[i].clk;
+ }
+ }
+ if (podn_vdd_dep_on_socclk->entries[podn_vdd_dep_on_socclk->count - 1].clk <
+ podn_vdd_dep->entries[podn_vdd_dep->count - 1].clk) {
+ data->need_update_dpm_table |= DPMTABLE_UPDATE_SOCCLK;
+ podn_vdd_dep_on_socclk->entries[podn_vdd_dep_on_socclk->count - 1].clk =
+ podn_vdd_dep->entries[podn_vdd_dep->count - 1].clk;
+ dpm_table->dpm_levels[podn_vdd_dep_on_socclk->count - 1].value =
+ podn_vdd_dep->entries[podn_vdd_dep->count - 1].clk;
+ }
+ if (podn_vdd_dep_on_socclk->entries[podn_vdd_dep_on_socclk->count - 1].vddInd <
+ podn_vdd_dep->entries[podn_vdd_dep->count - 1].vddInd) {
+ data->need_update_dpm_table |= DPMTABLE_UPDATE_SOCCLK;
+ podn_vdd_dep_on_socclk->entries[podn_vdd_dep_on_socclk->count - 1].vddInd =
+ podn_vdd_dep->entries[podn_vdd_dep->count - 1].vddInd;
+ }
+ }
+ vega10_odn_update_power_state(hwmgr);
+}
+
+static int vega10_odn_edit_dpm_table(struct pp_hwmgr *hwmgr,
+ enum PP_OD_DPM_TABLE_COMMAND type,
+ long *input, uint32_t size)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct vega10_odn_clock_voltage_dependency_table *podn_vdd_dep_table;
+ struct vega10_single_dpm_table *dpm_table;
+
+ uint32_t input_clk;
+ uint32_t input_vol;
+ uint32_t input_level;
+ uint32_t i;
+
+ PP_ASSERT_WITH_CODE(input, "NULL user input for clock and voltage",
+ return -EINVAL);
+
+ if (!hwmgr->od_enabled) {
+ pr_info("OverDrive feature not enabled\n");
+ return -EINVAL;
+ }
+
+ if (PP_OD_EDIT_SCLK_VDDC_TABLE == type) {
+ dpm_table = &data->dpm_table.gfx_table;
+ podn_vdd_dep_table = &data->odn_dpm_table.vdd_dep_on_sclk;
+ data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_SCLK;
+ } else if (PP_OD_EDIT_MCLK_VDDC_TABLE == type) {
+ dpm_table = &data->dpm_table.mem_table;
+ podn_vdd_dep_table = &data->odn_dpm_table.vdd_dep_on_mclk;
+ data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_MCLK;
+ } else if (PP_OD_RESTORE_DEFAULT_TABLE == type) {
+ memcpy(&(data->dpm_table), &(data->golden_dpm_table), sizeof(struct vega10_dpm_table));
+ vega10_odn_initial_default_setting(hwmgr);
+ vega10_odn_update_power_state(hwmgr);
+ /* force to update all clock tables */
+ data->need_update_dpm_table = DPMTABLE_UPDATE_SCLK |
+ DPMTABLE_UPDATE_MCLK |
+ DPMTABLE_UPDATE_SOCCLK;
+ return 0;
+ } else if (PP_OD_COMMIT_DPM_TABLE == type) {
+ vega10_check_dpm_table_updated(hwmgr);
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+
+ for (i = 0; i < size; i += 3) {
+ if (i + 3 > size || input[i] >= podn_vdd_dep_table->count) {
+ pr_info("invalid clock voltage input\n");
+ return 0;
+ }
+ input_level = input[i];
+ input_clk = input[i+1] * 100;
+ input_vol = input[i+2];
+
+ if (vega10_check_clk_voltage_valid(hwmgr, type, input_clk, input_vol)) {
+ dpm_table->dpm_levels[input_level].value = input_clk;
+ podn_vdd_dep_table->entries[input_level].clk = input_clk;
+ podn_vdd_dep_table->entries[input_level].vddc = input_vol;
+ } else {
+ return -EINVAL;
+ }
+ }
+ vega10_odn_update_soc_table(hwmgr, type);
+ return 0;
+}
+
+static int vega10_set_mp1_state(struct pp_hwmgr *hwmgr,
+ enum pp_mp1_state mp1_state)
+{
+ uint16_t msg;
+ int ret;
+
+ switch (mp1_state) {
+ case PP_MP1_STATE_UNLOAD:
+ msg = PPSMC_MSG_PrepareMp1ForUnload;
+ break;
+ case PP_MP1_STATE_SHUTDOWN:
+ case PP_MP1_STATE_RESET:
+ case PP_MP1_STATE_NONE:
+ default:
+ return 0;
+ }
+
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc(hwmgr, msg, NULL)) == 0,
+ "[PrepareMp1] Failed!",
+ return ret);
+
+ return 0;
+}
+
+static int vega10_get_performance_level(struct pp_hwmgr *hwmgr, const struct pp_hw_power_state *state,
+ PHM_PerformanceLevelDesignation designation, uint32_t index,
+ PHM_PerformanceLevel *level)
+{
+ const struct vega10_power_state *ps;
+ uint32_t i;
+
+ if (level == NULL || hwmgr == NULL || state == NULL)
+ return -EINVAL;
+
+ ps = cast_const_phw_vega10_power_state(state);
+
+ i = index > ps->performance_level_count - 1 ?
+ ps->performance_level_count - 1 : index;
+
+ level->coreClock = ps->performance_levels[i].gfx_clock;
+ level->memory_clock = ps->performance_levels[i].mem_clock;
+
+ return 0;
+}
+
+static int vega10_disable_power_features_for_compute_performance(struct pp_hwmgr *hwmgr, bool disable)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ uint32_t feature_mask = 0;
+
+ if (disable) {
+ feature_mask |= data->smu_features[GNLD_ULV].enabled ?
+ data->smu_features[GNLD_ULV].smu_feature_bitmap : 0;
+ feature_mask |= data->smu_features[GNLD_DS_GFXCLK].enabled ?
+ data->smu_features[GNLD_DS_GFXCLK].smu_feature_bitmap : 0;
+ feature_mask |= data->smu_features[GNLD_DS_SOCCLK].enabled ?
+ data->smu_features[GNLD_DS_SOCCLK].smu_feature_bitmap : 0;
+ feature_mask |= data->smu_features[GNLD_DS_LCLK].enabled ?
+ data->smu_features[GNLD_DS_LCLK].smu_feature_bitmap : 0;
+ feature_mask |= data->smu_features[GNLD_DS_DCEFCLK].enabled ?
+ data->smu_features[GNLD_DS_DCEFCLK].smu_feature_bitmap : 0;
+ } else {
+ feature_mask |= (!data->smu_features[GNLD_ULV].enabled) ?
+ data->smu_features[GNLD_ULV].smu_feature_bitmap : 0;
+ feature_mask |= (!data->smu_features[GNLD_DS_GFXCLK].enabled) ?
+ data->smu_features[GNLD_DS_GFXCLK].smu_feature_bitmap : 0;
+ feature_mask |= (!data->smu_features[GNLD_DS_SOCCLK].enabled) ?
+ data->smu_features[GNLD_DS_SOCCLK].smu_feature_bitmap : 0;
+ feature_mask |= (!data->smu_features[GNLD_DS_LCLK].enabled) ?
+ data->smu_features[GNLD_DS_LCLK].smu_feature_bitmap : 0;
+ feature_mask |= (!data->smu_features[GNLD_DS_DCEFCLK].enabled) ?
+ data->smu_features[GNLD_DS_DCEFCLK].smu_feature_bitmap : 0;
+ }
+
+ if (feature_mask)
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ !disable, feature_mask),
+ "enable/disable power features for compute performance Failed!",
+ return -EINVAL);
+
+ if (disable) {
+ data->smu_features[GNLD_ULV].enabled = false;
+ data->smu_features[GNLD_DS_GFXCLK].enabled = false;
+ data->smu_features[GNLD_DS_SOCCLK].enabled = false;
+ data->smu_features[GNLD_DS_LCLK].enabled = false;
+ data->smu_features[GNLD_DS_DCEFCLK].enabled = false;
+ } else {
+ data->smu_features[GNLD_ULV].enabled = true;
+ data->smu_features[GNLD_DS_GFXCLK].enabled = true;
+ data->smu_features[GNLD_DS_SOCCLK].enabled = true;
+ data->smu_features[GNLD_DS_LCLK].enabled = true;
+ data->smu_features[GNLD_DS_DCEFCLK].enabled = true;
+ }
+
+ return 0;
+
+}
+
+static const struct pp_hwmgr_func vega10_hwmgr_funcs = {
+ .backend_init = vega10_hwmgr_backend_init,
+ .backend_fini = vega10_hwmgr_backend_fini,
+ .asic_setup = vega10_setup_asic_task,
+ .dynamic_state_management_enable = vega10_enable_dpm_tasks,
+ .dynamic_state_management_disable = vega10_disable_dpm_tasks,
+ .get_num_of_pp_table_entries =
+ vega10_get_number_of_powerplay_table_entries,
+ .get_power_state_size = vega10_get_power_state_size,
+ .get_pp_table_entry = vega10_get_pp_table_entry,
+ .patch_boot_state = vega10_patch_boot_state,
+ .apply_state_adjust_rules = vega10_apply_state_adjust_rules,
+ .power_state_set = vega10_set_power_state_tasks,
+ .get_sclk = vega10_dpm_get_sclk,
+ .get_mclk = vega10_dpm_get_mclk,
+ .notify_smc_display_config_after_ps_adjustment =
+ vega10_notify_smc_display_config_after_ps_adjustment,
+ .force_dpm_level = vega10_dpm_force_dpm_level,
+ .stop_thermal_controller = vega10_thermal_stop_thermal_controller,
+ .get_fan_speed_info = vega10_fan_ctrl_get_fan_speed_info,
+ .get_fan_speed_percent = vega10_fan_ctrl_get_fan_speed_percent,
+ .set_fan_speed_percent = vega10_fan_ctrl_set_fan_speed_percent,
+ .reset_fan_speed_to_default =
+ vega10_fan_ctrl_reset_fan_speed_to_default,
+ .get_fan_speed_rpm = vega10_fan_ctrl_get_fan_speed_rpm,
+ .set_fan_speed_rpm = vega10_fan_ctrl_set_fan_speed_rpm,
+ .uninitialize_thermal_controller =
+ vega10_thermal_ctrl_uninitialize_thermal_controller,
+ .set_fan_control_mode = vega10_set_fan_control_mode,
+ .get_fan_control_mode = vega10_get_fan_control_mode,
+ .read_sensor = vega10_read_sensor,
+ .get_dal_power_level = vega10_get_dal_power_level,
+ .get_clock_by_type_with_latency = vega10_get_clock_by_type_with_latency,
+ .get_clock_by_type_with_voltage = vega10_get_clock_by_type_with_voltage,
+ .set_watermarks_for_clocks_ranges = vega10_set_watermarks_for_clocks_ranges,
+ .display_clock_voltage_request = vega10_display_clock_voltage_request,
+ .force_clock_level = vega10_force_clock_level,
+ .print_clock_levels = vega10_print_clock_levels,
+ .display_config_changed = vega10_display_configuration_changed_task,
+ .powergate_uvd = vega10_power_gate_uvd,
+ .powergate_vce = vega10_power_gate_vce,
+ .check_states_equal = vega10_check_states_equal,
+ .check_smc_update_required_for_display_configuration =
+ vega10_check_smc_update_required_for_display_configuration,
+ .power_off_asic = vega10_power_off_asic,
+ .disable_smc_firmware_ctf = vega10_thermal_disable_alert,
+ .get_sclk_od = vega10_get_sclk_od,
+ .set_sclk_od = vega10_set_sclk_od,
+ .get_mclk_od = vega10_get_mclk_od,
+ .set_mclk_od = vega10_set_mclk_od,
+ .avfs_control = vega10_avfs_enable,
+ .notify_cac_buffer_info = vega10_notify_cac_buffer_info,
+ .get_thermal_temperature_range = vega10_get_thermal_temperature_range,
+ .register_irq_handlers = smu9_register_irq_handlers,
+ .start_thermal_controller = vega10_start_thermal_controller,
+ .get_power_profile_mode = vega10_get_power_profile_mode,
+ .set_power_profile_mode = vega10_set_power_profile_mode,
+ .set_power_limit = vega10_set_power_limit,
+ .odn_edit_dpm_table = vega10_odn_edit_dpm_table,
+ .get_performance_level = vega10_get_performance_level,
+ .get_asic_baco_capability = smu9_baco_get_capability,
+ .get_asic_baco_state = smu9_baco_get_state,
+ .set_asic_baco_state = vega10_baco_set_state,
+ .enable_mgpu_fan_boost = vega10_enable_mgpu_fan_boost,
+ .get_ppfeature_status = vega10_get_ppfeature_status,
+ .set_ppfeature_status = vega10_set_ppfeature_status,
+ .set_mp1_state = vega10_set_mp1_state,
+ .disable_power_features_for_compute_performance =
+ vega10_disable_power_features_for_compute_performance,
+};
+
+int vega10_hwmgr_init(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ hwmgr->hwmgr_func = &vega10_hwmgr_funcs;
+ hwmgr->pptable_func = &vega10_pptable_funcs;
+ if (amdgpu_passthrough(adev))
+ return vega10_baco_set_cap(hwmgr);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.h
new file mode 100644
index 000000000000..f752b4ad0c8a
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.h
@@ -0,0 +1,446 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _VEGA10_HWMGR_H_
+#define _VEGA10_HWMGR_H_
+
+#include "hwmgr.h"
+#include "smu9_driver_if.h"
+#include "ppatomctrl.h"
+#include "ppatomfwctrl.h"
+#include "vega10_ppsmc.h"
+#include "vega10_powertune.h"
+
+#define VEGA10_MAX_HARDWARE_POWERLEVELS 2
+
+#define WaterMarksExist 1
+#define WaterMarksLoaded 2
+
+enum {
+ GNLD_DPM_PREFETCHER = 0,
+ GNLD_DPM_GFXCLK,
+ GNLD_DPM_UCLK,
+ GNLD_DPM_SOCCLK,
+ GNLD_DPM_UVD,
+ GNLD_DPM_VCE,
+ GNLD_ULV,
+ GNLD_DPM_MP0CLK,
+ GNLD_DPM_LINK,
+ GNLD_DPM_DCEFCLK,
+ GNLD_AVFS,
+ GNLD_DS_GFXCLK,
+ GNLD_DS_SOCCLK,
+ GNLD_DS_LCLK,
+ GNLD_PPT,
+ GNLD_TDC,
+ GNLD_THERMAL,
+ GNLD_GFX_PER_CU_CG,
+ GNLD_RM,
+ GNLD_DS_DCEFCLK,
+ GNLD_ACDC,
+ GNLD_VR0HOT,
+ GNLD_VR1HOT,
+ GNLD_FW_CTF,
+ GNLD_LED_DISPLAY,
+ GNLD_FAN_CONTROL,
+ GNLD_FEATURE_FAST_PPT_BIT,
+ GNLD_DIDT,
+ GNLD_ACG,
+ GNLD_PCC_LIMIT,
+ GNLD_FEATURES_MAX
+};
+
+#define GNLD_DPM_MAX (GNLD_DPM_DCEFCLK + 1)
+
+#define SMC_DPM_FEATURES 0x30F
+
+struct smu_features {
+ bool supported;
+ bool enabled;
+ uint32_t smu_feature_id;
+ uint32_t smu_feature_bitmap;
+};
+
+struct vega10_performance_level {
+ uint32_t soc_clock;
+ uint32_t gfx_clock;
+ uint32_t mem_clock;
+};
+
+struct vega10_bacos {
+ uint32_t baco_flags;
+ /* struct vega10_performance_level performance_level; */
+};
+
+struct vega10_uvd_clocks {
+ uint32_t vclk;
+ uint32_t dclk;
+};
+
+struct vega10_vce_clocks {
+ uint32_t evclk;
+ uint32_t ecclk;
+};
+
+struct vega10_power_state {
+ uint32_t magic;
+ struct vega10_uvd_clocks uvd_clks;
+ struct vega10_vce_clocks vce_clks;
+ uint16_t performance_level_count;
+ bool dc_compatible;
+ uint32_t sclk_threshold;
+ struct vega10_performance_level performance_levels[VEGA10_MAX_HARDWARE_POWERLEVELS];
+};
+
+struct vega10_dpm_level {
+ bool enabled;
+ uint32_t value;
+ uint32_t param1;
+};
+
+#define VEGA10_MAX_DEEPSLEEP_DIVIDER_ID 5
+#define MAX_REGULAR_DPM_NUMBER 8
+#define MAX_PCIE_CONF 2
+#define VEGA10_MINIMUM_ENGINE_CLOCK 2500
+
+struct vega10_dpm_state {
+ uint32_t soft_min_level;
+ uint32_t soft_max_level;
+ uint32_t hard_min_level;
+ uint32_t hard_max_level;
+};
+
+struct vega10_single_dpm_table {
+ uint32_t count;
+ struct vega10_dpm_state dpm_state;
+ struct vega10_dpm_level dpm_levels[MAX_REGULAR_DPM_NUMBER];
+};
+
+struct vega10_pcie_table {
+ uint16_t count;
+ uint8_t pcie_gen[MAX_PCIE_CONF];
+ uint8_t pcie_lane[MAX_PCIE_CONF];
+ uint32_t lclk[MAX_PCIE_CONF];
+};
+
+struct vega10_dpm_table {
+ struct vega10_single_dpm_table soc_table;
+ struct vega10_single_dpm_table gfx_table;
+ struct vega10_single_dpm_table mem_table;
+ struct vega10_single_dpm_table eclk_table;
+ struct vega10_single_dpm_table vclk_table;
+ struct vega10_single_dpm_table dclk_table;
+ struct vega10_single_dpm_table dcef_table;
+ struct vega10_single_dpm_table pixel_table;
+ struct vega10_single_dpm_table display_table;
+ struct vega10_single_dpm_table phy_table;
+ struct vega10_pcie_table pcie_table;
+};
+
+#define VEGA10_MAX_LEAKAGE_COUNT 8
+struct vega10_leakage_voltage {
+ uint16_t count;
+ uint16_t leakage_id[VEGA10_MAX_LEAKAGE_COUNT];
+ uint16_t actual_voltage[VEGA10_MAX_LEAKAGE_COUNT];
+};
+
+struct vega10_display_timing {
+ uint32_t min_clock_in_sr;
+ uint32_t num_existing_displays;
+};
+
+struct vega10_dpmlevel_enable_mask {
+ uint32_t uvd_dpm_enable_mask;
+ uint32_t vce_dpm_enable_mask;
+ uint32_t acp_dpm_enable_mask;
+ uint32_t samu_dpm_enable_mask;
+ uint32_t sclk_dpm_enable_mask;
+ uint32_t mclk_dpm_enable_mask;
+};
+
+struct vega10_vbios_boot_state {
+ bool bsoc_vddc_lock;
+ uint16_t vddc;
+ uint16_t vddci;
+ uint16_t mvddc;
+ uint16_t vdd_gfx;
+ uint32_t gfx_clock;
+ uint32_t mem_clock;
+ uint32_t soc_clock;
+ uint32_t dcef_clock;
+};
+
+struct vega10_smc_state_table {
+ uint32_t soc_boot_level;
+ uint32_t gfx_boot_level;
+ uint32_t dcef_boot_level;
+ uint32_t mem_boot_level;
+ uint32_t uvd_boot_level;
+ uint32_t vce_boot_level;
+ uint32_t gfx_max_level;
+ uint32_t mem_max_level;
+ uint32_t soc_max_level;
+ uint8_t vr_hot_gpio;
+ uint8_t ac_dc_gpio;
+ uint8_t therm_out_gpio;
+ uint8_t therm_out_polarity;
+ uint8_t therm_out_mode;
+ PPTable_t pp_table;
+ Watermarks_t water_marks_table;
+ AvfsTable_t avfs_table;
+ AvfsFuseOverride_t avfs_fuse_override_table;
+};
+
+struct vega10_mclk_latency_entries {
+ uint32_t frequency;
+ uint32_t latency;
+};
+
+struct vega10_mclk_latency_table {
+ uint32_t count;
+ struct vega10_mclk_latency_entries entries[MAX_REGULAR_DPM_NUMBER];
+};
+
+struct vega10_registry_data {
+ uint8_t ac_dc_switch_gpio_support;
+ uint8_t avfs_support;
+ uint8_t cac_support;
+ uint8_t clock_stretcher_support;
+ uint8_t db_ramping_support;
+ uint8_t didt_mode;
+ uint8_t didt_support;
+ uint8_t edc_didt_support;
+ uint8_t dynamic_state_patching_support;
+ uint8_t enable_pkg_pwr_tracking_feature;
+ uint8_t enable_tdc_limit_feature;
+ uint32_t fast_watermark_threshold;
+ uint8_t force_dpm_high;
+ uint8_t fuzzy_fan_control_support;
+ uint8_t long_idle_baco_support;
+ uint8_t mclk_dpm_key_disabled;
+ uint8_t od_state_in_dc_support;
+ uint8_t pcieLaneOverride;
+ uint8_t pcieSpeedOverride;
+ uint32_t pcieClockOverride;
+ uint8_t pcie_dpm_key_disabled;
+ uint8_t dcefclk_dpm_key_disabled;
+ uint8_t power_containment_support;
+ uint8_t ppt_support;
+ uint8_t prefetcher_dpm_key_disabled;
+ uint8_t quick_transition_support;
+ uint8_t regulator_hot_gpio_support;
+ uint8_t sclk_deep_sleep_support;
+ uint8_t sclk_dpm_key_disabled;
+ uint8_t sclk_from_vbios;
+ uint8_t sclk_throttle_low_notification;
+ uint8_t show_baco_dbg_info;
+ uint8_t skip_baco_hardware;
+ uint8_t socclk_dpm_key_disabled;
+ uint8_t spll_shutdown_support;
+ uint8_t sq_ramping_support;
+ uint32_t stable_pstate_sclk_dpm_percentage;
+ uint8_t tcp_ramping_support;
+ uint8_t tdc_support;
+ uint8_t td_ramping_support;
+ uint8_t dbr_ramping_support;
+ uint8_t gc_didt_support;
+ uint8_t psm_didt_support;
+ uint8_t thermal_out_gpio_support;
+ uint8_t thermal_support;
+ uint8_t fw_ctf_enabled;
+ uint8_t fan_control_support;
+ uint8_t ulps_support;
+ uint8_t ulv_support;
+ uint32_t vddc_vddci_delta;
+ uint8_t odn_feature_enable;
+ uint8_t disable_water_mark;
+ uint8_t zrpm_stop_temp;
+ uint8_t zrpm_start_temp;
+ uint8_t led_dpm_enabled;
+ uint8_t vr0hot_enabled;
+ uint8_t vr1hot_enabled;
+};
+
+struct vega10_odn_clock_voltage_dependency_table {
+ uint32_t count;
+ struct phm_ppt_v1_clock_voltage_dependency_record entries[MAX_REGULAR_DPM_NUMBER];
+};
+
+struct vega10_odn_vddc_lookup_table {
+ uint32_t count;
+ struct phm_ppt_v1_voltage_lookup_record entries[MAX_REGULAR_DPM_NUMBER];
+};
+
+struct vega10_odn_dpm_table {
+ struct vega10_odn_clock_voltage_dependency_table vdd_dep_on_sclk;
+ struct vega10_odn_clock_voltage_dependency_table vdd_dep_on_mclk;
+ struct vega10_odn_clock_voltage_dependency_table vdd_dep_on_socclk;
+ struct vega10_odn_vddc_lookup_table vddc_lookup_table;
+ uint32_t max_vddc;
+ uint32_t min_vddc;
+};
+
+struct vega10_odn_fan_table {
+ uint32_t target_fan_speed;
+ uint32_t target_temperature;
+ uint32_t min_performance_clock;
+ uint32_t min_fan_limit;
+};
+
+struct vega10_hwmgr {
+ struct vega10_dpm_table dpm_table;
+ struct vega10_dpm_table golden_dpm_table;
+ struct vega10_registry_data registry_data;
+ struct vega10_vbios_boot_state vbios_boot_state;
+ struct vega10_mclk_latency_table mclk_latency_table;
+
+ struct vega10_leakage_voltage vddc_leakage;
+
+ uint32_t vddc_control;
+ struct pp_atomfwctrl_voltage_table vddc_voltage_table;
+ uint32_t mvdd_control;
+ struct pp_atomfwctrl_voltage_table mvdd_voltage_table;
+ uint32_t vddci_control;
+ struct pp_atomfwctrl_voltage_table vddci_voltage_table;
+
+ uint32_t active_auto_throttle_sources;
+ uint32_t water_marks_bitmap;
+ struct vega10_bacos bacos;
+
+ struct vega10_odn_dpm_table odn_dpm_table;
+ struct vega10_odn_fan_table odn_fan_table;
+
+ /* ---- General data ---- */
+ uint8_t need_update_dpm_table;
+
+ bool cac_enabled;
+ bool battery_state;
+ bool is_tlu_enabled;
+
+ uint32_t low_sclk_interrupt_threshold;
+
+ uint32_t total_active_cus;
+
+ struct vega10_display_timing display_timing;
+
+ /* ---- Vega10 Dyn Register Settings ---- */
+
+ uint32_t debug_settings;
+ uint32_t lowest_uclk_reserved_for_ulv;
+ uint32_t gfxclk_average_alpha;
+ uint32_t socclk_average_alpha;
+ uint32_t uclk_average_alpha;
+ uint32_t gfx_activity_average_alpha;
+ uint32_t display_voltage_mode;
+ uint32_t dcef_clk_quad_eqn_a;
+ uint32_t dcef_clk_quad_eqn_b;
+ uint32_t dcef_clk_quad_eqn_c;
+ uint32_t disp_clk_quad_eqn_a;
+ uint32_t disp_clk_quad_eqn_b;
+ uint32_t disp_clk_quad_eqn_c;
+ uint32_t pixel_clk_quad_eqn_a;
+ uint32_t pixel_clk_quad_eqn_b;
+ uint32_t pixel_clk_quad_eqn_c;
+ uint32_t phy_clk_quad_eqn_a;
+ uint32_t phy_clk_quad_eqn_b;
+ uint32_t phy_clk_quad_eqn_c;
+
+ /* ---- Thermal Temperature Setting ---- */
+ struct vega10_dpmlevel_enable_mask dpm_level_enable_mask;
+
+ /* ---- Power Gating States ---- */
+ bool uvd_power_gated;
+ bool vce_power_gated;
+ bool need_long_memory_training;
+
+ /* Internal settings to apply the application power optimization parameters */
+ uint32_t disable_dpm_mask;
+
+ /* ---- SMU9 ---- */
+ struct smu_features smu_features[GNLD_FEATURES_MAX];
+ struct vega10_smc_state_table smc_state_table;
+
+ uint32_t config_telemetry;
+ uint32_t acg_loop_state;
+ uint32_t mem_channels;
+ uint8_t custom_profile_mode[4];
+};
+
+#define VEGA10_DPM2_NEAR_TDP_DEC 10
+#define VEGA10_DPM2_ABOVE_SAFE_INC 5
+#define VEGA10_DPM2_BELOW_SAFE_INC 20
+
+#define VEGA10_DPM2_LTA_WINDOW_SIZE 7
+
+#define VEGA10_DPM2_LTS_TRUNCATE 0
+
+#define VEGA10_DPM2_TDP_SAFE_LIMIT_PERCENT 80
+
+#define VEGA10_DPM2_MAXPS_PERCENT_M 90
+#define VEGA10_DPM2_MAXPS_PERCENT_H 90
+
+#define VEGA10_DPM2_PWREFFICIENCYRATIO_MARGIN 50
+
+#define VEGA10_DPM2_SQ_RAMP_MAX_POWER 0x3FFF
+#define VEGA10_DPM2_SQ_RAMP_MIN_POWER 0x12
+#define VEGA10_DPM2_SQ_RAMP_MAX_POWER_DELTA 0x15
+#define VEGA10_DPM2_SQ_RAMP_SHORT_TERM_INTERVAL_SIZE 0x1E
+#define VEGA10_DPM2_SQ_RAMP_LONG_TERM_INTERVAL_RATIO 0xF
+
+#define VEGA10_VOLTAGE_CONTROL_NONE 0x0
+#define VEGA10_VOLTAGE_CONTROL_BY_GPIO 0x1
+#define VEGA10_VOLTAGE_CONTROL_BY_SVID2 0x2
+#define VEGA10_VOLTAGE_CONTROL_MERGED 0x3
+/* To convert to Q8.8 format for firmware */
+#define VEGA10_Q88_FORMAT_CONVERSION_UNIT 256
+
+#define VEGA10_UNUSED_GPIO_PIN 0x7F
+
+#define VEGA10_THERM_OUT_MODE_DISABLE 0x0
+#define VEGA10_THERM_OUT_MODE_THERM_ONLY 0x1
+#define VEGA10_THERM_OUT_MODE_THERM_VRHOT 0x2
+
+#define PPVEGA10_VEGA10DISPLAYVOLTAGEMODE_DFLT 0xffffffff
+#define PPREGKEY_VEGA10QUADRATICEQUATION_DFLT 0xffffffff
+
+#define PPVEGA10_VEGA10GFXCLKAVERAGEALPHA_DFLT 25 /* 10% * 255 = 25 */
+#define PPVEGA10_VEGA10SOCCLKAVERAGEALPHA_DFLT 25 /* 10% * 255 = 25 */
+#define PPVEGA10_VEGA10UCLKCLKAVERAGEALPHA_DFLT 25 /* 10% * 255 = 25 */
+#define PPVEGA10_VEGA10GFXACTIVITYAVERAGEALPHA_DFLT 25 /* 10% * 255 = 25 */
+
+#define VEGA10_UMD_PSTATE_GFXCLK_LEVEL 0x3
+#define VEGA10_UMD_PSTATE_SOCCLK_LEVEL 0x3
+#define VEGA10_UMD_PSTATE_MCLK_LEVEL 0x2
+
+extern int tonga_initializa_dynamic_state_adjustment_rule_settings(struct pp_hwmgr *hwmgr);
+extern int tonga_hwmgr_backend_fini(struct pp_hwmgr *hwmgr);
+extern int tonga_get_mc_microcode_version (struct pp_hwmgr *hwmgr);
+extern int tonga_notify_smc_display_config_after_ps_adjustment(struct pp_hwmgr *hwmgr);
+extern int tonga_notify_smc_display_change(struct pp_hwmgr *hwmgr, bool has_display);
+int vega10_update_vce_dpm(struct pp_hwmgr *hwmgr, const void *input);
+int vega10_update_uvd_dpm(struct pp_hwmgr *hwmgr, bool bgate);
+int vega10_update_samu_dpm(struct pp_hwmgr *hwmgr, bool bgate);
+int vega10_update_acp_dpm(struct pp_hwmgr *hwmgr, bool bgate);
+int vega10_enable_disable_vce_dpm(struct pp_hwmgr *hwmgr, bool enable);
+
+#endif /* _VEGA10_HWMGR_H_ */
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_inc.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_inc.h
new file mode 100644
index 000000000000..faf7ac044348
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_inc.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 VEGA10_INC_H
+#define VEGA10_INC_H
+
+#include "asic_reg/thm/thm_9_0_default.h"
+#include "asic_reg/thm/thm_9_0_offset.h"
+#include "asic_reg/thm/thm_9_0_sh_mask.h"
+
+#include "asic_reg/mp/mp_9_0_offset.h"
+#include "asic_reg/mp/mp_9_0_sh_mask.h"
+
+#include "asic_reg/gc/gc_9_0_default.h"
+#include "asic_reg/gc/gc_9_0_offset.h"
+#include "asic_reg/gc/gc_9_0_sh_mask.h"
+
+#include "asic_reg/nbio/nbio_6_1_default.h"
+#include "asic_reg/nbio/nbio_6_1_offset.h"
+#include "asic_reg/nbio/nbio_6_1_sh_mask.h"
+
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_powertune.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_powertune.c
new file mode 100644
index 000000000000..9757d47dd6b8
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_powertune.c
@@ -0,0 +1,1392 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "hwmgr.h"
+#include "vega10_hwmgr.h"
+#include "vega10_smumgr.h"
+#include "vega10_powertune.h"
+#include "vega10_ppsmc.h"
+#include "vega10_inc.h"
+#include "pp_debug.h"
+#include "soc15_common.h"
+
+static const struct vega10_didt_config_reg SEDiDtTuningCtrlConfig_Vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* DIDT_SQ */
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x3853 },
+ { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x3153 },
+
+ /* DIDT_TD */
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x0dde },
+ { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x0dde },
+
+ /* DIDT_TCP */
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x3dde },
+ { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x3dde },
+
+ /* DIDT_DB */
+ { ixDIDT_DB_TUNING_CTRL, DIDT_DB_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_DB_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x3dde },
+ { ixDIDT_DB_TUNING_CTRL, DIDT_DB_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_DB_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x3dde },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg SEDiDtCtrl3Config_vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /*DIDT_SQ_CTRL3 */
+ { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__GC_DIDT_ENABLE_MASK, DIDT_SQ_CTRL3__GC_DIDT_ENABLE__SHIFT, 0x0000 },
+ { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__GC_DIDT_CLK_EN_OVERRIDE_MASK, DIDT_SQ_CTRL3__GC_DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
+ { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__THROTTLE_POLICY_MASK, DIDT_SQ_CTRL3__THROTTLE_POLICY__SHIFT, 0x0003 },
+ { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__DIDT_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_SQ_CTRL3__DIDT_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
+ { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__DIDT_POWER_LEVEL_LOWBIT_MASK, DIDT_SQ_CTRL3__DIDT_POWER_LEVEL_LOWBIT__SHIFT, 0x0000 },
+ { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__DIDT_STALL_PATTERN_BIT_NUMS_MASK, DIDT_SQ_CTRL3__DIDT_STALL_PATTERN_BIT_NUMS__SHIFT, 0x0003 },
+ { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__GC_DIDT_LEVEL_COMB_EN_MASK, DIDT_SQ_CTRL3__GC_DIDT_LEVEL_COMB_EN__SHIFT, 0x0000 },
+ { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__SE_DIDT_LEVEL_COMB_EN_MASK, DIDT_SQ_CTRL3__SE_DIDT_LEVEL_COMB_EN__SHIFT, 0x0000 },
+ { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__QUALIFY_STALL_EN_MASK, DIDT_SQ_CTRL3__QUALIFY_STALL_EN__SHIFT, 0x0000 },
+ { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__DIDT_STALL_SEL_MASK, DIDT_SQ_CTRL3__DIDT_STALL_SEL__SHIFT, 0x0000 },
+ { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__DIDT_FORCE_STALL_MASK, DIDT_SQ_CTRL3__DIDT_FORCE_STALL__SHIFT, 0x0000 },
+ { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__DIDT_STALL_DELAY_EN_MASK, DIDT_SQ_CTRL3__DIDT_STALL_DELAY_EN__SHIFT, 0x0000 },
+
+ /*DIDT_TCP_CTRL3 */
+ { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__GC_DIDT_ENABLE_MASK, DIDT_TCP_CTRL3__GC_DIDT_ENABLE__SHIFT, 0x0000 },
+ { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__GC_DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TCP_CTRL3__GC_DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
+ { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__THROTTLE_POLICY_MASK, DIDT_TCP_CTRL3__THROTTLE_POLICY__SHIFT, 0x0003 },
+ { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__DIDT_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_TCP_CTRL3__DIDT_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
+ { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__DIDT_POWER_LEVEL_LOWBIT_MASK, DIDT_TCP_CTRL3__DIDT_POWER_LEVEL_LOWBIT__SHIFT, 0x0000 },
+ { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__DIDT_STALL_PATTERN_BIT_NUMS_MASK, DIDT_TCP_CTRL3__DIDT_STALL_PATTERN_BIT_NUMS__SHIFT, 0x0003 },
+ { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__GC_DIDT_LEVEL_COMB_EN_MASK, DIDT_TCP_CTRL3__GC_DIDT_LEVEL_COMB_EN__SHIFT, 0x0000 },
+ { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__SE_DIDT_LEVEL_COMB_EN_MASK, DIDT_TCP_CTRL3__SE_DIDT_LEVEL_COMB_EN__SHIFT, 0x0000 },
+ { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__QUALIFY_STALL_EN_MASK, DIDT_TCP_CTRL3__QUALIFY_STALL_EN__SHIFT, 0x0000 },
+ { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__DIDT_STALL_SEL_MASK, DIDT_TCP_CTRL3__DIDT_STALL_SEL__SHIFT, 0x0000 },
+ { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__DIDT_FORCE_STALL_MASK, DIDT_TCP_CTRL3__DIDT_FORCE_STALL__SHIFT, 0x0000 },
+ { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__DIDT_STALL_DELAY_EN_MASK, DIDT_TCP_CTRL3__DIDT_STALL_DELAY_EN__SHIFT, 0x0000 },
+
+ /*DIDT_TD_CTRL3 */
+ { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__GC_DIDT_ENABLE_MASK, DIDT_TD_CTRL3__GC_DIDT_ENABLE__SHIFT, 0x0000 },
+ { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__GC_DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TD_CTRL3__GC_DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
+ { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__THROTTLE_POLICY_MASK, DIDT_TD_CTRL3__THROTTLE_POLICY__SHIFT, 0x0003 },
+ { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__DIDT_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_TD_CTRL3__DIDT_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
+ { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__DIDT_POWER_LEVEL_LOWBIT_MASK, DIDT_TD_CTRL3__DIDT_POWER_LEVEL_LOWBIT__SHIFT, 0x0000 },
+ { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__DIDT_STALL_PATTERN_BIT_NUMS_MASK, DIDT_TD_CTRL3__DIDT_STALL_PATTERN_BIT_NUMS__SHIFT, 0x0003 },
+ { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__GC_DIDT_LEVEL_COMB_EN_MASK, DIDT_TD_CTRL3__GC_DIDT_LEVEL_COMB_EN__SHIFT, 0x0000 },
+ { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__SE_DIDT_LEVEL_COMB_EN_MASK, DIDT_TD_CTRL3__SE_DIDT_LEVEL_COMB_EN__SHIFT, 0x0000 },
+ { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__QUALIFY_STALL_EN_MASK, DIDT_TD_CTRL3__QUALIFY_STALL_EN__SHIFT, 0x0000 },
+ { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__DIDT_STALL_SEL_MASK, DIDT_TD_CTRL3__DIDT_STALL_SEL__SHIFT, 0x0000 },
+ { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__DIDT_FORCE_STALL_MASK, DIDT_TD_CTRL3__DIDT_FORCE_STALL__SHIFT, 0x0000 },
+ { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__DIDT_STALL_DELAY_EN_MASK, DIDT_TD_CTRL3__DIDT_STALL_DELAY_EN__SHIFT, 0x0000 },
+
+ /*DIDT_DB_CTRL3 */
+ { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__GC_DIDT_ENABLE_MASK, DIDT_DB_CTRL3__GC_DIDT_ENABLE__SHIFT, 0x0000 },
+ { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__GC_DIDT_CLK_EN_OVERRIDE_MASK, DIDT_DB_CTRL3__GC_DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
+ { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__THROTTLE_POLICY_MASK, DIDT_DB_CTRL3__THROTTLE_POLICY__SHIFT, 0x0003 },
+ { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__DIDT_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_DB_CTRL3__DIDT_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
+ { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__DIDT_POWER_LEVEL_LOWBIT_MASK, DIDT_DB_CTRL3__DIDT_POWER_LEVEL_LOWBIT__SHIFT, 0x0000 },
+ { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__DIDT_STALL_PATTERN_BIT_NUMS_MASK, DIDT_DB_CTRL3__DIDT_STALL_PATTERN_BIT_NUMS__SHIFT, 0x0003 },
+ { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__GC_DIDT_LEVEL_COMB_EN_MASK, DIDT_DB_CTRL3__GC_DIDT_LEVEL_COMB_EN__SHIFT, 0x0000 },
+ { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__SE_DIDT_LEVEL_COMB_EN_MASK, DIDT_DB_CTRL3__SE_DIDT_LEVEL_COMB_EN__SHIFT, 0x0000 },
+ { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__QUALIFY_STALL_EN_MASK, DIDT_DB_CTRL3__QUALIFY_STALL_EN__SHIFT, 0x0000 },
+ { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__DIDT_STALL_SEL_MASK, DIDT_DB_CTRL3__DIDT_STALL_SEL__SHIFT, 0x0000 },
+ { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__DIDT_FORCE_STALL_MASK, DIDT_DB_CTRL3__DIDT_FORCE_STALL__SHIFT, 0x0000 },
+ { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__DIDT_STALL_DELAY_EN_MASK, DIDT_DB_CTRL3__DIDT_STALL_DELAY_EN__SHIFT, 0x0000 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg SEDiDtCtrl2Config_Vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* DIDT_SQ */
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__MAX_POWER_DELTA_MASK, DIDT_SQ_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3853 },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x00c0 },
+ { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000 },
+
+ /* DIDT_TD */
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__MAX_POWER_DELTA_MASK, DIDT_TD_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3fff },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x00c0 },
+ { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0001 },
+
+ /* DIDT_TCP */
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__MAX_POWER_DELTA_MASK, DIDT_TCP_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3dde },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x00c0 },
+ { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0001 },
+
+ /* DIDT_DB */
+ { ixDIDT_DB_CTRL2, DIDT_DB_CTRL2__MAX_POWER_DELTA_MASK, DIDT_DB_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3dde },
+ { ixDIDT_DB_CTRL2, DIDT_DB_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_DB_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x00c0 },
+ { ixDIDT_DB_CTRL2, DIDT_DB_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_DB_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0001 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg SEDiDtCtrl1Config_Vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* DIDT_SQ */
+ { ixDIDT_SQ_CTRL1, DIDT_SQ_CTRL1__MIN_POWER_MASK, DIDT_SQ_CTRL1__MIN_POWER__SHIFT, 0x0000 },
+ { ixDIDT_SQ_CTRL1, DIDT_SQ_CTRL1__MAX_POWER_MASK, DIDT_SQ_CTRL1__MAX_POWER__SHIFT, 0xffff },
+ /* DIDT_TD */
+ { ixDIDT_TD_CTRL1, DIDT_TD_CTRL1__MIN_POWER_MASK, DIDT_TD_CTRL1__MIN_POWER__SHIFT, 0x0000 },
+ { ixDIDT_TD_CTRL1, DIDT_TD_CTRL1__MAX_POWER_MASK, DIDT_TD_CTRL1__MAX_POWER__SHIFT, 0xffff },
+ /* DIDT_TCP */
+ { ixDIDT_TCP_CTRL1, DIDT_TCP_CTRL1__MIN_POWER_MASK, DIDT_TCP_CTRL1__MIN_POWER__SHIFT, 0x0000 },
+ { ixDIDT_TCP_CTRL1, DIDT_TCP_CTRL1__MAX_POWER_MASK, DIDT_TCP_CTRL1__MAX_POWER__SHIFT, 0xffff },
+ /* DIDT_DB */
+ { ixDIDT_DB_CTRL1, DIDT_DB_CTRL1__MIN_POWER_MASK, DIDT_DB_CTRL1__MIN_POWER__SHIFT, 0x0000 },
+ { ixDIDT_DB_CTRL1, DIDT_DB_CTRL1__MAX_POWER_MASK, DIDT_DB_CTRL1__MAX_POWER__SHIFT, 0xffff },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+
+static const struct vega10_didt_config_reg SEDiDtWeightConfig_Vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* DIDT_SQ */
+ { ixDIDT_SQ_WEIGHT0_3, 0xFFFFFFFF, 0, 0x2B363B1A },
+ { ixDIDT_SQ_WEIGHT4_7, 0xFFFFFFFF, 0, 0x270B2432 },
+ { ixDIDT_SQ_WEIGHT8_11, 0xFFFFFFFF, 0, 0x00000018 },
+
+ /* DIDT_TD */
+ { ixDIDT_TD_WEIGHT0_3, 0xFFFFFFFF, 0, 0x2B1D220F },
+ { ixDIDT_TD_WEIGHT4_7, 0xFFFFFFFF, 0, 0x00007558 },
+ { ixDIDT_TD_WEIGHT8_11, 0xFFFFFFFF, 0, 0x00000000 },
+
+ /* DIDT_TCP */
+ { ixDIDT_TCP_WEIGHT0_3, 0xFFFFFFFF, 0, 0x5ACE160D },
+ { ixDIDT_TCP_WEIGHT4_7, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_TCP_WEIGHT8_11, 0xFFFFFFFF, 0, 0x00000000 },
+
+ /* DIDT_DB */
+ { ixDIDT_DB_WEIGHT0_3, 0xFFFFFFFF, 0, 0x0E152A0F },
+ { ixDIDT_DB_WEIGHT4_7, 0xFFFFFFFF, 0, 0x09061813 },
+ { ixDIDT_DB_WEIGHT8_11, 0xFFFFFFFF, 0, 0x00000013 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg SEDiDtCtrl0Config_Vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* DIDT_SQ */
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK, DIDT_SQ_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0000 },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__PHASE_OFFSET_MASK, DIDT_SQ_CTRL0__PHASE_OFFSET__SHIFT, 0x0000 },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CTRL_RST_MASK, DIDT_SQ_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000 },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_STALL_CTRL_EN_MASK, DIDT_SQ_CTRL0__DIDT_STALL_CTRL_EN__SHIFT, 0x0001 },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_TUNING_CTRL_EN_MASK, DIDT_SQ_CTRL0__DIDT_TUNING_CTRL_EN__SHIFT, 0x0001 },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_STALL_AUTO_RELEASE_EN_MASK, DIDT_SQ_CTRL0__DIDT_STALL_AUTO_RELEASE_EN__SHIFT, 0x0001 },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_SQ_CTRL0__DIDT_HI_POWER_THRESHOLD__SHIFT, 0xffff },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_AUTO_MPD_EN_MASK, DIDT_SQ_CTRL0__DIDT_AUTO_MPD_EN__SHIFT, 0x0000 },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_STALL_EVENT_EN_MASK, DIDT_SQ_CTRL0__DIDT_STALL_EVENT_EN__SHIFT, 0x0000 },
+ { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_STALL_EVENT_COUNTER_CLEAR_MASK, DIDT_SQ_CTRL0__DIDT_STALL_EVENT_COUNTER_CLEAR__SHIFT, 0x0000 },
+ /* DIDT_TD */
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK, DIDT_TD_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0000 },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__PHASE_OFFSET_MASK, DIDT_TD_CTRL0__PHASE_OFFSET__SHIFT, 0x0000 },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CTRL_RST_MASK, DIDT_TD_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000 },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_STALL_CTRL_EN_MASK, DIDT_TD_CTRL0__DIDT_STALL_CTRL_EN__SHIFT, 0x0001 },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_TUNING_CTRL_EN_MASK, DIDT_TD_CTRL0__DIDT_TUNING_CTRL_EN__SHIFT, 0x0001 },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_STALL_AUTO_RELEASE_EN_MASK, DIDT_TD_CTRL0__DIDT_STALL_AUTO_RELEASE_EN__SHIFT, 0x0001 },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_TD_CTRL0__DIDT_HI_POWER_THRESHOLD__SHIFT, 0xffff },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_AUTO_MPD_EN_MASK, DIDT_TD_CTRL0__DIDT_AUTO_MPD_EN__SHIFT, 0x0000 },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_STALL_EVENT_EN_MASK, DIDT_TD_CTRL0__DIDT_STALL_EVENT_EN__SHIFT, 0x0000 },
+ { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_STALL_EVENT_COUNTER_CLEAR_MASK, DIDT_TD_CTRL0__DIDT_STALL_EVENT_COUNTER_CLEAR__SHIFT, 0x0000 },
+ /* DIDT_TCP */
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK, DIDT_TCP_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0000 },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__PHASE_OFFSET_MASK, DIDT_TCP_CTRL0__PHASE_OFFSET__SHIFT, 0x0000 },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CTRL_RST_MASK, DIDT_TCP_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000 },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_STALL_CTRL_EN_MASK, DIDT_TCP_CTRL0__DIDT_STALL_CTRL_EN__SHIFT, 0x0001 },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_TUNING_CTRL_EN_MASK, DIDT_TCP_CTRL0__DIDT_TUNING_CTRL_EN__SHIFT, 0x0001 },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_STALL_AUTO_RELEASE_EN_MASK, DIDT_TCP_CTRL0__DIDT_STALL_AUTO_RELEASE_EN__SHIFT, 0x0001 },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_TCP_CTRL0__DIDT_HI_POWER_THRESHOLD__SHIFT, 0xffff },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_AUTO_MPD_EN_MASK, DIDT_TCP_CTRL0__DIDT_AUTO_MPD_EN__SHIFT, 0x0000 },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_STALL_EVENT_EN_MASK, DIDT_TCP_CTRL0__DIDT_STALL_EVENT_EN__SHIFT, 0x0000 },
+ { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_STALL_EVENT_COUNTER_CLEAR_MASK, DIDT_TCP_CTRL0__DIDT_STALL_EVENT_COUNTER_CLEAR__SHIFT, 0x0000 },
+ /* DIDT_DB */
+ { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_CTRL_EN_MASK, DIDT_DB_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0000 },
+ { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__PHASE_OFFSET_MASK, DIDT_DB_CTRL0__PHASE_OFFSET__SHIFT, 0x0000 },
+ { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_CTRL_RST_MASK, DIDT_DB_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000 },
+ { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_DB_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
+ { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_STALL_CTRL_EN_MASK, DIDT_DB_CTRL0__DIDT_STALL_CTRL_EN__SHIFT, 0x0001 },
+ { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_TUNING_CTRL_EN_MASK, DIDT_DB_CTRL0__DIDT_TUNING_CTRL_EN__SHIFT, 0x0001 },
+ { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_STALL_AUTO_RELEASE_EN_MASK, DIDT_DB_CTRL0__DIDT_STALL_AUTO_RELEASE_EN__SHIFT, 0x0001 },
+ { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_DB_CTRL0__DIDT_HI_POWER_THRESHOLD__SHIFT, 0xffff },
+ { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_AUTO_MPD_EN_MASK, DIDT_DB_CTRL0__DIDT_AUTO_MPD_EN__SHIFT, 0x0000 },
+ { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_STALL_EVENT_EN_MASK, DIDT_DB_CTRL0__DIDT_STALL_EVENT_EN__SHIFT, 0x0000 },
+ { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_STALL_EVENT_COUNTER_CLEAR_MASK, DIDT_DB_CTRL0__DIDT_STALL_EVENT_COUNTER_CLEAR__SHIFT, 0x0000 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+
+static const struct vega10_didt_config_reg SEDiDtStallCtrlConfig_vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* DIDT_SQ */
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0004 },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0004 },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_SQ_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x000a },
+ { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_SQ_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x000a },
+
+ /* DIDT_TD */
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001 },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001 },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TD_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x000a },
+ { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TD_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x000a },
+
+ /* DIDT_TCP */
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001 },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001 },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TCP_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x000a },
+ { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TCP_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x000a },
+
+ /* DIDT_DB */
+ { ixDIDT_DB_STALL_CTRL, DIDT_DB_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_DB_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0004 },
+ { ixDIDT_DB_STALL_CTRL, DIDT_DB_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_DB_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0004 },
+ { ixDIDT_DB_STALL_CTRL, DIDT_DB_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_DB_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x000a },
+ { ixDIDT_DB_STALL_CTRL, DIDT_DB_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_DB_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x000a },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg SEDiDtStallPatternConfig_vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* DIDT_SQ_STALL_PATTERN_1_2 */
+ { ixDIDT_SQ_STALL_PATTERN_1_2, DIDT_SQ_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_1_MASK, DIDT_SQ_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_1__SHIFT, 0x0001 },
+ { ixDIDT_SQ_STALL_PATTERN_1_2, DIDT_SQ_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_2_MASK, DIDT_SQ_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_2__SHIFT, 0x0001 },
+
+ /* DIDT_SQ_STALL_PATTERN_3_4 */
+ { ixDIDT_SQ_STALL_PATTERN_3_4, DIDT_SQ_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_3_MASK, DIDT_SQ_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_3__SHIFT, 0x0001 },
+ { ixDIDT_SQ_STALL_PATTERN_3_4, DIDT_SQ_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_4_MASK, DIDT_SQ_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_4__SHIFT, 0x0001 },
+
+ /* DIDT_SQ_STALL_PATTERN_5_6 */
+ { ixDIDT_SQ_STALL_PATTERN_5_6, DIDT_SQ_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_5_MASK, DIDT_SQ_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_5__SHIFT, 0x0000 },
+ { ixDIDT_SQ_STALL_PATTERN_5_6, DIDT_SQ_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_6_MASK, DIDT_SQ_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_6__SHIFT, 0x0000 },
+
+ /* DIDT_SQ_STALL_PATTERN_7 */
+ { ixDIDT_SQ_STALL_PATTERN_7, DIDT_SQ_STALL_PATTERN_7__DIDT_STALL_PATTERN_7_MASK, DIDT_SQ_STALL_PATTERN_7__DIDT_STALL_PATTERN_7__SHIFT, 0x0000 },
+
+ /* DIDT_TCP_STALL_PATTERN_1_2 */
+ { ixDIDT_TCP_STALL_PATTERN_1_2, DIDT_TCP_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_1_MASK, DIDT_TCP_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_1__SHIFT, 0x0001 },
+ { ixDIDT_TCP_STALL_PATTERN_1_2, DIDT_TCP_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_2_MASK, DIDT_TCP_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_2__SHIFT, 0x0001 },
+
+ /* DIDT_TCP_STALL_PATTERN_3_4 */
+ { ixDIDT_TCP_STALL_PATTERN_3_4, DIDT_TCP_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_3_MASK, DIDT_TCP_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_3__SHIFT, 0x0001 },
+ { ixDIDT_TCP_STALL_PATTERN_3_4, DIDT_TCP_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_4_MASK, DIDT_TCP_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_4__SHIFT, 0x0001 },
+
+ /* DIDT_TCP_STALL_PATTERN_5_6 */
+ { ixDIDT_TCP_STALL_PATTERN_5_6, DIDT_TCP_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_5_MASK, DIDT_TCP_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_5__SHIFT, 0x0000 },
+ { ixDIDT_TCP_STALL_PATTERN_5_6, DIDT_TCP_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_6_MASK, DIDT_TCP_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_6__SHIFT, 0x0000 },
+
+ /* DIDT_TCP_STALL_PATTERN_7 */
+ { ixDIDT_TCP_STALL_PATTERN_7, DIDT_TCP_STALL_PATTERN_7__DIDT_STALL_PATTERN_7_MASK, DIDT_TCP_STALL_PATTERN_7__DIDT_STALL_PATTERN_7__SHIFT, 0x0000 },
+
+ /* DIDT_TD_STALL_PATTERN_1_2 */
+ { ixDIDT_TD_STALL_PATTERN_1_2, DIDT_TD_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_1_MASK, DIDT_TD_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_1__SHIFT, 0x0001 },
+ { ixDIDT_TD_STALL_PATTERN_1_2, DIDT_TD_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_2_MASK, DIDT_TD_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_2__SHIFT, 0x0001 },
+
+ /* DIDT_TD_STALL_PATTERN_3_4 */
+ { ixDIDT_TD_STALL_PATTERN_3_4, DIDT_TD_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_3_MASK, DIDT_TD_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_3__SHIFT, 0x0001 },
+ { ixDIDT_TD_STALL_PATTERN_3_4, DIDT_TD_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_4_MASK, DIDT_TD_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_4__SHIFT, 0x0001 },
+
+ /* DIDT_TD_STALL_PATTERN_5_6 */
+ { ixDIDT_TD_STALL_PATTERN_5_6, DIDT_TD_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_5_MASK, DIDT_TD_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_5__SHIFT, 0x0000 },
+ { ixDIDT_TD_STALL_PATTERN_5_6, DIDT_TD_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_6_MASK, DIDT_TD_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_6__SHIFT, 0x0000 },
+
+ /* DIDT_TD_STALL_PATTERN_7 */
+ { ixDIDT_TD_STALL_PATTERN_7, DIDT_TD_STALL_PATTERN_7__DIDT_STALL_PATTERN_7_MASK, DIDT_TD_STALL_PATTERN_7__DIDT_STALL_PATTERN_7__SHIFT, 0x0000 },
+
+ /* DIDT_DB_STALL_PATTERN_1_2 */
+ { ixDIDT_DB_STALL_PATTERN_1_2, DIDT_DB_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_1_MASK, DIDT_DB_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_1__SHIFT, 0x0001 },
+ { ixDIDT_DB_STALL_PATTERN_1_2, DIDT_DB_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_2_MASK, DIDT_DB_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_2__SHIFT, 0x0001 },
+
+ /* DIDT_DB_STALL_PATTERN_3_4 */
+ { ixDIDT_DB_STALL_PATTERN_3_4, DIDT_DB_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_3_MASK, DIDT_DB_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_3__SHIFT, 0x0001 },
+ { ixDIDT_DB_STALL_PATTERN_3_4, DIDT_DB_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_4_MASK, DIDT_DB_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_4__SHIFT, 0x0001 },
+
+ /* DIDT_DB_STALL_PATTERN_5_6 */
+ { ixDIDT_DB_STALL_PATTERN_5_6, DIDT_DB_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_5_MASK, DIDT_DB_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_5__SHIFT, 0x0000 },
+ { ixDIDT_DB_STALL_PATTERN_5_6, DIDT_DB_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_6_MASK, DIDT_DB_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_6__SHIFT, 0x0000 },
+
+ /* DIDT_DB_STALL_PATTERN_7 */
+ { ixDIDT_DB_STALL_PATTERN_7, DIDT_DB_STALL_PATTERN_7__DIDT_STALL_PATTERN_7_MASK, DIDT_DB_STALL_PATTERN_7__DIDT_STALL_PATTERN_7__SHIFT, 0x0000 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg SELCacConfig_Vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* SQ */
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x00060021 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x00860021 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x01060021 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x01860021 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x02060021 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x02860021 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x03060021 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x03860021 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x04060021 },
+ /* TD */
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x000E0020 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x008E0020 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x010E0020 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x018E0020 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x020E0020 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x028E0020 },
+ /* TCP */
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x001c0020 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x009c0020 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x011c0020 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x019c0020 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x021c0020 },
+ /* DB */
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x00200008 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x00820008 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x01020008 },
+ { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x01820008 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+
+static const struct vega10_didt_config_reg SEEDCStallPatternConfig_Vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* SQ */
+ { ixDIDT_SQ_EDC_STALL_PATTERN_1_2, 0xFFFFFFFF, 0, 0x00030001 },
+ { ixDIDT_SQ_EDC_STALL_PATTERN_3_4, 0xFFFFFFFF, 0, 0x000F0007 },
+ { ixDIDT_SQ_EDC_STALL_PATTERN_5_6, 0xFFFFFFFF, 0, 0x003F001F },
+ { ixDIDT_SQ_EDC_STALL_PATTERN_7, 0xFFFFFFFF, 0, 0x0000007F },
+ /* TD */
+ { ixDIDT_TD_EDC_STALL_PATTERN_1_2, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_TD_EDC_STALL_PATTERN_3_4, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_TD_EDC_STALL_PATTERN_5_6, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_TD_EDC_STALL_PATTERN_7, 0xFFFFFFFF, 0, 0x00000000 },
+ /* TCP */
+ { ixDIDT_TCP_EDC_STALL_PATTERN_1_2, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_TCP_EDC_STALL_PATTERN_3_4, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_TCP_EDC_STALL_PATTERN_5_6, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_TCP_EDC_STALL_PATTERN_7, 0xFFFFFFFF, 0, 0x00000000 },
+ /* DB */
+ { ixDIDT_DB_EDC_STALL_PATTERN_1_2, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_DB_EDC_STALL_PATTERN_3_4, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_DB_EDC_STALL_PATTERN_5_6, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_DB_EDC_STALL_PATTERN_7, 0xFFFFFFFF, 0, 0x00000000 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg SEEDCForceStallPatternConfig_Vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* SQ */
+ { ixDIDT_SQ_EDC_STALL_PATTERN_1_2, 0xFFFFFFFF, 0, 0x00000015 },
+ { ixDIDT_SQ_EDC_STALL_PATTERN_3_4, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_SQ_EDC_STALL_PATTERN_5_6, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_SQ_EDC_STALL_PATTERN_7, 0xFFFFFFFF, 0, 0x00000000 },
+ /* TD */
+ { ixDIDT_TD_EDC_STALL_PATTERN_1_2, 0xFFFFFFFF, 0, 0x00000015 },
+ { ixDIDT_TD_EDC_STALL_PATTERN_3_4, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_TD_EDC_STALL_PATTERN_5_6, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_TD_EDC_STALL_PATTERN_7, 0xFFFFFFFF, 0, 0x00000000 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg SEEDCStallDelayConfig_Vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* SQ */
+ { ixDIDT_SQ_EDC_STALL_DELAY_1, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_SQ_EDC_STALL_DELAY_2, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_SQ_EDC_STALL_DELAY_3, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_SQ_EDC_STALL_DELAY_4, 0xFFFFFFFF, 0, 0x00000000 },
+ /* TD */
+ { ixDIDT_TD_EDC_STALL_DELAY_1, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_TD_EDC_STALL_DELAY_2, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_TD_EDC_STALL_DELAY_3, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_TD_EDC_STALL_DELAY_4, 0xFFFFFFFF, 0, 0x00000000 },
+ /* TCP */
+ { ixDIDT_TCP_EDC_STALL_DELAY_1, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_TCP_EDC_STALL_DELAY_2, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_TCP_EDC_STALL_DELAY_3, 0xFFFFFFFF, 0, 0x00000000 },
+ { ixDIDT_TCP_EDC_STALL_DELAY_4, 0xFFFFFFFF, 0, 0x00000000 },
+ /* DB */
+ { ixDIDT_DB_EDC_STALL_DELAY_1, 0xFFFFFFFF, 0, 0x00000000 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg SEEDCThresholdConfig_Vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ { ixDIDT_SQ_EDC_THRESHOLD, 0xFFFFFFFF, 0, 0x0000010E },
+ { ixDIDT_TD_EDC_THRESHOLD, 0xFFFFFFFF, 0, 0xFFFFFFFF },
+ { ixDIDT_TCP_EDC_THRESHOLD, 0xFFFFFFFF, 0, 0xFFFFFFFF },
+ { ixDIDT_DB_EDC_THRESHOLD, 0xFFFFFFFF, 0, 0xFFFFFFFF },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg SEEDCCtrlResetConfig_Vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* SQ */
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_EN_MASK, DIDT_SQ_EDC_CTRL__EDC_EN__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_SW_RST_MASK, DIDT_SQ_EDC_CTRL__EDC_SW_RST__SHIFT, 0x0001 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE_MASK, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL_MASK, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS_MASK, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA_MASK, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_EN__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN__SHIFT, 0x0000 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg SEEDCCtrlConfig_Vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* SQ */
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_EN_MASK, DIDT_SQ_EDC_CTRL__EDC_EN__SHIFT, 0x0001 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_SW_RST_MASK, DIDT_SQ_EDC_CTRL__EDC_SW_RST__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE_MASK, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL_MASK, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0004 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS_MASK, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS__SHIFT, 0x0006 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA_MASK, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_EN__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN__SHIFT, 0x0001 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN__SHIFT, 0x0000 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg SEEDCCtrlForceStallConfig_Vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* SQ */
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_EN_MASK, DIDT_SQ_EDC_CTRL__EDC_EN__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_SW_RST_MASK, DIDT_SQ_EDC_CTRL__EDC_SW_RST__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE_MASK, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL_MASK, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL__SHIFT, 0x0001 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0001 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS_MASK, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS__SHIFT, 0x000C },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA_MASK, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_EN__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN__SHIFT, 0x0001 },
+
+ /* TD */
+ { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__EDC_EN_MASK, DIDT_TD_EDC_CTRL__EDC_EN__SHIFT, 0x0000 },
+ { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__EDC_SW_RST_MASK, DIDT_TD_EDC_CTRL__EDC_SW_RST__SHIFT, 0x0000 },
+ { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__EDC_CLK_EN_OVERRIDE_MASK, DIDT_TD_EDC_CTRL__EDC_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
+ { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__EDC_FORCE_STALL_MASK, DIDT_TD_EDC_CTRL__EDC_FORCE_STALL__SHIFT, 0x0001 },
+ { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_TD_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0001 },
+ { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS_MASK, DIDT_TD_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS__SHIFT, 0x000E },
+ { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA_MASK, DIDT_TD_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA__SHIFT, 0x0000 },
+ { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__GC_EDC_EN_MASK, DIDT_TD_EDC_CTRL__GC_EDC_EN__SHIFT, 0x0000 },
+ { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__GC_EDC_STALL_POLICY_MASK, DIDT_TD_EDC_CTRL__GC_EDC_STALL_POLICY__SHIFT, 0x0000 },
+ { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__GC_EDC_LEVEL_COMB_EN_MASK, DIDT_TD_EDC_CTRL__GC_EDC_LEVEL_COMB_EN__SHIFT, 0x0000 },
+ { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__SE_EDC_LEVEL_COMB_EN_MASK, DIDT_TD_EDC_CTRL__SE_EDC_LEVEL_COMB_EN__SHIFT, 0x0001 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg GCDiDtDroopCtrlConfig_vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ { mmGC_DIDT_DROOP_CTRL, GC_DIDT_DROOP_CTRL__DIDT_DROOP_LEVEL_EN_MASK, GC_DIDT_DROOP_CTRL__DIDT_DROOP_LEVEL_EN__SHIFT, 0x0000 },
+ { mmGC_DIDT_DROOP_CTRL, GC_DIDT_DROOP_CTRL__DIDT_DROOP_THRESHOLD_MASK, GC_DIDT_DROOP_CTRL__DIDT_DROOP_THRESHOLD__SHIFT, 0x0000 },
+ { mmGC_DIDT_DROOP_CTRL, GC_DIDT_DROOP_CTRL__DIDT_DROOP_LEVEL_INDEX_MASK, GC_DIDT_DROOP_CTRL__DIDT_DROOP_LEVEL_INDEX__SHIFT, 0x0000 },
+ { mmGC_DIDT_DROOP_CTRL, GC_DIDT_DROOP_CTRL__DIDT_LEVEL_SEL_MASK, GC_DIDT_DROOP_CTRL__DIDT_LEVEL_SEL__SHIFT, 0x0000 },
+ { mmGC_DIDT_DROOP_CTRL, GC_DIDT_DROOP_CTRL__DIDT_DROOP_LEVEL_OVERFLOW_MASK, GC_DIDT_DROOP_CTRL__DIDT_DROOP_LEVEL_OVERFLOW__SHIFT, 0x0000 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg GCDiDtCtrl0Config_vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ { mmGC_DIDT_CTRL0, GC_DIDT_CTRL0__DIDT_CTRL_EN_MASK, GC_DIDT_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0000 },
+ { mmGC_DIDT_CTRL0, GC_DIDT_CTRL0__PHASE_OFFSET_MASK, GC_DIDT_CTRL0__PHASE_OFFSET__SHIFT, 0x0000 },
+ { mmGC_DIDT_CTRL0, GC_DIDT_CTRL0__DIDT_SW_RST_MASK, GC_DIDT_CTRL0__DIDT_SW_RST__SHIFT, 0x0000 },
+ { mmGC_DIDT_CTRL0, GC_DIDT_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, GC_DIDT_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
+ { mmGC_DIDT_CTRL0, GC_DIDT_CTRL0__DIDT_TRIGGER_THROTTLE_LOWBIT_MASK, GC_DIDT_CTRL0__DIDT_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
+ { 0xFFFFFFFF } /* End of list */
+};
+
+
+static const struct vega10_didt_config_reg PSMSEEDCStallPatternConfig_Vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* SQ EDC STALL PATTERNs */
+ { ixDIDT_SQ_EDC_STALL_PATTERN_1_2, DIDT_SQ_EDC_STALL_PATTERN_1_2__EDC_STALL_PATTERN_1_MASK, DIDT_SQ_EDC_STALL_PATTERN_1_2__EDC_STALL_PATTERN_1__SHIFT, 0x0101 },
+ { ixDIDT_SQ_EDC_STALL_PATTERN_1_2, DIDT_SQ_EDC_STALL_PATTERN_1_2__EDC_STALL_PATTERN_2_MASK, DIDT_SQ_EDC_STALL_PATTERN_1_2__EDC_STALL_PATTERN_2__SHIFT, 0x0101 },
+ { ixDIDT_SQ_EDC_STALL_PATTERN_3_4, DIDT_SQ_EDC_STALL_PATTERN_3_4__EDC_STALL_PATTERN_3_MASK, DIDT_SQ_EDC_STALL_PATTERN_3_4__EDC_STALL_PATTERN_3__SHIFT, 0x1111 },
+ { ixDIDT_SQ_EDC_STALL_PATTERN_3_4, DIDT_SQ_EDC_STALL_PATTERN_3_4__EDC_STALL_PATTERN_4_MASK, DIDT_SQ_EDC_STALL_PATTERN_3_4__EDC_STALL_PATTERN_4__SHIFT, 0x1111 },
+
+ { ixDIDT_SQ_EDC_STALL_PATTERN_5_6, DIDT_SQ_EDC_STALL_PATTERN_5_6__EDC_STALL_PATTERN_5_MASK, DIDT_SQ_EDC_STALL_PATTERN_5_6__EDC_STALL_PATTERN_5__SHIFT, 0x1515 },
+ { ixDIDT_SQ_EDC_STALL_PATTERN_5_6, DIDT_SQ_EDC_STALL_PATTERN_5_6__EDC_STALL_PATTERN_6_MASK, DIDT_SQ_EDC_STALL_PATTERN_5_6__EDC_STALL_PATTERN_6__SHIFT, 0x1515 },
+
+ { ixDIDT_SQ_EDC_STALL_PATTERN_7, DIDT_SQ_EDC_STALL_PATTERN_7__EDC_STALL_PATTERN_7_MASK, DIDT_SQ_EDC_STALL_PATTERN_7__EDC_STALL_PATTERN_7__SHIFT, 0x5555 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg PSMSEEDCStallDelayConfig_Vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* SQ EDC STALL DELAYs */
+ { ixDIDT_SQ_EDC_STALL_DELAY_1, DIDT_SQ_EDC_STALL_DELAY_1__EDC_STALL_DELAY_SQ0_MASK, DIDT_SQ_EDC_STALL_DELAY_1__EDC_STALL_DELAY_SQ0__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_STALL_DELAY_1, DIDT_SQ_EDC_STALL_DELAY_1__EDC_STALL_DELAY_SQ1_MASK, DIDT_SQ_EDC_STALL_DELAY_1__EDC_STALL_DELAY_SQ1__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_STALL_DELAY_1, DIDT_SQ_EDC_STALL_DELAY_1__EDC_STALL_DELAY_SQ2_MASK, DIDT_SQ_EDC_STALL_DELAY_1__EDC_STALL_DELAY_SQ2__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_STALL_DELAY_1, DIDT_SQ_EDC_STALL_DELAY_1__EDC_STALL_DELAY_SQ3_MASK, DIDT_SQ_EDC_STALL_DELAY_1__EDC_STALL_DELAY_SQ3__SHIFT, 0x0000 },
+
+ { ixDIDT_SQ_EDC_STALL_DELAY_2, DIDT_SQ_EDC_STALL_DELAY_2__EDC_STALL_DELAY_SQ4_MASK, DIDT_SQ_EDC_STALL_DELAY_2__EDC_STALL_DELAY_SQ4__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_STALL_DELAY_2, DIDT_SQ_EDC_STALL_DELAY_2__EDC_STALL_DELAY_SQ5_MASK, DIDT_SQ_EDC_STALL_DELAY_2__EDC_STALL_DELAY_SQ5__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_STALL_DELAY_2, DIDT_SQ_EDC_STALL_DELAY_2__EDC_STALL_DELAY_SQ6_MASK, DIDT_SQ_EDC_STALL_DELAY_2__EDC_STALL_DELAY_SQ6__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_STALL_DELAY_2, DIDT_SQ_EDC_STALL_DELAY_2__EDC_STALL_DELAY_SQ7_MASK, DIDT_SQ_EDC_STALL_DELAY_2__EDC_STALL_DELAY_SQ7__SHIFT, 0x0000 },
+
+ { ixDIDT_SQ_EDC_STALL_DELAY_3, DIDT_SQ_EDC_STALL_DELAY_3__EDC_STALL_DELAY_SQ8_MASK, DIDT_SQ_EDC_STALL_DELAY_3__EDC_STALL_DELAY_SQ8__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_STALL_DELAY_3, DIDT_SQ_EDC_STALL_DELAY_3__EDC_STALL_DELAY_SQ9_MASK, DIDT_SQ_EDC_STALL_DELAY_3__EDC_STALL_DELAY_SQ9__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_STALL_DELAY_3, DIDT_SQ_EDC_STALL_DELAY_3__EDC_STALL_DELAY_SQ10_MASK, DIDT_SQ_EDC_STALL_DELAY_3__EDC_STALL_DELAY_SQ10__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_STALL_DELAY_3, DIDT_SQ_EDC_STALL_DELAY_3__EDC_STALL_DELAY_SQ11_MASK, DIDT_SQ_EDC_STALL_DELAY_3__EDC_STALL_DELAY_SQ11__SHIFT, 0x0000 },
+
+ { ixDIDT_SQ_EDC_STALL_DELAY_4, DIDT_SQ_EDC_STALL_DELAY_4__EDC_STALL_DELAY_SQ12_MASK, DIDT_SQ_EDC_STALL_DELAY_4__EDC_STALL_DELAY_SQ12__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_STALL_DELAY_4, DIDT_SQ_EDC_STALL_DELAY_4__EDC_STALL_DELAY_SQ12_MASK, DIDT_SQ_EDC_STALL_DELAY_4__EDC_STALL_DELAY_SQ13__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_STALL_DELAY_4, DIDT_SQ_EDC_STALL_DELAY_4__EDC_STALL_DELAY_SQ14_MASK, DIDT_SQ_EDC_STALL_DELAY_4__EDC_STALL_DELAY_SQ14__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_STALL_DELAY_4, DIDT_SQ_EDC_STALL_DELAY_4__EDC_STALL_DELAY_SQ15_MASK, DIDT_SQ_EDC_STALL_DELAY_4__EDC_STALL_DELAY_SQ15__SHIFT, 0x0000 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg PSMSEEDCCtrlResetConfig_Vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* SQ EDC CTRL */
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_EN_MASK, DIDT_SQ_EDC_CTRL__EDC_EN__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_SW_RST_MASK, DIDT_SQ_EDC_CTRL__EDC_SW_RST__SHIFT, 0x0001 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE_MASK, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL_MASK, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS_MASK, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA_MASK, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_EN__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN__SHIFT, 0x0000 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg PSMSEEDCCtrlConfig_Vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ /* SQ EDC CTRL */
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_EN_MASK, DIDT_SQ_EDC_CTRL__EDC_EN__SHIFT, 0x0001 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_SW_RST_MASK, DIDT_SQ_EDC_CTRL__EDC_SW_RST__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE_MASK, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL_MASK, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS_MASK, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS__SHIFT, 0x000E },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA_MASK, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA__SHIFT, 0x0000 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_EN__SHIFT, 0x0001 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY__SHIFT, 0x0003 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN__SHIFT, 0x0001 },
+ { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN__SHIFT, 0x0000 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg PSMGCEDCDroopCtrlConfig_vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ { mmGC_EDC_DROOP_CTRL, GC_EDC_DROOP_CTRL__EDC_DROOP_LEVEL_EN_MASK, GC_EDC_DROOP_CTRL__EDC_DROOP_LEVEL_EN__SHIFT, 0x0001 },
+ { mmGC_EDC_DROOP_CTRL, GC_EDC_DROOP_CTRL__EDC_DROOP_THRESHOLD_MASK, GC_EDC_DROOP_CTRL__EDC_DROOP_THRESHOLD__SHIFT, 0x0384 },
+ { mmGC_EDC_DROOP_CTRL, GC_EDC_DROOP_CTRL__EDC_DROOP_LEVEL_INDEX_MASK, GC_EDC_DROOP_CTRL__EDC_DROOP_LEVEL_INDEX__SHIFT, 0x0001 },
+ { mmGC_EDC_DROOP_CTRL, GC_EDC_DROOP_CTRL__AVG_PSM_SEL_MASK, GC_EDC_DROOP_CTRL__AVG_PSM_SEL__SHIFT, 0x0001 },
+ { mmGC_EDC_DROOP_CTRL, GC_EDC_DROOP_CTRL__EDC_LEVEL_SEL_MASK, GC_EDC_DROOP_CTRL__EDC_LEVEL_SEL__SHIFT, 0x0001 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg PSMGCEDCCtrlResetConfig_vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_EN_MASK, GC_EDC_CTRL__EDC_EN__SHIFT, 0x0000 },
+ { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_SW_RST_MASK, GC_EDC_CTRL__EDC_SW_RST__SHIFT, 0x0001 },
+ { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_CLK_EN_OVERRIDE_MASK, GC_EDC_CTRL__EDC_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
+ { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_FORCE_STALL_MASK, GC_EDC_CTRL__EDC_FORCE_STALL__SHIFT, 0x0000 },
+ { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT_MASK, GC_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
+ { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA_MASK, GC_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA__SHIFT, 0x0000 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg PSMGCEDCCtrlConfig_vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_EN_MASK, GC_EDC_CTRL__EDC_EN__SHIFT, 0x0001 },
+ { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_SW_RST_MASK, GC_EDC_CTRL__EDC_SW_RST__SHIFT, 0x0000 },
+ { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_CLK_EN_OVERRIDE_MASK, GC_EDC_CTRL__EDC_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
+ { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_FORCE_STALL_MASK, GC_EDC_CTRL__EDC_FORCE_STALL__SHIFT, 0x0000 },
+ { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT_MASK, GC_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
+ { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA_MASK, GC_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA__SHIFT, 0x0000 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg AvfsPSMResetConfig_vega10[]=
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ { 0x16A02, 0xFFFFFFFF, 0x0, 0x0000005F },
+ { 0x16A05, 0xFFFFFFFF, 0x0, 0x00000001 },
+ { 0x16A06, 0x00000001, 0x0, 0x02000000 },
+ { 0x16A01, 0xFFFFFFFF, 0x0, 0x00003027 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static const struct vega10_didt_config_reg AvfsPSMInitConfig_vega10[] =
+{
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * Offset Mask Shift Value
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+ { 0x16A05, 0xFFFFFFFF, 0x18, 0x00000001 },
+ { 0x16A05, 0xFFFFFFFF, 0x8, 0x00000003 },
+ { 0x16A05, 0xFFFFFFFF, 0xa, 0x00000006 },
+ { 0x16A05, 0xFFFFFFFF, 0x7, 0x00000000 },
+ { 0x16A06, 0xFFFFFFFF, 0x18, 0x00000001 },
+ { 0x16A06, 0xFFFFFFFF, 0x19, 0x00000001 },
+ { 0x16A01, 0xFFFFFFFF, 0x0, 0x00003027 },
+
+ { 0xFFFFFFFF } /* End of list */
+};
+
+static int vega10_program_didt_config_registers(struct pp_hwmgr *hwmgr, const struct vega10_didt_config_reg *config_regs, enum vega10_didt_config_reg_type reg_type)
+{
+ uint32_t data;
+
+ PP_ASSERT_WITH_CODE((config_regs != NULL), "[vega10_program_didt_config_registers] Invalid config register table!", return -EINVAL);
+
+ while (config_regs->offset != 0xFFFFFFFF) {
+ switch (reg_type) {
+ case VEGA10_CONFIGREG_DIDT:
+ data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, config_regs->offset);
+ data &= ~config_regs->mask;
+ data |= ((config_regs->value << config_regs->shift) & config_regs->mask);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, config_regs->offset, data);
+ break;
+ case VEGA10_CONFIGREG_GCCAC:
+ data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG_GC_CAC, config_regs->offset);
+ data &= ~config_regs->mask;
+ data |= ((config_regs->value << config_regs->shift) & config_regs->mask);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG_GC_CAC, config_regs->offset, data);
+ break;
+ case VEGA10_CONFIGREG_SECAC:
+ data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG_SE_CAC, config_regs->offset);
+ data &= ~config_regs->mask;
+ data |= ((config_regs->value << config_regs->shift) & config_regs->mask);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG_SE_CAC, config_regs->offset, data);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ config_regs++;
+ }
+
+ return 0;
+}
+
+static int vega10_program_gc_didt_config_registers(struct pp_hwmgr *hwmgr, const struct vega10_didt_config_reg *config_regs)
+{
+ uint32_t data;
+
+ while (config_regs->offset != 0xFFFFFFFF) {
+ data = cgs_read_register(hwmgr->device, config_regs->offset);
+ data &= ~config_regs->mask;
+ data |= ((config_regs->value << config_regs->shift) & config_regs->mask);
+ cgs_write_register(hwmgr->device, config_regs->offset, data);
+ config_regs++;
+ }
+
+ return 0;
+}
+
+static void vega10_didt_set_mask(struct pp_hwmgr *hwmgr, const bool enable)
+{
+ uint32_t data;
+ uint32_t en = (enable ? 1 : 0);
+ uint32_t didt_block_info = SQ_IR_MASK | TCP_IR_MASK | TD_PCC_MASK;
+
+ if (PP_CAP(PHM_PlatformCaps_SQRamping)) {
+ CGS_WREG32_FIELD_IND(hwmgr->device, CGS_IND_REG__DIDT,
+ DIDT_SQ_CTRL0, DIDT_CTRL_EN, en);
+ didt_block_info &= ~SQ_Enable_MASK;
+ didt_block_info |= en << SQ_Enable_SHIFT;
+ }
+
+ if (PP_CAP(PHM_PlatformCaps_DBRamping)) {
+ CGS_WREG32_FIELD_IND(hwmgr->device, CGS_IND_REG__DIDT,
+ DIDT_DB_CTRL0, DIDT_CTRL_EN, en);
+ didt_block_info &= ~DB_Enable_MASK;
+ didt_block_info |= en << DB_Enable_SHIFT;
+ }
+
+ if (PP_CAP(PHM_PlatformCaps_TDRamping)) {
+ CGS_WREG32_FIELD_IND(hwmgr->device, CGS_IND_REG__DIDT,
+ DIDT_TD_CTRL0, DIDT_CTRL_EN, en);
+ didt_block_info &= ~TD_Enable_MASK;
+ didt_block_info |= en << TD_Enable_SHIFT;
+ }
+
+ if (PP_CAP(PHM_PlatformCaps_TCPRamping)) {
+ CGS_WREG32_FIELD_IND(hwmgr->device, CGS_IND_REG__DIDT,
+ DIDT_TCP_CTRL0, DIDT_CTRL_EN, en);
+ didt_block_info &= ~TCP_Enable_MASK;
+ didt_block_info |= en << TCP_Enable_SHIFT;
+ }
+
+ if (PP_CAP(PHM_PlatformCaps_DBRRamping)) {
+ CGS_WREG32_FIELD_IND(hwmgr->device, CGS_IND_REG__DIDT,
+ DIDT_DBR_CTRL0, DIDT_CTRL_EN, en);
+ }
+
+ if (PP_CAP(PHM_PlatformCaps_DiDtEDCEnable)) {
+ if (PP_CAP(PHM_PlatformCaps_SQRamping)) {
+ data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_SQ_EDC_CTRL);
+ data = REG_SET_FIELD(data, DIDT_SQ_EDC_CTRL, EDC_EN, en);
+ data = REG_SET_FIELD(data, DIDT_SQ_EDC_CTRL, EDC_SW_RST, ~en);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_SQ_EDC_CTRL, data);
+ }
+
+ if (PP_CAP(PHM_PlatformCaps_DBRamping)) {
+ data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_DB_EDC_CTRL);
+ data = REG_SET_FIELD(data, DIDT_DB_EDC_CTRL, EDC_EN, en);
+ data = REG_SET_FIELD(data, DIDT_DB_EDC_CTRL, EDC_SW_RST, ~en);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_DB_EDC_CTRL, data);
+ }
+
+ if (PP_CAP(PHM_PlatformCaps_TDRamping)) {
+ data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TD_EDC_CTRL);
+ data = REG_SET_FIELD(data, DIDT_TD_EDC_CTRL, EDC_EN, en);
+ data = REG_SET_FIELD(data, DIDT_TD_EDC_CTRL, EDC_SW_RST, ~en);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TD_EDC_CTRL, data);
+ }
+
+ if (PP_CAP(PHM_PlatformCaps_TCPRamping)) {
+ data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TCP_EDC_CTRL);
+ data = REG_SET_FIELD(data, DIDT_TCP_EDC_CTRL, EDC_EN, en);
+ data = REG_SET_FIELD(data, DIDT_TCP_EDC_CTRL, EDC_SW_RST, ~en);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TCP_EDC_CTRL, data);
+ }
+
+ if (PP_CAP(PHM_PlatformCaps_DBRRamping)) {
+ data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_DBR_EDC_CTRL);
+ data = REG_SET_FIELD(data, DIDT_DBR_EDC_CTRL, EDC_EN, en);
+ data = REG_SET_FIELD(data, DIDT_DBR_EDC_CTRL, EDC_SW_RST, ~en);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_DBR_EDC_CTRL, data);
+ }
+ }
+
+ /* For Vega10, SMC does not support any mask yet. */
+ if (enable)
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_ConfigureGfxDidt, didt_block_info,
+ NULL);
+
+}
+
+static int vega10_enable_cac_driving_se_didt_config(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ int result;
+ uint32_t num_se = 0, count, data;
+
+ num_se = adev->gfx.config.max_shader_engines;
+
+ amdgpu_gfx_rlc_enter_safe_mode(adev);
+
+ mutex_lock(&adev->grbm_idx_mutex);
+ for (count = 0; count < num_se; count++) {
+ data = GRBM_GFX_INDEX__INSTANCE_BROADCAST_WRITES_MASK | GRBM_GFX_INDEX__SH_BROADCAST_WRITES_MASK | ( count << GRBM_GFX_INDEX__SE_INDEX__SHIFT);
+ WREG32_SOC15(GC, 0, mmGRBM_GFX_INDEX, data);
+
+ result = vega10_program_didt_config_registers(hwmgr, SEDiDtStallCtrlConfig_vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, SEDiDtStallPatternConfig_vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, SEDiDtWeightConfig_Vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, SEDiDtCtrl1Config_Vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, SEDiDtCtrl2Config_Vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, SEDiDtCtrl3Config_vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, SEDiDtTuningCtrlConfig_Vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, SELCacConfig_Vega10, VEGA10_CONFIGREG_SECAC);
+ result |= vega10_program_didt_config_registers(hwmgr, SEDiDtCtrl0Config_Vega10, VEGA10_CONFIGREG_DIDT);
+
+ if (0 != result)
+ break;
+ }
+ WREG32_SOC15(GC, 0, mmGRBM_GFX_INDEX, 0xE0000000);
+ mutex_unlock(&adev->grbm_idx_mutex);
+
+ vega10_didt_set_mask(hwmgr, true);
+
+ amdgpu_gfx_rlc_exit_safe_mode(adev);
+
+ return 0;
+}
+
+static int vega10_disable_cac_driving_se_didt_config(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ amdgpu_gfx_rlc_enter_safe_mode(adev);
+
+ vega10_didt_set_mask(hwmgr, false);
+
+ amdgpu_gfx_rlc_exit_safe_mode(adev);
+
+ return 0;
+}
+
+static int vega10_enable_psm_gc_didt_config(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ int result;
+ uint32_t num_se = 0, count, data;
+
+ num_se = adev->gfx.config.max_shader_engines;
+
+ amdgpu_gfx_rlc_enter_safe_mode(adev);
+
+ mutex_lock(&adev->grbm_idx_mutex);
+ for (count = 0; count < num_se; count++) {
+ data = GRBM_GFX_INDEX__INSTANCE_BROADCAST_WRITES_MASK | GRBM_GFX_INDEX__SH_BROADCAST_WRITES_MASK | ( count << GRBM_GFX_INDEX__SE_INDEX__SHIFT);
+ WREG32_SOC15(GC, 0, mmGRBM_GFX_INDEX, data);
+
+ result = vega10_program_didt_config_registers(hwmgr, SEDiDtStallCtrlConfig_vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, SEDiDtStallPatternConfig_vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, SEDiDtCtrl3Config_vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, SEDiDtCtrl0Config_Vega10, VEGA10_CONFIGREG_DIDT);
+ if (0 != result)
+ break;
+ }
+ WREG32_SOC15(GC, 0, mmGRBM_GFX_INDEX, 0xE0000000);
+ mutex_unlock(&adev->grbm_idx_mutex);
+
+ vega10_didt_set_mask(hwmgr, true);
+
+ amdgpu_gfx_rlc_exit_safe_mode(adev);
+
+ vega10_program_gc_didt_config_registers(hwmgr, GCDiDtDroopCtrlConfig_vega10);
+ if (PP_CAP(PHM_PlatformCaps_GCEDC))
+ vega10_program_gc_didt_config_registers(hwmgr, GCDiDtCtrl0Config_vega10);
+
+ if (PP_CAP(PHM_PlatformCaps_PSM))
+ vega10_program_gc_didt_config_registers(hwmgr, AvfsPSMInitConfig_vega10);
+
+ return 0;
+}
+
+static int vega10_disable_psm_gc_didt_config(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t data;
+
+ amdgpu_gfx_rlc_enter_safe_mode(adev);
+
+ vega10_didt_set_mask(hwmgr, false);
+
+ amdgpu_gfx_rlc_exit_safe_mode(adev);
+
+ if (PP_CAP(PHM_PlatformCaps_GCEDC)) {
+ data = 0x00000000;
+ cgs_write_register(hwmgr->device, mmGC_DIDT_CTRL0, data);
+ }
+
+ if (PP_CAP(PHM_PlatformCaps_PSM))
+ vega10_program_gc_didt_config_registers(hwmgr, AvfsPSMResetConfig_vega10);
+
+ return 0;
+}
+
+static int vega10_enable_se_edc_config(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ int result;
+ uint32_t num_se = 0, count, data;
+
+ num_se = adev->gfx.config.max_shader_engines;
+
+ amdgpu_gfx_rlc_enter_safe_mode(adev);
+
+ mutex_lock(&adev->grbm_idx_mutex);
+ for (count = 0; count < num_se; count++) {
+ data = GRBM_GFX_INDEX__INSTANCE_BROADCAST_WRITES_MASK | GRBM_GFX_INDEX__SH_BROADCAST_WRITES_MASK | ( count << GRBM_GFX_INDEX__SE_INDEX__SHIFT);
+ WREG32_SOC15(GC, 0, mmGRBM_GFX_INDEX, data);
+ result = vega10_program_didt_config_registers(hwmgr, SEDiDtWeightConfig_Vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, SEEDCStallPatternConfig_Vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, SEEDCStallDelayConfig_Vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, SEEDCThresholdConfig_Vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, SEEDCCtrlResetConfig_Vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, SEEDCCtrlConfig_Vega10, VEGA10_CONFIGREG_DIDT);
+
+ if (0 != result)
+ break;
+ }
+ WREG32_SOC15(GC, 0, mmGRBM_GFX_INDEX, 0xE0000000);
+ mutex_unlock(&adev->grbm_idx_mutex);
+
+ vega10_didt_set_mask(hwmgr, true);
+
+ amdgpu_gfx_rlc_exit_safe_mode(adev);
+
+ return 0;
+}
+
+static int vega10_disable_se_edc_config(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ amdgpu_gfx_rlc_enter_safe_mode(adev);
+
+ vega10_didt_set_mask(hwmgr, false);
+
+ amdgpu_gfx_rlc_exit_safe_mode(adev);
+
+ return 0;
+}
+
+static int vega10_enable_psm_gc_edc_config(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ int result = 0;
+ uint32_t num_se = 0;
+ uint32_t count, data;
+
+ num_se = adev->gfx.config.max_shader_engines;
+
+ amdgpu_gfx_rlc_enter_safe_mode(adev);
+
+ vega10_program_gc_didt_config_registers(hwmgr, AvfsPSMResetConfig_vega10);
+
+ mutex_lock(&adev->grbm_idx_mutex);
+ for (count = 0; count < num_se; count++) {
+ data = GRBM_GFX_INDEX__INSTANCE_BROADCAST_WRITES_MASK | GRBM_GFX_INDEX__SH_BROADCAST_WRITES_MASK | ( count << GRBM_GFX_INDEX__SE_INDEX__SHIFT);
+ WREG32_SOC15(GC, 0, mmGRBM_GFX_INDEX, data);
+ result = vega10_program_didt_config_registers(hwmgr, PSMSEEDCStallPatternConfig_Vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, PSMSEEDCStallDelayConfig_Vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, PSMSEEDCCtrlResetConfig_Vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, PSMSEEDCCtrlConfig_Vega10, VEGA10_CONFIGREG_DIDT);
+
+ if (0 != result)
+ break;
+ }
+ WREG32_SOC15(GC, 0, mmGRBM_GFX_INDEX, 0xE0000000);
+ mutex_unlock(&adev->grbm_idx_mutex);
+
+ vega10_didt_set_mask(hwmgr, true);
+
+ amdgpu_gfx_rlc_exit_safe_mode(adev);
+
+ vega10_program_gc_didt_config_registers(hwmgr, PSMGCEDCDroopCtrlConfig_vega10);
+
+ if (PP_CAP(PHM_PlatformCaps_GCEDC)) {
+ vega10_program_gc_didt_config_registers(hwmgr, PSMGCEDCCtrlResetConfig_vega10);
+ vega10_program_gc_didt_config_registers(hwmgr, PSMGCEDCCtrlConfig_vega10);
+ }
+
+ if (PP_CAP(PHM_PlatformCaps_PSM))
+ vega10_program_gc_didt_config_registers(hwmgr, AvfsPSMInitConfig_vega10);
+
+ return 0;
+}
+
+static int vega10_disable_psm_gc_edc_config(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t data;
+
+ amdgpu_gfx_rlc_enter_safe_mode(adev);
+
+ vega10_didt_set_mask(hwmgr, false);
+
+ amdgpu_gfx_rlc_exit_safe_mode(adev);
+
+ if (PP_CAP(PHM_PlatformCaps_GCEDC)) {
+ data = 0x00000000;
+ cgs_write_register(hwmgr->device, mmGC_EDC_CTRL, data);
+ }
+
+ if (PP_CAP(PHM_PlatformCaps_PSM))
+ vega10_program_gc_didt_config_registers(hwmgr, AvfsPSMResetConfig_vega10);
+
+ return 0;
+}
+
+static int vega10_enable_se_edc_force_stall_config(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ int result;
+
+ amdgpu_gfx_rlc_enter_safe_mode(adev);
+
+ mutex_lock(&adev->grbm_idx_mutex);
+ WREG32_SOC15(GC, 0, mmGRBM_GFX_INDEX, 0xE0000000);
+ mutex_unlock(&adev->grbm_idx_mutex);
+
+ result = vega10_program_didt_config_registers(hwmgr, SEEDCForceStallPatternConfig_Vega10, VEGA10_CONFIGREG_DIDT);
+ result |= vega10_program_didt_config_registers(hwmgr, SEEDCCtrlForceStallConfig_Vega10, VEGA10_CONFIGREG_DIDT);
+ if (0 != result)
+ return result;
+
+ vega10_didt_set_mask(hwmgr, false);
+
+ amdgpu_gfx_rlc_exit_safe_mode(adev);
+
+ return 0;
+}
+
+static int vega10_disable_se_edc_force_stall_config(struct pp_hwmgr *hwmgr)
+{
+ int result;
+
+ result = vega10_disable_se_edc_config(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result), "[DisableDiDtConfig] Pre DIDT disable clock gating failed!", return result);
+
+ return 0;
+}
+
+int vega10_enable_didt_config(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->smu_features[GNLD_DIDT].supported) {
+ if (data->smu_features[GNLD_DIDT].enabled)
+ PP_DBG_LOG("[EnableDiDtConfig] Feature DiDt Already enabled!\n");
+
+ switch (data->registry_data.didt_mode) {
+ case 0:
+ result = vega10_enable_cac_driving_se_didt_config(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result), "[EnableDiDt] Attempt to enable DiDt Mode 0 Failed!", return result);
+ break;
+ case 2:
+ result = vega10_enable_psm_gc_didt_config(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result), "[EnableDiDt] Attempt to enable DiDt Mode 2 Failed!", return result);
+ break;
+ case 3:
+ result = vega10_enable_se_edc_config(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result), "[EnableDiDt] Attempt to enable DiDt Mode 3 Failed!", return result);
+ break;
+ case 1:
+ case 4:
+ case 5:
+ result = vega10_enable_psm_gc_edc_config(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result), "[EnableDiDt] Attempt to enable DiDt Mode 5 Failed!", return result);
+ break;
+ case 6:
+ result = vega10_enable_se_edc_force_stall_config(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result), "[EnableDiDt] Attempt to enable DiDt Mode 6 Failed!", return result);
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+
+ if (0 == result) {
+ result = vega10_enable_smc_features(hwmgr, true, data->smu_features[GNLD_DIDT].smu_feature_bitmap);
+ PP_ASSERT_WITH_CODE((0 == result), "[EnableDiDtConfig] Attempt to Enable DiDt feature Failed!", return result);
+ data->smu_features[GNLD_DIDT].enabled = true;
+ }
+ }
+
+ return result;
+}
+
+int vega10_disable_didt_config(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->smu_features[GNLD_DIDT].supported) {
+ if (!data->smu_features[GNLD_DIDT].enabled)
+ PP_DBG_LOG("[DisableDiDtConfig] Feature DiDt Already Disabled!\n");
+
+ switch (data->registry_data.didt_mode) {
+ case 0:
+ result = vega10_disable_cac_driving_se_didt_config(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result), "[DisableDiDt] Attempt to disable DiDt Mode 0 Failed!", return result);
+ break;
+ case 2:
+ result = vega10_disable_psm_gc_didt_config(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result), "[DisableDiDt] Attempt to disable DiDt Mode 2 Failed!", return result);
+ break;
+ case 3:
+ result = vega10_disable_se_edc_config(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result), "[DisableDiDt] Attempt to disable DiDt Mode 3 Failed!", return result);
+ break;
+ case 1:
+ case 4:
+ case 5:
+ result = vega10_disable_psm_gc_edc_config(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result), "[DisableDiDt] Attempt to disable DiDt Mode 5 Failed!", return result);
+ break;
+ case 6:
+ result = vega10_disable_se_edc_force_stall_config(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result), "[DisableDiDt] Attempt to disable DiDt Mode 6 Failed!", return result);
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+
+ if (0 == result) {
+ result = vega10_enable_smc_features(hwmgr, false, data->smu_features[GNLD_DIDT].smu_feature_bitmap);
+ PP_ASSERT_WITH_CODE((0 == result), "[DisableDiDtConfig] Attempt to Disable DiDt feature Failed!", return result);
+ data->smu_features[GNLD_DIDT].enabled = false;
+ }
+ }
+
+ return result;
+}
+
+void vega10_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ struct phm_tdp_table *tdp_table = table_info->tdp_table;
+ PPTable_t *table = &(data->smc_state_table.pp_table);
+
+ table->SocketPowerLimit = cpu_to_le16(
+ tdp_table->usMaximumPowerDeliveryLimit);
+ table->TdcLimit = cpu_to_le16(tdp_table->usTDC);
+ table->EdcLimit = cpu_to_le16(tdp_table->usEDCLimit);
+ table->TedgeLimit = cpu_to_le16(tdp_table->usTemperatureLimitTedge);
+ table->ThotspotLimit = cpu_to_le16(tdp_table->usTemperatureLimitHotspot);
+ table->ThbmLimit = cpu_to_le16(tdp_table->usTemperatureLimitHBM);
+ table->Tvr_socLimit = cpu_to_le16(tdp_table->usTemperatureLimitVrVddc);
+ table->Tvr_memLimit = cpu_to_le16(tdp_table->usTemperatureLimitVrMvdd);
+ table->Tliquid1Limit = cpu_to_le16(tdp_table->usTemperatureLimitLiquid1);
+ table->Tliquid2Limit = cpu_to_le16(tdp_table->usTemperatureLimitLiquid2);
+ table->TplxLimit = cpu_to_le16(tdp_table->usTemperatureLimitPlx);
+ table->LoadLineResistance =
+ hwmgr->platform_descriptor.LoadLineSlope * 256;
+ table->FitLimit = 0; /* Not used for Vega10 */
+
+ table->Liquid1_I2C_address = tdp_table->ucLiquid1_I2C_address;
+ table->Liquid2_I2C_address = tdp_table->ucLiquid2_I2C_address;
+ table->Vr_I2C_address = tdp_table->ucVr_I2C_address;
+ table->Plx_I2C_address = tdp_table->ucPlx_I2C_address;
+
+ table->Liquid_I2C_LineSCL = tdp_table->ucLiquid_I2C_Line;
+ table->Liquid_I2C_LineSDA = tdp_table->ucLiquid_I2C_LineSDA;
+
+ table->Vr_I2C_LineSCL = tdp_table->ucVr_I2C_Line;
+ table->Vr_I2C_LineSDA = tdp_table->ucVr_I2C_LineSDA;
+
+ table->Plx_I2C_LineSCL = tdp_table->ucPlx_I2C_Line;
+ table->Plx_I2C_LineSDA = tdp_table->ucPlx_I2C_LineSDA;
+}
+
+int vega10_set_power_limit(struct pp_hwmgr *hwmgr, uint32_t n)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->registry_data.enable_pkg_pwr_tracking_feature)
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetPptLimit, n,
+ NULL);
+
+ return 0;
+}
+
+int vega10_enable_power_containment(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ struct phm_tdp_table *tdp_table = table_info->tdp_table;
+ int result = 0;
+
+ hwmgr->default_power_limit = hwmgr->power_limit =
+ (uint32_t)(tdp_table->usMaximumPowerDeliveryLimit);
+
+ if (!hwmgr->not_vf)
+ return 0;
+
+ if (PP_CAP(PHM_PlatformCaps_PowerContainment)) {
+ if (data->smu_features[GNLD_PPT].supported)
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ true, data->smu_features[GNLD_PPT].smu_feature_bitmap),
+ "Attempt to enable PPT feature Failed!",
+ data->smu_features[GNLD_PPT].supported = false);
+
+ if (data->smu_features[GNLD_TDC].supported)
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ true, data->smu_features[GNLD_TDC].smu_feature_bitmap),
+ "Attempt to enable PPT feature Failed!",
+ data->smu_features[GNLD_TDC].supported = false);
+
+ result = vega10_set_power_limit(hwmgr, hwmgr->power_limit);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to set Default Power Limit in SMC!",
+ return result);
+ }
+
+ return result;
+}
+
+int vega10_disable_power_containment(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (PP_CAP(PHM_PlatformCaps_PowerContainment)) {
+ if (data->smu_features[GNLD_PPT].supported)
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ false, data->smu_features[GNLD_PPT].smu_feature_bitmap),
+ "Attempt to disable PPT feature Failed!",
+ data->smu_features[GNLD_PPT].supported = false);
+
+ if (data->smu_features[GNLD_TDC].supported)
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ false, data->smu_features[GNLD_TDC].smu_feature_bitmap),
+ "Attempt to disable PPT feature Failed!",
+ data->smu_features[GNLD_TDC].supported = false);
+ }
+
+ return 0;
+}
+
+static void vega10_set_overdrive_target_percentage(struct pp_hwmgr *hwmgr,
+ uint32_t adjust_percent)
+{
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_OverDriveSetPercentage, adjust_percent,
+ NULL);
+}
+
+int vega10_power_control_set_level(struct pp_hwmgr *hwmgr)
+{
+ int adjust_percent;
+
+ if (PP_CAP(PHM_PlatformCaps_PowerContainment)) {
+ adjust_percent =
+ hwmgr->platform_descriptor.TDPAdjustmentPolarity ?
+ hwmgr->platform_descriptor.TDPAdjustment :
+ (-1 * hwmgr->platform_descriptor.TDPAdjustment);
+ vega10_set_overdrive_target_percentage(hwmgr,
+ (uint32_t)adjust_percent);
+ }
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_powertune.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_powertune.h
new file mode 100644
index 000000000000..b95771ab89cd
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_powertune.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _VEGA10_POWERTUNE_H_
+#define _VEGA10_POWERTUNE_H_
+
+enum vega10_pt_config_reg_type {
+ VEGA10_CONFIGREG_MMR = 0,
+ VEGA10_CONFIGREG_SMC_IND,
+ VEGA10_CONFIGREG_DIDT_IND,
+ VEGA10_CONFIGREG_CACHE,
+ VEGA10_CONFIGREG_MAX
+};
+
+enum vega10_didt_config_reg_type {
+ VEGA10_CONFIGREG_DIDT = 0,
+ VEGA10_CONFIGREG_GCCAC,
+ VEGA10_CONFIGREG_SECAC
+};
+
+/* PowerContainment Features */
+#define POWERCONTAINMENT_FEATURE_DTE 0x00000001
+#define POWERCONTAINMENT_FEATURE_TDCLimit 0x00000002
+#define POWERCONTAINMENT_FEATURE_PkgPwrLimit 0x00000004
+
+struct vega10_pt_config_reg {
+ uint32_t offset;
+ uint32_t mask;
+ uint32_t shift;
+ uint32_t value;
+ enum vega10_pt_config_reg_type type;
+};
+
+struct vega10_didt_config_reg {
+ uint32_t offset;
+ uint32_t mask;
+ uint32_t shift;
+ uint32_t value;
+};
+
+struct vega10_pt_defaults {
+ uint8_t SviLoadLineEn;
+ uint8_t SviLoadLineVddC;
+ uint8_t TDC_VDDC_ThrottleReleaseLimitPerc;
+ uint8_t TDC_MAWt;
+ uint8_t TdcWaterfallCtl;
+ uint8_t DTEAmbientTempBase;
+};
+
+void vega10_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr);
+int vega10_populate_bapm_parameters_in_dpm_table(struct pp_hwmgr *hwmgr);
+int vega10_populate_pm_fuses(struct pp_hwmgr *hwmgr);
+int vega10_enable_smc_cac(struct pp_hwmgr *hwmgr);
+int vega10_enable_power_containment(struct pp_hwmgr *hwmgr);
+int vega10_set_power_limit(struct pp_hwmgr *hwmgr, uint32_t n);
+int vega10_power_control_set_level(struct pp_hwmgr *hwmgr);
+int vega10_disable_power_containment(struct pp_hwmgr *hwmgr);
+
+int vega10_enable_didt_config(struct pp_hwmgr *hwmgr);
+int vega10_disable_didt_config(struct pp_hwmgr *hwmgr);
+
+#endif /* _VEGA10_POWERTUNE_H_ */
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_pptable.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_pptable.h
new file mode 100644
index 000000000000..c934e9612c1b
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_pptable.h
@@ -0,0 +1,445 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _VEGA10_PPTABLE_H_
+#define _VEGA10_PPTABLE_H_
+
+#pragma pack(push, 1)
+
+#define ATOM_VEGA10_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK 0x0f
+#define ATOM_VEGA10_PP_FANPARAMETERS_NOFAN 0x80
+
+#define ATOM_VEGA10_PP_THERMALCONTROLLER_NONE 0
+#define ATOM_VEGA10_PP_THERMALCONTROLLER_LM96163 17
+#define ATOM_VEGA10_PP_THERMALCONTROLLER_VEGA10 24
+
+#define ATOM_VEGA10_PP_THERMALCONTROLLER_ADT7473_WITH_INTERNAL 0x89
+#define ATOM_VEGA10_PP_THERMALCONTROLLER_EMC2103_WITH_INTERNAL 0x8D
+
+#define ATOM_VEGA10_PP_PLATFORM_CAP_POWERPLAY 0x1
+#define ATOM_VEGA10_PP_PLATFORM_CAP_SBIOSPOWERSOURCE 0x2
+#define ATOM_VEGA10_PP_PLATFORM_CAP_HARDWAREDC 0x4
+#define ATOM_VEGA10_PP_PLATFORM_CAP_BACO 0x8
+#define ATOM_VEGA10_PP_PLATFORM_COMBINE_PCC_WITH_THERMAL_SIGNAL 0x10
+
+
+/* ATOM_PPLIB_NONCLOCK_INFO::usClassification */
+#define ATOM_PPLIB_CLASSIFICATION_UI_MASK 0x0007
+#define ATOM_PPLIB_CLASSIFICATION_UI_SHIFT 0
+#define ATOM_PPLIB_CLASSIFICATION_UI_NONE 0
+#define ATOM_PPLIB_CLASSIFICATION_UI_BATTERY 1
+#define ATOM_PPLIB_CLASSIFICATION_UI_BALANCED 3
+#define ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE 5
+/* 2, 4, 6, 7 are reserved */
+
+#define ATOM_PPLIB_CLASSIFICATION_BOOT 0x0008
+#define ATOM_PPLIB_CLASSIFICATION_THERMAL 0x0010
+#define ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE 0x0020
+#define ATOM_PPLIB_CLASSIFICATION_REST 0x0040
+#define ATOM_PPLIB_CLASSIFICATION_FORCED 0x0080
+#define ATOM_PPLIB_CLASSIFICATION_ACPI 0x1000
+
+/* ATOM_PPLIB_NONCLOCK_INFO::usClassification2 */
+#define ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2 0x0001
+
+#define ATOM_Vega10_DISALLOW_ON_DC 0x00004000
+#define ATOM_Vega10_ENABLE_VARIBRIGHT 0x00008000
+
+#define ATOM_Vega10_TABLE_REVISION_VEGA10 8
+
+#define ATOM_Vega10_VoltageMode_AVFS_Interpolate 0
+#define ATOM_Vega10_VoltageMode_AVFS_WorstCase 1
+#define ATOM_Vega10_VoltageMode_Static 2
+
+typedef struct _ATOM_Vega10_POWERPLAYTABLE {
+ struct atom_common_table_header sHeader;
+ UCHAR ucTableRevision;
+ USHORT usTableSize; /* the size of header structure */
+ ULONG ulGoldenPPID; /* PPGen use only */
+ ULONG ulGoldenRevision; /* PPGen use only */
+ USHORT usFormatID; /* PPGen use only */
+ ULONG ulPlatformCaps; /* See ATOM_Vega10_CAPS_* */
+ ULONG ulMaxODEngineClock; /* For Overdrive. */
+ ULONG ulMaxODMemoryClock; /* For Overdrive. */
+ USHORT usPowerControlLimit;
+ USHORT usUlvVoltageOffset; /* in mv units */
+ USHORT usUlvSmnclkDid;
+ USHORT usUlvMp1clkDid;
+ USHORT usUlvGfxclkBypass;
+ USHORT usGfxclkSlewRate;
+ UCHAR ucGfxVoltageMode;
+ UCHAR ucSocVoltageMode;
+ UCHAR ucUclkVoltageMode;
+ UCHAR ucUvdVoltageMode;
+ UCHAR ucVceVoltageMode;
+ UCHAR ucMp0VoltageMode;
+ UCHAR ucDcefVoltageMode;
+ USHORT usStateArrayOffset; /* points to ATOM_Vega10_State_Array */
+ USHORT usFanTableOffset; /* points to ATOM_Vega10_Fan_Table */
+ USHORT usThermalControllerOffset; /* points to ATOM_Vega10_Thermal_Controller */
+ USHORT usSocclkDependencyTableOffset; /* points to ATOM_Vega10_SOCCLK_Dependency_Table */
+ USHORT usMclkDependencyTableOffset; /* points to ATOM_Vega10_MCLK_Dependency_Table */
+ USHORT usGfxclkDependencyTableOffset; /* points to ATOM_Vega10_GFXCLK_Dependency_Table */
+ USHORT usDcefclkDependencyTableOffset; /* points to ATOM_Vega10_DCEFCLK_Dependency_Table */
+ USHORT usVddcLookupTableOffset; /* points to ATOM_Vega10_Voltage_Lookup_Table */
+ USHORT usVddmemLookupTableOffset; /* points to ATOM_Vega10_Voltage_Lookup_Table */
+ USHORT usMMDependencyTableOffset; /* points to ATOM_Vega10_MM_Dependency_Table */
+ USHORT usVCEStateTableOffset; /* points to ATOM_Vega10_VCE_State_Table */
+ USHORT usReserve; /* No PPM Support for Vega10 */
+ USHORT usPowerTuneTableOffset; /* points to ATOM_Vega10_PowerTune_Table */
+ USHORT usHardLimitTableOffset; /* points to ATOM_Vega10_Hard_Limit_Table */
+ USHORT usVddciLookupTableOffset; /* points to ATOM_Vega10_Voltage_Lookup_Table */
+ USHORT usPCIETableOffset; /* points to ATOM_Vega10_PCIE_Table */
+ USHORT usPixclkDependencyTableOffset; /* points to ATOM_Vega10_PIXCLK_Dependency_Table */
+ USHORT usDispClkDependencyTableOffset; /* points to ATOM_Vega10_DISPCLK_Dependency_Table */
+ USHORT usPhyClkDependencyTableOffset; /* points to ATOM_Vega10_PHYCLK_Dependency_Table */
+} ATOM_Vega10_POWERPLAYTABLE;
+
+typedef struct _ATOM_Vega10_State {
+ UCHAR ucSocClockIndexHigh;
+ UCHAR ucSocClockIndexLow;
+ UCHAR ucGfxClockIndexHigh;
+ UCHAR ucGfxClockIndexLow;
+ UCHAR ucMemClockIndexHigh;
+ UCHAR ucMemClockIndexLow;
+ USHORT usClassification;
+ ULONG ulCapsAndSettings;
+ USHORT usClassification2;
+} ATOM_Vega10_State;
+
+typedef struct _ATOM_Vega10_State_Array {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries. */
+ ATOM_Vega10_State states[1]; /* Dynamically allocate entries. */
+} ATOM_Vega10_State_Array;
+
+typedef struct _ATOM_Vega10_CLK_Dependency_Record {
+ ULONG ulClk; /* Frequency of Clock */
+ UCHAR ucVddInd; /* Base voltage */
+} ATOM_Vega10_CLK_Dependency_Record;
+
+typedef struct _ATOM_Vega10_GFXCLK_Dependency_Record {
+ ULONG ulClk; /* Clock Frequency */
+ UCHAR ucVddInd; /* SOC_VDD index */
+ USHORT usCKSVOffsetandDisable; /* Bits 0~30: Voltage offset for CKS, Bit 31: Disable/enable for the GFXCLK level. */
+ USHORT usAVFSOffset; /* AVFS Voltage offset */
+} ATOM_Vega10_GFXCLK_Dependency_Record;
+
+typedef struct _ATOM_Vega10_GFXCLK_Dependency_Record_V2 {
+ ULONG ulClk;
+ UCHAR ucVddInd;
+ USHORT usCKSVOffsetandDisable;
+ USHORT usAVFSOffset;
+ UCHAR ucACGEnable;
+ UCHAR ucReserved[3];
+} ATOM_Vega10_GFXCLK_Dependency_Record_V2;
+
+typedef struct _ATOM_Vega10_MCLK_Dependency_Record {
+ ULONG ulMemClk; /* Clock Frequency */
+ UCHAR ucVddInd; /* SOC_VDD index */
+ UCHAR ucVddMemInd; /* MEM_VDD - only non zero for MCLK record */
+ UCHAR ucVddciInd; /* VDDCI = only non zero for MCLK record */
+} ATOM_Vega10_MCLK_Dependency_Record;
+
+typedef struct _ATOM_Vega10_GFXCLK_Dependency_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries. */
+ ATOM_Vega10_GFXCLK_Dependency_Record entries[1]; /* Dynamically allocate entries. */
+} ATOM_Vega10_GFXCLK_Dependency_Table;
+
+typedef struct _ATOM_Vega10_MCLK_Dependency_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries. */
+ ATOM_Vega10_MCLK_Dependency_Record entries[1]; /* Dynamically allocate entries. */
+} ATOM_Vega10_MCLK_Dependency_Table;
+
+typedef struct _ATOM_Vega10_SOCCLK_Dependency_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries. */
+ ATOM_Vega10_CLK_Dependency_Record entries[1]; /* Dynamically allocate entries. */
+} ATOM_Vega10_SOCCLK_Dependency_Table;
+
+typedef struct _ATOM_Vega10_DCEFCLK_Dependency_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries. */
+ ATOM_Vega10_CLK_Dependency_Record entries[1]; /* Dynamically allocate entries. */
+} ATOM_Vega10_DCEFCLK_Dependency_Table;
+
+typedef struct _ATOM_Vega10_PIXCLK_Dependency_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries. */
+ ATOM_Vega10_CLK_Dependency_Record entries[1]; /* Dynamically allocate entries. */
+} ATOM_Vega10_PIXCLK_Dependency_Table;
+
+typedef struct _ATOM_Vega10_DISPCLK_Dependency_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries.*/
+ ATOM_Vega10_CLK_Dependency_Record entries[1]; /* Dynamically allocate entries. */
+} ATOM_Vega10_DISPCLK_Dependency_Table;
+
+typedef struct _ATOM_Vega10_PHYCLK_Dependency_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries. */
+ ATOM_Vega10_CLK_Dependency_Record entries[1]; /* Dynamically allocate entries. */
+} ATOM_Vega10_PHYCLK_Dependency_Table;
+
+typedef struct _ATOM_Vega10_MM_Dependency_Record {
+ UCHAR ucVddcInd; /* SOC_VDD voltage */
+ ULONG ulDClk; /* UVD D-clock */
+ ULONG ulVClk; /* UVD V-clock */
+ ULONG ulEClk; /* VCE clock */
+ ULONG ulPSPClk; /* PSP clock */
+} ATOM_Vega10_MM_Dependency_Record;
+
+typedef struct _ATOM_Vega10_MM_Dependency_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries */
+ ATOM_Vega10_MM_Dependency_Record entries[1]; /* Dynamically allocate entries */
+} ATOM_Vega10_MM_Dependency_Table;
+
+typedef struct _ATOM_Vega10_PCIE_Record {
+ ULONG ulLCLK; /* LClock */
+ UCHAR ucPCIEGenSpeed; /* PCIE Speed */
+ UCHAR ucPCIELaneWidth; /* PCIE Lane Width */
+} ATOM_Vega10_PCIE_Record;
+
+typedef struct _ATOM_Vega10_PCIE_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries */
+ ATOM_Vega10_PCIE_Record entries[1]; /* Dynamically allocate entries. */
+} ATOM_Vega10_PCIE_Table;
+
+typedef struct _ATOM_Vega10_Voltage_Lookup_Record {
+ USHORT usVdd; /* Base voltage */
+} ATOM_Vega10_Voltage_Lookup_Record;
+
+typedef struct _ATOM_Vega10_Voltage_Lookup_Table {
+ UCHAR ucRevId;
+ UCHAR ucNumEntries; /* Number of entries */
+ ATOM_Vega10_Voltage_Lookup_Record entries[1]; /* Dynamically allocate entries */
+} ATOM_Vega10_Voltage_Lookup_Table;
+
+typedef struct _ATOM_Vega10_Fan_Table {
+ UCHAR ucRevId; /* Change this if the table format changes or version changes so that the other fields are not the same. */
+ USHORT usFanOutputSensitivity; /* Sensitivity of fan reaction to temepature changes. */
+ USHORT usFanRPMMax; /* The default value in RPM. */
+ USHORT usThrottlingRPM;
+ USHORT usFanAcousticLimit; /* Minimum Fan Controller Frequency Acoustic Limit. */
+ USHORT usTargetTemperature; /* The default ideal temperature in Celcius. */
+ USHORT usMinimumPWMLimit; /* The minimum PWM that the advanced fan controller can set. */
+ USHORT usTargetGfxClk; /* The ideal Fan Controller GFXCLK Frequency Acoustic Limit. */
+ USHORT usFanGainEdge;
+ USHORT usFanGainHotspot;
+ USHORT usFanGainLiquid;
+ USHORT usFanGainVrVddc;
+ USHORT usFanGainVrMvdd;
+ USHORT usFanGainPlx;
+ USHORT usFanGainHbm;
+ UCHAR ucEnableZeroRPM;
+ USHORT usFanStopTemperature;
+ USHORT usFanStartTemperature;
+} ATOM_Vega10_Fan_Table;
+
+typedef struct _ATOM_Vega10_Fan_Table_V2 {
+ UCHAR ucRevId;
+ USHORT usFanOutputSensitivity;
+ USHORT usFanAcousticLimitRpm;
+ USHORT usThrottlingRPM;
+ USHORT usTargetTemperature;
+ USHORT usMinimumPWMLimit;
+ USHORT usTargetGfxClk;
+ USHORT usFanGainEdge;
+ USHORT usFanGainHotspot;
+ USHORT usFanGainLiquid;
+ USHORT usFanGainVrVddc;
+ USHORT usFanGainVrMvdd;
+ USHORT usFanGainPlx;
+ USHORT usFanGainHbm;
+ UCHAR ucEnableZeroRPM;
+ USHORT usFanStopTemperature;
+ USHORT usFanStartTemperature;
+ UCHAR ucFanParameters;
+ UCHAR ucFanMinRPM;
+ UCHAR ucFanMaxRPM;
+} ATOM_Vega10_Fan_Table_V2;
+
+typedef struct _ATOM_Vega10_Fan_Table_V3 {
+ UCHAR ucRevId;
+ USHORT usFanOutputSensitivity;
+ USHORT usFanAcousticLimitRpm;
+ USHORT usThrottlingRPM;
+ USHORT usTargetTemperature;
+ USHORT usMinimumPWMLimit;
+ USHORT usTargetGfxClk;
+ USHORT usFanGainEdge;
+ USHORT usFanGainHotspot;
+ USHORT usFanGainLiquid;
+ USHORT usFanGainVrVddc;
+ USHORT usFanGainVrMvdd;
+ USHORT usFanGainPlx;
+ USHORT usFanGainHbm;
+ UCHAR ucEnableZeroRPM;
+ USHORT usFanStopTemperature;
+ USHORT usFanStartTemperature;
+ UCHAR ucFanParameters;
+ UCHAR ucFanMinRPM;
+ UCHAR ucFanMaxRPM;
+ USHORT usMGpuThrottlingRPM;
+} ATOM_Vega10_Fan_Table_V3;
+
+typedef struct _ATOM_Vega10_Thermal_Controller {
+ UCHAR ucRevId;
+ UCHAR ucType; /* one of ATOM_VEGA10_PP_THERMALCONTROLLER_*/
+ UCHAR ucI2cLine; /* as interpreted by DAL I2C */
+ UCHAR ucI2cAddress;
+ UCHAR ucFanParameters; /* Fan Control Parameters. */
+ UCHAR ucFanMinRPM; /* Fan Minimum RPM (hundreds) -- for display purposes only.*/
+ UCHAR ucFanMaxRPM; /* Fan Maximum RPM (hundreds) -- for display purposes only.*/
+ UCHAR ucFlags; /* to be defined */
+} ATOM_Vega10_Thermal_Controller;
+
+typedef struct _ATOM_Vega10_VCE_State_Record
+{
+ UCHAR ucVCEClockIndex; /*index into usVCEDependencyTableOffset of 'ATOM_Vega10_MM_Dependency_Table' type */
+ UCHAR ucFlag; /* 2 bits indicates memory p-states */
+ UCHAR ucSCLKIndex; /* index into ATOM_Vega10_SCLK_Dependency_Table */
+ UCHAR ucMCLKIndex; /* index into ATOM_Vega10_MCLK_Dependency_Table */
+} ATOM_Vega10_VCE_State_Record;
+
+typedef struct _ATOM_Vega10_VCE_State_Table
+{
+ UCHAR ucRevId;
+ UCHAR ucNumEntries;
+ ATOM_Vega10_VCE_State_Record entries[1];
+} ATOM_Vega10_VCE_State_Table;
+
+typedef struct _ATOM_Vega10_PowerTune_Table {
+ UCHAR ucRevId;
+ USHORT usSocketPowerLimit;
+ USHORT usBatteryPowerLimit;
+ USHORT usSmallPowerLimit;
+ USHORT usTdcLimit;
+ USHORT usEdcLimit;
+ USHORT usSoftwareShutdownTemp;
+ USHORT usTemperatureLimitHotSpot;
+ USHORT usTemperatureLimitLiquid1;
+ USHORT usTemperatureLimitLiquid2;
+ USHORT usTemperatureLimitHBM;
+ USHORT usTemperatureLimitVrSoc;
+ USHORT usTemperatureLimitVrMem;
+ USHORT usTemperatureLimitPlx;
+ USHORT usLoadLineResistance;
+ UCHAR ucLiquid1_I2C_address;
+ UCHAR ucLiquid2_I2C_address;
+ UCHAR ucVr_I2C_address;
+ UCHAR ucPlx_I2C_address;
+ UCHAR ucLiquid_I2C_LineSCL;
+ UCHAR ucLiquid_I2C_LineSDA;
+ UCHAR ucVr_I2C_LineSCL;
+ UCHAR ucVr_I2C_LineSDA;
+ UCHAR ucPlx_I2C_LineSCL;
+ UCHAR ucPlx_I2C_LineSDA;
+ USHORT usTemperatureLimitTedge;
+} ATOM_Vega10_PowerTune_Table;
+
+typedef struct _ATOM_Vega10_PowerTune_Table_V2
+{
+ UCHAR ucRevId;
+ USHORT usSocketPowerLimit;
+ USHORT usBatteryPowerLimit;
+ USHORT usSmallPowerLimit;
+ USHORT usTdcLimit;
+ USHORT usEdcLimit;
+ USHORT usSoftwareShutdownTemp;
+ USHORT usTemperatureLimitHotSpot;
+ USHORT usTemperatureLimitLiquid1;
+ USHORT usTemperatureLimitLiquid2;
+ USHORT usTemperatureLimitHBM;
+ USHORT usTemperatureLimitVrSoc;
+ USHORT usTemperatureLimitVrMem;
+ USHORT usTemperatureLimitPlx;
+ USHORT usLoadLineResistance;
+ UCHAR ucLiquid1_I2C_address;
+ UCHAR ucLiquid2_I2C_address;
+ UCHAR ucLiquid_I2C_Line;
+ UCHAR ucVr_I2C_address;
+ UCHAR ucVr_I2C_Line;
+ UCHAR ucPlx_I2C_address;
+ UCHAR ucPlx_I2C_Line;
+ USHORT usTemperatureLimitTedge;
+} ATOM_Vega10_PowerTune_Table_V2;
+
+typedef struct _ATOM_Vega10_PowerTune_Table_V3
+{
+ UCHAR ucRevId;
+ USHORT usSocketPowerLimit;
+ USHORT usBatteryPowerLimit;
+ USHORT usSmallPowerLimit;
+ USHORT usTdcLimit;
+ USHORT usEdcLimit;
+ USHORT usSoftwareShutdownTemp;
+ USHORT usTemperatureLimitHotSpot;
+ USHORT usTemperatureLimitLiquid1;
+ USHORT usTemperatureLimitLiquid2;
+ USHORT usTemperatureLimitHBM;
+ USHORT usTemperatureLimitVrSoc;
+ USHORT usTemperatureLimitVrMem;
+ USHORT usTemperatureLimitPlx;
+ USHORT usLoadLineResistance;
+ UCHAR ucLiquid1_I2C_address;
+ UCHAR ucLiquid2_I2C_address;
+ UCHAR ucLiquid_I2C_Line;
+ UCHAR ucVr_I2C_address;
+ UCHAR ucVr_I2C_Line;
+ UCHAR ucPlx_I2C_address;
+ UCHAR ucPlx_I2C_Line;
+ USHORT usTemperatureLimitTedge;
+ USHORT usBoostStartTemperature;
+ USHORT usBoostStopTemperature;
+ ULONG ulBoostClock;
+ ULONG Reserved[2];
+} ATOM_Vega10_PowerTune_Table_V3;
+
+typedef struct _ATOM_Vega10_Hard_Limit_Record {
+ ULONG ulSOCCLKLimit;
+ ULONG ulGFXCLKLimit;
+ ULONG ulMCLKLimit;
+ USHORT usVddcLimit;
+ USHORT usVddciLimit;
+ USHORT usVddMemLimit;
+} ATOM_Vega10_Hard_Limit_Record;
+
+typedef struct _ATOM_Vega10_Hard_Limit_Table
+{
+ UCHAR ucRevId;
+ UCHAR ucNumEntries;
+ ATOM_Vega10_Hard_Limit_Record entries[1];
+} ATOM_Vega10_Hard_Limit_Table;
+
+typedef struct _Vega10_PPTable_Generic_SubTable_Header
+{
+ UCHAR ucRevId;
+} Vega10_PPTable_Generic_SubTable_Header;
+
+#pragma pack(pop)
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c
new file mode 100644
index 000000000000..f29af5ca0aa0
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c
@@ -0,0 +1,1398 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+
+#include "vega10_processpptables.h"
+#include "ppatomfwctrl.h"
+#include "atomfirmware.h"
+#include "pp_debug.h"
+#include "cgs_common.h"
+#include "vega10_pptable.h"
+
+#define NUM_DSPCLK_LEVELS 8
+#define VEGA10_ENGINECLOCK_HARDMAX 198000
+
+static void set_hw_cap(struct pp_hwmgr *hwmgr, bool enable,
+ enum phm_platform_caps cap)
+{
+ if (enable)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, cap);
+ else
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps, cap);
+}
+
+static const void *get_powerplay_table(struct pp_hwmgr *hwmgr)
+{
+ int index = GetIndexIntoMasterDataTable(powerplayinfo);
+
+ u16 size;
+ u8 frev, crev;
+ const void *table_address = hwmgr->soft_pp_table;
+
+ if (!table_address) {
+ table_address = (ATOM_Vega10_POWERPLAYTABLE *)
+ smu_atom_get_data_table(hwmgr->adev, index,
+ &size, &frev, &crev);
+
+ hwmgr->soft_pp_table = table_address; /*Cache the result in RAM.*/
+ hwmgr->soft_pp_table_size = size;
+ }
+
+ return table_address;
+}
+
+static int check_powerplay_tables(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table)
+{
+ const ATOM_Vega10_State_Array *state_arrays;
+
+ state_arrays = (ATOM_Vega10_State_Array *)(((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usStateArrayOffset));
+
+ PP_ASSERT_WITH_CODE((powerplay_table->sHeader.format_revision >=
+ ATOM_Vega10_TABLE_REVISION_VEGA10),
+ "Unsupported PPTable format!", return -1);
+ PP_ASSERT_WITH_CODE(powerplay_table->usStateArrayOffset,
+ "State table is not set!", return -1);
+ PP_ASSERT_WITH_CODE(powerplay_table->sHeader.structuresize > 0,
+ "Invalid PowerPlay Table!", return -1);
+ PP_ASSERT_WITH_CODE(state_arrays->ucNumEntries > 0,
+ "Invalid PowerPlay Table!", return -1);
+
+ return 0;
+}
+
+static int set_platform_caps(struct pp_hwmgr *hwmgr, uint32_t powerplay_caps)
+{
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_VEGA10_PP_PLATFORM_CAP_POWERPLAY),
+ PHM_PlatformCaps_PowerPlaySupport);
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_VEGA10_PP_PLATFORM_CAP_SBIOSPOWERSOURCE),
+ PHM_PlatformCaps_BiosPowerSourceControl);
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_VEGA10_PP_PLATFORM_CAP_HARDWAREDC),
+ PHM_PlatformCaps_AutomaticDCTransition);
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_VEGA10_PP_PLATFORM_CAP_BACO),
+ PHM_PlatformCaps_BACO);
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_VEGA10_PP_PLATFORM_COMBINE_PCC_WITH_THERMAL_SIGNAL),
+ PHM_PlatformCaps_CombinePCCWithThermalSignal);
+
+ return 0;
+}
+
+static int init_thermal_controller(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table)
+{
+ const ATOM_Vega10_Thermal_Controller *thermal_controller;
+ const Vega10_PPTable_Generic_SubTable_Header *header;
+ const ATOM_Vega10_Fan_Table *fan_table_v1;
+ const ATOM_Vega10_Fan_Table_V2 *fan_table_v2;
+ const ATOM_Vega10_Fan_Table_V3 *fan_table_v3;
+
+ thermal_controller = (ATOM_Vega10_Thermal_Controller *)
+ (((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usThermalControllerOffset));
+
+ PP_ASSERT_WITH_CODE((powerplay_table->usThermalControllerOffset != 0),
+ "Thermal controller table not set!", return -EINVAL);
+
+ hwmgr->thermal_controller.ucType = thermal_controller->ucType;
+ hwmgr->thermal_controller.ucI2cLine = thermal_controller->ucI2cLine;
+ hwmgr->thermal_controller.ucI2cAddress = thermal_controller->ucI2cAddress;
+
+ hwmgr->thermal_controller.fanInfo.bNoFan =
+ (0 != (thermal_controller->ucFanParameters &
+ ATOM_VEGA10_PP_FANPARAMETERS_NOFAN));
+
+ hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution =
+ thermal_controller->ucFanParameters &
+ ATOM_VEGA10_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK;
+
+ hwmgr->thermal_controller.fanInfo.ulMinRPM =
+ thermal_controller->ucFanMinRPM * 100UL;
+ hwmgr->thermal_controller.fanInfo.ulMaxRPM =
+ thermal_controller->ucFanMaxRPM * 100UL;
+
+ hwmgr->thermal_controller.advanceFanControlParameters.ulCycleDelay
+ = 100000;
+
+ set_hw_cap(
+ hwmgr,
+ ATOM_VEGA10_PP_THERMALCONTROLLER_NONE != hwmgr->thermal_controller.ucType,
+ PHM_PlatformCaps_ThermalController);
+
+ if (!powerplay_table->usFanTableOffset)
+ return 0;
+
+ header = (const Vega10_PPTable_Generic_SubTable_Header *)
+ (((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usFanTableOffset));
+
+ if (header->ucRevId == 10) {
+ fan_table_v1 = (ATOM_Vega10_Fan_Table *)header;
+
+ PP_ASSERT_WITH_CODE((fan_table_v1->ucRevId >= 8),
+ "Invalid Input Fan Table!", return -EINVAL);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity =
+ le16_to_cpu(fan_table_v1->usFanOutputSensitivity);
+ hwmgr->thermal_controller.advanceFanControlParameters.usMaxFanRPM =
+ le16_to_cpu(fan_table_v1->usFanRPMMax);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanRPMMaxLimit =
+ le16_to_cpu(fan_table_v1->usThrottlingRPM);
+ hwmgr->thermal_controller.advanceFanControlParameters.ulMinFanSCLKAcousticLimit =
+ le16_to_cpu(fan_table_v1->usFanAcousticLimit);
+ hwmgr->thermal_controller.advanceFanControlParameters.usTMax =
+ le16_to_cpu(fan_table_v1->usTargetTemperature);
+ hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin =
+ le16_to_cpu(fan_table_v1->usMinimumPWMLimit);
+ hwmgr->thermal_controller.advanceFanControlParameters.ulTargetGfxClk =
+ le16_to_cpu(fan_table_v1->usTargetGfxClk);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainEdge =
+ le16_to_cpu(fan_table_v1->usFanGainEdge);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainHotspot =
+ le16_to_cpu(fan_table_v1->usFanGainHotspot);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainLiquid =
+ le16_to_cpu(fan_table_v1->usFanGainLiquid);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainVrVddc =
+ le16_to_cpu(fan_table_v1->usFanGainVrVddc);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainVrMvdd =
+ le16_to_cpu(fan_table_v1->usFanGainVrMvdd);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainPlx =
+ le16_to_cpu(fan_table_v1->usFanGainPlx);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainHbm =
+ le16_to_cpu(fan_table_v1->usFanGainHbm);
+
+ hwmgr->thermal_controller.advanceFanControlParameters.ucEnableZeroRPM =
+ fan_table_v1->ucEnableZeroRPM;
+ hwmgr->thermal_controller.advanceFanControlParameters.usZeroRPMStopTemperature =
+ le16_to_cpu(fan_table_v1->usFanStopTemperature);
+ hwmgr->thermal_controller.advanceFanControlParameters.usZeroRPMStartTemperature =
+ le16_to_cpu(fan_table_v1->usFanStartTemperature);
+ } else if (header->ucRevId == 0xb) {
+ fan_table_v2 = (ATOM_Vega10_Fan_Table_V2 *)header;
+
+ hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution =
+ fan_table_v2->ucFanParameters & ATOM_VEGA10_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK;
+ hwmgr->thermal_controller.fanInfo.ulMinRPM = fan_table_v2->ucFanMinRPM * 100UL;
+ hwmgr->thermal_controller.fanInfo.ulMaxRPM = fan_table_v2->ucFanMaxRPM * 100UL;
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity =
+ le16_to_cpu(fan_table_v2->usFanOutputSensitivity);
+ hwmgr->thermal_controller.advanceFanControlParameters.usMaxFanRPM =
+ fan_table_v2->ucFanMaxRPM * 100UL;
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanRPMMaxLimit =
+ le16_to_cpu(fan_table_v2->usThrottlingRPM);
+ hwmgr->thermal_controller.advanceFanControlParameters.ulMinFanSCLKAcousticLimit =
+ le16_to_cpu(fan_table_v2->usFanAcousticLimitRpm);
+ hwmgr->thermal_controller.advanceFanControlParameters.usTMax =
+ le16_to_cpu(fan_table_v2->usTargetTemperature);
+ hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin =
+ le16_to_cpu(fan_table_v2->usMinimumPWMLimit);
+ hwmgr->thermal_controller.advanceFanControlParameters.ulTargetGfxClk =
+ le16_to_cpu(fan_table_v2->usTargetGfxClk);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainEdge =
+ le16_to_cpu(fan_table_v2->usFanGainEdge);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainHotspot =
+ le16_to_cpu(fan_table_v2->usFanGainHotspot);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainLiquid =
+ le16_to_cpu(fan_table_v2->usFanGainLiquid);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainVrVddc =
+ le16_to_cpu(fan_table_v2->usFanGainVrVddc);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainVrMvdd =
+ le16_to_cpu(fan_table_v2->usFanGainVrMvdd);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainPlx =
+ le16_to_cpu(fan_table_v2->usFanGainPlx);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainHbm =
+ le16_to_cpu(fan_table_v2->usFanGainHbm);
+
+ hwmgr->thermal_controller.advanceFanControlParameters.ucEnableZeroRPM =
+ fan_table_v2->ucEnableZeroRPM;
+ hwmgr->thermal_controller.advanceFanControlParameters.usZeroRPMStopTemperature =
+ le16_to_cpu(fan_table_v2->usFanStopTemperature);
+ hwmgr->thermal_controller.advanceFanControlParameters.usZeroRPMStartTemperature =
+ le16_to_cpu(fan_table_v2->usFanStartTemperature);
+ } else if (header->ucRevId > 0xb) {
+ fan_table_v3 = (ATOM_Vega10_Fan_Table_V3 *)header;
+
+ hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution =
+ fan_table_v3->ucFanParameters & ATOM_VEGA10_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK;
+ hwmgr->thermal_controller.fanInfo.ulMinRPM = fan_table_v3->ucFanMinRPM * 100UL;
+ hwmgr->thermal_controller.fanInfo.ulMaxRPM = fan_table_v3->ucFanMaxRPM * 100UL;
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity =
+ le16_to_cpu(fan_table_v3->usFanOutputSensitivity);
+ hwmgr->thermal_controller.advanceFanControlParameters.usMaxFanRPM =
+ fan_table_v3->ucFanMaxRPM * 100UL;
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanRPMMaxLimit =
+ le16_to_cpu(fan_table_v3->usThrottlingRPM);
+ hwmgr->thermal_controller.advanceFanControlParameters.ulMinFanSCLKAcousticLimit =
+ le16_to_cpu(fan_table_v3->usFanAcousticLimitRpm);
+ hwmgr->thermal_controller.advanceFanControlParameters.usTMax =
+ le16_to_cpu(fan_table_v3->usTargetTemperature);
+ hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin =
+ le16_to_cpu(fan_table_v3->usMinimumPWMLimit);
+ hwmgr->thermal_controller.advanceFanControlParameters.ulTargetGfxClk =
+ le16_to_cpu(fan_table_v3->usTargetGfxClk);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainEdge =
+ le16_to_cpu(fan_table_v3->usFanGainEdge);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainHotspot =
+ le16_to_cpu(fan_table_v3->usFanGainHotspot);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainLiquid =
+ le16_to_cpu(fan_table_v3->usFanGainLiquid);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainVrVddc =
+ le16_to_cpu(fan_table_v3->usFanGainVrVddc);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainVrMvdd =
+ le16_to_cpu(fan_table_v3->usFanGainVrMvdd);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainPlx =
+ le16_to_cpu(fan_table_v3->usFanGainPlx);
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanGainHbm =
+ le16_to_cpu(fan_table_v3->usFanGainHbm);
+
+ hwmgr->thermal_controller.advanceFanControlParameters.ucEnableZeroRPM =
+ fan_table_v3->ucEnableZeroRPM;
+ hwmgr->thermal_controller.advanceFanControlParameters.usZeroRPMStopTemperature =
+ le16_to_cpu(fan_table_v3->usFanStopTemperature);
+ hwmgr->thermal_controller.advanceFanControlParameters.usZeroRPMStartTemperature =
+ le16_to_cpu(fan_table_v3->usFanStartTemperature);
+ hwmgr->thermal_controller.advanceFanControlParameters.usMGpuThrottlingRPMLimit =
+ le16_to_cpu(fan_table_v3->usMGpuThrottlingRPM);
+ }
+
+ return 0;
+}
+
+static int init_over_drive_limits(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table)
+{
+ const ATOM_Vega10_GFXCLK_Dependency_Table *gfxclk_dep_table =
+ (const ATOM_Vega10_GFXCLK_Dependency_Table *)
+ (((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usGfxclkDependencyTableOffset));
+ bool is_acg_enabled = false;
+ ATOM_Vega10_GFXCLK_Dependency_Record_V2 *patom_record_v2;
+
+ if (gfxclk_dep_table->ucRevId == 1) {
+ patom_record_v2 =
+ (ATOM_Vega10_GFXCLK_Dependency_Record_V2 *)gfxclk_dep_table->entries;
+ is_acg_enabled =
+ (bool)patom_record_v2[gfxclk_dep_table->ucNumEntries-1].ucACGEnable;
+ }
+
+ if (powerplay_table->ulMaxODEngineClock > VEGA10_ENGINECLOCK_HARDMAX &&
+ !is_acg_enabled)
+ hwmgr->platform_descriptor.overdriveLimit.engineClock =
+ VEGA10_ENGINECLOCK_HARDMAX;
+ else
+ hwmgr->platform_descriptor.overdriveLimit.engineClock =
+ le32_to_cpu(powerplay_table->ulMaxODEngineClock);
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock =
+ le32_to_cpu(powerplay_table->ulMaxODMemoryClock);
+
+ hwmgr->platform_descriptor.minOverdriveVDDC = 0;
+ hwmgr->platform_descriptor.maxOverdriveVDDC = 0;
+ hwmgr->platform_descriptor.overdriveVDDCStep = 0;
+
+ return 0;
+}
+
+static int get_mm_clock_voltage_table(
+ struct pp_hwmgr *hwmgr,
+ phm_ppt_v1_mm_clock_voltage_dependency_table **vega10_mm_table,
+ const ATOM_Vega10_MM_Dependency_Table *mm_dependency_table)
+{
+ uint32_t table_size, i;
+ const ATOM_Vega10_MM_Dependency_Record *mm_dependency_record;
+ phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table;
+
+ PP_ASSERT_WITH_CODE((mm_dependency_table->ucNumEntries != 0),
+ "Invalid PowerPlay Table!", return -1);
+
+ table_size = sizeof(uint32_t) +
+ sizeof(phm_ppt_v1_mm_clock_voltage_dependency_record) *
+ mm_dependency_table->ucNumEntries;
+ mm_table = kzalloc(table_size, GFP_KERNEL);
+
+ if (!mm_table)
+ return -ENOMEM;
+
+ mm_table->count = mm_dependency_table->ucNumEntries;
+
+ for (i = 0; i < mm_dependency_table->ucNumEntries; i++) {
+ mm_dependency_record = &mm_dependency_table->entries[i];
+ mm_table->entries[i].vddcInd = mm_dependency_record->ucVddcInd;
+ mm_table->entries[i].samclock =
+ le32_to_cpu(mm_dependency_record->ulPSPClk);
+ mm_table->entries[i].eclk = le32_to_cpu(mm_dependency_record->ulEClk);
+ mm_table->entries[i].vclk = le32_to_cpu(mm_dependency_record->ulVClk);
+ mm_table->entries[i].dclk = le32_to_cpu(mm_dependency_record->ulDClk);
+ }
+
+ *vega10_mm_table = mm_table;
+
+ return 0;
+}
+
+static void get_scl_sda_value(uint8_t line, uint8_t *scl, uint8_t* sda)
+{
+ switch(line){
+ case Vega10_I2CLineID_DDC1:
+ *scl = Vega10_I2C_DDC1CLK;
+ *sda = Vega10_I2C_DDC1DATA;
+ break;
+ case Vega10_I2CLineID_DDC2:
+ *scl = Vega10_I2C_DDC2CLK;
+ *sda = Vega10_I2C_DDC2DATA;
+ break;
+ case Vega10_I2CLineID_DDC3:
+ *scl = Vega10_I2C_DDC3CLK;
+ *sda = Vega10_I2C_DDC3DATA;
+ break;
+ case Vega10_I2CLineID_DDC4:
+ *scl = Vega10_I2C_DDC4CLK;
+ *sda = Vega10_I2C_DDC4DATA;
+ break;
+ case Vega10_I2CLineID_DDC5:
+ *scl = Vega10_I2C_DDC5CLK;
+ *sda = Vega10_I2C_DDC5DATA;
+ break;
+ case Vega10_I2CLineID_DDC6:
+ *scl = Vega10_I2C_DDC6CLK;
+ *sda = Vega10_I2C_DDC6DATA;
+ break;
+ case Vega10_I2CLineID_SCLSDA:
+ *scl = Vega10_I2C_SCL;
+ *sda = Vega10_I2C_SDA;
+ break;
+ case Vega10_I2CLineID_DDCVGA:
+ *scl = Vega10_I2C_DDCVGACLK;
+ *sda = Vega10_I2C_DDCVGADATA;
+ break;
+ default:
+ *scl = 0;
+ *sda = 0;
+ break;
+ }
+}
+
+static int get_tdp_table(
+ struct pp_hwmgr *hwmgr,
+ struct phm_tdp_table **info_tdp_table,
+ const Vega10_PPTable_Generic_SubTable_Header *table)
+{
+ uint32_t table_size;
+ struct phm_tdp_table *tdp_table;
+ uint8_t scl;
+ uint8_t sda;
+ const ATOM_Vega10_PowerTune_Table *power_tune_table;
+ const ATOM_Vega10_PowerTune_Table_V2 *power_tune_table_v2;
+ const ATOM_Vega10_PowerTune_Table_V3 *power_tune_table_v3;
+
+ table_size = sizeof(uint32_t) + sizeof(struct phm_tdp_table);
+
+ tdp_table = kzalloc(table_size, GFP_KERNEL);
+
+ if (!tdp_table)
+ return -ENOMEM;
+
+ if (table->ucRevId == 5) {
+ power_tune_table = (ATOM_Vega10_PowerTune_Table *)table;
+ tdp_table->usMaximumPowerDeliveryLimit = le16_to_cpu(power_tune_table->usSocketPowerLimit);
+ tdp_table->usTDC = le16_to_cpu(power_tune_table->usTdcLimit);
+ tdp_table->usEDCLimit = le16_to_cpu(power_tune_table->usEdcLimit);
+ tdp_table->usSoftwareShutdownTemp =
+ le16_to_cpu(power_tune_table->usSoftwareShutdownTemp);
+ tdp_table->usTemperatureLimitTedge =
+ le16_to_cpu(power_tune_table->usTemperatureLimitTedge);
+ tdp_table->usTemperatureLimitHotspot =
+ le16_to_cpu(power_tune_table->usTemperatureLimitHotSpot);
+ tdp_table->usTemperatureLimitLiquid1 =
+ le16_to_cpu(power_tune_table->usTemperatureLimitLiquid1);
+ tdp_table->usTemperatureLimitLiquid2 =
+ le16_to_cpu(power_tune_table->usTemperatureLimitLiquid2);
+ tdp_table->usTemperatureLimitHBM =
+ le16_to_cpu(power_tune_table->usTemperatureLimitHBM);
+ tdp_table->usTemperatureLimitVrVddc =
+ le16_to_cpu(power_tune_table->usTemperatureLimitVrSoc);
+ tdp_table->usTemperatureLimitVrMvdd =
+ le16_to_cpu(power_tune_table->usTemperatureLimitVrMem);
+ tdp_table->usTemperatureLimitPlx =
+ le16_to_cpu(power_tune_table->usTemperatureLimitPlx);
+ tdp_table->ucLiquid1_I2C_address = power_tune_table->ucLiquid1_I2C_address;
+ tdp_table->ucLiquid2_I2C_address = power_tune_table->ucLiquid2_I2C_address;
+ tdp_table->ucLiquid_I2C_Line = power_tune_table->ucLiquid_I2C_LineSCL;
+ tdp_table->ucLiquid_I2C_LineSDA = power_tune_table->ucLiquid_I2C_LineSDA;
+ tdp_table->ucVr_I2C_address = power_tune_table->ucVr_I2C_address;
+ tdp_table->ucVr_I2C_Line = power_tune_table->ucVr_I2C_LineSCL;
+ tdp_table->ucVr_I2C_LineSDA = power_tune_table->ucVr_I2C_LineSDA;
+ tdp_table->ucPlx_I2C_address = power_tune_table->ucPlx_I2C_address;
+ tdp_table->ucPlx_I2C_Line = power_tune_table->ucPlx_I2C_LineSCL;
+ tdp_table->ucPlx_I2C_LineSDA = power_tune_table->ucPlx_I2C_LineSDA;
+ hwmgr->platform_descriptor.LoadLineSlope = le16_to_cpu(power_tune_table->usLoadLineResistance);
+ } else if (table->ucRevId == 6) {
+ power_tune_table_v2 = (ATOM_Vega10_PowerTune_Table_V2 *)table;
+ tdp_table->usMaximumPowerDeliveryLimit = le16_to_cpu(power_tune_table_v2->usSocketPowerLimit);
+ tdp_table->usTDC = le16_to_cpu(power_tune_table_v2->usTdcLimit);
+ tdp_table->usEDCLimit = le16_to_cpu(power_tune_table_v2->usEdcLimit);
+ tdp_table->usSoftwareShutdownTemp =
+ le16_to_cpu(power_tune_table_v2->usSoftwareShutdownTemp);
+ tdp_table->usTemperatureLimitTedge =
+ le16_to_cpu(power_tune_table_v2->usTemperatureLimitTedge);
+ tdp_table->usTemperatureLimitHotspot =
+ le16_to_cpu(power_tune_table_v2->usTemperatureLimitHotSpot);
+ tdp_table->usTemperatureLimitLiquid1 =
+ le16_to_cpu(power_tune_table_v2->usTemperatureLimitLiquid1);
+ tdp_table->usTemperatureLimitLiquid2 =
+ le16_to_cpu(power_tune_table_v2->usTemperatureLimitLiquid2);
+ tdp_table->usTemperatureLimitHBM =
+ le16_to_cpu(power_tune_table_v2->usTemperatureLimitHBM);
+ tdp_table->usTemperatureLimitVrVddc =
+ le16_to_cpu(power_tune_table_v2->usTemperatureLimitVrSoc);
+ tdp_table->usTemperatureLimitVrMvdd =
+ le16_to_cpu(power_tune_table_v2->usTemperatureLimitVrMem);
+ tdp_table->usTemperatureLimitPlx =
+ le16_to_cpu(power_tune_table_v2->usTemperatureLimitPlx);
+ tdp_table->ucLiquid1_I2C_address = power_tune_table_v2->ucLiquid1_I2C_address;
+ tdp_table->ucLiquid2_I2C_address = power_tune_table_v2->ucLiquid2_I2C_address;
+
+ get_scl_sda_value(power_tune_table_v2->ucLiquid_I2C_Line, &scl, &sda);
+
+ tdp_table->ucLiquid_I2C_Line = scl;
+ tdp_table->ucLiquid_I2C_LineSDA = sda;
+
+ tdp_table->ucVr_I2C_address = power_tune_table_v2->ucVr_I2C_address;
+
+ get_scl_sda_value(power_tune_table_v2->ucVr_I2C_Line, &scl, &sda);
+
+ tdp_table->ucVr_I2C_Line = scl;
+ tdp_table->ucVr_I2C_LineSDA = sda;
+ tdp_table->ucPlx_I2C_address = power_tune_table_v2->ucPlx_I2C_address;
+
+ get_scl_sda_value(power_tune_table_v2->ucPlx_I2C_Line, &scl, &sda);
+
+ tdp_table->ucPlx_I2C_Line = scl;
+ tdp_table->ucPlx_I2C_LineSDA = sda;
+
+ hwmgr->platform_descriptor.LoadLineSlope =
+ le16_to_cpu(power_tune_table_v2->usLoadLineResistance);
+ } else {
+ power_tune_table_v3 = (ATOM_Vega10_PowerTune_Table_V3 *)table;
+ tdp_table->usMaximumPowerDeliveryLimit = le16_to_cpu(power_tune_table_v3->usSocketPowerLimit);
+ tdp_table->usTDC = le16_to_cpu(power_tune_table_v3->usTdcLimit);
+ tdp_table->usEDCLimit = le16_to_cpu(power_tune_table_v3->usEdcLimit);
+ tdp_table->usSoftwareShutdownTemp = le16_to_cpu(power_tune_table_v3->usSoftwareShutdownTemp);
+ tdp_table->usTemperatureLimitTedge = le16_to_cpu(power_tune_table_v3->usTemperatureLimitTedge);
+ tdp_table->usTemperatureLimitHotspot = le16_to_cpu(power_tune_table_v3->usTemperatureLimitHotSpot);
+ tdp_table->usTemperatureLimitLiquid1 = le16_to_cpu(power_tune_table_v3->usTemperatureLimitLiquid1);
+ tdp_table->usTemperatureLimitLiquid2 = le16_to_cpu(power_tune_table_v3->usTemperatureLimitLiquid2);
+ tdp_table->usTemperatureLimitHBM = le16_to_cpu(power_tune_table_v3->usTemperatureLimitHBM);
+ tdp_table->usTemperatureLimitVrVddc = le16_to_cpu(power_tune_table_v3->usTemperatureLimitVrSoc);
+ tdp_table->usTemperatureLimitVrMvdd = le16_to_cpu(power_tune_table_v3->usTemperatureLimitVrMem);
+ tdp_table->usTemperatureLimitPlx = le16_to_cpu(power_tune_table_v3->usTemperatureLimitPlx);
+ tdp_table->ucLiquid1_I2C_address = power_tune_table_v3->ucLiquid1_I2C_address;
+ tdp_table->ucLiquid2_I2C_address = power_tune_table_v3->ucLiquid2_I2C_address;
+ tdp_table->usBoostStartTemperature = le16_to_cpu(power_tune_table_v3->usBoostStartTemperature);
+ tdp_table->usBoostStopTemperature = le16_to_cpu(power_tune_table_v3->usBoostStopTemperature);
+ tdp_table->ulBoostClock = le32_to_cpu(power_tune_table_v3->ulBoostClock);
+
+ get_scl_sda_value(power_tune_table_v3->ucLiquid_I2C_Line, &scl, &sda);
+
+ tdp_table->ucLiquid_I2C_Line = scl;
+ tdp_table->ucLiquid_I2C_LineSDA = sda;
+
+ tdp_table->ucVr_I2C_address = power_tune_table_v3->ucVr_I2C_address;
+
+ get_scl_sda_value(power_tune_table_v3->ucVr_I2C_Line, &scl, &sda);
+
+ tdp_table->ucVr_I2C_Line = scl;
+ tdp_table->ucVr_I2C_LineSDA = sda;
+
+ tdp_table->ucPlx_I2C_address = power_tune_table_v3->ucPlx_I2C_address;
+
+ get_scl_sda_value(power_tune_table_v3->ucPlx_I2C_Line, &scl, &sda);
+
+ tdp_table->ucPlx_I2C_Line = scl;
+ tdp_table->ucPlx_I2C_LineSDA = sda;
+
+ hwmgr->platform_descriptor.LoadLineSlope =
+ le16_to_cpu(power_tune_table_v3->usLoadLineResistance);
+ }
+
+ *info_tdp_table = tdp_table;
+
+ return 0;
+}
+
+static int get_socclk_voltage_dependency_table(
+ struct pp_hwmgr *hwmgr,
+ phm_ppt_v1_clock_voltage_dependency_table **pp_vega10_clk_dep_table,
+ const ATOM_Vega10_SOCCLK_Dependency_Table *clk_dep_table)
+{
+ uint32_t table_size, i;
+ phm_ppt_v1_clock_voltage_dependency_table *clk_table;
+
+ PP_ASSERT_WITH_CODE(clk_dep_table->ucNumEntries,
+ "Invalid PowerPlay Table!", return -1);
+
+ table_size = sizeof(uint32_t) +
+ sizeof(phm_ppt_v1_clock_voltage_dependency_record) *
+ clk_dep_table->ucNumEntries;
+
+ clk_table = kzalloc(table_size, GFP_KERNEL);
+
+ if (!clk_table)
+ return -ENOMEM;
+
+ clk_table->count = (uint32_t)clk_dep_table->ucNumEntries;
+
+ for (i = 0; i < clk_dep_table->ucNumEntries; i++) {
+ clk_table->entries[i].vddInd =
+ clk_dep_table->entries[i].ucVddInd;
+ clk_table->entries[i].clk =
+ le32_to_cpu(clk_dep_table->entries[i].ulClk);
+ }
+
+ *pp_vega10_clk_dep_table = clk_table;
+
+ return 0;
+}
+
+static int get_mclk_voltage_dependency_table(
+ struct pp_hwmgr *hwmgr,
+ phm_ppt_v1_clock_voltage_dependency_table **pp_vega10_mclk_dep_table,
+ const ATOM_Vega10_MCLK_Dependency_Table *mclk_dep_table)
+{
+ uint32_t table_size, i;
+ phm_ppt_v1_clock_voltage_dependency_table *mclk_table;
+
+ PP_ASSERT_WITH_CODE(mclk_dep_table->ucNumEntries,
+ "Invalid PowerPlay Table!", return -1);
+
+ table_size = sizeof(uint32_t) +
+ sizeof(phm_ppt_v1_clock_voltage_dependency_record) *
+ mclk_dep_table->ucNumEntries;
+
+ mclk_table = kzalloc(table_size, GFP_KERNEL);
+
+ if (!mclk_table)
+ return -ENOMEM;
+
+ mclk_table->count = (uint32_t)mclk_dep_table->ucNumEntries;
+
+ for (i = 0; i < mclk_dep_table->ucNumEntries; i++) {
+ mclk_table->entries[i].vddInd =
+ mclk_dep_table->entries[i].ucVddInd;
+ mclk_table->entries[i].vddciInd =
+ mclk_dep_table->entries[i].ucVddciInd;
+ mclk_table->entries[i].mvddInd =
+ mclk_dep_table->entries[i].ucVddMemInd;
+ mclk_table->entries[i].clk =
+ le32_to_cpu(mclk_dep_table->entries[i].ulMemClk);
+ }
+
+ *pp_vega10_mclk_dep_table = mclk_table;
+
+ return 0;
+}
+
+static int get_gfxclk_voltage_dependency_table(
+ struct pp_hwmgr *hwmgr,
+ struct phm_ppt_v1_clock_voltage_dependency_table
+ **pp_vega10_clk_dep_table,
+ const ATOM_Vega10_GFXCLK_Dependency_Table *clk_dep_table)
+{
+ uint32_t table_size, i;
+ struct phm_ppt_v1_clock_voltage_dependency_table
+ *clk_table;
+ ATOM_Vega10_GFXCLK_Dependency_Record_V2 *patom_record_v2;
+
+ PP_ASSERT_WITH_CODE((clk_dep_table->ucNumEntries != 0),
+ "Invalid PowerPlay Table!", return -1);
+
+ table_size = sizeof(uint32_t) +
+ sizeof(phm_ppt_v1_clock_voltage_dependency_record) *
+ clk_dep_table->ucNumEntries;
+
+ clk_table = kzalloc(table_size, GFP_KERNEL);
+
+ if (!clk_table)
+ return -ENOMEM;
+
+ clk_table->count = clk_dep_table->ucNumEntries;
+
+ if (clk_dep_table->ucRevId == 0) {
+ for (i = 0; i < clk_table->count; i++) {
+ clk_table->entries[i].vddInd =
+ clk_dep_table->entries[i].ucVddInd;
+ clk_table->entries[i].clk =
+ le32_to_cpu(clk_dep_table->entries[i].ulClk);
+ clk_table->entries[i].cks_enable =
+ (((le16_to_cpu(clk_dep_table->entries[i].usCKSVOffsetandDisable) & 0x8000)
+ >> 15) == 0) ? 1 : 0;
+ clk_table->entries[i].cks_voffset =
+ le16_to_cpu(clk_dep_table->entries[i].usCKSVOffsetandDisable) & 0x7F;
+ clk_table->entries[i].sclk_offset =
+ le16_to_cpu(clk_dep_table->entries[i].usAVFSOffset);
+ }
+ } else if (clk_dep_table->ucRevId == 1) {
+ patom_record_v2 = (ATOM_Vega10_GFXCLK_Dependency_Record_V2 *)clk_dep_table->entries;
+ for (i = 0; i < clk_table->count; i++) {
+ clk_table->entries[i].vddInd =
+ patom_record_v2->ucVddInd;
+ clk_table->entries[i].clk =
+ le32_to_cpu(patom_record_v2->ulClk);
+ clk_table->entries[i].cks_enable =
+ (((le16_to_cpu(patom_record_v2->usCKSVOffsetandDisable) & 0x8000)
+ >> 15) == 0) ? 1 : 0;
+ clk_table->entries[i].cks_voffset =
+ le16_to_cpu(patom_record_v2->usCKSVOffsetandDisable) & 0x7F;
+ clk_table->entries[i].sclk_offset =
+ le16_to_cpu(patom_record_v2->usAVFSOffset);
+ patom_record_v2++;
+ }
+ } else {
+ kfree(clk_table);
+ PP_ASSERT_WITH_CODE(false,
+ "Unsupported GFXClockDependencyTable Revision!",
+ return -EINVAL);
+ }
+
+ *pp_vega10_clk_dep_table = clk_table;
+
+ return 0;
+}
+
+static int get_pix_clk_voltage_dependency_table(
+ struct pp_hwmgr *hwmgr,
+ struct phm_ppt_v1_clock_voltage_dependency_table
+ **pp_vega10_clk_dep_table,
+ const ATOM_Vega10_PIXCLK_Dependency_Table *clk_dep_table)
+{
+ uint32_t table_size, i;
+ struct phm_ppt_v1_clock_voltage_dependency_table
+ *clk_table;
+
+ PP_ASSERT_WITH_CODE((clk_dep_table->ucNumEntries != 0),
+ "Invalid PowerPlay Table!", return -1);
+
+ table_size = sizeof(uint32_t) +
+ sizeof(phm_ppt_v1_clock_voltage_dependency_record) *
+ clk_dep_table->ucNumEntries;
+
+ clk_table = kzalloc(table_size, GFP_KERNEL);
+
+ if (!clk_table)
+ return -ENOMEM;
+
+ clk_table->count = clk_dep_table->ucNumEntries;
+
+ for (i = 0; i < clk_table->count; i++) {
+ clk_table->entries[i].vddInd =
+ clk_dep_table->entries[i].ucVddInd;
+ clk_table->entries[i].clk =
+ le32_to_cpu(clk_dep_table->entries[i].ulClk);
+ }
+
+ *pp_vega10_clk_dep_table = clk_table;
+
+ return 0;
+}
+
+static int get_dcefclk_voltage_dependency_table(
+ struct pp_hwmgr *hwmgr,
+ struct phm_ppt_v1_clock_voltage_dependency_table
+ **pp_vega10_clk_dep_table,
+ const ATOM_Vega10_DCEFCLK_Dependency_Table *clk_dep_table)
+{
+ uint32_t table_size, i;
+ uint8_t num_entries;
+ struct phm_ppt_v1_clock_voltage_dependency_table
+ *clk_table;
+ uint32_t dev_id;
+ uint32_t rev_id;
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ PP_ASSERT_WITH_CODE((clk_dep_table->ucNumEntries != 0),
+ "Invalid PowerPlay Table!", return -1);
+
+/*
+ * workaround needed to add another DPM level for pioneer cards
+ * as VBIOS is locked down.
+ * This DPM level was added to support 3DPM monitors @ 4K120Hz
+ *
+ */
+ dev_id = adev->pdev->device;
+ rev_id = adev->pdev->revision;
+
+ if (dev_id == 0x6863 && rev_id == 0 &&
+ clk_dep_table->entries[clk_dep_table->ucNumEntries - 1].ulClk < 90000)
+ num_entries = clk_dep_table->ucNumEntries + 1 > NUM_DSPCLK_LEVELS ?
+ NUM_DSPCLK_LEVELS : clk_dep_table->ucNumEntries + 1;
+ else
+ num_entries = clk_dep_table->ucNumEntries;
+
+
+ table_size = sizeof(uint32_t) +
+ sizeof(phm_ppt_v1_clock_voltage_dependency_record) *
+ num_entries;
+
+ clk_table = kzalloc(table_size, GFP_KERNEL);
+
+ if (!clk_table)
+ return -ENOMEM;
+
+ clk_table->count = (uint32_t)num_entries;
+
+ for (i = 0; i < clk_dep_table->ucNumEntries; i++) {
+ clk_table->entries[i].vddInd =
+ clk_dep_table->entries[i].ucVddInd;
+ clk_table->entries[i].clk =
+ le32_to_cpu(clk_dep_table->entries[i].ulClk);
+ }
+
+ if (i < num_entries) {
+ clk_table->entries[i].vddInd = clk_dep_table->entries[i-1].ucVddInd;
+ clk_table->entries[i].clk = 90000;
+ }
+
+ *pp_vega10_clk_dep_table = clk_table;
+
+ return 0;
+}
+
+static int get_pcie_table(struct pp_hwmgr *hwmgr,
+ struct phm_ppt_v1_pcie_table **vega10_pcie_table,
+ const Vega10_PPTable_Generic_SubTable_Header *table)
+{
+ uint32_t table_size, i, pcie_count;
+ struct phm_ppt_v1_pcie_table *pcie_table;
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ const ATOM_Vega10_PCIE_Table *atom_pcie_table =
+ (ATOM_Vega10_PCIE_Table *)table;
+
+ PP_ASSERT_WITH_CODE(atom_pcie_table->ucNumEntries,
+ "Invalid PowerPlay Table!",
+ return 0);
+
+ table_size = sizeof(uint32_t) +
+ sizeof(struct phm_ppt_v1_pcie_record) *
+ atom_pcie_table->ucNumEntries;
+
+ pcie_table = kzalloc(table_size, GFP_KERNEL);
+
+ if (!pcie_table)
+ return -ENOMEM;
+
+ pcie_count = table_info->vdd_dep_on_sclk->count;
+ if (atom_pcie_table->ucNumEntries <= pcie_count)
+ pcie_count = atom_pcie_table->ucNumEntries;
+ else
+ pr_info("Number of Pcie Entries exceed the number of"
+ " GFXCLK Dpm Levels!"
+ " Disregarding the excess entries...\n");
+
+ pcie_table->count = pcie_count;
+
+ for (i = 0; i < pcie_count; i++) {
+ pcie_table->entries[i].gen_speed =
+ atom_pcie_table->entries[i].ucPCIEGenSpeed;
+ pcie_table->entries[i].lane_width =
+ atom_pcie_table->entries[i].ucPCIELaneWidth;
+ pcie_table->entries[i].pcie_sclk =
+ atom_pcie_table->entries[i].ulLCLK;
+ }
+
+ *vega10_pcie_table = pcie_table;
+
+ return 0;
+}
+
+static int get_hard_limits(
+ struct pp_hwmgr *hwmgr,
+ struct phm_clock_and_voltage_limits *limits,
+ const ATOM_Vega10_Hard_Limit_Table *limit_table)
+{
+ PP_ASSERT_WITH_CODE(limit_table->ucNumEntries,
+ "Invalid PowerPlay Table!", return -1);
+
+ /* currently we always take entries[0] parameters */
+ limits->sclk = le32_to_cpu(limit_table->entries[0].ulSOCCLKLimit);
+ limits->mclk = le32_to_cpu(limit_table->entries[0].ulMCLKLimit);
+ limits->gfxclk = le32_to_cpu(limit_table->entries[0].ulGFXCLKLimit);
+ limits->vddc = le16_to_cpu(limit_table->entries[0].usVddcLimit);
+ limits->vddci = le16_to_cpu(limit_table->entries[0].usVddciLimit);
+ limits->vddmem = le16_to_cpu(limit_table->entries[0].usVddMemLimit);
+
+ return 0;
+}
+
+static int get_valid_clk(
+ struct pp_hwmgr *hwmgr,
+ struct phm_clock_array **clk_table,
+ const phm_ppt_v1_clock_voltage_dependency_table *clk_volt_pp_table)
+{
+ uint32_t table_size, i;
+ struct phm_clock_array *table;
+
+ PP_ASSERT_WITH_CODE(clk_volt_pp_table->count,
+ "Invalid PowerPlay Table!", return -1);
+
+ table_size = sizeof(uint32_t) +
+ sizeof(uint32_t) * clk_volt_pp_table->count;
+
+ table = kzalloc(table_size, GFP_KERNEL);
+
+ if (!table)
+ return -ENOMEM;
+
+ table->count = (uint32_t)clk_volt_pp_table->count;
+
+ for (i = 0; i < table->count; i++)
+ table->values[i] = (uint32_t)clk_volt_pp_table->entries[i].clk;
+
+ *clk_table = table;
+
+ return 0;
+}
+
+static int init_powerplay_extended_tables(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table)
+{
+ int result = 0;
+ struct phm_ppt_v2_information *pp_table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+
+ const ATOM_Vega10_MM_Dependency_Table *mm_dependency_table =
+ (const ATOM_Vega10_MM_Dependency_Table *)
+ (((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usMMDependencyTableOffset));
+ const Vega10_PPTable_Generic_SubTable_Header *power_tune_table =
+ (const Vega10_PPTable_Generic_SubTable_Header *)
+ (((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usPowerTuneTableOffset));
+ const ATOM_Vega10_SOCCLK_Dependency_Table *socclk_dep_table =
+ (const ATOM_Vega10_SOCCLK_Dependency_Table *)
+ (((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usSocclkDependencyTableOffset));
+ const ATOM_Vega10_GFXCLK_Dependency_Table *gfxclk_dep_table =
+ (const ATOM_Vega10_GFXCLK_Dependency_Table *)
+ (((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usGfxclkDependencyTableOffset));
+ const ATOM_Vega10_DCEFCLK_Dependency_Table *dcefclk_dep_table =
+ (const ATOM_Vega10_DCEFCLK_Dependency_Table *)
+ (((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usDcefclkDependencyTableOffset));
+ const ATOM_Vega10_MCLK_Dependency_Table *mclk_dep_table =
+ (const ATOM_Vega10_MCLK_Dependency_Table *)
+ (((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usMclkDependencyTableOffset));
+ const ATOM_Vega10_Hard_Limit_Table *hard_limits =
+ (const ATOM_Vega10_Hard_Limit_Table *)
+ (((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usHardLimitTableOffset));
+ const Vega10_PPTable_Generic_SubTable_Header *pcie_table =
+ (const Vega10_PPTable_Generic_SubTable_Header *)
+ (((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usPCIETableOffset));
+ const ATOM_Vega10_PIXCLK_Dependency_Table *pixclk_dep_table =
+ (const ATOM_Vega10_PIXCLK_Dependency_Table *)
+ (((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usPixclkDependencyTableOffset));
+ const ATOM_Vega10_PHYCLK_Dependency_Table *phyclk_dep_table =
+ (const ATOM_Vega10_PHYCLK_Dependency_Table *)
+ (((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usPhyClkDependencyTableOffset));
+ const ATOM_Vega10_DISPCLK_Dependency_Table *dispclk_dep_table =
+ (const ATOM_Vega10_DISPCLK_Dependency_Table *)
+ (((unsigned long) powerplay_table) +
+ le16_to_cpu(powerplay_table->usDispClkDependencyTableOffset));
+
+ pp_table_info->vdd_dep_on_socclk = NULL;
+ pp_table_info->vdd_dep_on_sclk = NULL;
+ pp_table_info->vdd_dep_on_mclk = NULL;
+ pp_table_info->vdd_dep_on_dcefclk = NULL;
+ pp_table_info->mm_dep_table = NULL;
+ pp_table_info->tdp_table = NULL;
+ pp_table_info->vdd_dep_on_pixclk = NULL;
+ pp_table_info->vdd_dep_on_phyclk = NULL;
+ pp_table_info->vdd_dep_on_dispclk = NULL;
+
+ if (powerplay_table->usMMDependencyTableOffset)
+ result = get_mm_clock_voltage_table(hwmgr,
+ &pp_table_info->mm_dep_table,
+ mm_dependency_table);
+
+ if (!result && powerplay_table->usPowerTuneTableOffset)
+ result = get_tdp_table(hwmgr,
+ &pp_table_info->tdp_table,
+ power_tune_table);
+
+ if (!result && powerplay_table->usSocclkDependencyTableOffset)
+ result = get_socclk_voltage_dependency_table(hwmgr,
+ &pp_table_info->vdd_dep_on_socclk,
+ socclk_dep_table);
+
+ if (!result && powerplay_table->usGfxclkDependencyTableOffset)
+ result = get_gfxclk_voltage_dependency_table(hwmgr,
+ &pp_table_info->vdd_dep_on_sclk,
+ gfxclk_dep_table);
+
+ if (!result && powerplay_table->usPixclkDependencyTableOffset)
+ result = get_pix_clk_voltage_dependency_table(hwmgr,
+ &pp_table_info->vdd_dep_on_pixclk,
+ (const ATOM_Vega10_PIXCLK_Dependency_Table*)
+ pixclk_dep_table);
+
+ if (!result && powerplay_table->usPhyClkDependencyTableOffset)
+ result = get_pix_clk_voltage_dependency_table(hwmgr,
+ &pp_table_info->vdd_dep_on_phyclk,
+ (const ATOM_Vega10_PIXCLK_Dependency_Table *)
+ phyclk_dep_table);
+
+ if (!result && powerplay_table->usDispClkDependencyTableOffset)
+ result = get_pix_clk_voltage_dependency_table(hwmgr,
+ &pp_table_info->vdd_dep_on_dispclk,
+ (const ATOM_Vega10_PIXCLK_Dependency_Table *)
+ dispclk_dep_table);
+
+ if (!result && powerplay_table->usDcefclkDependencyTableOffset)
+ result = get_dcefclk_voltage_dependency_table(hwmgr,
+ &pp_table_info->vdd_dep_on_dcefclk,
+ dcefclk_dep_table);
+
+ if (!result && powerplay_table->usMclkDependencyTableOffset)
+ result = get_mclk_voltage_dependency_table(hwmgr,
+ &pp_table_info->vdd_dep_on_mclk,
+ mclk_dep_table);
+
+ if (!result && powerplay_table->usPCIETableOffset)
+ result = get_pcie_table(hwmgr,
+ &pp_table_info->pcie_table,
+ pcie_table);
+
+ if (!result && powerplay_table->usHardLimitTableOffset)
+ result = get_hard_limits(hwmgr,
+ &pp_table_info->max_clock_voltage_on_dc,
+ hard_limits);
+
+ hwmgr->dyn_state.max_clock_voltage_on_dc.sclk =
+ pp_table_info->max_clock_voltage_on_dc.sclk;
+ hwmgr->dyn_state.max_clock_voltage_on_dc.mclk =
+ pp_table_info->max_clock_voltage_on_dc.mclk;
+ hwmgr->dyn_state.max_clock_voltage_on_dc.vddc =
+ pp_table_info->max_clock_voltage_on_dc.vddc;
+ hwmgr->dyn_state.max_clock_voltage_on_dc.vddci =
+ pp_table_info->max_clock_voltage_on_dc.vddci;
+
+ if (!result &&
+ pp_table_info->vdd_dep_on_socclk &&
+ pp_table_info->vdd_dep_on_socclk->count)
+ result = get_valid_clk(hwmgr,
+ &pp_table_info->valid_socclk_values,
+ pp_table_info->vdd_dep_on_socclk);
+
+ if (!result &&
+ pp_table_info->vdd_dep_on_sclk &&
+ pp_table_info->vdd_dep_on_sclk->count)
+ result = get_valid_clk(hwmgr,
+ &pp_table_info->valid_sclk_values,
+ pp_table_info->vdd_dep_on_sclk);
+
+ if (!result &&
+ pp_table_info->vdd_dep_on_dcefclk &&
+ pp_table_info->vdd_dep_on_dcefclk->count)
+ result = get_valid_clk(hwmgr,
+ &pp_table_info->valid_dcefclk_values,
+ pp_table_info->vdd_dep_on_dcefclk);
+
+ if (!result &&
+ pp_table_info->vdd_dep_on_mclk &&
+ pp_table_info->vdd_dep_on_mclk->count)
+ result = get_valid_clk(hwmgr,
+ &pp_table_info->valid_mclk_values,
+ pp_table_info->vdd_dep_on_mclk);
+
+ return result;
+}
+
+static int get_vddc_lookup_table(
+ struct pp_hwmgr *hwmgr,
+ phm_ppt_v1_voltage_lookup_table **lookup_table,
+ const ATOM_Vega10_Voltage_Lookup_Table *vddc_lookup_pp_tables,
+ uint32_t max_levels)
+{
+ uint32_t table_size, i;
+ phm_ppt_v1_voltage_lookup_table *table;
+
+ PP_ASSERT_WITH_CODE((vddc_lookup_pp_tables->ucNumEntries != 0),
+ "Invalid SOC_VDDD Lookup Table!", return 1);
+
+ table_size = sizeof(uint32_t) +
+ sizeof(phm_ppt_v1_voltage_lookup_record) * max_levels;
+
+ table = kzalloc(table_size, GFP_KERNEL);
+
+ if (table == NULL)
+ return -ENOMEM;
+
+ table->count = vddc_lookup_pp_tables->ucNumEntries;
+
+ for (i = 0; i < vddc_lookup_pp_tables->ucNumEntries; i++)
+ table->entries[i].us_vdd =
+ le16_to_cpu(vddc_lookup_pp_tables->entries[i].usVdd);
+
+ *lookup_table = table;
+
+ return 0;
+}
+
+static int init_dpm_2_parameters(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table)
+{
+ int result = 0;
+ struct phm_ppt_v2_information *pp_table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ uint32_t disable_power_control = 0;
+
+ pp_table_info->us_ulv_voltage_offset =
+ le16_to_cpu(powerplay_table->usUlvVoltageOffset);
+
+ pp_table_info->us_ulv_smnclk_did =
+ le16_to_cpu(powerplay_table->usUlvSmnclkDid);
+ pp_table_info->us_ulv_mp1clk_did =
+ le16_to_cpu(powerplay_table->usUlvMp1clkDid);
+ pp_table_info->us_ulv_gfxclk_bypass =
+ le16_to_cpu(powerplay_table->usUlvGfxclkBypass);
+ pp_table_info->us_gfxclk_slew_rate =
+ le16_to_cpu(powerplay_table->usGfxclkSlewRate);
+ pp_table_info->uc_gfx_dpm_voltage_mode =
+ le16_to_cpu(powerplay_table->ucGfxVoltageMode);
+ pp_table_info->uc_soc_dpm_voltage_mode =
+ le16_to_cpu(powerplay_table->ucSocVoltageMode);
+ pp_table_info->uc_uclk_dpm_voltage_mode =
+ le16_to_cpu(powerplay_table->ucUclkVoltageMode);
+ pp_table_info->uc_uvd_dpm_voltage_mode =
+ le16_to_cpu(powerplay_table->ucUvdVoltageMode);
+ pp_table_info->uc_vce_dpm_voltage_mode =
+ le16_to_cpu(powerplay_table->ucVceVoltageMode);
+ pp_table_info->uc_mp0_dpm_voltage_mode =
+ le16_to_cpu(powerplay_table->ucMp0VoltageMode);
+ pp_table_info->uc_dcef_dpm_voltage_mode =
+ le16_to_cpu(powerplay_table->ucDcefVoltageMode);
+
+ pp_table_info->ppm_parameter_table = NULL;
+ pp_table_info->vddc_lookup_table = NULL;
+ pp_table_info->vddmem_lookup_table = NULL;
+ pp_table_info->vddci_lookup_table = NULL;
+
+ /* TDP limits */
+ hwmgr->platform_descriptor.TDPODLimit =
+ le16_to_cpu(powerplay_table->usPowerControlLimit);
+ hwmgr->platform_descriptor.TDPAdjustment = 0;
+ hwmgr->platform_descriptor.VidAdjustment = 0;
+ hwmgr->platform_descriptor.VidAdjustmentPolarity = 0;
+ hwmgr->platform_descriptor.VidMinLimit = 0;
+ hwmgr->platform_descriptor.VidMaxLimit = 1500000;
+ hwmgr->platform_descriptor.VidStep = 6250;
+
+ disable_power_control = 0;
+ if (!disable_power_control) {
+ /* enable TDP overdrive (PowerControl) feature as well if supported */
+ if (hwmgr->platform_descriptor.TDPODLimit)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PowerControl);
+ }
+
+ if (powerplay_table->usVddcLookupTableOffset) {
+ const ATOM_Vega10_Voltage_Lookup_Table *vddc_table =
+ (ATOM_Vega10_Voltage_Lookup_Table *)
+ (((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usVddcLookupTableOffset));
+ result = get_vddc_lookup_table(hwmgr,
+ &pp_table_info->vddc_lookup_table, vddc_table, 8);
+ }
+
+ if (powerplay_table->usVddmemLookupTableOffset) {
+ const ATOM_Vega10_Voltage_Lookup_Table *vdd_mem_table =
+ (ATOM_Vega10_Voltage_Lookup_Table *)
+ (((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usVddmemLookupTableOffset));
+ result = get_vddc_lookup_table(hwmgr,
+ &pp_table_info->vddmem_lookup_table, vdd_mem_table, 4);
+ }
+
+ if (powerplay_table->usVddciLookupTableOffset) {
+ const ATOM_Vega10_Voltage_Lookup_Table *vddci_table =
+ (ATOM_Vega10_Voltage_Lookup_Table *)
+ (((unsigned long)powerplay_table) +
+ le16_to_cpu(powerplay_table->usVddciLookupTableOffset));
+ result = get_vddc_lookup_table(hwmgr,
+ &pp_table_info->vddci_lookup_table, vddci_table, 4);
+ }
+
+ return result;
+}
+
+int vega10_pp_tables_initialize(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table;
+
+ hwmgr->pptable = kzalloc(sizeof(struct phm_ppt_v2_information), GFP_KERNEL);
+
+ PP_ASSERT_WITH_CODE((hwmgr->pptable != NULL),
+ "Failed to allocate hwmgr->pptable!", return -ENOMEM);
+
+ powerplay_table = get_powerplay_table(hwmgr);
+
+ PP_ASSERT_WITH_CODE((powerplay_table != NULL),
+ "Missing PowerPlay Table!", return -1);
+
+ result = check_powerplay_tables(hwmgr, powerplay_table);
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "check_powerplay_tables failed", return result);
+
+ result = set_platform_caps(hwmgr,
+ le32_to_cpu(powerplay_table->ulPlatformCaps));
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "set_platform_caps failed", return result);
+
+ result = init_thermal_controller(hwmgr, powerplay_table);
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "init_thermal_controller failed", return result);
+
+ result = init_over_drive_limits(hwmgr, powerplay_table);
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "init_over_drive_limits failed", return result);
+
+ result = init_powerplay_extended_tables(hwmgr, powerplay_table);
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "init_powerplay_extended_tables failed", return result);
+
+ result = init_dpm_2_parameters(hwmgr, powerplay_table);
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "init_dpm_2_parameters failed", return result);
+
+ return result;
+}
+
+static int vega10_pp_tables_uninitialize(struct pp_hwmgr *hwmgr)
+{
+ struct phm_ppt_v2_information *pp_table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+
+ kfree(pp_table_info->vdd_dep_on_sclk);
+ pp_table_info->vdd_dep_on_sclk = NULL;
+
+ kfree(pp_table_info->vdd_dep_on_mclk);
+ pp_table_info->vdd_dep_on_mclk = NULL;
+
+ kfree(pp_table_info->valid_mclk_values);
+ pp_table_info->valid_mclk_values = NULL;
+
+ kfree(pp_table_info->valid_sclk_values);
+ pp_table_info->valid_sclk_values = NULL;
+
+ kfree(pp_table_info->vddc_lookup_table);
+ pp_table_info->vddc_lookup_table = NULL;
+
+ kfree(pp_table_info->vddmem_lookup_table);
+ pp_table_info->vddmem_lookup_table = NULL;
+
+ kfree(pp_table_info->vddci_lookup_table);
+ pp_table_info->vddci_lookup_table = NULL;
+
+ kfree(pp_table_info->ppm_parameter_table);
+ pp_table_info->ppm_parameter_table = NULL;
+
+ kfree(pp_table_info->mm_dep_table);
+ pp_table_info->mm_dep_table = NULL;
+
+ kfree(pp_table_info->cac_dtp_table);
+ pp_table_info->cac_dtp_table = NULL;
+
+ kfree(hwmgr->dyn_state.cac_dtp_table);
+ hwmgr->dyn_state.cac_dtp_table = NULL;
+
+ kfree(pp_table_info->tdp_table);
+ pp_table_info->tdp_table = NULL;
+
+ kfree(hwmgr->pptable);
+ hwmgr->pptable = NULL;
+
+ return 0;
+}
+
+const struct pp_table_func vega10_pptable_funcs = {
+ .pptable_init = vega10_pp_tables_initialize,
+ .pptable_fini = vega10_pp_tables_uninitialize,
+};
+
+int vega10_get_number_of_powerplay_table_entries(struct pp_hwmgr *hwmgr)
+{
+ const ATOM_Vega10_State_Array *state_arrays;
+ const ATOM_Vega10_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr);
+
+ PP_ASSERT_WITH_CODE((pp_table != NULL),
+ "Missing PowerPlay Table!", return -1);
+ PP_ASSERT_WITH_CODE((pp_table->sHeader.format_revision >=
+ ATOM_Vega10_TABLE_REVISION_VEGA10),
+ "Incorrect PowerPlay table revision!", return -1);
+
+ state_arrays = (ATOM_Vega10_State_Array *)(((unsigned long)pp_table) +
+ le16_to_cpu(pp_table->usStateArrayOffset));
+
+ return (uint32_t)(state_arrays->ucNumEntries);
+}
+
+static uint32_t make_classification_flags(struct pp_hwmgr *hwmgr,
+ uint16_t classification, uint16_t classification2)
+{
+ uint32_t result = 0;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_BOOT)
+ result |= PP_StateClassificationFlag_Boot;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_THERMAL)
+ result |= PP_StateClassificationFlag_Thermal;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE)
+ result |= PP_StateClassificationFlag_LimitedPowerSource;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_REST)
+ result |= PP_StateClassificationFlag_Rest;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_FORCED)
+ result |= PP_StateClassificationFlag_Forced;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_ACPI)
+ result |= PP_StateClassificationFlag_ACPI;
+
+ if (classification2 & ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2)
+ result |= PP_StateClassificationFlag_LimitedPowerSource_2;
+
+ return result;
+}
+
+int vega10_get_powerplay_table_entry(struct pp_hwmgr *hwmgr,
+ uint32_t entry_index, struct pp_power_state *power_state,
+ int (*call_back_func)(struct pp_hwmgr *, void *,
+ struct pp_power_state *, void *, uint32_t))
+{
+ int result = 0;
+ const ATOM_Vega10_State_Array *state_arrays;
+ const ATOM_Vega10_State *state_entry;
+ const ATOM_Vega10_POWERPLAYTABLE *pp_table =
+ get_powerplay_table(hwmgr);
+
+ PP_ASSERT_WITH_CODE(pp_table, "Missing PowerPlay Table!",
+ return -1;);
+ power_state->classification.bios_index = entry_index;
+
+ if (pp_table->sHeader.format_revision >=
+ ATOM_Vega10_TABLE_REVISION_VEGA10) {
+ state_arrays = (ATOM_Vega10_State_Array *)
+ (((unsigned long)pp_table) +
+ le16_to_cpu(pp_table->usStateArrayOffset));
+
+ PP_ASSERT_WITH_CODE(pp_table->usStateArrayOffset > 0,
+ "Invalid PowerPlay Table State Array Offset.",
+ return -1);
+ PP_ASSERT_WITH_CODE(state_arrays->ucNumEntries > 0,
+ "Invalid PowerPlay Table State Array.",
+ return -1);
+ PP_ASSERT_WITH_CODE((entry_index <= state_arrays->ucNumEntries),
+ "Invalid PowerPlay Table State Array Entry.",
+ return -1);
+
+ state_entry = &(state_arrays->states[entry_index]);
+
+ result = call_back_func(hwmgr, (void *)state_entry, power_state,
+ (void *)pp_table,
+ make_classification_flags(hwmgr,
+ le16_to_cpu(state_entry->usClassification),
+ le16_to_cpu(state_entry->usClassification2)));
+ }
+
+ if (!result && (power_state->classification.flags &
+ PP_StateClassificationFlag_Boot))
+ result = hwmgr->hwmgr_func->patch_boot_state(hwmgr, &(power_state->hardware));
+
+ return result;
+}
+
+int vega10_baco_set_cap(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table;
+
+ powerplay_table = get_powerplay_table(hwmgr);
+
+ PP_ASSERT_WITH_CODE((powerplay_table != NULL),
+ "Missing PowerPlay Table!", return -1);
+
+ result = check_powerplay_tables(hwmgr, powerplay_table);
+
+ PP_ASSERT_WITH_CODE((result == 0),
+ "check_powerplay_tables failed", return result);
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (le32_to_cpu(powerplay_table->ulPlatformCaps) & ATOM_VEGA10_PP_PLATFORM_CAP_BACO),
+ PHM_PlatformCaps_BACO);
+ return result;
+}
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.h
new file mode 100644
index 000000000000..da5fbec9b0cd
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 VEGA10_PROCESSPPTABLES_H
+#define VEGA10_PROCESSPPTABLES_H
+
+#include "hwmgr.h"
+
+enum Vega10_I2CLineID {
+ Vega10_I2CLineID_DDC1 = 0x90,
+ Vega10_I2CLineID_DDC2 = 0x91,
+ Vega10_I2CLineID_DDC3 = 0x92,
+ Vega10_I2CLineID_DDC4 = 0x93,
+ Vega10_I2CLineID_DDC5 = 0x94,
+ Vega10_I2CLineID_DDC6 = 0x95,
+ Vega10_I2CLineID_SCLSDA = 0x96,
+ Vega10_I2CLineID_DDCVGA = 0x97
+};
+
+#define Vega10_I2C_DDC1DATA 0
+#define Vega10_I2C_DDC1CLK 1
+#define Vega10_I2C_DDC2DATA 2
+#define Vega10_I2C_DDC2CLK 3
+#define Vega10_I2C_DDC3DATA 4
+#define Vega10_I2C_DDC3CLK 5
+#define Vega10_I2C_SDA 40
+#define Vega10_I2C_SCL 41
+#define Vega10_I2C_DDC4DATA 65
+#define Vega10_I2C_DDC4CLK 66
+#define Vega10_I2C_DDC5DATA 0x48
+#define Vega10_I2C_DDC5CLK 0x49
+#define Vega10_I2C_DDC6DATA 0x4a
+#define Vega10_I2C_DDC6CLK 0x4b
+#define Vega10_I2C_DDCVGADATA 0x4c
+#define Vega10_I2C_DDCVGACLK 0x4d
+
+extern const struct pp_table_func vega10_pptable_funcs;
+extern int vega10_get_number_of_powerplay_table_entries(struct pp_hwmgr *hwmgr);
+extern int vega10_get_powerplay_table_entry(struct pp_hwmgr *hwmgr, uint32_t entry_index,
+ struct pp_power_state *power_state, int (*call_back_func)(struct pp_hwmgr *, void *,
+ struct pp_power_state *, void *, uint32_t));
+extern int vega10_baco_set_cap(struct pp_hwmgr *hwmgr);
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_thermal.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_thermal.c
new file mode 100644
index 000000000000..952cd3d7240e
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_thermal.c
@@ -0,0 +1,669 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "vega10_thermal.h"
+#include "vega10_hwmgr.h"
+#include "vega10_smumgr.h"
+#include "vega10_ppsmc.h"
+#include "vega10_inc.h"
+#include "soc15_common.h"
+#include "pp_debug.h"
+
+static int vega10_get_current_rpm(struct pp_hwmgr *hwmgr, uint32_t *current_rpm)
+{
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetCurrentRpm, current_rpm);
+ return 0;
+}
+
+int vega10_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr,
+ struct phm_fan_speed_info *fan_speed_info)
+{
+
+ if (hwmgr->thermal_controller.fanInfo.bNoFan)
+ return 0;
+
+ fan_speed_info->supports_percent_read = true;
+ fan_speed_info->supports_percent_write = true;
+ fan_speed_info->min_percent = 0;
+ fan_speed_info->max_percent = 100;
+
+ if (PP_CAP(PHM_PlatformCaps_FanSpeedInTableIsRPM) &&
+ hwmgr->thermal_controller.fanInfo.
+ ucTachometerPulsesPerRevolution) {
+ fan_speed_info->supports_rpm_read = true;
+ fan_speed_info->supports_rpm_write = true;
+ fan_speed_info->min_rpm =
+ hwmgr->thermal_controller.fanInfo.ulMinRPM;
+ fan_speed_info->max_rpm =
+ hwmgr->thermal_controller.fanInfo.ulMaxRPM;
+ } else {
+ fan_speed_info->min_rpm = 0;
+ fan_speed_info->max_rpm = 0;
+ }
+
+ return 0;
+}
+
+int vega10_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr,
+ uint32_t *speed)
+{
+ uint32_t current_rpm;
+ uint32_t percent = 0;
+
+ if (hwmgr->thermal_controller.fanInfo.bNoFan)
+ return 0;
+
+ if (vega10_get_current_rpm(hwmgr, &current_rpm))
+ return -1;
+
+ if (hwmgr->thermal_controller.
+ advanceFanControlParameters.usMaxFanRPM != 0)
+ percent = current_rpm * 100 /
+ hwmgr->thermal_controller.
+ advanceFanControlParameters.usMaxFanRPM;
+
+ *speed = percent > 100 ? 100 : percent;
+
+ return 0;
+}
+
+int vega10_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ struct vega10_hwmgr *data = hwmgr->backend;
+ uint32_t tach_period;
+ uint32_t crystal_clock_freq;
+ int result = 0;
+
+ if (hwmgr->thermal_controller.fanInfo.bNoFan)
+ return -1;
+
+ if (data->smu_features[GNLD_FAN_CONTROL].supported) {
+ result = vega10_get_current_rpm(hwmgr, speed);
+ } else {
+ tach_period =
+ REG_GET_FIELD(RREG32_SOC15(THM, 0, mmCG_TACH_STATUS),
+ CG_TACH_STATUS,
+ TACH_PERIOD);
+
+ if (tach_period == 0)
+ return -EINVAL;
+
+ crystal_clock_freq = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev);
+
+ *speed = 60 * crystal_clock_freq * 10000 / tach_period;
+ }
+
+ return result;
+}
+
+/**
+* Set Fan Speed Control to static mode,
+* so that the user can decide what speed to use.
+* @param hwmgr the address of the powerplay hardware manager.
+* mode the fan control mode, 0 default, 1 by percent, 5, by RPM
+* @exception Should always succeed.
+*/
+int vega10_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr, uint32_t mode)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ if (hwmgr->fan_ctrl_is_in_default_mode) {
+ hwmgr->fan_ctrl_default_mode =
+ REG_GET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL2),
+ CG_FDO_CTRL2, FDO_PWM_MODE);
+ hwmgr->tmin =
+ REG_GET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL2),
+ CG_FDO_CTRL2, TMIN);
+ hwmgr->fan_ctrl_is_in_default_mode = false;
+ }
+
+ WREG32_SOC15(THM, 0, mmCG_FDO_CTRL2,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL2),
+ CG_FDO_CTRL2, TMIN, 0));
+ WREG32_SOC15(THM, 0, mmCG_FDO_CTRL2,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL2),
+ CG_FDO_CTRL2, FDO_PWM_MODE, mode));
+
+ return 0;
+}
+
+/**
+* Reset Fan Speed Control to default mode.
+* @param hwmgr the address of the powerplay hardware manager.
+* @exception Should always succeed.
+*/
+int vega10_fan_ctrl_set_default_mode(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ if (!hwmgr->fan_ctrl_is_in_default_mode) {
+ WREG32_SOC15(THM, 0, mmCG_FDO_CTRL2,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL2),
+ CG_FDO_CTRL2, FDO_PWM_MODE,
+ hwmgr->fan_ctrl_default_mode));
+ WREG32_SOC15(THM, 0, mmCG_FDO_CTRL2,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL2),
+ CG_FDO_CTRL2, TMIN,
+ hwmgr->tmin << CG_FDO_CTRL2__TMIN__SHIFT));
+ hwmgr->fan_ctrl_is_in_default_mode = true;
+ }
+
+ return 0;
+}
+
+/**
+ * @fn vega10_enable_fan_control_feature
+ * @brief Enables the SMC Fan Control Feature.
+ *
+ * @param hwmgr - the address of the powerplay hardware manager.
+ * @return 0 on success. -1 otherwise.
+ */
+static int vega10_enable_fan_control_feature(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->smu_features[GNLD_FAN_CONTROL].supported) {
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(
+ hwmgr, true,
+ data->smu_features[GNLD_FAN_CONTROL].
+ smu_feature_bitmap),
+ "Attempt to Enable FAN CONTROL feature Failed!",
+ return -1);
+ data->smu_features[GNLD_FAN_CONTROL].enabled = true;
+ }
+
+ return 0;
+}
+
+static int vega10_disable_fan_control_feature(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->smu_features[GNLD_FAN_CONTROL].supported) {
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(
+ hwmgr, false,
+ data->smu_features[GNLD_FAN_CONTROL].
+ smu_feature_bitmap),
+ "Attempt to Enable FAN CONTROL feature Failed!",
+ return -1);
+ data->smu_features[GNLD_FAN_CONTROL].enabled = false;
+ }
+
+ return 0;
+}
+
+int vega10_fan_ctrl_start_smc_fan_control(struct pp_hwmgr *hwmgr)
+{
+ if (hwmgr->thermal_controller.fanInfo.bNoFan)
+ return -1;
+
+ PP_ASSERT_WITH_CODE(!vega10_enable_fan_control_feature(hwmgr),
+ "Attempt to Enable SMC FAN CONTROL Feature Failed!",
+ return -1);
+
+ return 0;
+}
+
+
+int vega10_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (hwmgr->thermal_controller.fanInfo.bNoFan)
+ return -1;
+
+ if (data->smu_features[GNLD_FAN_CONTROL].supported) {
+ PP_ASSERT_WITH_CODE(!vega10_disable_fan_control_feature(hwmgr),
+ "Attempt to Disable SMC FAN CONTROL Feature Failed!",
+ return -1);
+ }
+ return 0;
+}
+
+/**
+* Set Fan Speed in percent.
+* @param hwmgr the address of the powerplay hardware manager.
+* @param speed is the percentage value (0% - 100%) to be set.
+* @exception Fails is the 100% setting appears to be 0.
+*/
+int vega10_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr,
+ uint32_t speed)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t duty100;
+ uint32_t duty;
+ uint64_t tmp64;
+
+ if (hwmgr->thermal_controller.fanInfo.bNoFan)
+ return 0;
+
+ if (speed > 100)
+ speed = 100;
+
+ if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl))
+ vega10_fan_ctrl_stop_smc_fan_control(hwmgr);
+
+ duty100 = REG_GET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL1),
+ CG_FDO_CTRL1, FMAX_DUTY100);
+
+ if (duty100 == 0)
+ return -EINVAL;
+
+ tmp64 = (uint64_t)speed * duty100;
+ do_div(tmp64, 100);
+ duty = (uint32_t)tmp64;
+
+ WREG32_SOC15(THM, 0, mmCG_FDO_CTRL0,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL0),
+ CG_FDO_CTRL0, FDO_STATIC_DUTY, duty));
+
+ return vega10_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
+}
+
+/**
+* Reset Fan Speed to default.
+* @param hwmgr the address of the powerplay hardware manager.
+* @exception Always succeeds.
+*/
+int vega10_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr)
+{
+ if (hwmgr->thermal_controller.fanInfo.bNoFan)
+ return 0;
+
+ if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl))
+ return vega10_fan_ctrl_start_smc_fan_control(hwmgr);
+ else
+ return vega10_fan_ctrl_set_default_mode(hwmgr);
+}
+
+/**
+* Set Fan Speed in RPM.
+* @param hwmgr the address of the powerplay hardware manager.
+* @param speed is the percentage value (min - max) to be set.
+* @exception Fails is the speed not lie between min and max.
+*/
+int vega10_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t tach_period;
+ uint32_t crystal_clock_freq;
+ int result = 0;
+
+ if (hwmgr->thermal_controller.fanInfo.bNoFan ||
+ speed == 0 ||
+ (speed < hwmgr->thermal_controller.fanInfo.ulMinRPM) ||
+ (speed > hwmgr->thermal_controller.fanInfo.ulMaxRPM))
+ return -1;
+
+ if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl))
+ result = vega10_fan_ctrl_stop_smc_fan_control(hwmgr);
+
+ if (!result) {
+ crystal_clock_freq = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev);
+ tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed);
+ WREG32_SOC15(THM, 0, mmCG_TACH_CTRL,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_TACH_CTRL),
+ CG_TACH_CTRL, TARGET_PERIOD,
+ tach_period));
+ }
+ return vega10_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC_RPM);
+}
+
+/**
+* Reads the remote temperature from the SIslands thermal controller.
+*
+* @param hwmgr The address of the hardware manager.
+*/
+int vega10_thermal_get_temperature(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ int temp;
+
+ temp = RREG32_SOC15(THM, 0, mmCG_MULT_THERMAL_STATUS);
+
+ temp = (temp & CG_MULT_THERMAL_STATUS__CTF_TEMP_MASK) >>
+ CG_MULT_THERMAL_STATUS__CTF_TEMP__SHIFT;
+
+ temp = temp & 0x1ff;
+
+ temp *= PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+ return temp;
+}
+
+/**
+* Set the requested temperature range for high and low alert signals
+*
+* @param hwmgr The address of the hardware manager.
+* @param range Temperature range to be programmed for
+* high and low alert signals
+* @exception PP_Result_BadInput if the input data is not valid.
+*/
+static int vega10_thermal_set_temperature_range(struct pp_hwmgr *hwmgr,
+ struct PP_TemperatureRange *range)
+{
+ struct phm_ppt_v2_information *pp_table_info =
+ (struct phm_ppt_v2_information *)(hwmgr->pptable);
+ struct phm_tdp_table *tdp_table = pp_table_info->tdp_table;
+ struct amdgpu_device *adev = hwmgr->adev;
+ int low = VEGA10_THERMAL_MINIMUM_ALERT_TEMP;
+ int high = VEGA10_THERMAL_MAXIMUM_ALERT_TEMP;
+ uint32_t val;
+
+ /* compare them in unit celsius degree */
+ if (low < range->min / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)
+ low = range->min / PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+ /*
+ * As a common sense, usSoftwareShutdownTemp should be bigger
+ * than ThotspotLimit. For any invalid usSoftwareShutdownTemp,
+ * we will just use the max possible setting VEGA10_THERMAL_MAXIMUM_ALERT_TEMP
+ * to avoid false alarms.
+ */
+ if ((tdp_table->usSoftwareShutdownTemp >
+ range->hotspot_crit_max / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)) {
+ if (high > tdp_table->usSoftwareShutdownTemp)
+ high = tdp_table->usSoftwareShutdownTemp;
+ }
+
+ if (low > high)
+ return -EINVAL;
+
+ val = RREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_CTRL);
+
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, MAX_IH_CREDIT, 5);
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, THERM_IH_HW_ENA, 1);
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, DIG_THERM_INTH, high);
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, DIG_THERM_INTL, low);
+ val &= (~THM_THERMAL_INT_CTRL__THERM_TRIGGER_MASK_MASK) &
+ (~THM_THERMAL_INT_CTRL__THERM_INTH_MASK_MASK) &
+ (~THM_THERMAL_INT_CTRL__THERM_INTL_MASK_MASK);
+
+ WREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_CTRL, val);
+
+ return 0;
+}
+
+/**
+* Programs thermal controller one-time setting registers
+*
+* @param hwmgr The address of the hardware manager.
+*/
+static int vega10_thermal_initialize(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ if (hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution) {
+ WREG32_SOC15(THM, 0, mmCG_TACH_CTRL,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_TACH_CTRL),
+ CG_TACH_CTRL, EDGE_PER_REV,
+ hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution - 1));
+ }
+
+ WREG32_SOC15(THM, 0, mmCG_FDO_CTRL2,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL2),
+ CG_FDO_CTRL2, TACH_PWM_RESP_RATE, 0x28));
+
+ return 0;
+}
+
+/**
+* Enable thermal alerts on the RV770 thermal controller.
+*
+* @param hwmgr The address of the hardware manager.
+*/
+static int vega10_thermal_enable_alert(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ struct vega10_hwmgr *data = hwmgr->backend;
+ uint32_t val = 0;
+
+ if (data->smu_features[GNLD_FW_CTF].supported) {
+ if (data->smu_features[GNLD_FW_CTF].enabled)
+ printk("[Thermal_EnableAlert] FW CTF Already Enabled!\n");
+
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ true,
+ data->smu_features[GNLD_FW_CTF].smu_feature_bitmap),
+ "Attempt to Enable FW CTF feature Failed!",
+ return -1);
+ data->smu_features[GNLD_FW_CTF].enabled = true;
+ }
+
+ val |= (1 << THM_THERMAL_INT_ENA__THERM_INTH_CLR__SHIFT);
+ val |= (1 << THM_THERMAL_INT_ENA__THERM_INTL_CLR__SHIFT);
+ val |= (1 << THM_THERMAL_INT_ENA__THERM_TRIGGER_CLR__SHIFT);
+
+ WREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_ENA, val);
+
+ return 0;
+}
+
+/**
+* Disable thermal alerts on the RV770 thermal controller.
+* @param hwmgr The address of the hardware manager.
+*/
+int vega10_thermal_disable_alert(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ struct vega10_hwmgr *data = hwmgr->backend;
+
+ if (data->smu_features[GNLD_FW_CTF].supported) {
+ if (!data->smu_features[GNLD_FW_CTF].enabled)
+ printk("[Thermal_EnableAlert] FW CTF Already disabled!\n");
+
+
+ PP_ASSERT_WITH_CODE(!vega10_enable_smc_features(hwmgr,
+ false,
+ data->smu_features[GNLD_FW_CTF].smu_feature_bitmap),
+ "Attempt to disable FW CTF feature Failed!",
+ return -1);
+ data->smu_features[GNLD_FW_CTF].enabled = false;
+ }
+
+ WREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_ENA, 0);
+
+ return 0;
+}
+
+/**
+* Uninitialize the thermal controller.
+* Currently just disables alerts.
+* @param hwmgr The address of the hardware manager.
+*/
+int vega10_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr)
+{
+ int result = vega10_thermal_disable_alert(hwmgr);
+
+ if (!hwmgr->thermal_controller.fanInfo.bNoFan)
+ vega10_fan_ctrl_set_default_mode(hwmgr);
+
+ return result;
+}
+
+/**
+* Set up the fan table to control the fan using the SMC.
+* @param hwmgr the address of the powerplay hardware manager.
+* @param pInput the pointer to input data
+* @param pOutput the pointer to output data
+* @param pStorage the pointer to temporary storage
+* @param Result the last failure code
+* @return result from set temperature range routine
+*/
+static int vega10_thermal_setup_fan_table(struct pp_hwmgr *hwmgr)
+{
+ int ret;
+ struct vega10_hwmgr *data = hwmgr->backend;
+ PPTable_t *table = &(data->smc_state_table.pp_table);
+
+ if (!data->smu_features[GNLD_FAN_CONTROL].supported)
+ return 0;
+
+ table->FanMaximumRpm = (uint16_t)hwmgr->thermal_controller.
+ advanceFanControlParameters.usMaxFanRPM;
+ table->FanThrottlingRpm = hwmgr->thermal_controller.
+ advanceFanControlParameters.usFanRPMMaxLimit;
+ table->FanAcousticLimitRpm = (uint16_t)(hwmgr->thermal_controller.
+ advanceFanControlParameters.ulMinFanSCLKAcousticLimit);
+ table->FanTargetTemperature = hwmgr->thermal_controller.
+ advanceFanControlParameters.usTMax;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetFanTemperatureTarget,
+ (uint32_t)table->FanTargetTemperature,
+ NULL);
+
+ table->FanPwmMin = hwmgr->thermal_controller.
+ advanceFanControlParameters.usPWMMin * 255 / 100;
+ table->FanTargetGfxclk = (uint16_t)(hwmgr->thermal_controller.
+ advanceFanControlParameters.ulTargetGfxClk);
+ table->FanGainEdge = hwmgr->thermal_controller.
+ advanceFanControlParameters.usFanGainEdge;
+ table->FanGainHotspot = hwmgr->thermal_controller.
+ advanceFanControlParameters.usFanGainHotspot;
+ table->FanGainLiquid = hwmgr->thermal_controller.
+ advanceFanControlParameters.usFanGainLiquid;
+ table->FanGainVrVddc = hwmgr->thermal_controller.
+ advanceFanControlParameters.usFanGainVrVddc;
+ table->FanGainVrMvdd = hwmgr->thermal_controller.
+ advanceFanControlParameters.usFanGainVrMvdd;
+ table->FanGainPlx = hwmgr->thermal_controller.
+ advanceFanControlParameters.usFanGainPlx;
+ table->FanGainHbm = hwmgr->thermal_controller.
+ advanceFanControlParameters.usFanGainHbm;
+ table->FanZeroRpmEnable = hwmgr->thermal_controller.
+ advanceFanControlParameters.ucEnableZeroRPM;
+ table->FanStopTemp = hwmgr->thermal_controller.
+ advanceFanControlParameters.usZeroRPMStopTemperature;
+ table->FanStartTemp = hwmgr->thermal_controller.
+ advanceFanControlParameters.usZeroRPMStartTemperature;
+
+ ret = smum_smc_table_manager(hwmgr,
+ (uint8_t *)(&(data->smc_state_table.pp_table)),
+ PPTABLE, false);
+ if (ret)
+ pr_info("Failed to update Fan Control Table in PPTable!");
+
+ return ret;
+}
+
+int vega10_enable_mgpu_fan_boost(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_hwmgr *data = hwmgr->backend;
+ PPTable_t *table = &(data->smc_state_table.pp_table);
+ int ret;
+
+ if (!data->smu_features[GNLD_FAN_CONTROL].supported)
+ return 0;
+
+ if (!hwmgr->thermal_controller.advanceFanControlParameters.
+ usMGpuThrottlingRPMLimit)
+ return 0;
+
+ table->FanThrottlingRpm = hwmgr->thermal_controller.
+ advanceFanControlParameters.usMGpuThrottlingRPMLimit;
+
+ ret = smum_smc_table_manager(hwmgr,
+ (uint8_t *)(&(data->smc_state_table.pp_table)),
+ PPTABLE, false);
+ if (ret) {
+ pr_info("Failed to update fan control table in pptable!");
+ return ret;
+ }
+
+ ret = vega10_disable_fan_control_feature(hwmgr);
+ if (ret) {
+ pr_info("Attempt to disable SMC fan control feature failed!");
+ return ret;
+ }
+
+ ret = vega10_enable_fan_control_feature(hwmgr);
+ if (ret)
+ pr_info("Attempt to enable SMC fan control feature failed!");
+
+ return ret;
+}
+
+/**
+* Start the fan control on the SMC.
+* @param hwmgr the address of the powerplay hardware manager.
+* @param pInput the pointer to input data
+* @param pOutput the pointer to output data
+* @param pStorage the pointer to temporary storage
+* @param Result the last failure code
+* @return result from set temperature range routine
+*/
+static int vega10_thermal_start_smc_fan_control(struct pp_hwmgr *hwmgr)
+{
+/* If the fantable setup has failed we could have disabled
+ * PHM_PlatformCaps_MicrocodeFanControl even after
+ * this function was included in the table.
+ * Make sure that we still think controlling the fan is OK.
+*/
+ if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl))
+ vega10_fan_ctrl_start_smc_fan_control(hwmgr);
+
+ return 0;
+}
+
+
+int vega10_start_thermal_controller(struct pp_hwmgr *hwmgr,
+ struct PP_TemperatureRange *range)
+{
+ int ret = 0;
+
+ if (range == NULL)
+ return -EINVAL;
+
+ vega10_thermal_initialize(hwmgr);
+ ret = vega10_thermal_set_temperature_range(hwmgr, range);
+ if (ret)
+ return -EINVAL;
+
+ vega10_thermal_enable_alert(hwmgr);
+/* We should restrict performance levels to low before we halt the SMC.
+ * On the other hand we are still in boot state when we do this
+ * so it would be pointless.
+ * If this assumption changes we have to revisit this table.
+ */
+ ret = vega10_thermal_setup_fan_table(hwmgr);
+ if (ret)
+ return -EINVAL;
+
+ vega10_thermal_start_smc_fan_control(hwmgr);
+
+ return 0;
+};
+
+
+
+
+int vega10_thermal_ctrl_uninitialize_thermal_controller(struct pp_hwmgr *hwmgr)
+{
+ if (!hwmgr->thermal_controller.fanInfo.bNoFan) {
+ vega10_fan_ctrl_set_default_mode(hwmgr);
+ vega10_fan_ctrl_stop_smc_fan_control(hwmgr);
+ }
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_thermal.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_thermal.h
new file mode 100644
index 000000000000..4a0ede7c1f07
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_thermal.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 VEGA10_THERMAL_H
+#define VEGA10_THERMAL_H
+
+#include "hwmgr.h"
+
+struct vega10_temperature {
+ uint16_t edge_temp;
+ uint16_t hot_spot_temp;
+ uint16_t hbm_temp;
+ uint16_t vr_soc_temp;
+ uint16_t vr_mem_temp;
+ uint16_t liquid1_temp;
+ uint16_t liquid2_temp;
+ uint16_t plx_temp;
+};
+
+#define VEGA10_THERMAL_HIGH_ALERT_MASK 0x1
+#define VEGA10_THERMAL_LOW_ALERT_MASK 0x2
+
+#define VEGA10_THERMAL_MINIMUM_TEMP_READING -256
+#define VEGA10_THERMAL_MAXIMUM_TEMP_READING 255
+
+#define VEGA10_THERMAL_MINIMUM_ALERT_TEMP 0
+#define VEGA10_THERMAL_MAXIMUM_ALERT_TEMP 255
+
+#define FDO_PWM_MODE_STATIC 1
+#define FDO_PWM_MODE_STATIC_RPM 5
+
+
+extern int vega10_thermal_get_temperature(struct pp_hwmgr *hwmgr);
+extern int vega10_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr);
+extern int vega10_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr,
+ struct phm_fan_speed_info *fan_speed_info);
+extern int vega10_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr,
+ uint32_t *speed);
+extern int vega10_fan_ctrl_set_default_mode(struct pp_hwmgr *hwmgr);
+extern int vega10_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr,
+ uint32_t mode);
+extern int vega10_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr,
+ uint32_t speed);
+extern int vega10_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr);
+extern int vega10_thermal_ctrl_uninitialize_thermal_controller(
+ struct pp_hwmgr *hwmgr);
+extern int vega10_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr,
+ uint32_t speed);
+extern int vega10_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr,
+ uint32_t *speed);
+extern int vega10_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr);
+extern int vega10_thermal_disable_alert(struct pp_hwmgr *hwmgr);
+extern int vega10_fan_ctrl_start_smc_fan_control(struct pp_hwmgr *hwmgr);
+extern int vega10_start_thermal_controller(struct pp_hwmgr *hwmgr,
+ struct PP_TemperatureRange *range);
+extern int vega10_enable_mgpu_fan_boost(struct pp_hwmgr *hwmgr);
+
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_baco.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_baco.c
new file mode 100644
index 000000000000..bc53cce4f32d
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_baco.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "amdgpu.h"
+#include "soc15.h"
+#include "soc15_hw_ip.h"
+#include "vega10_ip_offset.h"
+#include "soc15_common.h"
+#include "vega12_inc.h"
+#include "vega12_ppsmc.h"
+#include "vega12_baco.h"
+
+static const struct soc15_baco_cmd_entry pre_baco_tbl[] =
+{
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmBIF_DOORBELL_CNTL_BASE_IDX, mmBIF_DOORBELL_CNTL, BIF_DOORBELL_CNTL__DOORBELL_MONITOR_EN_MASK, BIF_DOORBELL_CNTL__DOORBELL_MONITOR_EN__SHIFT, 0, 0 },
+ { CMD_WRITE, NBIF_HWID, 0, mmBIF_FB_EN_BASE_IDX, mmBIF_FB_EN, 0, 0, 0, 0 },
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_DSTATE_BYPASS_MASK, BACO_CNTL__BACO_DSTATE_BYPASS__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_RST_INTR_MASK_MASK, BACO_CNTL__BACO_RST_INTR_MASK__SHIFT, 0, 1 }
+};
+
+static const struct soc15_baco_cmd_entry enter_baco_tbl[] =
+{
+ { CMD_WAITFOR, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__SOC_DOMAIN_IDLE_MASK, THM_BACO_CNTL__SOC_DOMAIN_IDLE__SHIFT, 0xffffffff, 0x80000000 },
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_EN_MASK, BACO_CNTL__BACO_EN__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_BIF_LCLK_SWITCH_MASK, BACO_CNTL__BACO_BIF_LCLK_SWITCH__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_DUMMY_EN_MASK, BACO_CNTL__BACO_DUMMY_EN__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_SOC_VDCI_RESET_MASK, THM_BACO_CNTL__BACO_SOC_VDCI_RESET__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_SMNCLK_MUX_MASK, THM_BACO_CNTL__BACO_SMNCLK_MUX__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_ISO_EN_MASK, THM_BACO_CNTL__BACO_ISO_EN__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_AEB_ISO_EN_MASK, THM_BACO_CNTL__BACO_AEB_ISO_EN__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_ANA_ISO_EN_MASK, THM_BACO_CNTL__BACO_ANA_ISO_EN__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_SOC_REFCLK_OFF_MASK, THM_BACO_CNTL__BACO_SOC_REFCLK_OFF__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_POWER_OFF_MASK, BACO_CNTL__BACO_POWER_OFF__SHIFT, 0, 1 },
+ { CMD_DELAY_MS, 0, 0, 0, 5, 0 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_RESET_EN_MASK, THM_BACO_CNTL__BACO_RESET_EN__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_PWROKRAW_CNTL_MASK, THM_BACO_CNTL__BACO_PWROKRAW_CNTL__SHIFT, 0, 0 },
+ { CMD_WAITFOR, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_MODE_MASK, BACO_CNTL__BACO_MODE__SHIFT, 0xffffffff, 0x100 }
+};
+
+static const struct soc15_baco_cmd_entry exit_baco_tbl[] =
+{
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_POWER_OFF_MASK, BACO_CNTL__BACO_POWER_OFF__SHIFT, 0, 0 },
+ { CMD_DELAY_MS, 0, 0, 0, 0, 0, 0, 10, 0 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_SOC_REFCLK_OFF_MASK, THM_BACO_CNTL__BACO_SOC_REFCLK_OFF__SHIFT, 0, 0 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_ANA_ISO_EN_MASK, THM_BACO_CNTL__BACO_ANA_ISO_EN__SHIFT, 0, 0 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_AEB_ISO_EN_MASK, THM_BACO_CNTL__BACO_AEB_ISO_EN__SHIFT, 0, 0 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_ISO_EN_MASK, THM_BACO_CNTL__BACO_ISO_EN__SHIFT, 0, 0 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_PWROKRAW_CNTL_MASK, THM_BACO_CNTL__BACO_PWROKRAW_CNTL__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_SMNCLK_MUX_MASK, THM_BACO_CNTL__BACO_SMNCLK_MUX__SHIFT, 0, 0 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_SOC_VDCI_RESET_MASK, THM_BACO_CNTL__BACO_SOC_VDCI_RESET__SHIFT, 0, 0 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_EXIT_MASK, THM_BACO_CNTL__BACO_EXIT__SHIFT, 0, 1 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_RESET_EN_MASK, THM_BACO_CNTL__BACO_RESET_EN__SHIFT, 0, 0 },
+ { CMD_WAITFOR, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_EXIT_MASK, 0, 0xffffffff, 0 },
+ { CMD_READMODIFYWRITE, THM_HWID, 0, mmTHM_BACO_CNTL_BASE_IDX, mmTHM_BACO_CNTL, THM_BACO_CNTL__BACO_SB_AXI_FENCE_MASK, THM_BACO_CNTL__BACO_SB_AXI_FENCE__SHIFT, 0, 0 },
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_DUMMY_EN_MASK, BACO_CNTL__BACO_DUMMY_EN__SHIFT, 0, 0 },
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_BIF_LCLK_SWITCH_MASK, BACO_CNTL__BACO_BIF_LCLK_SWITCH__SHIFT, 0, 0 },
+ { CMD_READMODIFYWRITE, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_EN_MASK, BACO_CNTL__BACO_EN__SHIFT, 0, 0 },
+ { CMD_WAITFOR, NBIF_HWID, 0, mmRCC_BACO_CNTL_MISC_BASE_IDX, mmBACO_CNTL, BACO_CNTL__BACO_MODE_MASK, 0, 0xffffffff, 0 }
+};
+
+static const struct soc15_baco_cmd_entry clean_baco_tbl[] =
+{
+ { CMD_WRITE, NBIF_HWID, 0, mmBIOS_SCRATCH_6_BASE_IDX, mmBIOS_SCRATCH_6, 0, 0, 0, 0 },
+ { CMD_WRITE, NBIF_HWID, 0, mmBIOS_SCRATCH_7_BASE_IDX, mmBIOS_SCRATCH_7, 0, 0, 0, 0 }
+};
+
+int vega12_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state)
+{
+ enum BACO_STATE cur_state;
+
+ smu9_baco_get_state(hwmgr, &cur_state);
+
+ if (cur_state == state)
+ /* aisc already in the target state */
+ return 0;
+
+ if (state == BACO_STATE_IN) {
+ if (soc15_baco_program_registers(hwmgr, pre_baco_tbl,
+ ARRAY_SIZE(pre_baco_tbl))) {
+ if (smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_EnterBaco, 0, NULL))
+ return -EINVAL;
+
+ if (soc15_baco_program_registers(hwmgr, enter_baco_tbl,
+ ARRAY_SIZE(enter_baco_tbl)))
+ return 0;
+ }
+ } else if (state == BACO_STATE_OUT) {
+ /* HW requires at least 20ms between regulator off and on */
+ msleep(20);
+ /* Execute Hardware BACO exit sequence */
+ if (soc15_baco_program_registers(hwmgr, exit_baco_tbl,
+ ARRAY_SIZE(exit_baco_tbl))) {
+ if (soc15_baco_program_registers(hwmgr, clean_baco_tbl,
+ ARRAY_SIZE(clean_baco_tbl)))
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_baco.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_baco.h
new file mode 100644
index 000000000000..57b72e5a95ae
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_baco.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 __VEGA12_BACO_H__
+#define __VEGA12_BACO_H__
+#include "smu9_baco.h"
+
+extern int vega12_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c
new file mode 100644
index 000000000000..dc206fa88c5e
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c
@@ -0,0 +1,2868 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "hwmgr.h"
+#include "amd_powerplay.h"
+#include "vega12_smumgr.h"
+#include "hardwaremanager.h"
+#include "ppatomfwctrl.h"
+#include "atomfirmware.h"
+#include "cgs_common.h"
+#include "vega12_inc.h"
+#include "pppcielanes.h"
+#include "vega12_hwmgr.h"
+#include "vega12_processpptables.h"
+#include "vega12_pptable.h"
+#include "vega12_thermal.h"
+#include "vega12_ppsmc.h"
+#include "pp_debug.h"
+#include "amd_pcie_helpers.h"
+#include "ppinterrupt.h"
+#include "pp_overdriver.h"
+#include "pp_thermal.h"
+#include "vega12_baco.h"
+
+#define smnPCIE_LC_SPEED_CNTL 0x11140290
+#define smnPCIE_LC_LINK_WIDTH_CNTL 0x11140288
+
+#define LINK_WIDTH_MAX 6
+#define LINK_SPEED_MAX 3
+static int link_width[] = {0, 1, 2, 4, 8, 12, 16};
+static int link_speed[] = {25, 50, 80, 160};
+
+static int vega12_force_clock_level(struct pp_hwmgr *hwmgr,
+ enum pp_clock_type type, uint32_t mask);
+static int vega12_get_clock_ranges(struct pp_hwmgr *hwmgr,
+ uint32_t *clock,
+ PPCLK_e clock_select,
+ bool max);
+
+static void vega12_set_default_registry_data(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+
+ data->gfxclk_average_alpha = PPVEGA12_VEGA12GFXCLKAVERAGEALPHA_DFLT;
+ data->socclk_average_alpha = PPVEGA12_VEGA12SOCCLKAVERAGEALPHA_DFLT;
+ data->uclk_average_alpha = PPVEGA12_VEGA12UCLKCLKAVERAGEALPHA_DFLT;
+ data->gfx_activity_average_alpha = PPVEGA12_VEGA12GFXACTIVITYAVERAGEALPHA_DFLT;
+ data->lowest_uclk_reserved_for_ulv = PPVEGA12_VEGA12LOWESTUCLKRESERVEDFORULV_DFLT;
+
+ data->display_voltage_mode = PPVEGA12_VEGA12DISPLAYVOLTAGEMODE_DFLT;
+ data->dcef_clk_quad_eqn_a = PPREGKEY_VEGA12QUADRATICEQUATION_DFLT;
+ data->dcef_clk_quad_eqn_b = PPREGKEY_VEGA12QUADRATICEQUATION_DFLT;
+ data->dcef_clk_quad_eqn_c = PPREGKEY_VEGA12QUADRATICEQUATION_DFLT;
+ data->disp_clk_quad_eqn_a = PPREGKEY_VEGA12QUADRATICEQUATION_DFLT;
+ data->disp_clk_quad_eqn_b = PPREGKEY_VEGA12QUADRATICEQUATION_DFLT;
+ data->disp_clk_quad_eqn_c = PPREGKEY_VEGA12QUADRATICEQUATION_DFLT;
+ data->pixel_clk_quad_eqn_a = PPREGKEY_VEGA12QUADRATICEQUATION_DFLT;
+ data->pixel_clk_quad_eqn_b = PPREGKEY_VEGA12QUADRATICEQUATION_DFLT;
+ data->pixel_clk_quad_eqn_c = PPREGKEY_VEGA12QUADRATICEQUATION_DFLT;
+ data->phy_clk_quad_eqn_a = PPREGKEY_VEGA12QUADRATICEQUATION_DFLT;
+ data->phy_clk_quad_eqn_b = PPREGKEY_VEGA12QUADRATICEQUATION_DFLT;
+ data->phy_clk_quad_eqn_c = PPREGKEY_VEGA12QUADRATICEQUATION_DFLT;
+
+ data->registry_data.disallowed_features = 0x0;
+ data->registry_data.od_state_in_dc_support = 0;
+ data->registry_data.thermal_support = 1;
+ data->registry_data.skip_baco_hardware = 0;
+
+ data->registry_data.log_avfs_param = 0;
+ data->registry_data.sclk_throttle_low_notification = 1;
+ data->registry_data.force_dpm_high = 0;
+ data->registry_data.stable_pstate_sclk_dpm_percentage = 75;
+
+ data->registry_data.didt_support = 0;
+ if (data->registry_data.didt_support) {
+ data->registry_data.didt_mode = 6;
+ data->registry_data.sq_ramping_support = 1;
+ data->registry_data.db_ramping_support = 0;
+ data->registry_data.td_ramping_support = 0;
+ data->registry_data.tcp_ramping_support = 0;
+ data->registry_data.dbr_ramping_support = 0;
+ data->registry_data.edc_didt_support = 1;
+ data->registry_data.gc_didt_support = 0;
+ data->registry_data.psm_didt_support = 0;
+ }
+
+ data->registry_data.pcie_lane_override = 0xff;
+ data->registry_data.pcie_speed_override = 0xff;
+ data->registry_data.pcie_clock_override = 0xffffffff;
+ data->registry_data.regulator_hot_gpio_support = 1;
+ data->registry_data.ac_dc_switch_gpio_support = 0;
+ data->registry_data.quick_transition_support = 0;
+ data->registry_data.zrpm_start_temp = 0xffff;
+ data->registry_data.zrpm_stop_temp = 0xffff;
+ data->registry_data.odn_feature_enable = 1;
+ data->registry_data.disable_water_mark = 0;
+ data->registry_data.disable_pp_tuning = 0;
+ data->registry_data.disable_xlpp_tuning = 0;
+ data->registry_data.disable_workload_policy = 0;
+ data->registry_data.perf_ui_tuning_profile_turbo = 0x19190F0F;
+ data->registry_data.perf_ui_tuning_profile_powerSave = 0x19191919;
+ data->registry_data.perf_ui_tuning_profile_xl = 0x00000F0A;
+ data->registry_data.force_workload_policy_mask = 0;
+ data->registry_data.disable_3d_fs_detection = 0;
+ data->registry_data.fps_support = 1;
+ data->registry_data.disable_auto_wattman = 1;
+ data->registry_data.auto_wattman_debug = 0;
+ data->registry_data.auto_wattman_sample_period = 100;
+ data->registry_data.auto_wattman_threshold = 50;
+}
+
+static int vega12_set_features_platform_caps(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ if (data->vddci_control == VEGA12_VOLTAGE_CONTROL_NONE)
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ControlVDDCI);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TablelessHardwareInterface);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EnableSMU7ThermalManagement);
+
+ if (adev->pg_flags & AMD_PG_SUPPORT_UVD) {
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UVDPowerGating);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UVDDynamicPowerGating);
+ }
+
+ if (adev->pg_flags & AMD_PG_SUPPORT_VCE)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_VCEPowerGating);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UnTabledHardwareInterface);
+
+ if (data->registry_data.odn_feature_enable)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ODNinACSupport);
+ else {
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_OD6inACSupport);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_OD6PlusinACSupport);
+ }
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ActivityReporting);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_FanSpeedInTableIsRPM);
+
+ if (data->registry_data.od_state_in_dc_support) {
+ if (data->registry_data.odn_feature_enable)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ODNinDCSupport);
+ else {
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_OD6inDCSupport);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_OD6PlusinDCSupport);
+ }
+ }
+
+ if (data->registry_data.thermal_support
+ && data->registry_data.fuzzy_fan_control_support
+ && hwmgr->thermal_controller.advanceFanControlParameters.usTMax)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ODFuzzyFanControlSupport);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DynamicPowerManagement);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SMC);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ThermalPolicyDelay);
+
+ if (data->registry_data.force_dpm_high)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ExclusiveModeAlwaysHigh);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DynamicUVDState);
+
+ if (data->registry_data.sclk_throttle_low_notification)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkThrottleLowNotification);
+
+ /* power tune caps */
+ /* assume disabled */
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PowerContainment);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DiDtSupport);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SQRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DBRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TDRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TCPRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DBRRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DiDtEDCEnable);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_GCEDC);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PSM);
+
+ if (data->registry_data.didt_support) {
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DiDtSupport);
+ if (data->registry_data.sq_ramping_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SQRamping);
+ if (data->registry_data.db_ramping_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DBRamping);
+ if (data->registry_data.td_ramping_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TDRamping);
+ if (data->registry_data.tcp_ramping_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TCPRamping);
+ if (data->registry_data.dbr_ramping_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DBRRamping);
+ if (data->registry_data.edc_didt_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DiDtEDCEnable);
+ if (data->registry_data.gc_didt_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_GCEDC);
+ if (data->registry_data.psm_didt_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_PSM);
+ }
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_RegulatorHot);
+
+ if (data->registry_data.ac_dc_switch_gpio_support) {
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SMCtoPPLIBAcdcGpioScheme);
+ }
+
+ if (data->registry_data.quick_transition_support) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SMCtoPPLIBAcdcGpioScheme);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_Falcon_QuickTransition);
+ }
+
+ if (data->lowest_uclk_reserved_for_ulv != PPVEGA12_VEGA12LOWESTUCLKRESERVEDFORULV_DFLT) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_LowestUclkReservedForUlv);
+ if (data->lowest_uclk_reserved_for_ulv == 1)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_LowestUclkReservedForUlv);
+ }
+
+ if (data->registry_data.custom_fan_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_CustomFanControlSupport);
+
+ return 0;
+}
+
+static void vega12_init_dpm_defaults(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t top32, bottom32;
+ int i;
+
+ data->smu_features[GNLD_DPM_PREFETCHER].smu_feature_id =
+ FEATURE_DPM_PREFETCHER_BIT;
+ data->smu_features[GNLD_DPM_GFXCLK].smu_feature_id =
+ FEATURE_DPM_GFXCLK_BIT;
+ data->smu_features[GNLD_DPM_UCLK].smu_feature_id =
+ FEATURE_DPM_UCLK_BIT;
+ data->smu_features[GNLD_DPM_SOCCLK].smu_feature_id =
+ FEATURE_DPM_SOCCLK_BIT;
+ data->smu_features[GNLD_DPM_UVD].smu_feature_id =
+ FEATURE_DPM_UVD_BIT;
+ data->smu_features[GNLD_DPM_VCE].smu_feature_id =
+ FEATURE_DPM_VCE_BIT;
+ data->smu_features[GNLD_ULV].smu_feature_id =
+ FEATURE_ULV_BIT;
+ data->smu_features[GNLD_DPM_MP0CLK].smu_feature_id =
+ FEATURE_DPM_MP0CLK_BIT;
+ data->smu_features[GNLD_DPM_LINK].smu_feature_id =
+ FEATURE_DPM_LINK_BIT;
+ data->smu_features[GNLD_DPM_DCEFCLK].smu_feature_id =
+ FEATURE_DPM_DCEFCLK_BIT;
+ data->smu_features[GNLD_DS_GFXCLK].smu_feature_id =
+ FEATURE_DS_GFXCLK_BIT;
+ data->smu_features[GNLD_DS_SOCCLK].smu_feature_id =
+ FEATURE_DS_SOCCLK_BIT;
+ data->smu_features[GNLD_DS_LCLK].smu_feature_id =
+ FEATURE_DS_LCLK_BIT;
+ data->smu_features[GNLD_PPT].smu_feature_id =
+ FEATURE_PPT_BIT;
+ data->smu_features[GNLD_TDC].smu_feature_id =
+ FEATURE_TDC_BIT;
+ data->smu_features[GNLD_THERMAL].smu_feature_id =
+ FEATURE_THERMAL_BIT;
+ data->smu_features[GNLD_GFX_PER_CU_CG].smu_feature_id =
+ FEATURE_GFX_PER_CU_CG_BIT;
+ data->smu_features[GNLD_RM].smu_feature_id =
+ FEATURE_RM_BIT;
+ data->smu_features[GNLD_DS_DCEFCLK].smu_feature_id =
+ FEATURE_DS_DCEFCLK_BIT;
+ data->smu_features[GNLD_ACDC].smu_feature_id =
+ FEATURE_ACDC_BIT;
+ data->smu_features[GNLD_VR0HOT].smu_feature_id =
+ FEATURE_VR0HOT_BIT;
+ data->smu_features[GNLD_VR1HOT].smu_feature_id =
+ FEATURE_VR1HOT_BIT;
+ data->smu_features[GNLD_FW_CTF].smu_feature_id =
+ FEATURE_FW_CTF_BIT;
+ data->smu_features[GNLD_LED_DISPLAY].smu_feature_id =
+ FEATURE_LED_DISPLAY_BIT;
+ data->smu_features[GNLD_FAN_CONTROL].smu_feature_id =
+ FEATURE_FAN_CONTROL_BIT;
+ data->smu_features[GNLD_DIDT].smu_feature_id = FEATURE_GFX_EDC_BIT;
+ data->smu_features[GNLD_GFXOFF].smu_feature_id = FEATURE_GFXOFF_BIT;
+ data->smu_features[GNLD_CG].smu_feature_id = FEATURE_CG_BIT;
+ data->smu_features[GNLD_ACG].smu_feature_id = FEATURE_ACG_BIT;
+
+ for (i = 0; i < GNLD_FEATURES_MAX; i++) {
+ data->smu_features[i].smu_feature_bitmap =
+ (uint64_t)(1ULL << data->smu_features[i].smu_feature_id);
+ data->smu_features[i].allowed =
+ ((data->registry_data.disallowed_features >> i) & 1) ?
+ false : true;
+ }
+
+ /* Get the SN to turn into a Unique ID */
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ReadSerialNumTop32, &top32);
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ReadSerialNumBottom32, &bottom32);
+
+ adev->unique_id = ((uint64_t)bottom32 << 32) | top32;
+}
+
+static int vega12_set_private_data_based_on_pptable(struct pp_hwmgr *hwmgr)
+{
+ return 0;
+}
+
+static int vega12_hwmgr_backend_fini(struct pp_hwmgr *hwmgr)
+{
+ kfree(hwmgr->backend);
+ hwmgr->backend = NULL;
+
+ return 0;
+}
+
+static int vega12_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+ struct vega12_hwmgr *data;
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ data = kzalloc(sizeof(struct vega12_hwmgr), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ hwmgr->backend = data;
+
+ vega12_set_default_registry_data(hwmgr);
+
+ data->disable_dpm_mask = 0xff;
+ data->workload_mask = 0xff;
+
+ /* need to set voltage control types before EVV patching */
+ data->vddc_control = VEGA12_VOLTAGE_CONTROL_NONE;
+ data->mvdd_control = VEGA12_VOLTAGE_CONTROL_NONE;
+ data->vddci_control = VEGA12_VOLTAGE_CONTROL_NONE;
+
+ data->water_marks_bitmap = 0;
+ data->avfs_exist = false;
+
+ vega12_set_features_platform_caps(hwmgr);
+
+ vega12_init_dpm_defaults(hwmgr);
+
+ /* Parse pptable data read from VBIOS */
+ vega12_set_private_data_based_on_pptable(hwmgr);
+
+ data->is_tlu_enabled = false;
+
+ hwmgr->platform_descriptor.hardwareActivityPerformanceLevels =
+ VEGA12_MAX_HARDWARE_POWERLEVELS;
+ hwmgr->platform_descriptor.hardwarePerformanceLevels = 2;
+ hwmgr->platform_descriptor.minimumClocksReductionPercentage = 50;
+
+ hwmgr->platform_descriptor.vbiosInterruptId = 0x20000400; /* IRQ_SOURCE1_SW_INT */
+ /* The true clock step depends on the frequency, typically 4.5 or 9 MHz. Here we use 5. */
+ hwmgr->platform_descriptor.clockStep.engineClock = 500;
+ hwmgr->platform_descriptor.clockStep.memoryClock = 500;
+
+ data->total_active_cus = adev->gfx.cu_info.number;
+ /* Setup default Overdrive Fan control settings */
+ data->odn_fan_table.target_fan_speed =
+ hwmgr->thermal_controller.advanceFanControlParameters.usMaxFanRPM;
+ data->odn_fan_table.target_temperature =
+ hwmgr->thermal_controller.advanceFanControlParameters.ucTargetTemperature;
+ data->odn_fan_table.min_performance_clock =
+ hwmgr->thermal_controller.advanceFanControlParameters.ulMinFanSCLKAcousticLimit;
+ data->odn_fan_table.min_fan_limit =
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanPWMMinLimit *
+ hwmgr->thermal_controller.fanInfo.ulMaxRPM / 100;
+
+ if (hwmgr->feature_mask & PP_GFXOFF_MASK)
+ data->gfxoff_controlled_by_driver = true;
+ else
+ data->gfxoff_controlled_by_driver = false;
+
+ return result;
+}
+
+static int vega12_init_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+
+ data->low_sclk_interrupt_threshold = 0;
+
+ return 0;
+}
+
+static int vega12_setup_asic_task(struct pp_hwmgr *hwmgr)
+{
+ PP_ASSERT_WITH_CODE(!vega12_init_sclk_threshold(hwmgr),
+ "Failed to init sclk threshold!",
+ return -EINVAL);
+
+ return 0;
+}
+
+/*
+ * @fn vega12_init_dpm_state
+ * @brief Function to initialize all Soft Min/Max and Hard Min/Max to 0xff.
+ *
+ * @param dpm_state - the address of the DPM Table to initiailize.
+ * @return None.
+ */
+static void vega12_init_dpm_state(struct vega12_dpm_state *dpm_state)
+{
+ dpm_state->soft_min_level = 0x0;
+ dpm_state->soft_max_level = 0xffff;
+ dpm_state->hard_min_level = 0x0;
+ dpm_state->hard_max_level = 0xffff;
+}
+
+static int vega12_get_number_of_dpm_level(struct pp_hwmgr *hwmgr,
+ PPCLK_e clk_id, uint32_t *num_of_levels)
+{
+ int ret = 0;
+
+ ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_GetDpmFreqByIndex,
+ (clk_id << 16 | 0xFF),
+ num_of_levels);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[GetNumOfDpmLevel] failed to get dpm levels!",
+ return ret);
+
+ return ret;
+}
+
+static int vega12_get_dpm_frequency_by_index(struct pp_hwmgr *hwmgr,
+ PPCLK_e clkID, uint32_t index, uint32_t *clock)
+{
+ /*
+ *SMU expects the Clock ID to be in the top 16 bits.
+ *Lower 16 bits specify the level
+ */
+ PP_ASSERT_WITH_CODE(smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_GetDpmFreqByIndex, (clkID << 16 | index),
+ clock) == 0,
+ "[GetDpmFrequencyByIndex] Failed to get dpm frequency from SMU!",
+ return -EINVAL);
+
+ return 0;
+}
+
+static int vega12_setup_single_dpm_table(struct pp_hwmgr *hwmgr,
+ struct vega12_single_dpm_table *dpm_table, PPCLK_e clk_id)
+{
+ int ret = 0;
+ uint32_t i, num_of_levels, clk;
+
+ ret = vega12_get_number_of_dpm_level(hwmgr, clk_id, &num_of_levels);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupSingleDpmTable] failed to get clk levels!",
+ return ret);
+
+ dpm_table->count = num_of_levels;
+
+ for (i = 0; i < num_of_levels; i++) {
+ ret = vega12_get_dpm_frequency_by_index(hwmgr, clk_id, i, &clk);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupSingleDpmTable] failed to get clk of specific level!",
+ return ret);
+ dpm_table->dpm_levels[i].value = clk;
+ dpm_table->dpm_levels[i].enabled = true;
+ }
+
+ return ret;
+}
+
+/*
+ * This function is to initialize all DPM state tables
+ * for SMU based on the dependency table.
+ * Dynamic state patching function will then trim these
+ * state tables to the allowed range based
+ * on the power policy or external client requests,
+ * such as UVD request, etc.
+ */
+static int vega12_setup_default_dpm_tables(struct pp_hwmgr *hwmgr)
+{
+
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+ struct vega12_single_dpm_table *dpm_table;
+ int ret = 0;
+
+ memset(&data->dpm_table, 0, sizeof(data->dpm_table));
+
+ /* socclk */
+ dpm_table = &(data->dpm_table.soc_table);
+ if (data->smu_features[GNLD_DPM_SOCCLK].enabled) {
+ ret = vega12_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_SOCCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get socclk dpm levels!",
+ return ret);
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = data->vbios_boot_state.soc_clock / 100;
+ }
+ vega12_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* gfxclk */
+ dpm_table = &(data->dpm_table.gfx_table);
+ if (data->smu_features[GNLD_DPM_GFXCLK].enabled) {
+ ret = vega12_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_GFXCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get gfxclk dpm levels!",
+ return ret);
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = data->vbios_boot_state.gfx_clock / 100;
+ }
+ vega12_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* memclk */
+ dpm_table = &(data->dpm_table.mem_table);
+ if (data->smu_features[GNLD_DPM_UCLK].enabled) {
+ ret = vega12_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_UCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get memclk dpm levels!",
+ return ret);
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = data->vbios_boot_state.mem_clock / 100;
+ }
+ vega12_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* eclk */
+ dpm_table = &(data->dpm_table.eclk_table);
+ if (data->smu_features[GNLD_DPM_VCE].enabled) {
+ ret = vega12_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_ECLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get eclk dpm levels!",
+ return ret);
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = data->vbios_boot_state.eclock / 100;
+ }
+ vega12_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* vclk */
+ dpm_table = &(data->dpm_table.vclk_table);
+ if (data->smu_features[GNLD_DPM_UVD].enabled) {
+ ret = vega12_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_VCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get vclk dpm levels!",
+ return ret);
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = data->vbios_boot_state.vclock / 100;
+ }
+ vega12_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* dclk */
+ dpm_table = &(data->dpm_table.dclk_table);
+ if (data->smu_features[GNLD_DPM_UVD].enabled) {
+ ret = vega12_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_DCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get dclk dpm levels!",
+ return ret);
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = data->vbios_boot_state.dclock / 100;
+ }
+ vega12_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* dcefclk */
+ dpm_table = &(data->dpm_table.dcef_table);
+ if (data->smu_features[GNLD_DPM_DCEFCLK].enabled) {
+ ret = vega12_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_DCEFCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get dcefclk dpm levels!",
+ return ret);
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = data->vbios_boot_state.dcef_clock / 100;
+ }
+ vega12_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* pixclk */
+ dpm_table = &(data->dpm_table.pixel_table);
+ if (data->smu_features[GNLD_DPM_DCEFCLK].enabled) {
+ ret = vega12_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_PIXCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get pixclk dpm levels!",
+ return ret);
+ } else
+ dpm_table->count = 0;
+ vega12_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* dispclk */
+ dpm_table = &(data->dpm_table.display_table);
+ if (data->smu_features[GNLD_DPM_DCEFCLK].enabled) {
+ ret = vega12_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_DISPCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get dispclk dpm levels!",
+ return ret);
+ } else
+ dpm_table->count = 0;
+ vega12_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* phyclk */
+ dpm_table = &(data->dpm_table.phy_table);
+ if (data->smu_features[GNLD_DPM_DCEFCLK].enabled) {
+ ret = vega12_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_PHYCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get phyclk dpm levels!",
+ return ret);
+ } else
+ dpm_table->count = 0;
+ vega12_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* save a copy of the default DPM table */
+ memcpy(&(data->golden_dpm_table), &(data->dpm_table),
+ sizeof(struct vega12_dpm_table));
+
+ return 0;
+}
+
+#if 0
+static int vega12_save_default_power_profile(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ struct vega12_single_dpm_table *dpm_table = &(data->dpm_table.gfx_table);
+ uint32_t min_level;
+
+ hwmgr->default_gfx_power_profile.type = AMD_PP_GFX_PROFILE;
+ hwmgr->default_compute_power_profile.type = AMD_PP_COMPUTE_PROFILE;
+
+ /* Optimize compute power profile: Use only highest
+ * 2 power levels (if more than 2 are available)
+ */
+ if (dpm_table->count > 2)
+ min_level = dpm_table->count - 2;
+ else if (dpm_table->count == 2)
+ min_level = 1;
+ else
+ min_level = 0;
+
+ hwmgr->default_compute_power_profile.min_sclk =
+ dpm_table->dpm_levels[min_level].value;
+
+ hwmgr->gfx_power_profile = hwmgr->default_gfx_power_profile;
+ hwmgr->compute_power_profile = hwmgr->default_compute_power_profile;
+
+ return 0;
+}
+#endif
+
+/**
+* Initializes the SMC table and uploads it
+*
+* @param hwmgr the address of the powerplay hardware manager.
+* @param pInput the pointer to input data (PowerState)
+* @return always 0
+*/
+static int vega12_init_smc_table(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+ struct pp_atomfwctrl_bios_boot_up_values boot_up_values;
+ struct phm_ppt_v3_information *pptable_information =
+ (struct phm_ppt_v3_information *)hwmgr->pptable;
+
+ result = pp_atomfwctrl_get_vbios_bootup_values(hwmgr, &boot_up_values);
+ if (!result) {
+ data->vbios_boot_state.vddc = boot_up_values.usVddc;
+ data->vbios_boot_state.vddci = boot_up_values.usVddci;
+ data->vbios_boot_state.mvddc = boot_up_values.usMvddc;
+ data->vbios_boot_state.gfx_clock = boot_up_values.ulGfxClk;
+ data->vbios_boot_state.mem_clock = boot_up_values.ulUClk;
+ data->vbios_boot_state.soc_clock = boot_up_values.ulSocClk;
+ data->vbios_boot_state.dcef_clock = boot_up_values.ulDCEFClk;
+ data->vbios_boot_state.uc_cooling_id = boot_up_values.ucCoolingID;
+ data->vbios_boot_state.eclock = boot_up_values.ulEClk;
+ data->vbios_boot_state.dclock = boot_up_values.ulDClk;
+ data->vbios_boot_state.vclock = boot_up_values.ulVClk;
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetMinDeepSleepDcefclk,
+ (uint32_t)(data->vbios_boot_state.dcef_clock / 100),
+ NULL);
+ }
+
+ memcpy(pp_table, pptable_information->smc_pptable, sizeof(PPTable_t));
+
+ result = smum_smc_table_manager(hwmgr,
+ (uint8_t *)pp_table, TABLE_PPTABLE, false);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to upload PPtable!", return result);
+
+ return 0;
+}
+
+static int vega12_run_acg_btc(struct pp_hwmgr *hwmgr)
+{
+ uint32_t result;
+
+ PP_ASSERT_WITH_CODE(
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_RunAcgBtc, &result) == 0,
+ "[Run_ACG_BTC] Attempt to run ACG BTC failed!",
+ return -EINVAL);
+
+ PP_ASSERT_WITH_CODE(result == 1,
+ "Failed to run ACG BTC!", return -EINVAL);
+
+ return 0;
+}
+
+static int vega12_set_allowed_featuresmask(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+ int i;
+ uint32_t allowed_features_low = 0, allowed_features_high = 0;
+
+ for (i = 0; i < GNLD_FEATURES_MAX; i++)
+ if (data->smu_features[i].allowed)
+ data->smu_features[i].smu_feature_id > 31 ?
+ (allowed_features_high |= ((data->smu_features[i].smu_feature_bitmap >> SMU_FEATURES_HIGH_SHIFT) & 0xFFFFFFFF)) :
+ (allowed_features_low |= ((data->smu_features[i].smu_feature_bitmap >> SMU_FEATURES_LOW_SHIFT) & 0xFFFFFFFF));
+
+ PP_ASSERT_WITH_CODE(
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_SetAllowedFeaturesMaskHigh, allowed_features_high,
+ NULL) == 0,
+ "[SetAllowedFeaturesMask] Attempt to set allowed features mask (high) failed!",
+ return -1);
+
+ PP_ASSERT_WITH_CODE(
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_SetAllowedFeaturesMaskLow, allowed_features_low,
+ NULL) == 0,
+ "[SetAllowedFeaturesMask] Attempt to set allowed features mask (low) failed!",
+ return -1);
+
+ return 0;
+}
+
+static void vega12_init_powergate_state(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+
+ data->uvd_power_gated = true;
+ data->vce_power_gated = true;
+
+ if (data->smu_features[GNLD_DPM_UVD].enabled)
+ data->uvd_power_gated = false;
+
+ if (data->smu_features[GNLD_DPM_VCE].enabled)
+ data->vce_power_gated = false;
+}
+
+static int vega12_enable_all_smu_features(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+ uint64_t features_enabled;
+ int i;
+ bool enabled;
+
+ PP_ASSERT_WITH_CODE(
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_EnableAllSmuFeatures, NULL) == 0,
+ "[EnableAllSMUFeatures] Failed to enable all smu features!",
+ return -1);
+
+ if (vega12_get_enabled_smc_features(hwmgr, &features_enabled) == 0) {
+ for (i = 0; i < GNLD_FEATURES_MAX; i++) {
+ enabled = (features_enabled & data->smu_features[i].smu_feature_bitmap) ? true : false;
+ data->smu_features[i].enabled = enabled;
+ data->smu_features[i].supported = enabled;
+ }
+ }
+
+ vega12_init_powergate_state(hwmgr);
+
+ return 0;
+}
+
+static int vega12_disable_all_smu_features(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+ uint64_t features_enabled;
+ int i;
+ bool enabled;
+
+ PP_ASSERT_WITH_CODE(
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_DisableAllSmuFeatures, NULL) == 0,
+ "[DisableAllSMUFeatures] Failed to disable all smu features!",
+ return -1);
+
+ if (vega12_get_enabled_smc_features(hwmgr, &features_enabled) == 0) {
+ for (i = 0; i < GNLD_FEATURES_MAX; i++) {
+ enabled = (features_enabled & data->smu_features[i].smu_feature_bitmap) ? true : false;
+ data->smu_features[i].enabled = enabled;
+ data->smu_features[i].supported = enabled;
+ }
+ }
+
+ return 0;
+}
+
+static int vega12_odn_initialize_default_settings(
+ struct pp_hwmgr *hwmgr)
+{
+ return 0;
+}
+
+static int vega12_set_overdrive_target_percentage(struct pp_hwmgr *hwmgr,
+ uint32_t adjust_percent)
+{
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_OverDriveSetPercentage, adjust_percent,
+ NULL);
+}
+
+static int vega12_power_control_set_level(struct pp_hwmgr *hwmgr)
+{
+ int adjust_percent, result = 0;
+
+ if (PP_CAP(PHM_PlatformCaps_PowerContainment)) {
+ adjust_percent =
+ hwmgr->platform_descriptor.TDPAdjustmentPolarity ?
+ hwmgr->platform_descriptor.TDPAdjustment :
+ (-1 * hwmgr->platform_descriptor.TDPAdjustment);
+ result = vega12_set_overdrive_target_percentage(hwmgr,
+ (uint32_t)adjust_percent);
+ }
+ return result;
+}
+
+static int vega12_get_all_clock_ranges_helper(struct pp_hwmgr *hwmgr,
+ PPCLK_e clkid, struct vega12_clock_range *clock)
+{
+ /* AC Max */
+ PP_ASSERT_WITH_CODE(
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_GetMaxDpmFreq, (clkid << 16),
+ &(clock->ACMax)) == 0,
+ "[GetClockRanges] Failed to get max ac clock from SMC!",
+ return -EINVAL);
+
+ /* AC Min */
+ PP_ASSERT_WITH_CODE(
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_GetMinDpmFreq, (clkid << 16),
+ &(clock->ACMin)) == 0,
+ "[GetClockRanges] Failed to get min ac clock from SMC!",
+ return -EINVAL);
+
+ /* DC Max */
+ PP_ASSERT_WITH_CODE(
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_GetDcModeMaxDpmFreq, (clkid << 16),
+ &(clock->DCMax)) == 0,
+ "[GetClockRanges] Failed to get max dc clock from SMC!",
+ return -EINVAL);
+
+ return 0;
+}
+
+static int vega12_get_all_clock_ranges(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+ uint32_t i;
+
+ for (i = 0; i < PPCLK_COUNT; i++)
+ PP_ASSERT_WITH_CODE(!vega12_get_all_clock_ranges_helper(hwmgr,
+ i, &(data->clk_range[i])),
+ "Failed to get clk range from SMC!",
+ return -EINVAL);
+
+ return 0;
+}
+
+static int vega12_enable_dpm_tasks(struct pp_hwmgr *hwmgr)
+{
+ int tmp_result, result = 0;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_NumOfDisplays, 0, NULL);
+
+ result = vega12_set_allowed_featuresmask(hwmgr);
+ PP_ASSERT_WITH_CODE(result == 0,
+ "[EnableDPMTasks] Failed to set allowed featuresmask!\n",
+ return result);
+
+ tmp_result = vega12_init_smc_table(hwmgr);
+ PP_ASSERT_WITH_CODE(!tmp_result,
+ "Failed to initialize SMC table!",
+ result = tmp_result);
+
+ tmp_result = vega12_run_acg_btc(hwmgr);
+ PP_ASSERT_WITH_CODE(!tmp_result,
+ "Failed to run ACG BTC!",
+ result = tmp_result);
+
+ result = vega12_enable_all_smu_features(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to enable all smu features!",
+ return result);
+
+ tmp_result = vega12_power_control_set_level(hwmgr);
+ PP_ASSERT_WITH_CODE(!tmp_result,
+ "Failed to power control set level!",
+ result = tmp_result);
+
+ result = vega12_get_all_clock_ranges(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to get all clock ranges!",
+ return result);
+
+ result = vega12_odn_initialize_default_settings(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to power control set level!",
+ return result);
+
+ result = vega12_setup_default_dpm_tables(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to setup default DPM tables!",
+ return result);
+ return result;
+}
+
+static int vega12_patch_boot_state(struct pp_hwmgr *hwmgr,
+ struct pp_hw_power_state *hw_ps)
+{
+ return 0;
+}
+
+static uint32_t vega12_find_lowest_dpm_level(
+ struct vega12_single_dpm_table *table)
+{
+ uint32_t i;
+
+ for (i = 0; i < table->count; i++) {
+ if (table->dpm_levels[i].enabled)
+ break;
+ }
+
+ if (i >= table->count) {
+ i = 0;
+ table->dpm_levels[i].enabled = true;
+ }
+
+ return i;
+}
+
+static uint32_t vega12_find_highest_dpm_level(
+ struct vega12_single_dpm_table *table)
+{
+ int32_t i = 0;
+ PP_ASSERT_WITH_CODE(table->count <= MAX_REGULAR_DPM_NUMBER,
+ "[FindHighestDPMLevel] DPM Table has too many entries!",
+ return MAX_REGULAR_DPM_NUMBER - 1);
+
+ for (i = table->count - 1; i >= 0; i--) {
+ if (table->dpm_levels[i].enabled)
+ break;
+ }
+
+ if (i < 0) {
+ i = 0;
+ table->dpm_levels[i].enabled = true;
+ }
+
+ return (uint32_t)i;
+}
+
+static int vega12_upload_dpm_min_level(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data = hwmgr->backend;
+ uint32_t min_freq;
+ int ret = 0;
+
+ if (data->smu_features[GNLD_DPM_GFXCLK].enabled) {
+ min_freq = data->dpm_table.gfx_table.dpm_state.soft_min_level;
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMinByFreq,
+ (PPCLK_GFXCLK << 16) | (min_freq & 0xffff),
+ NULL)),
+ "Failed to set soft min gfxclk !",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_UCLK].enabled) {
+ min_freq = data->dpm_table.mem_table.dpm_state.soft_min_level;
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMinByFreq,
+ (PPCLK_UCLK << 16) | (min_freq & 0xffff),
+ NULL)),
+ "Failed to set soft min memclk !",
+ return ret);
+
+ min_freq = data->dpm_table.mem_table.dpm_state.hard_min_level;
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetHardMinByFreq,
+ (PPCLK_UCLK << 16) | (min_freq & 0xffff),
+ NULL)),
+ "Failed to set hard min memclk !",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_UVD].enabled) {
+ min_freq = data->dpm_table.vclk_table.dpm_state.soft_min_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMinByFreq,
+ (PPCLK_VCLK << 16) | (min_freq & 0xffff),
+ NULL)),
+ "Failed to set soft min vclk!",
+ return ret);
+
+ min_freq = data->dpm_table.dclk_table.dpm_state.soft_min_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMinByFreq,
+ (PPCLK_DCLK << 16) | (min_freq & 0xffff),
+ NULL)),
+ "Failed to set soft min dclk!",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_VCE].enabled) {
+ min_freq = data->dpm_table.eclk_table.dpm_state.soft_min_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMinByFreq,
+ (PPCLK_ECLK << 16) | (min_freq & 0xffff),
+ NULL)),
+ "Failed to set soft min eclk!",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_SOCCLK].enabled) {
+ min_freq = data->dpm_table.soc_table.dpm_state.soft_min_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMinByFreq,
+ (PPCLK_SOCCLK << 16) | (min_freq & 0xffff),
+ NULL)),
+ "Failed to set soft min socclk!",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_DCEFCLK].enabled) {
+ min_freq = data->dpm_table.dcef_table.dpm_state.hard_min_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetHardMinByFreq,
+ (PPCLK_DCEFCLK << 16) | (min_freq & 0xffff),
+ NULL)),
+ "Failed to set hard min dcefclk!",
+ return ret);
+ }
+
+ return ret;
+
+}
+
+static int vega12_upload_dpm_max_level(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data = hwmgr->backend;
+ uint32_t max_freq;
+ int ret = 0;
+
+ if (data->smu_features[GNLD_DPM_GFXCLK].enabled) {
+ max_freq = data->dpm_table.gfx_table.dpm_state.soft_max_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMaxByFreq,
+ (PPCLK_GFXCLK << 16) | (max_freq & 0xffff),
+ NULL)),
+ "Failed to set soft max gfxclk!",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_UCLK].enabled) {
+ max_freq = data->dpm_table.mem_table.dpm_state.soft_max_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMaxByFreq,
+ (PPCLK_UCLK << 16) | (max_freq & 0xffff),
+ NULL)),
+ "Failed to set soft max memclk!",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_UVD].enabled) {
+ max_freq = data->dpm_table.vclk_table.dpm_state.soft_max_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMaxByFreq,
+ (PPCLK_VCLK << 16) | (max_freq & 0xffff),
+ NULL)),
+ "Failed to set soft max vclk!",
+ return ret);
+
+ max_freq = data->dpm_table.dclk_table.dpm_state.soft_max_level;
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMaxByFreq,
+ (PPCLK_DCLK << 16) | (max_freq & 0xffff),
+ NULL)),
+ "Failed to set soft max dclk!",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_VCE].enabled) {
+ max_freq = data->dpm_table.eclk_table.dpm_state.soft_max_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMaxByFreq,
+ (PPCLK_ECLK << 16) | (max_freq & 0xffff),
+ NULL)),
+ "Failed to set soft max eclk!",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_SOCCLK].enabled) {
+ max_freq = data->dpm_table.soc_table.dpm_state.soft_max_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMaxByFreq,
+ (PPCLK_SOCCLK << 16) | (max_freq & 0xffff),
+ NULL)),
+ "Failed to set soft max socclk!",
+ return ret);
+ }
+
+ return ret;
+}
+
+int vega12_enable_disable_vce_dpm(struct pp_hwmgr *hwmgr, bool enable)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+
+ if (data->smu_features[GNLD_DPM_VCE].supported) {
+ PP_ASSERT_WITH_CODE(!vega12_enable_smc_features(hwmgr,
+ enable,
+ data->smu_features[GNLD_DPM_VCE].smu_feature_bitmap),
+ "Attempt to Enable/Disable DPM VCE Failed!",
+ return -1);
+ data->smu_features[GNLD_DPM_VCE].enabled = enable;
+ }
+
+ return 0;
+}
+
+static uint32_t vega12_dpm_get_sclk(struct pp_hwmgr *hwmgr, bool low)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+ uint32_t gfx_clk;
+
+ if (!data->smu_features[GNLD_DPM_GFXCLK].enabled)
+ return -1;
+
+ if (low)
+ PP_ASSERT_WITH_CODE(
+ vega12_get_clock_ranges(hwmgr, &gfx_clk, PPCLK_GFXCLK, false) == 0,
+ "[GetSclks]: fail to get min PPCLK_GFXCLK\n",
+ return -1);
+ else
+ PP_ASSERT_WITH_CODE(
+ vega12_get_clock_ranges(hwmgr, &gfx_clk, PPCLK_GFXCLK, true) == 0,
+ "[GetSclks]: fail to get max PPCLK_GFXCLK\n",
+ return -1);
+
+ return (gfx_clk * 100);
+}
+
+static uint32_t vega12_dpm_get_mclk(struct pp_hwmgr *hwmgr, bool low)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+ uint32_t mem_clk;
+
+ if (!data->smu_features[GNLD_DPM_UCLK].enabled)
+ return -1;
+
+ if (low)
+ PP_ASSERT_WITH_CODE(
+ vega12_get_clock_ranges(hwmgr, &mem_clk, PPCLK_UCLK, false) == 0,
+ "[GetMclks]: fail to get min PPCLK_UCLK\n",
+ return -1);
+ else
+ PP_ASSERT_WITH_CODE(
+ vega12_get_clock_ranges(hwmgr, &mem_clk, PPCLK_UCLK, true) == 0,
+ "[GetMclks]: fail to get max PPCLK_UCLK\n",
+ return -1);
+
+ return (mem_clk * 100);
+}
+
+static int vega12_get_metrics_table(struct pp_hwmgr *hwmgr,
+ SmuMetrics_t *metrics_table,
+ bool bypass_cache)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+ int ret = 0;
+
+ if (bypass_cache ||
+ !data->metrics_time ||
+ time_after(jiffies, data->metrics_time + msecs_to_jiffies(1))) {
+ ret = smum_smc_table_manager(hwmgr,
+ (uint8_t *)(&data->metrics_table),
+ TABLE_SMU_METRICS,
+ true);
+ if (ret) {
+ pr_info("Failed to export SMU metrics table!\n");
+ return ret;
+ }
+ data->metrics_time = jiffies;
+ }
+
+ if (metrics_table)
+ memcpy(metrics_table, &data->metrics_table, sizeof(SmuMetrics_t));
+
+ return ret;
+}
+
+static int vega12_get_gpu_power(struct pp_hwmgr *hwmgr, uint32_t *query)
+{
+ SmuMetrics_t metrics_table;
+ int ret = 0;
+
+ ret = vega12_get_metrics_table(hwmgr, &metrics_table, false);
+ if (ret)
+ return ret;
+
+ *query = metrics_table.CurrSocketPower << 8;
+
+ return ret;
+}
+
+static int vega12_get_current_gfx_clk_freq(struct pp_hwmgr *hwmgr, uint32_t *gfx_freq)
+{
+ uint32_t gfx_clk = 0;
+
+ *gfx_freq = 0;
+
+ PP_ASSERT_WITH_CODE(smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_GetDpmClockFreq, (PPCLK_GFXCLK << 16),
+ &gfx_clk) == 0,
+ "[GetCurrentGfxClkFreq] Attempt to get Current GFXCLK Frequency Failed!",
+ return -EINVAL);
+
+ *gfx_freq = gfx_clk * 100;
+
+ return 0;
+}
+
+static int vega12_get_current_mclk_freq(struct pp_hwmgr *hwmgr, uint32_t *mclk_freq)
+{
+ uint32_t mem_clk = 0;
+
+ *mclk_freq = 0;
+
+ PP_ASSERT_WITH_CODE(
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_GetDpmClockFreq, (PPCLK_UCLK << 16),
+ &mem_clk) == 0,
+ "[GetCurrentMClkFreq] Attempt to get Current MCLK Frequency Failed!",
+ return -EINVAL);
+
+ *mclk_freq = mem_clk * 100;
+
+ return 0;
+}
+
+static int vega12_get_current_activity_percent(
+ struct pp_hwmgr *hwmgr,
+ int idx,
+ uint32_t *activity_percent)
+{
+ SmuMetrics_t metrics_table;
+ int ret = 0;
+
+ ret = vega12_get_metrics_table(hwmgr, &metrics_table, false);
+ if (ret)
+ return ret;
+
+ switch (idx) {
+ case AMDGPU_PP_SENSOR_GPU_LOAD:
+ *activity_percent = metrics_table.AverageGfxActivity;
+ break;
+ case AMDGPU_PP_SENSOR_MEM_LOAD:
+ *activity_percent = metrics_table.AverageUclkActivity;
+ break;
+ default:
+ pr_err("Invalid index for retrieving clock activity\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int vega12_read_sensor(struct pp_hwmgr *hwmgr, int idx,
+ void *value, int *size)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ SmuMetrics_t metrics_table;
+ int ret = 0;
+
+ switch (idx) {
+ case AMDGPU_PP_SENSOR_GFX_SCLK:
+ ret = vega12_get_current_gfx_clk_freq(hwmgr, (uint32_t *)value);
+ if (!ret)
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GFX_MCLK:
+ ret = vega12_get_current_mclk_freq(hwmgr, (uint32_t *)value);
+ if (!ret)
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GPU_LOAD:
+ case AMDGPU_PP_SENSOR_MEM_LOAD:
+ ret = vega12_get_current_activity_percent(hwmgr, idx, (uint32_t *)value);
+ if (!ret)
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GPU_TEMP:
+ *((uint32_t *)value) = vega12_thermal_get_temperature(hwmgr);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_HOTSPOT_TEMP:
+ ret = vega12_get_metrics_table(hwmgr, &metrics_table, false);
+ if (ret)
+ return ret;
+
+ *((uint32_t *)value) = metrics_table.TemperatureHotspot *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_MEM_TEMP:
+ ret = vega12_get_metrics_table(hwmgr, &metrics_table, false);
+ if (ret)
+ return ret;
+
+ *((uint32_t *)value) = metrics_table.TemperatureHBM *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_UVD_POWER:
+ *((uint32_t *)value) = data->uvd_power_gated ? 0 : 1;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_VCE_POWER:
+ *((uint32_t *)value) = data->vce_power_gated ? 0 : 1;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GPU_POWER:
+ ret = vega12_get_gpu_power(hwmgr, (uint32_t *)value);
+ if (!ret)
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_ENABLED_SMC_FEATURES_MASK:
+ ret = vega12_get_enabled_smc_features(hwmgr, (uint64_t *)value);
+ if (!ret)
+ *size = 8;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int vega12_notify_smc_display_change(struct pp_hwmgr *hwmgr,
+ bool has_disp)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+
+ if (data->smu_features[GNLD_DPM_UCLK].enabled)
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetUclkFastSwitch,
+ has_disp ? 1 : 0,
+ NULL);
+
+ return 0;
+}
+
+static int vega12_display_clock_voltage_request(struct pp_hwmgr *hwmgr,
+ struct pp_display_clock_request *clock_req)
+{
+ int result = 0;
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ enum amd_pp_clock_type clk_type = clock_req->clock_type;
+ uint32_t clk_freq = clock_req->clock_freq_in_khz / 1000;
+ PPCLK_e clk_select = 0;
+ uint32_t clk_request = 0;
+
+ if (data->smu_features[GNLD_DPM_DCEFCLK].enabled) {
+ switch (clk_type) {
+ case amd_pp_dcef_clock:
+ clk_select = PPCLK_DCEFCLK;
+ break;
+ case amd_pp_disp_clock:
+ clk_select = PPCLK_DISPCLK;
+ break;
+ case amd_pp_pixel_clock:
+ clk_select = PPCLK_PIXCLK;
+ break;
+ case amd_pp_phy_clock:
+ clk_select = PPCLK_PHYCLK;
+ break;
+ default:
+ pr_info("[DisplayClockVoltageRequest]Invalid Clock Type!");
+ result = -1;
+ break;
+ }
+
+ if (!result) {
+ clk_request = (clk_select << 16) | clk_freq;
+ result = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinByFreq,
+ clk_request,
+ NULL);
+ }
+ }
+
+ return result;
+}
+
+static int vega12_notify_smc_display_config_after_ps_adjustment(
+ struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+ struct PP_Clocks min_clocks = {0};
+ struct pp_display_clock_request clock_req;
+
+ if ((hwmgr->display_config->num_display > 1) &&
+ !hwmgr->display_config->multi_monitor_in_sync &&
+ !hwmgr->display_config->nb_pstate_switch_disable)
+ vega12_notify_smc_display_change(hwmgr, false);
+ else
+ vega12_notify_smc_display_change(hwmgr, true);
+
+ min_clocks.dcefClock = hwmgr->display_config->min_dcef_set_clk;
+ min_clocks.dcefClockInSR = hwmgr->display_config->min_dcef_deep_sleep_set_clk;
+ min_clocks.memoryClock = hwmgr->display_config->min_mem_set_clock;
+
+ if (data->smu_features[GNLD_DPM_DCEFCLK].supported) {
+ clock_req.clock_type = amd_pp_dcef_clock;
+ clock_req.clock_freq_in_khz = min_clocks.dcefClock/10;
+ if (!vega12_display_clock_voltage_request(hwmgr, &clock_req)) {
+ if (data->smu_features[GNLD_DS_DCEFCLK].supported)
+ PP_ASSERT_WITH_CODE(
+ !smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetMinDeepSleepDcefclk,
+ min_clocks.dcefClockInSR /100,
+ NULL),
+ "Attempt to set divider for DCEFCLK Failed!",
+ return -1);
+ } else {
+ pr_info("Attempt to set Hard Min for DCEFCLK Failed!");
+ }
+ }
+
+ return 0;
+}
+
+static int vega12_force_dpm_highest(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+
+ uint32_t soft_level;
+
+ soft_level = vega12_find_highest_dpm_level(&(data->dpm_table.gfx_table));
+
+ data->dpm_table.gfx_table.dpm_state.soft_min_level =
+ data->dpm_table.gfx_table.dpm_state.soft_max_level =
+ data->dpm_table.gfx_table.dpm_levels[soft_level].value;
+
+ soft_level = vega12_find_highest_dpm_level(&(data->dpm_table.mem_table));
+
+ data->dpm_table.mem_table.dpm_state.soft_min_level =
+ data->dpm_table.mem_table.dpm_state.soft_max_level =
+ data->dpm_table.mem_table.dpm_levels[soft_level].value;
+
+ PP_ASSERT_WITH_CODE(!vega12_upload_dpm_min_level(hwmgr),
+ "Failed to upload boot level to highest!",
+ return -1);
+
+ PP_ASSERT_WITH_CODE(!vega12_upload_dpm_max_level(hwmgr),
+ "Failed to upload dpm max level to highest!",
+ return -1);
+
+ return 0;
+}
+
+static int vega12_force_dpm_lowest(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+ uint32_t soft_level;
+
+ soft_level = vega12_find_lowest_dpm_level(&(data->dpm_table.gfx_table));
+
+ data->dpm_table.gfx_table.dpm_state.soft_min_level =
+ data->dpm_table.gfx_table.dpm_state.soft_max_level =
+ data->dpm_table.gfx_table.dpm_levels[soft_level].value;
+
+ soft_level = vega12_find_lowest_dpm_level(&(data->dpm_table.mem_table));
+
+ data->dpm_table.mem_table.dpm_state.soft_min_level =
+ data->dpm_table.mem_table.dpm_state.soft_max_level =
+ data->dpm_table.mem_table.dpm_levels[soft_level].value;
+
+ PP_ASSERT_WITH_CODE(!vega12_upload_dpm_min_level(hwmgr),
+ "Failed to upload boot level to highest!",
+ return -1);
+
+ PP_ASSERT_WITH_CODE(!vega12_upload_dpm_max_level(hwmgr),
+ "Failed to upload dpm max level to highest!",
+ return -1);
+
+ return 0;
+
+}
+
+static int vega12_unforce_dpm_levels(struct pp_hwmgr *hwmgr)
+{
+ PP_ASSERT_WITH_CODE(!vega12_upload_dpm_min_level(hwmgr),
+ "Failed to upload DPM Bootup Levels!",
+ return -1);
+
+ PP_ASSERT_WITH_CODE(!vega12_upload_dpm_max_level(hwmgr),
+ "Failed to upload DPM Max Levels!",
+ return -1);
+
+ return 0;
+}
+
+static int vega12_get_profiling_clk_mask(struct pp_hwmgr *hwmgr, enum amd_dpm_forced_level level,
+ uint32_t *sclk_mask, uint32_t *mclk_mask, uint32_t *soc_mask)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ struct vega12_single_dpm_table *gfx_dpm_table = &(data->dpm_table.gfx_table);
+ struct vega12_single_dpm_table *mem_dpm_table = &(data->dpm_table.mem_table);
+ struct vega12_single_dpm_table *soc_dpm_table = &(data->dpm_table.soc_table);
+
+ *sclk_mask = 0;
+ *mclk_mask = 0;
+ *soc_mask = 0;
+
+ if (gfx_dpm_table->count > VEGA12_UMD_PSTATE_GFXCLK_LEVEL &&
+ mem_dpm_table->count > VEGA12_UMD_PSTATE_MCLK_LEVEL &&
+ soc_dpm_table->count > VEGA12_UMD_PSTATE_SOCCLK_LEVEL) {
+ *sclk_mask = VEGA12_UMD_PSTATE_GFXCLK_LEVEL;
+ *mclk_mask = VEGA12_UMD_PSTATE_MCLK_LEVEL;
+ *soc_mask = VEGA12_UMD_PSTATE_SOCCLK_LEVEL;
+ }
+
+ if (level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK) {
+ *sclk_mask = 0;
+ } else if (level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK) {
+ *mclk_mask = 0;
+ } else if (level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ *sclk_mask = gfx_dpm_table->count - 1;
+ *mclk_mask = mem_dpm_table->count - 1;
+ *soc_mask = soc_dpm_table->count - 1;
+ }
+
+ return 0;
+}
+
+static void vega12_set_fan_control_mode(struct pp_hwmgr *hwmgr, uint32_t mode)
+{
+ switch (mode) {
+ case AMD_FAN_CTRL_NONE:
+ break;
+ case AMD_FAN_CTRL_MANUAL:
+ if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl))
+ vega12_fan_ctrl_stop_smc_fan_control(hwmgr);
+ break;
+ case AMD_FAN_CTRL_AUTO:
+ if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl))
+ vega12_fan_ctrl_start_smc_fan_control(hwmgr);
+ break;
+ default:
+ break;
+ }
+}
+
+static int vega12_dpm_force_dpm_level(struct pp_hwmgr *hwmgr,
+ enum amd_dpm_forced_level level)
+{
+ int ret = 0;
+ uint32_t sclk_mask = 0;
+ uint32_t mclk_mask = 0;
+ uint32_t soc_mask = 0;
+
+ switch (level) {
+ case AMD_DPM_FORCED_LEVEL_HIGH:
+ ret = vega12_force_dpm_highest(hwmgr);
+ break;
+ case AMD_DPM_FORCED_LEVEL_LOW:
+ ret = vega12_force_dpm_lowest(hwmgr);
+ break;
+ case AMD_DPM_FORCED_LEVEL_AUTO:
+ ret = vega12_unforce_dpm_levels(hwmgr);
+ break;
+ case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+ ret = vega12_get_profiling_clk_mask(hwmgr, level, &sclk_mask, &mclk_mask, &soc_mask);
+ if (ret)
+ return ret;
+ vega12_force_clock_level(hwmgr, PP_SCLK, 1 << sclk_mask);
+ vega12_force_clock_level(hwmgr, PP_MCLK, 1 << mclk_mask);
+ break;
+ case AMD_DPM_FORCED_LEVEL_MANUAL:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_EXIT:
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static uint32_t vega12_get_fan_control_mode(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+
+ if (data->smu_features[GNLD_FAN_CONTROL].enabled == false)
+ return AMD_FAN_CTRL_MANUAL;
+ else
+ return AMD_FAN_CTRL_AUTO;
+}
+
+static int vega12_get_dal_power_level(struct pp_hwmgr *hwmgr,
+ struct amd_pp_simple_clock_info *info)
+{
+#if 0
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)hwmgr->pptable;
+ struct phm_clock_and_voltage_limits *max_limits =
+ &table_info->max_clock_voltage_on_ac;
+
+ info->engine_max_clock = max_limits->sclk;
+ info->memory_max_clock = max_limits->mclk;
+#endif
+ return 0;
+}
+
+static int vega12_get_clock_ranges(struct pp_hwmgr *hwmgr,
+ uint32_t *clock,
+ PPCLK_e clock_select,
+ bool max)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+
+ if (max)
+ *clock = data->clk_range[clock_select].ACMax;
+ else
+ *clock = data->clk_range[clock_select].ACMin;
+
+ return 0;
+}
+
+static int vega12_get_sclks(struct pp_hwmgr *hwmgr,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ uint32_t ucount;
+ int i;
+ struct vega12_single_dpm_table *dpm_table;
+
+ if (!data->smu_features[GNLD_DPM_GFXCLK].enabled)
+ return -1;
+
+ dpm_table = &(data->dpm_table.gfx_table);
+ ucount = (dpm_table->count > MAX_NUM_CLOCKS) ?
+ MAX_NUM_CLOCKS : dpm_table->count;
+
+ for (i = 0; i < ucount; i++) {
+ clocks->data[i].clocks_in_khz =
+ dpm_table->dpm_levels[i].value * 1000;
+
+ clocks->data[i].latency_in_us = 0;
+ }
+
+ clocks->num_levels = ucount;
+
+ return 0;
+}
+
+static uint32_t vega12_get_mem_latency(struct pp_hwmgr *hwmgr,
+ uint32_t clock)
+{
+ return 25;
+}
+
+static int vega12_get_memclocks(struct pp_hwmgr *hwmgr,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ uint32_t ucount;
+ int i;
+ struct vega12_single_dpm_table *dpm_table;
+ if (!data->smu_features[GNLD_DPM_UCLK].enabled)
+ return -1;
+
+ dpm_table = &(data->dpm_table.mem_table);
+ ucount = (dpm_table->count > MAX_NUM_CLOCKS) ?
+ MAX_NUM_CLOCKS : dpm_table->count;
+
+ for (i = 0; i < ucount; i++) {
+ clocks->data[i].clocks_in_khz = dpm_table->dpm_levels[i].value * 1000;
+ data->mclk_latency_table.entries[i].frequency = dpm_table->dpm_levels[i].value * 100;
+ clocks->data[i].latency_in_us =
+ data->mclk_latency_table.entries[i].latency =
+ vega12_get_mem_latency(hwmgr, dpm_table->dpm_levels[i].value);
+ }
+
+ clocks->num_levels = data->mclk_latency_table.count = ucount;
+
+ return 0;
+}
+
+static int vega12_get_dcefclocks(struct pp_hwmgr *hwmgr,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ uint32_t ucount;
+ int i;
+ struct vega12_single_dpm_table *dpm_table;
+
+ if (!data->smu_features[GNLD_DPM_DCEFCLK].enabled)
+ return -1;
+
+
+ dpm_table = &(data->dpm_table.dcef_table);
+ ucount = (dpm_table->count > MAX_NUM_CLOCKS) ?
+ MAX_NUM_CLOCKS : dpm_table->count;
+
+ for (i = 0; i < ucount; i++) {
+ clocks->data[i].clocks_in_khz =
+ dpm_table->dpm_levels[i].value * 1000;
+
+ clocks->data[i].latency_in_us = 0;
+ }
+
+ clocks->num_levels = ucount;
+
+ return 0;
+}
+
+static int vega12_get_socclocks(struct pp_hwmgr *hwmgr,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ uint32_t ucount;
+ int i;
+ struct vega12_single_dpm_table *dpm_table;
+
+ if (!data->smu_features[GNLD_DPM_SOCCLK].enabled)
+ return -1;
+
+
+ dpm_table = &(data->dpm_table.soc_table);
+ ucount = (dpm_table->count > MAX_NUM_CLOCKS) ?
+ MAX_NUM_CLOCKS : dpm_table->count;
+
+ for (i = 0; i < ucount; i++) {
+ clocks->data[i].clocks_in_khz =
+ dpm_table->dpm_levels[i].value * 1000;
+
+ clocks->data[i].latency_in_us = 0;
+ }
+
+ clocks->num_levels = ucount;
+
+ return 0;
+
+}
+
+static int vega12_get_clock_by_type_with_latency(struct pp_hwmgr *hwmgr,
+ enum amd_pp_clock_type type,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ int ret;
+
+ switch (type) {
+ case amd_pp_sys_clock:
+ ret = vega12_get_sclks(hwmgr, clocks);
+ break;
+ case amd_pp_mem_clock:
+ ret = vega12_get_memclocks(hwmgr, clocks);
+ break;
+ case amd_pp_dcef_clock:
+ ret = vega12_get_dcefclocks(hwmgr, clocks);
+ break;
+ case amd_pp_soc_clock:
+ ret = vega12_get_socclocks(hwmgr, clocks);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int vega12_get_clock_by_type_with_voltage(struct pp_hwmgr *hwmgr,
+ enum amd_pp_clock_type type,
+ struct pp_clock_levels_with_voltage *clocks)
+{
+ clocks->num_levels = 0;
+
+ return 0;
+}
+
+static int vega12_set_watermarks_for_clocks_ranges(struct pp_hwmgr *hwmgr,
+ void *clock_ranges)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ Watermarks_t *table = &(data->smc_state_table.water_marks_table);
+ struct dm_pp_wm_sets_with_clock_ranges_soc15 *wm_with_clock_ranges = clock_ranges;
+
+ if (!data->registry_data.disable_water_mark &&
+ data->smu_features[GNLD_DPM_DCEFCLK].supported &&
+ data->smu_features[GNLD_DPM_SOCCLK].supported) {
+ smu_set_watermarks_for_clocks_ranges(table, wm_with_clock_ranges);
+ data->water_marks_bitmap |= WaterMarksExist;
+ data->water_marks_bitmap &= ~WaterMarksLoaded;
+ }
+
+ return 0;
+}
+
+static int vega12_force_clock_level(struct pp_hwmgr *hwmgr,
+ enum pp_clock_type type, uint32_t mask)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ uint32_t soft_min_level, soft_max_level, hard_min_level;
+ int ret = 0;
+
+ switch (type) {
+ case PP_SCLK:
+ soft_min_level = mask ? (ffs(mask) - 1) : 0;
+ soft_max_level = mask ? (fls(mask) - 1) : 0;
+
+ data->dpm_table.gfx_table.dpm_state.soft_min_level =
+ data->dpm_table.gfx_table.dpm_levels[soft_min_level].value;
+ data->dpm_table.gfx_table.dpm_state.soft_max_level =
+ data->dpm_table.gfx_table.dpm_levels[soft_max_level].value;
+
+ ret = vega12_upload_dpm_min_level(hwmgr);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload boot level to lowest!",
+ return ret);
+
+ ret = vega12_upload_dpm_max_level(hwmgr);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload dpm max level to highest!",
+ return ret);
+ break;
+
+ case PP_MCLK:
+ soft_min_level = mask ? (ffs(mask) - 1) : 0;
+ soft_max_level = mask ? (fls(mask) - 1) : 0;
+
+ data->dpm_table.mem_table.dpm_state.soft_min_level =
+ data->dpm_table.mem_table.dpm_levels[soft_min_level].value;
+ data->dpm_table.mem_table.dpm_state.soft_max_level =
+ data->dpm_table.mem_table.dpm_levels[soft_max_level].value;
+
+ ret = vega12_upload_dpm_min_level(hwmgr);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload boot level to lowest!",
+ return ret);
+
+ ret = vega12_upload_dpm_max_level(hwmgr);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload dpm max level to highest!",
+ return ret);
+
+ break;
+
+ case PP_SOCCLK:
+ soft_min_level = mask ? (ffs(mask) - 1) : 0;
+ soft_max_level = mask ? (fls(mask) - 1) : 0;
+
+ if (soft_max_level >= data->dpm_table.soc_table.count) {
+ pr_err("Clock level specified %d is over max allowed %d\n",
+ soft_max_level,
+ data->dpm_table.soc_table.count - 1);
+ return -EINVAL;
+ }
+
+ data->dpm_table.soc_table.dpm_state.soft_min_level =
+ data->dpm_table.soc_table.dpm_levels[soft_min_level].value;
+ data->dpm_table.soc_table.dpm_state.soft_max_level =
+ data->dpm_table.soc_table.dpm_levels[soft_max_level].value;
+
+ ret = vega12_upload_dpm_min_level(hwmgr);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload boot level to lowest!",
+ return ret);
+
+ ret = vega12_upload_dpm_max_level(hwmgr);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload dpm max level to highest!",
+ return ret);
+
+ break;
+
+ case PP_DCEFCLK:
+ hard_min_level = mask ? (ffs(mask) - 1) : 0;
+
+ if (hard_min_level >= data->dpm_table.dcef_table.count) {
+ pr_err("Clock level specified %d is over max allowed %d\n",
+ hard_min_level,
+ data->dpm_table.dcef_table.count - 1);
+ return -EINVAL;
+ }
+
+ data->dpm_table.dcef_table.dpm_state.hard_min_level =
+ data->dpm_table.dcef_table.dpm_levels[hard_min_level].value;
+
+ ret = vega12_upload_dpm_min_level(hwmgr);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload boot level to lowest!",
+ return ret);
+
+ //TODO: Setting DCEFCLK max dpm level is not supported
+
+ break;
+
+ case PP_PCIE:
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int vega12_get_ppfeature_status(struct pp_hwmgr *hwmgr, char *buf)
+{
+ static const char *ppfeature_name[] = {
+ "DPM_PREFETCHER",
+ "GFXCLK_DPM",
+ "UCLK_DPM",
+ "SOCCLK_DPM",
+ "UVD_DPM",
+ "VCE_DPM",
+ "ULV",
+ "MP0CLK_DPM",
+ "LINK_DPM",
+ "DCEFCLK_DPM",
+ "GFXCLK_DS",
+ "SOCCLK_DS",
+ "LCLK_DS",
+ "PPT",
+ "TDC",
+ "THERMAL",
+ "GFX_PER_CU_CG",
+ "RM",
+ "DCEFCLK_DS",
+ "ACDC",
+ "VR0HOT",
+ "VR1HOT",
+ "FW_CTF",
+ "LED_DISPLAY",
+ "FAN_CONTROL",
+ "DIDT",
+ "GFXOFF",
+ "CG",
+ "ACG"};
+ static const char *output_title[] = {
+ "FEATURES",
+ "BITMASK",
+ "ENABLEMENT"};
+ uint64_t features_enabled;
+ int i;
+ int ret = 0;
+ int size = 0;
+
+ ret = vega12_get_enabled_smc_features(hwmgr, &features_enabled);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[EnableAllSmuFeatures] Failed to get enabled smc features!",
+ return ret);
+
+ size += sprintf(buf + size, "Current ppfeatures: 0x%016llx\n", features_enabled);
+ size += sprintf(buf + size, "%-19s %-22s %s\n",
+ output_title[0],
+ output_title[1],
+ output_title[2]);
+ for (i = 0; i < GNLD_FEATURES_MAX; i++) {
+ size += sprintf(buf + size, "%-19s 0x%016llx %6s\n",
+ ppfeature_name[i],
+ 1ULL << i,
+ (features_enabled & (1ULL << i)) ? "Y" : "N");
+ }
+
+ return size;
+}
+
+static int vega12_set_ppfeature_status(struct pp_hwmgr *hwmgr, uint64_t new_ppfeature_masks)
+{
+ uint64_t features_enabled;
+ uint64_t features_to_enable;
+ uint64_t features_to_disable;
+ int ret = 0;
+
+ if (new_ppfeature_masks >= (1ULL << GNLD_FEATURES_MAX))
+ return -EINVAL;
+
+ ret = vega12_get_enabled_smc_features(hwmgr, &features_enabled);
+ if (ret)
+ return ret;
+
+ features_to_disable =
+ features_enabled & ~new_ppfeature_masks;
+ features_to_enable =
+ ~features_enabled & new_ppfeature_masks;
+
+ pr_debug("features_to_disable 0x%llx\n", features_to_disable);
+ pr_debug("features_to_enable 0x%llx\n", features_to_enable);
+
+ if (features_to_disable) {
+ ret = vega12_enable_smc_features(hwmgr, false, features_to_disable);
+ if (ret)
+ return ret;
+ }
+
+ if (features_to_enable) {
+ ret = vega12_enable_smc_features(hwmgr, true, features_to_enable);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int vega12_get_current_pcie_link_width_level(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ return (RREG32_PCIE(smnPCIE_LC_LINK_WIDTH_CNTL) &
+ PCIE_LC_LINK_WIDTH_CNTL__LC_LINK_WIDTH_RD_MASK)
+ >> PCIE_LC_LINK_WIDTH_CNTL__LC_LINK_WIDTH_RD__SHIFT;
+}
+
+static int vega12_get_current_pcie_link_width(struct pp_hwmgr *hwmgr)
+{
+ uint32_t width_level;
+
+ width_level = vega12_get_current_pcie_link_width_level(hwmgr);
+ if (width_level > LINK_WIDTH_MAX)
+ width_level = 0;
+
+ return link_width[width_level];
+}
+
+static int vega12_get_current_pcie_link_speed_level(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ return (RREG32_PCIE(smnPCIE_LC_SPEED_CNTL) &
+ PSWUSP0_PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE_MASK)
+ >> PSWUSP0_PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE__SHIFT;
+}
+
+static int vega12_get_current_pcie_link_speed(struct pp_hwmgr *hwmgr)
+{
+ uint32_t speed_level;
+
+ speed_level = vega12_get_current_pcie_link_speed_level(hwmgr);
+ if (speed_level > LINK_SPEED_MAX)
+ speed_level = 0;
+
+ return link_speed[speed_level];
+}
+
+static int vega12_print_clock_levels(struct pp_hwmgr *hwmgr,
+ enum pp_clock_type type, char *buf)
+{
+ int i, now, size = 0;
+ struct pp_clock_levels_with_latency clocks;
+
+ switch (type) {
+ case PP_SCLK:
+ PP_ASSERT_WITH_CODE(
+ vega12_get_current_gfx_clk_freq(hwmgr, &now) == 0,
+ "Attempt to get current gfx clk Failed!",
+ return -1);
+
+ PP_ASSERT_WITH_CODE(
+ vega12_get_sclks(hwmgr, &clocks) == 0,
+ "Attempt to get gfx clk levels Failed!",
+ return -1);
+ for (i = 0; i < clocks.num_levels; i++)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ i, clocks.data[i].clocks_in_khz / 1000,
+ (clocks.data[i].clocks_in_khz / 1000 == now / 100) ? "*" : "");
+ break;
+
+ case PP_MCLK:
+ PP_ASSERT_WITH_CODE(
+ vega12_get_current_mclk_freq(hwmgr, &now) == 0,
+ "Attempt to get current mclk freq Failed!",
+ return -1);
+
+ PP_ASSERT_WITH_CODE(
+ vega12_get_memclocks(hwmgr, &clocks) == 0,
+ "Attempt to get memory clk levels Failed!",
+ return -1);
+ for (i = 0; i < clocks.num_levels; i++)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ i, clocks.data[i].clocks_in_khz / 1000,
+ (clocks.data[i].clocks_in_khz / 1000 == now / 100) ? "*" : "");
+ break;
+
+ case PP_SOCCLK:
+ PP_ASSERT_WITH_CODE(
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_GetDpmClockFreq, (PPCLK_SOCCLK << 16),
+ &now) == 0,
+ "Attempt to get Current SOCCLK Frequency Failed!",
+ return -EINVAL);
+
+ PP_ASSERT_WITH_CODE(
+ vega12_get_socclocks(hwmgr, &clocks) == 0,
+ "Attempt to get soc clk levels Failed!",
+ return -1);
+ for (i = 0; i < clocks.num_levels; i++)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ i, clocks.data[i].clocks_in_khz / 1000,
+ (clocks.data[i].clocks_in_khz / 1000 == now) ? "*" : "");
+ break;
+
+ case PP_DCEFCLK:
+ PP_ASSERT_WITH_CODE(
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_GetDpmClockFreq, (PPCLK_DCEFCLK << 16),
+ &now) == 0,
+ "Attempt to get Current DCEFCLK Frequency Failed!",
+ return -EINVAL);
+
+ PP_ASSERT_WITH_CODE(
+ vega12_get_dcefclocks(hwmgr, &clocks) == 0,
+ "Attempt to get dcef clk levels Failed!",
+ return -1);
+ for (i = 0; i < clocks.num_levels; i++)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ i, clocks.data[i].clocks_in_khz / 1000,
+ (clocks.data[i].clocks_in_khz / 1000 == now) ? "*" : "");
+ break;
+
+ case PP_PCIE:
+ break;
+
+ default:
+ break;
+ }
+ return size;
+}
+
+static int vega12_apply_clocks_adjust_rules(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ struct vega12_single_dpm_table *dpm_table;
+ bool vblank_too_short = false;
+ bool disable_mclk_switching;
+ uint32_t i, latency;
+
+ disable_mclk_switching = ((1 < hwmgr->display_config->num_display) &&
+ !hwmgr->display_config->multi_monitor_in_sync) ||
+ vblank_too_short;
+ latency = hwmgr->display_config->dce_tolerable_mclk_in_active_latency;
+
+ /* gfxclk */
+ dpm_table = &(data->dpm_table.gfx_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+
+ if (PP_CAP(PHM_PlatformCaps_UMDPState)) {
+ if (VEGA12_UMD_PSTATE_GFXCLK_LEVEL < dpm_table->count) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[VEGA12_UMD_PSTATE_GFXCLK_LEVEL].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[VEGA12_UMD_PSTATE_GFXCLK_LEVEL].value;
+ }
+
+ if (hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[0].value;
+ }
+
+ if (hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ }
+ }
+
+ /* memclk */
+ dpm_table = &(data->dpm_table.mem_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+
+ if (PP_CAP(PHM_PlatformCaps_UMDPState)) {
+ if (VEGA12_UMD_PSTATE_MCLK_LEVEL < dpm_table->count) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[VEGA12_UMD_PSTATE_MCLK_LEVEL].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[VEGA12_UMD_PSTATE_MCLK_LEVEL].value;
+ }
+
+ if (hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[0].value;
+ }
+
+ if (hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ }
+ }
+
+ /* honour DAL's UCLK Hardmin */
+ if (dpm_table->dpm_state.hard_min_level < (hwmgr->display_config->min_mem_set_clock / 100))
+ dpm_table->dpm_state.hard_min_level = hwmgr->display_config->min_mem_set_clock / 100;
+
+ /* Hardmin is dependent on displayconfig */
+ if (disable_mclk_switching) {
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ for (i = 0; i < data->mclk_latency_table.count - 1; i++) {
+ if (data->mclk_latency_table.entries[i].latency <= latency) {
+ if (dpm_table->dpm_levels[i].value >= (hwmgr->display_config->min_mem_set_clock / 100)) {
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[i].value;
+ break;
+ }
+ }
+ }
+ }
+
+ if (hwmgr->display_config->nb_pstate_switch_disable)
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+
+ /* vclk */
+ dpm_table = &(data->dpm_table.vclk_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+
+ if (PP_CAP(PHM_PlatformCaps_UMDPState)) {
+ if (VEGA12_UMD_PSTATE_UVDCLK_LEVEL < dpm_table->count) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[VEGA12_UMD_PSTATE_UVDCLK_LEVEL].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[VEGA12_UMD_PSTATE_UVDCLK_LEVEL].value;
+ }
+
+ if (hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ }
+ }
+
+ /* dclk */
+ dpm_table = &(data->dpm_table.dclk_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+
+ if (PP_CAP(PHM_PlatformCaps_UMDPState)) {
+ if (VEGA12_UMD_PSTATE_UVDCLK_LEVEL < dpm_table->count) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[VEGA12_UMD_PSTATE_UVDCLK_LEVEL].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[VEGA12_UMD_PSTATE_UVDCLK_LEVEL].value;
+ }
+
+ if (hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ }
+ }
+
+ /* socclk */
+ dpm_table = &(data->dpm_table.soc_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+
+ if (PP_CAP(PHM_PlatformCaps_UMDPState)) {
+ if (VEGA12_UMD_PSTATE_SOCCLK_LEVEL < dpm_table->count) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[VEGA12_UMD_PSTATE_SOCCLK_LEVEL].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[VEGA12_UMD_PSTATE_SOCCLK_LEVEL].value;
+ }
+
+ if (hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ }
+ }
+
+ /* eclk */
+ dpm_table = &(data->dpm_table.eclk_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+
+ if (PP_CAP(PHM_PlatformCaps_UMDPState)) {
+ if (VEGA12_UMD_PSTATE_VCEMCLK_LEVEL < dpm_table->count) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[VEGA12_UMD_PSTATE_VCEMCLK_LEVEL].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[VEGA12_UMD_PSTATE_VCEMCLK_LEVEL].value;
+ }
+
+ if (hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ }
+ }
+
+ return 0;
+}
+
+static int vega12_set_uclk_to_highest_dpm_level(struct pp_hwmgr *hwmgr,
+ struct vega12_single_dpm_table *dpm_table)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ int ret = 0;
+
+ if (data->smu_features[GNLD_DPM_UCLK].enabled) {
+ PP_ASSERT_WITH_CODE(dpm_table->count > 0,
+ "[SetUclkToHightestDpmLevel] Dpm table has no entry!",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(dpm_table->count <= NUM_UCLK_DPM_LEVELS,
+ "[SetUclkToHightestDpmLevel] Dpm table has too many entries!",
+ return -EINVAL);
+
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinByFreq,
+ (PPCLK_UCLK << 16 ) | dpm_table->dpm_state.hard_min_level,
+ NULL)),
+ "[SetUclkToHightestDpmLevel] Set hard min uclk failed!",
+ return ret);
+ }
+
+ return ret;
+}
+
+static int vega12_pre_display_configuration_changed_task(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ int ret = 0;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_NumOfDisplays, 0,
+ NULL);
+
+ ret = vega12_set_uclk_to_highest_dpm_level(hwmgr,
+ &data->dpm_table.mem_table);
+
+ return ret;
+}
+
+static int vega12_display_configuration_changed_task(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ int result = 0;
+ Watermarks_t *wm_table = &(data->smc_state_table.water_marks_table);
+
+ if ((data->water_marks_bitmap & WaterMarksExist) &&
+ !(data->water_marks_bitmap & WaterMarksLoaded)) {
+ result = smum_smc_table_manager(hwmgr,
+ (uint8_t *)wm_table, TABLE_WATERMARKS, false);
+ PP_ASSERT_WITH_CODE(result, "Failed to update WMTABLE!", return -EINVAL);
+ data->water_marks_bitmap |= WaterMarksLoaded;
+ }
+
+ if ((data->water_marks_bitmap & WaterMarksExist) &&
+ data->smu_features[GNLD_DPM_DCEFCLK].supported &&
+ data->smu_features[GNLD_DPM_SOCCLK].supported)
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_NumOfDisplays, hwmgr->display_config->num_display,
+ NULL);
+
+ return result;
+}
+
+static int vega12_enable_disable_uvd_dpm(struct pp_hwmgr *hwmgr, bool enable)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+
+ if (data->smu_features[GNLD_DPM_UVD].supported) {
+ PP_ASSERT_WITH_CODE(!vega12_enable_smc_features(hwmgr,
+ enable,
+ data->smu_features[GNLD_DPM_UVD].smu_feature_bitmap),
+ "Attempt to Enable/Disable DPM UVD Failed!",
+ return -1);
+ data->smu_features[GNLD_DPM_UVD].enabled = enable;
+ }
+
+ return 0;
+}
+
+static void vega12_power_gate_vce(struct pp_hwmgr *hwmgr, bool bgate)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+
+ if (data->vce_power_gated == bgate)
+ return;
+
+ data->vce_power_gated = bgate;
+ vega12_enable_disable_vce_dpm(hwmgr, !bgate);
+}
+
+static void vega12_power_gate_uvd(struct pp_hwmgr *hwmgr, bool bgate)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+
+ if (data->uvd_power_gated == bgate)
+ return;
+
+ data->uvd_power_gated = bgate;
+ vega12_enable_disable_uvd_dpm(hwmgr, !bgate);
+}
+
+static bool
+vega12_check_smc_update_required_for_display_configuration(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ bool is_update_required = false;
+
+ if (data->display_timing.num_existing_displays != hwmgr->display_config->num_display)
+ is_update_required = true;
+
+ if (data->registry_data.gfx_clk_deep_sleep_support) {
+ if (data->display_timing.min_clock_in_sr != hwmgr->display_config->min_core_set_clock_in_sr)
+ is_update_required = true;
+ }
+
+ return is_update_required;
+}
+
+static int vega12_disable_dpm_tasks(struct pp_hwmgr *hwmgr)
+{
+ int tmp_result, result = 0;
+
+ tmp_result = vega12_disable_all_smu_features(hwmgr);
+ PP_ASSERT_WITH_CODE((tmp_result == 0),
+ "Failed to disable all smu features!", result = tmp_result);
+
+ return result;
+}
+
+static int vega12_power_off_asic(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ int result;
+
+ result = vega12_disable_dpm_tasks(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "[disable_dpm_tasks] Failed to disable DPM!",
+ );
+ data->water_marks_bitmap &= ~(WaterMarksLoaded);
+
+ return result;
+}
+
+#if 0
+static void vega12_find_min_clock_index(struct pp_hwmgr *hwmgr,
+ uint32_t *sclk_idx, uint32_t *mclk_idx,
+ uint32_t min_sclk, uint32_t min_mclk)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ struct vega12_dpm_table *dpm_table = &(data->dpm_table);
+ uint32_t i;
+
+ for (i = 0; i < dpm_table->gfx_table.count; i++) {
+ if (dpm_table->gfx_table.dpm_levels[i].enabled &&
+ dpm_table->gfx_table.dpm_levels[i].value >= min_sclk) {
+ *sclk_idx = i;
+ break;
+ }
+ }
+
+ for (i = 0; i < dpm_table->mem_table.count; i++) {
+ if (dpm_table->mem_table.dpm_levels[i].enabled &&
+ dpm_table->mem_table.dpm_levels[i].value >= min_mclk) {
+ *mclk_idx = i;
+ break;
+ }
+ }
+}
+#endif
+
+#if 0
+static int vega12_set_power_profile_state(struct pp_hwmgr *hwmgr,
+ struct amd_pp_profile *request)
+{
+ return 0;
+}
+
+static int vega12_get_sclk_od(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ struct vega12_single_dpm_table *sclk_table = &(data->dpm_table.gfx_table);
+ struct vega12_single_dpm_table *golden_sclk_table =
+ &(data->golden_dpm_table.gfx_table);
+ int value = sclk_table->dpm_levels[sclk_table->count - 1].value;
+ int golden_value = golden_sclk_table->dpm_levels
+ [golden_sclk_table->count - 1].value;
+
+ value -= golden_value;
+ value = DIV_ROUND_UP(value * 100, golden_value);
+
+ return value;
+}
+
+static int vega12_set_sclk_od(struct pp_hwmgr *hwmgr, uint32_t value)
+{
+ return 0;
+}
+
+static int vega12_get_mclk_od(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ struct vega12_single_dpm_table *mclk_table = &(data->dpm_table.mem_table);
+ struct vega12_single_dpm_table *golden_mclk_table =
+ &(data->golden_dpm_table.mem_table);
+ int value = mclk_table->dpm_levels[mclk_table->count - 1].value;
+ int golden_value = golden_mclk_table->dpm_levels
+ [golden_mclk_table->count - 1].value;
+
+ value -= golden_value;
+ value = DIV_ROUND_UP(value * 100, golden_value);
+
+ return value;
+}
+
+static int vega12_set_mclk_od(struct pp_hwmgr *hwmgr, uint32_t value)
+{
+ return 0;
+}
+#endif
+
+static int vega12_notify_cac_buffer_info(struct pp_hwmgr *hwmgr,
+ uint32_t virtual_addr_low,
+ uint32_t virtual_addr_hi,
+ uint32_t mc_addr_low,
+ uint32_t mc_addr_hi,
+ uint32_t size)
+{
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSystemVirtualDramAddrHigh,
+ virtual_addr_hi,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSystemVirtualDramAddrLow,
+ virtual_addr_low,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DramLogSetDramAddrHigh,
+ mc_addr_hi,
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DramLogSetDramAddrLow,
+ mc_addr_low,
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DramLogSetDramSize,
+ size,
+ NULL);
+ return 0;
+}
+
+static int vega12_get_thermal_temperature_range(struct pp_hwmgr *hwmgr,
+ struct PP_TemperatureRange *thermal_data)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+
+ memcpy(thermal_data, &SMU7ThermalWithDelayPolicy[0], sizeof(struct PP_TemperatureRange));
+
+ thermal_data->max = pp_table->TedgeLimit *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ thermal_data->edge_emergency_max = (pp_table->TedgeLimit + CTF_OFFSET_EDGE) *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ thermal_data->hotspot_crit_max = pp_table->ThotspotLimit *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ thermal_data->hotspot_emergency_max = (pp_table->ThotspotLimit + CTF_OFFSET_HOTSPOT) *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ thermal_data->mem_crit_max = pp_table->ThbmLimit *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ thermal_data->mem_emergency_max = (pp_table->ThbmLimit + CTF_OFFSET_HBM)*
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+ return 0;
+}
+
+static int vega12_enable_gfx_off(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+ int ret = 0;
+
+ if (data->gfxoff_controlled_by_driver)
+ ret = smum_send_msg_to_smc(hwmgr, PPSMC_MSG_AllowGfxOff, NULL);
+
+ return ret;
+}
+
+static int vega12_disable_gfx_off(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+ int ret = 0;
+
+ if (data->gfxoff_controlled_by_driver)
+ ret = smum_send_msg_to_smc(hwmgr, PPSMC_MSG_DisallowGfxOff, NULL);
+
+ return ret;
+}
+
+static int vega12_gfx_off_control(struct pp_hwmgr *hwmgr, bool enable)
+{
+ if (enable)
+ return vega12_enable_gfx_off(hwmgr);
+ else
+ return vega12_disable_gfx_off(hwmgr);
+}
+
+static int vega12_get_performance_level(struct pp_hwmgr *hwmgr, const struct pp_hw_power_state *state,
+ PHM_PerformanceLevelDesignation designation, uint32_t index,
+ PHM_PerformanceLevel *level)
+{
+ return 0;
+}
+
+static int vega12_set_mp1_state(struct pp_hwmgr *hwmgr,
+ enum pp_mp1_state mp1_state)
+{
+ uint16_t msg;
+ int ret;
+
+ switch (mp1_state) {
+ case PP_MP1_STATE_UNLOAD:
+ msg = PPSMC_MSG_PrepareMp1ForUnload;
+ break;
+ case PP_MP1_STATE_SHUTDOWN:
+ case PP_MP1_STATE_RESET:
+ case PP_MP1_STATE_NONE:
+ default:
+ return 0;
+ }
+
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc(hwmgr, msg, NULL)) == 0,
+ "[PrepareMp1] Failed!",
+ return ret);
+
+ return 0;
+}
+
+static void vega12_init_gpu_metrics_v1_0(struct gpu_metrics_v1_0 *gpu_metrics)
+{
+ memset(gpu_metrics, 0xFF, sizeof(struct gpu_metrics_v1_0));
+
+ gpu_metrics->common_header.structure_size =
+ sizeof(struct gpu_metrics_v1_0);
+ gpu_metrics->common_header.format_revision = 1;
+ gpu_metrics->common_header.content_revision = 0;
+
+ gpu_metrics->system_clock_counter = ktime_get_boottime_ns();
+}
+
+static ssize_t vega12_get_gpu_metrics(struct pp_hwmgr *hwmgr,
+ void **table)
+{
+ struct vega12_hwmgr *data =
+ (struct vega12_hwmgr *)(hwmgr->backend);
+ struct gpu_metrics_v1_0 *gpu_metrics =
+ &data->gpu_metrics_table;
+ SmuMetrics_t metrics;
+ uint32_t fan_speed_rpm;
+ int ret;
+
+ ret = vega12_get_metrics_table(hwmgr, &metrics, true);
+ if (ret)
+ return ret;
+
+ vega12_init_gpu_metrics_v1_0(gpu_metrics);
+
+ gpu_metrics->temperature_edge = metrics.TemperatureEdge;
+ gpu_metrics->temperature_hotspot = metrics.TemperatureHotspot;
+ gpu_metrics->temperature_mem = metrics.TemperatureHBM;
+ gpu_metrics->temperature_vrgfx = metrics.TemperatureVrGfx;
+ gpu_metrics->temperature_vrmem = metrics.TemperatureVrMem;
+
+ gpu_metrics->average_gfx_activity = metrics.AverageGfxActivity;
+ gpu_metrics->average_umc_activity = metrics.AverageUclkActivity;
+
+ gpu_metrics->average_gfxclk_frequency = metrics.AverageGfxclkFrequency;
+ gpu_metrics->average_socclk_frequency = metrics.AverageSocclkFrequency;
+ gpu_metrics->average_uclk_frequency = metrics.AverageUclkFrequency;
+
+ gpu_metrics->current_gfxclk = metrics.CurrClock[PPCLK_GFXCLK];
+ gpu_metrics->current_socclk = metrics.CurrClock[PPCLK_SOCCLK];
+ gpu_metrics->current_uclk = metrics.CurrClock[PPCLK_UCLK];
+ gpu_metrics->current_vclk0 = metrics.CurrClock[PPCLK_VCLK];
+ gpu_metrics->current_dclk0 = metrics.CurrClock[PPCLK_DCLK];
+
+ gpu_metrics->throttle_status = metrics.ThrottlerStatus;
+
+ vega12_fan_ctrl_get_fan_speed_rpm(hwmgr, &fan_speed_rpm);
+ gpu_metrics->current_fan_speed = (uint16_t)fan_speed_rpm;
+
+ gpu_metrics->pcie_link_width =
+ vega12_get_current_pcie_link_width(hwmgr);
+ gpu_metrics->pcie_link_speed =
+ vega12_get_current_pcie_link_speed(hwmgr);
+
+ *table = (void *)gpu_metrics;
+
+ return sizeof(struct gpu_metrics_v1_0);
+}
+
+static const struct pp_hwmgr_func vega12_hwmgr_funcs = {
+ .backend_init = vega12_hwmgr_backend_init,
+ .backend_fini = vega12_hwmgr_backend_fini,
+ .asic_setup = vega12_setup_asic_task,
+ .dynamic_state_management_enable = vega12_enable_dpm_tasks,
+ .dynamic_state_management_disable = vega12_disable_dpm_tasks,
+ .patch_boot_state = vega12_patch_boot_state,
+ .get_sclk = vega12_dpm_get_sclk,
+ .get_mclk = vega12_dpm_get_mclk,
+ .notify_smc_display_config_after_ps_adjustment =
+ vega12_notify_smc_display_config_after_ps_adjustment,
+ .force_dpm_level = vega12_dpm_force_dpm_level,
+ .stop_thermal_controller = vega12_thermal_stop_thermal_controller,
+ .get_fan_speed_info = vega12_fan_ctrl_get_fan_speed_info,
+ .reset_fan_speed_to_default =
+ vega12_fan_ctrl_reset_fan_speed_to_default,
+ .get_fan_speed_rpm = vega12_fan_ctrl_get_fan_speed_rpm,
+ .set_fan_control_mode = vega12_set_fan_control_mode,
+ .get_fan_control_mode = vega12_get_fan_control_mode,
+ .read_sensor = vega12_read_sensor,
+ .get_dal_power_level = vega12_get_dal_power_level,
+ .get_clock_by_type_with_latency = vega12_get_clock_by_type_with_latency,
+ .get_clock_by_type_with_voltage = vega12_get_clock_by_type_with_voltage,
+ .set_watermarks_for_clocks_ranges = vega12_set_watermarks_for_clocks_ranges,
+ .display_clock_voltage_request = vega12_display_clock_voltage_request,
+ .force_clock_level = vega12_force_clock_level,
+ .print_clock_levels = vega12_print_clock_levels,
+ .apply_clocks_adjust_rules =
+ vega12_apply_clocks_adjust_rules,
+ .pre_display_config_changed =
+ vega12_pre_display_configuration_changed_task,
+ .display_config_changed = vega12_display_configuration_changed_task,
+ .powergate_uvd = vega12_power_gate_uvd,
+ .powergate_vce = vega12_power_gate_vce,
+ .check_smc_update_required_for_display_configuration =
+ vega12_check_smc_update_required_for_display_configuration,
+ .power_off_asic = vega12_power_off_asic,
+ .disable_smc_firmware_ctf = vega12_thermal_disable_alert,
+#if 0
+ .set_power_profile_state = vega12_set_power_profile_state,
+ .get_sclk_od = vega12_get_sclk_od,
+ .set_sclk_od = vega12_set_sclk_od,
+ .get_mclk_od = vega12_get_mclk_od,
+ .set_mclk_od = vega12_set_mclk_od,
+#endif
+ .notify_cac_buffer_info = vega12_notify_cac_buffer_info,
+ .get_thermal_temperature_range = vega12_get_thermal_temperature_range,
+ .register_irq_handlers = smu9_register_irq_handlers,
+ .start_thermal_controller = vega12_start_thermal_controller,
+ .powergate_gfx = vega12_gfx_off_control,
+ .get_performance_level = vega12_get_performance_level,
+ .get_asic_baco_capability = smu9_baco_get_capability,
+ .get_asic_baco_state = smu9_baco_get_state,
+ .set_asic_baco_state = vega12_baco_set_state,
+ .get_ppfeature_status = vega12_get_ppfeature_status,
+ .set_ppfeature_status = vega12_set_ppfeature_status,
+ .set_mp1_state = vega12_set_mp1_state,
+ .get_gpu_metrics = vega12_get_gpu_metrics,
+};
+
+int vega12_hwmgr_init(struct pp_hwmgr *hwmgr)
+{
+ hwmgr->hwmgr_func = &vega12_hwmgr_funcs;
+ hwmgr->pptable_func = &vega12_pptable_funcs;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.h
new file mode 100644
index 000000000000..aa63ae41942d
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.h
@@ -0,0 +1,458 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _VEGA12_HWMGR_H_
+#define _VEGA12_HWMGR_H_
+
+#include "hwmgr.h"
+#include "vega12/smu9_driver_if.h"
+#include "ppatomfwctrl.h"
+
+#define VEGA12_MAX_HARDWARE_POWERLEVELS 2
+
+#define WaterMarksExist 1
+#define WaterMarksLoaded 2
+
+#define VG12_PSUEDO_NUM_GFXCLK_DPM_LEVELS 16
+#define VG12_PSUEDO_NUM_SOCCLK_DPM_LEVELS 8
+#define VG12_PSUEDO_NUM_DCEFCLK_DPM_LEVELS 8
+#define VG12_PSUEDO_NUM_UCLK_DPM_LEVELS 4
+
+enum
+{
+ GNLD_DPM_PREFETCHER = 0,
+ GNLD_DPM_GFXCLK,
+ GNLD_DPM_UCLK,
+ GNLD_DPM_SOCCLK,
+ GNLD_DPM_UVD,
+ GNLD_DPM_VCE,
+ GNLD_ULV,
+ GNLD_DPM_MP0CLK,
+ GNLD_DPM_LINK,
+ GNLD_DPM_DCEFCLK,
+ GNLD_DS_GFXCLK,
+ GNLD_DS_SOCCLK,
+ GNLD_DS_LCLK,
+ GNLD_PPT,
+ GNLD_TDC,
+ GNLD_THERMAL,
+ GNLD_GFX_PER_CU_CG,
+ GNLD_RM,
+ GNLD_DS_DCEFCLK,
+ GNLD_ACDC,
+ GNLD_VR0HOT,
+ GNLD_VR1HOT,
+ GNLD_FW_CTF,
+ GNLD_LED_DISPLAY,
+ GNLD_FAN_CONTROL,
+ GNLD_DIDT,
+ GNLD_GFXOFF,
+ GNLD_CG,
+ GNLD_ACG,
+
+ GNLD_FEATURES_MAX
+};
+
+
+#define GNLD_DPM_MAX (GNLD_DPM_DCEFCLK + 1)
+
+#define SMC_DPM_FEATURES 0x30F
+
+struct smu_features {
+ bool supported;
+ bool enabled;
+ bool allowed;
+ uint32_t smu_feature_id;
+ uint64_t smu_feature_bitmap;
+};
+
+struct vega12_dpm_level {
+ bool enabled;
+ uint32_t value;
+ uint32_t param1;
+};
+
+#define VEGA12_MAX_DEEPSLEEP_DIVIDER_ID 5
+#define MAX_REGULAR_DPM_NUMBER 16
+#define MAX_PCIE_CONF 2
+#define VEGA12_MINIMUM_ENGINE_CLOCK 2500
+
+struct vega12_dpm_state {
+ uint32_t soft_min_level;
+ uint32_t soft_max_level;
+ uint32_t hard_min_level;
+ uint32_t hard_max_level;
+};
+
+struct vega12_single_dpm_table {
+ uint32_t count;
+ struct vega12_dpm_state dpm_state;
+ struct vega12_dpm_level dpm_levels[MAX_REGULAR_DPM_NUMBER];
+};
+
+struct vega12_odn_dpm_control {
+ uint32_t count;
+ uint32_t entries[MAX_REGULAR_DPM_NUMBER];
+};
+
+struct vega12_pcie_table {
+ uint16_t count;
+ uint8_t pcie_gen[MAX_PCIE_CONF];
+ uint8_t pcie_lane[MAX_PCIE_CONF];
+ uint32_t lclk[MAX_PCIE_CONF];
+};
+
+struct vega12_dpm_table {
+ struct vega12_single_dpm_table soc_table;
+ struct vega12_single_dpm_table gfx_table;
+ struct vega12_single_dpm_table mem_table;
+ struct vega12_single_dpm_table eclk_table;
+ struct vega12_single_dpm_table vclk_table;
+ struct vega12_single_dpm_table dclk_table;
+ struct vega12_single_dpm_table dcef_table;
+ struct vega12_single_dpm_table pixel_table;
+ struct vega12_single_dpm_table display_table;
+ struct vega12_single_dpm_table phy_table;
+ struct vega12_pcie_table pcie_table;
+};
+
+#define VEGA12_MAX_LEAKAGE_COUNT 8
+struct vega12_leakage_voltage {
+ uint16_t count;
+ uint16_t leakage_id[VEGA12_MAX_LEAKAGE_COUNT];
+ uint16_t actual_voltage[VEGA12_MAX_LEAKAGE_COUNT];
+};
+
+struct vega12_display_timing {
+ uint32_t min_clock_in_sr;
+ uint32_t num_existing_displays;
+};
+
+struct vega12_dpmlevel_enable_mask {
+ uint32_t uvd_dpm_enable_mask;
+ uint32_t vce_dpm_enable_mask;
+ uint32_t samu_dpm_enable_mask;
+ uint32_t sclk_dpm_enable_mask;
+ uint32_t mclk_dpm_enable_mask;
+};
+
+struct vega12_vbios_boot_state {
+ bool bsoc_vddc_lock;
+ uint8_t uc_cooling_id;
+ uint16_t vddc;
+ uint16_t vddci;
+ uint16_t mvddc;
+ uint16_t vdd_gfx;
+ uint32_t gfx_clock;
+ uint32_t mem_clock;
+ uint32_t soc_clock;
+ uint32_t dcef_clock;
+ uint32_t eclock;
+ uint32_t dclock;
+ uint32_t vclock;
+};
+
+#define DPMTABLE_OD_UPDATE_SCLK 0x00000001
+#define DPMTABLE_OD_UPDATE_MCLK 0x00000002
+#define DPMTABLE_UPDATE_SCLK 0x00000004
+#define DPMTABLE_UPDATE_MCLK 0x00000008
+#define DPMTABLE_OD_UPDATE_VDDC 0x00000010
+
+struct vega12_smc_state_table {
+ uint32_t soc_boot_level;
+ uint32_t gfx_boot_level;
+ uint32_t dcef_boot_level;
+ uint32_t mem_boot_level;
+ uint32_t uvd_boot_level;
+ uint32_t vce_boot_level;
+ uint32_t gfx_max_level;
+ uint32_t mem_max_level;
+ uint8_t vr_hot_gpio;
+ uint8_t ac_dc_gpio;
+ uint8_t therm_out_gpio;
+ uint8_t therm_out_polarity;
+ uint8_t therm_out_mode;
+ PPTable_t pp_table;
+ Watermarks_t water_marks_table;
+ AvfsDebugTable_t avfs_debug_table;
+ AvfsFuseOverride_t avfs_fuse_override_table;
+ SmuMetrics_t smu_metrics;
+ DriverSmuConfig_t driver_smu_config;
+ DpmActivityMonitorCoeffInt_t dpm_activity_monitor_coeffint;
+ OverDriveTable_t overdrive_table;
+};
+
+struct vega12_mclk_latency_entries {
+ uint32_t frequency;
+ uint32_t latency;
+};
+
+struct vega12_mclk_latency_table {
+ uint32_t count;
+ struct vega12_mclk_latency_entries entries[MAX_REGULAR_DPM_NUMBER];
+};
+
+struct vega12_registry_data {
+ uint64_t disallowed_features;
+ uint8_t ac_dc_switch_gpio_support;
+ uint8_t acg_loop_support;
+ uint8_t clock_stretcher_support;
+ uint8_t db_ramping_support;
+ uint8_t didt_mode;
+ uint8_t didt_support;
+ uint8_t edc_didt_support;
+ uint8_t force_dpm_high;
+ uint8_t fuzzy_fan_control_support;
+ uint8_t mclk_dpm_key_disabled;
+ uint8_t od_state_in_dc_support;
+ uint8_t pcie_lane_override;
+ uint8_t pcie_speed_override;
+ uint32_t pcie_clock_override;
+ uint8_t pcie_dpm_key_disabled;
+ uint8_t dcefclk_dpm_key_disabled;
+ uint8_t prefetcher_dpm_key_disabled;
+ uint8_t quick_transition_support;
+ uint8_t regulator_hot_gpio_support;
+ uint8_t master_deep_sleep_support;
+ uint8_t gfx_clk_deep_sleep_support;
+ uint8_t sclk_deep_sleep_support;
+ uint8_t lclk_deep_sleep_support;
+ uint8_t dce_fclk_deep_sleep_support;
+ uint8_t sclk_dpm_key_disabled;
+ uint8_t sclk_throttle_low_notification;
+ uint8_t skip_baco_hardware;
+ uint8_t socclk_dpm_key_disabled;
+ uint8_t sq_ramping_support;
+ uint8_t tcp_ramping_support;
+ uint8_t td_ramping_support;
+ uint8_t dbr_ramping_support;
+ uint8_t gc_didt_support;
+ uint8_t psm_didt_support;
+ uint8_t thermal_support;
+ uint8_t fw_ctf_enabled;
+ uint8_t led_dpm_enabled;
+ uint8_t fan_control_support;
+ uint8_t ulv_support;
+ uint8_t odn_feature_enable;
+ uint8_t disable_water_mark;
+ uint8_t disable_workload_policy;
+ uint32_t force_workload_policy_mask;
+ uint8_t disable_3d_fs_detection;
+ uint8_t disable_pp_tuning;
+ uint8_t disable_xlpp_tuning;
+ uint32_t perf_ui_tuning_profile_turbo;
+ uint32_t perf_ui_tuning_profile_powerSave;
+ uint32_t perf_ui_tuning_profile_xl;
+ uint16_t zrpm_stop_temp;
+ uint16_t zrpm_start_temp;
+ uint32_t stable_pstate_sclk_dpm_percentage;
+ uint8_t fps_support;
+ uint8_t vr0hot;
+ uint8_t vr1hot;
+ uint8_t disable_auto_wattman;
+ uint32_t auto_wattman_debug;
+ uint32_t auto_wattman_sample_period;
+ uint8_t auto_wattman_threshold;
+ uint8_t log_avfs_param;
+ uint8_t enable_enginess;
+ uint8_t custom_fan_support;
+ uint8_t disable_pcc_limit_control;
+};
+
+struct vega12_odn_clock_voltage_dependency_table {
+ uint32_t count;
+ struct phm_ppt_v1_clock_voltage_dependency_record
+ entries[MAX_REGULAR_DPM_NUMBER];
+};
+
+struct vega12_odn_dpm_table {
+ struct vega12_odn_dpm_control control_gfxclk_state;
+ struct vega12_odn_dpm_control control_memclk_state;
+ struct phm_odn_clock_levels odn_core_clock_dpm_levels;
+ struct phm_odn_clock_levels odn_memory_clock_dpm_levels;
+ struct vega12_odn_clock_voltage_dependency_table vdd_dependency_on_sclk;
+ struct vega12_odn_clock_voltage_dependency_table vdd_dependency_on_mclk;
+ struct vega12_odn_clock_voltage_dependency_table vdd_dependency_on_socclk;
+ uint32_t odn_mclk_min_limit;
+};
+
+struct vega12_odn_fan_table {
+ uint32_t target_fan_speed;
+ uint32_t target_temperature;
+ uint32_t min_performance_clock;
+ uint32_t min_fan_limit;
+ bool force_fan_pwm;
+};
+
+struct vega12_clock_range {
+ uint32_t ACMax;
+ uint32_t ACMin;
+ uint32_t DCMax;
+};
+
+struct vega12_hwmgr {
+ struct vega12_dpm_table dpm_table;
+ struct vega12_dpm_table golden_dpm_table;
+ struct vega12_registry_data registry_data;
+ struct vega12_vbios_boot_state vbios_boot_state;
+ struct vega12_mclk_latency_table mclk_latency_table;
+
+ struct vega12_leakage_voltage vddc_leakage;
+
+ uint32_t vddc_control;
+ struct pp_atomfwctrl_voltage_table vddc_voltage_table;
+ uint32_t mvdd_control;
+ struct pp_atomfwctrl_voltage_table mvdd_voltage_table;
+ uint32_t vddci_control;
+ struct pp_atomfwctrl_voltage_table vddci_voltage_table;
+
+ uint32_t active_auto_throttle_sources;
+ uint32_t water_marks_bitmap;
+
+ struct vega12_odn_dpm_table odn_dpm_table;
+ struct vega12_odn_fan_table odn_fan_table;
+
+ /* ---- General data ---- */
+ uint8_t need_update_dpm_table;
+
+ bool cac_enabled;
+ bool battery_state;
+ bool is_tlu_enabled;
+ bool avfs_exist;
+
+ uint32_t low_sclk_interrupt_threshold;
+
+ uint32_t total_active_cus;
+
+ struct vega12_display_timing display_timing;
+
+ /* ---- Vega12 Dyn Register Settings ---- */
+
+ uint32_t debug_settings;
+ uint32_t lowest_uclk_reserved_for_ulv;
+ uint32_t gfxclk_average_alpha;
+ uint32_t socclk_average_alpha;
+ uint32_t uclk_average_alpha;
+ uint32_t gfx_activity_average_alpha;
+ uint32_t display_voltage_mode;
+ uint32_t dcef_clk_quad_eqn_a;
+ uint32_t dcef_clk_quad_eqn_b;
+ uint32_t dcef_clk_quad_eqn_c;
+ uint32_t disp_clk_quad_eqn_a;
+ uint32_t disp_clk_quad_eqn_b;
+ uint32_t disp_clk_quad_eqn_c;
+ uint32_t pixel_clk_quad_eqn_a;
+ uint32_t pixel_clk_quad_eqn_b;
+ uint32_t pixel_clk_quad_eqn_c;
+ uint32_t phy_clk_quad_eqn_a;
+ uint32_t phy_clk_quad_eqn_b;
+ uint32_t phy_clk_quad_eqn_c;
+
+ /* ---- Thermal Temperature Setting ---- */
+ struct vega12_dpmlevel_enable_mask dpm_level_enable_mask;
+
+ /* ---- Power Gating States ---- */
+ bool uvd_power_gated;
+ bool vce_power_gated;
+ bool samu_power_gated;
+ bool need_long_memory_training;
+
+ /* Internal settings to apply the application power optimization parameters */
+ bool apply_optimized_settings;
+ uint32_t disable_dpm_mask;
+
+ /* ---- Overdrive next setting ---- */
+ uint32_t apply_overdrive_next_settings_mask;
+
+ /* ---- Workload Mask ---- */
+ uint32_t workload_mask;
+
+ /* ---- SMU9 ---- */
+ uint32_t smu_version;
+ struct smu_features smu_features[GNLD_FEATURES_MAX];
+ struct vega12_smc_state_table smc_state_table;
+
+ struct vega12_clock_range clk_range[PPCLK_COUNT];
+
+ /* ---- Gfxoff ---- */
+ bool gfxoff_controlled_by_driver;
+
+ unsigned long metrics_time;
+ SmuMetrics_t metrics_table;
+ struct gpu_metrics_v1_0 gpu_metrics_table;
+};
+
+#define VEGA12_DPM2_NEAR_TDP_DEC 10
+#define VEGA12_DPM2_ABOVE_SAFE_INC 5
+#define VEGA12_DPM2_BELOW_SAFE_INC 20
+
+#define VEGA12_DPM2_LTA_WINDOW_SIZE 7
+
+#define VEGA12_DPM2_LTS_TRUNCATE 0
+
+#define VEGA12_DPM2_TDP_SAFE_LIMIT_PERCENT 80
+
+#define VEGA12_DPM2_MAXPS_PERCENT_M 90
+#define VEGA12_DPM2_MAXPS_PERCENT_H 90
+
+#define VEGA12_DPM2_PWREFFICIENCYRATIO_MARGIN 50
+
+#define VEGA12_DPM2_SQ_RAMP_MAX_POWER 0x3FFF
+#define VEGA12_DPM2_SQ_RAMP_MIN_POWER 0x12
+#define VEGA12_DPM2_SQ_RAMP_MAX_POWER_DELTA 0x15
+#define VEGA12_DPM2_SQ_RAMP_SHORT_TERM_INTERVAL_SIZE 0x1E
+#define VEGA12_DPM2_SQ_RAMP_LONG_TERM_INTERVAL_RATIO 0xF
+
+#define VEGA12_VOLTAGE_CONTROL_NONE 0x0
+#define VEGA12_VOLTAGE_CONTROL_BY_GPIO 0x1
+#define VEGA12_VOLTAGE_CONTROL_BY_SVID2 0x2
+#define VEGA12_VOLTAGE_CONTROL_MERGED 0x3
+/* To convert to Q8.8 format for firmware */
+#define VEGA12_Q88_FORMAT_CONVERSION_UNIT 256
+
+#define VEGA12_UNUSED_GPIO_PIN 0x7F
+
+#define VEGA12_THERM_OUT_MODE_DISABLE 0x0
+#define VEGA12_THERM_OUT_MODE_THERM_ONLY 0x1
+#define VEGA12_THERM_OUT_MODE_THERM_VRHOT 0x2
+
+#define PPVEGA12_VEGA12DISPLAYVOLTAGEMODE_DFLT 0xffffffff
+#define PPREGKEY_VEGA12QUADRATICEQUATION_DFLT 0xffffffff
+
+#define PPVEGA12_VEGA12GFXCLKAVERAGEALPHA_DFLT 25 /* 10% * 255 = 25 */
+#define PPVEGA12_VEGA12SOCCLKAVERAGEALPHA_DFLT 25 /* 10% * 255 = 25 */
+#define PPVEGA12_VEGA12UCLKCLKAVERAGEALPHA_DFLT 25 /* 10% * 255 = 25 */
+#define PPVEGA12_VEGA12GFXACTIVITYAVERAGEALPHA_DFLT 25 /* 10% * 255 = 25 */
+#define PPVEGA12_VEGA12LOWESTUCLKRESERVEDFORULV_DFLT 0xffffffff
+#define PPVEGA12_VEGA12DISPLAYVOLTAGEMODE_DFLT 0xffffffff
+#define PPREGKEY_VEGA12QUADRATICEQUATION_DFLT 0xffffffff
+
+#define VEGA12_UMD_PSTATE_GFXCLK_LEVEL 0x3
+#define VEGA12_UMD_PSTATE_SOCCLK_LEVEL 0x3
+#define VEGA12_UMD_PSTATE_MCLK_LEVEL 0x2
+#define VEGA12_UMD_PSTATE_UVDCLK_LEVEL 0x3
+#define VEGA12_UMD_PSTATE_VCEMCLK_LEVEL 0x3
+
+int vega12_enable_disable_vce_dpm(struct pp_hwmgr *hwmgr, bool enable);
+
+#endif /* _VEGA12_HWMGR_H_ */
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_inc.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_inc.h
new file mode 100644
index 000000000000..0d08c57d3bca
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_inc.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 VEGA12_INC_H
+#define VEGA12_INC_H
+
+#include "asic_reg/thm/thm_9_0_default.h"
+#include "asic_reg/thm/thm_9_0_offset.h"
+#include "asic_reg/thm/thm_9_0_sh_mask.h"
+
+#include "asic_reg/mp/mp_9_0_offset.h"
+#include "asic_reg/mp/mp_9_0_sh_mask.h"
+
+#include "asic_reg/gc/gc_9_2_1_offset.h"
+#include "asic_reg/gc/gc_9_2_1_sh_mask.h"
+
+#include "asic_reg/nbio/nbio_6_1_offset.h"
+#include "asic_reg/nbio/nbio_6_1_sh_mask.h"
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_pptable.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_pptable.h
new file mode 100644
index 000000000000..bf4f5095b80d
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_pptable.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _VEGA12_PPTABLE_H_
+#define _VEGA12_PPTABLE_H_
+
+#pragma pack(push, 1)
+
+#define ATOM_VEGA12_PP_THERMALCONTROLLER_NONE 0
+#define ATOM_VEGA12_PP_THERMALCONTROLLER_VEGA12 25
+
+#define ATOM_VEGA12_PP_PLATFORM_CAP_POWERPLAY 0x1
+#define ATOM_VEGA12_PP_PLATFORM_CAP_SBIOSPOWERSOURCE 0x2
+#define ATOM_VEGA12_PP_PLATFORM_CAP_HARDWAREDC 0x4
+#define ATOM_VEGA12_PP_PLATFORM_CAP_BACO 0x8
+#define ATOM_VEGA12_PP_PLATFORM_CAP_BAMACO 0x10
+#define ATOM_VEGA12_PP_PLATFORM_CAP_ENABLESHADOWPSTATE 0x20
+
+#define ATOM_VEGA12_TABLE_REVISION_VEGA12 9
+
+enum ATOM_VEGA12_ODSETTING_ID {
+ ATOM_VEGA12_ODSETTING_GFXCLKFMAX = 0,
+ ATOM_VEGA12_ODSETTING_GFXCLKFMIN,
+ ATOM_VEGA12_ODSETTING_VDDGFXCURVEFREQ_P1,
+ ATOM_VEGA12_ODSETTING_VDDGFXCURVEVOLTAGEOFFSET_P1,
+ ATOM_VEGA12_ODSETTING_VDDGFXCURVEFREQ_P2,
+ ATOM_VEGA12_ODSETTING_VDDGFXCURVEVOLTAGEOFFSET_P2,
+ ATOM_VEGA12_ODSETTING_VDDGFXCURVEFREQ_P3,
+ ATOM_VEGA12_ODSETTING_VDDGFXCURVEVOLTAGEOFFSET_P3,
+ ATOM_VEGA12_ODSETTING_UCLKFMAX,
+ ATOM_VEGA12_ODSETTING_POWERPERCENTAGE,
+ ATOM_VEGA12_ODSETTING_FANRPMMIN,
+ ATOM_VEGA12_ODSETTING_FANRPMACOUSTICLIMIT,
+ ATOM_VEGA12_ODSETTING_FANTARGETTEMPERATURE,
+ ATOM_VEGA12_ODSETTING_OPERATINGTEMPMAX,
+ ATOM_VEGA12_ODSETTING_COUNT,
+};
+typedef enum ATOM_VEGA12_ODSETTING_ID ATOM_VEGA12_ODSETTING_ID;
+
+enum ATOM_VEGA12_PPCLOCK_ID {
+ ATOM_VEGA12_PPCLOCK_GFXCLK = 0,
+ ATOM_VEGA12_PPCLOCK_VCLK,
+ ATOM_VEGA12_PPCLOCK_DCLK,
+ ATOM_VEGA12_PPCLOCK_ECLK,
+ ATOM_VEGA12_PPCLOCK_SOCCLK,
+ ATOM_VEGA12_PPCLOCK_UCLK,
+ ATOM_VEGA12_PPCLOCK_DCEFCLK,
+ ATOM_VEGA12_PPCLOCK_DISPCLK,
+ ATOM_VEGA12_PPCLOCK_PIXCLK,
+ ATOM_VEGA12_PPCLOCK_PHYCLK,
+ ATOM_VEGA12_PPCLOCK_COUNT,
+};
+typedef enum ATOM_VEGA12_PPCLOCK_ID ATOM_VEGA12_PPCLOCK_ID;
+
+
+typedef struct _ATOM_VEGA12_POWERPLAYTABLE
+{
+ struct atom_common_table_header sHeader;
+ UCHAR ucTableRevision;
+ USHORT usTableSize;
+ ULONG ulGoldenPPID;
+ ULONG ulGoldenRevision;
+ USHORT usFormatID;
+
+ ULONG ulPlatformCaps;
+
+ UCHAR ucThermalControllerType;
+
+ USHORT usSmallPowerLimit1;
+ USHORT usSmallPowerLimit2;
+ USHORT usBoostPowerLimit;
+ USHORT usODTurboPowerLimit;
+ USHORT usODPowerSavePowerLimit;
+ USHORT usSoftwareShutdownTemp;
+
+ ULONG PowerSavingClockMax [ATOM_VEGA12_PPCLOCK_COUNT];
+ ULONG PowerSavingClockMin [ATOM_VEGA12_PPCLOCK_COUNT];
+
+ ULONG ODSettingsMax [ATOM_VEGA12_ODSETTING_COUNT];
+ ULONG ODSettingsMin [ATOM_VEGA12_ODSETTING_COUNT];
+
+ USHORT usReserve[5];
+
+ PPTable_t smcPPTable;
+
+} ATOM_Vega12_POWERPLAYTABLE;
+
+#pragma pack(pop)
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c
new file mode 100644
index 000000000000..740e2fc7a034
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <linux/module.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+
+#include "vega12/smu9_driver_if.h"
+#include "vega12_processpptables.h"
+#include "ppatomfwctrl.h"
+#include "atomfirmware.h"
+#include "pp_debug.h"
+#include "cgs_common.h"
+#include "vega12_pptable.h"
+
+static void set_hw_cap(struct pp_hwmgr *hwmgr, bool enable,
+ enum phm_platform_caps cap)
+{
+ if (enable)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, cap);
+ else
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps, cap);
+}
+
+static const void *get_powerplay_table(struct pp_hwmgr *hwmgr)
+{
+ int index = GetIndexIntoMasterDataTable(powerplayinfo);
+
+ u16 size;
+ u8 frev, crev;
+ const void *table_address = hwmgr->soft_pp_table;
+
+ if (!table_address) {
+ table_address = (ATOM_Vega12_POWERPLAYTABLE *)
+ smu_atom_get_data_table(hwmgr->adev, index,
+ &size, &frev, &crev);
+
+ hwmgr->soft_pp_table = table_address; /*Cache the result in RAM.*/
+ hwmgr->soft_pp_table_size = size;
+ }
+
+ return table_address;
+}
+
+static int check_powerplay_tables(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_Vega12_POWERPLAYTABLE *powerplay_table)
+{
+ PP_ASSERT_WITH_CODE((powerplay_table->sHeader.format_revision >=
+ ATOM_VEGA12_TABLE_REVISION_VEGA12),
+ "Unsupported PPTable format!", return -1);
+ PP_ASSERT_WITH_CODE(powerplay_table->sHeader.structuresize > 0,
+ "Invalid PowerPlay Table!", return -1);
+
+ return 0;
+}
+
+static int set_platform_caps(struct pp_hwmgr *hwmgr, uint32_t powerplay_caps)
+{
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_VEGA12_PP_PLATFORM_CAP_POWERPLAY),
+ PHM_PlatformCaps_PowerPlaySupport);
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_VEGA12_PP_PLATFORM_CAP_SBIOSPOWERSOURCE),
+ PHM_PlatformCaps_BiosPowerSourceControl);
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_VEGA12_PP_PLATFORM_CAP_BACO),
+ PHM_PlatformCaps_BACO);
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_VEGA12_PP_PLATFORM_CAP_BAMACO),
+ PHM_PlatformCaps_BAMACO);
+
+ return 0;
+}
+
+static int append_vbios_pptable(struct pp_hwmgr *hwmgr, PPTable_t *ppsmc_pptable)
+{
+ struct pp_atomfwctrl_smc_dpm_parameters smc_dpm_table;
+
+ PP_ASSERT_WITH_CODE(
+ pp_atomfwctrl_get_smc_dpm_information(hwmgr, &smc_dpm_table) == 0,
+ "[appendVbiosPPTable] Failed to retrieve Smc Dpm Table from VBIOS!",
+ return -1);
+
+ ppsmc_pptable->Liquid1_I2C_address = smc_dpm_table.liquid1_i2c_address;
+ ppsmc_pptable->Liquid2_I2C_address = smc_dpm_table.liquid2_i2c_address;
+ ppsmc_pptable->Vr_I2C_address = smc_dpm_table.vr_i2c_address;
+ ppsmc_pptable->Plx_I2C_address = smc_dpm_table.plx_i2c_address;
+
+ ppsmc_pptable->Liquid_I2C_LineSCL = smc_dpm_table.liquid_i2c_linescl;
+ ppsmc_pptable->Liquid_I2C_LineSDA = smc_dpm_table.liquid_i2c_linesda;
+ ppsmc_pptable->Vr_I2C_LineSCL = smc_dpm_table.vr_i2c_linescl;
+ ppsmc_pptable->Vr_I2C_LineSDA = smc_dpm_table.vr_i2c_linesda;
+
+ ppsmc_pptable->Plx_I2C_LineSCL = smc_dpm_table.plx_i2c_linescl;
+ ppsmc_pptable->Plx_I2C_LineSDA = smc_dpm_table.plx_i2c_linesda;
+ ppsmc_pptable->VrSensorPresent = smc_dpm_table.vrsensorpresent;
+ ppsmc_pptable->LiquidSensorPresent = smc_dpm_table.liquidsensorpresent;
+
+ ppsmc_pptable->MaxVoltageStepGfx = smc_dpm_table.maxvoltagestepgfx;
+ ppsmc_pptable->MaxVoltageStepSoc = smc_dpm_table.maxvoltagestepsoc;
+
+ ppsmc_pptable->VddGfxVrMapping = smc_dpm_table.vddgfxvrmapping;
+ ppsmc_pptable->VddSocVrMapping = smc_dpm_table.vddsocvrmapping;
+ ppsmc_pptable->VddMem0VrMapping = smc_dpm_table.vddmem0vrmapping;
+ ppsmc_pptable->VddMem1VrMapping = smc_dpm_table.vddmem1vrmapping;
+
+ ppsmc_pptable->GfxUlvPhaseSheddingMask = smc_dpm_table.gfxulvphasesheddingmask;
+ ppsmc_pptable->SocUlvPhaseSheddingMask = smc_dpm_table.soculvphasesheddingmask;
+
+ ppsmc_pptable->GfxMaxCurrent = smc_dpm_table.gfxmaxcurrent;
+ ppsmc_pptable->GfxOffset = smc_dpm_table.gfxoffset;
+ ppsmc_pptable->Padding_TelemetryGfx = smc_dpm_table.padding_telemetrygfx;
+
+ ppsmc_pptable->SocMaxCurrent = smc_dpm_table.socmaxcurrent;
+ ppsmc_pptable->SocOffset = smc_dpm_table.socoffset;
+ ppsmc_pptable->Padding_TelemetrySoc = smc_dpm_table.padding_telemetrysoc;
+
+ ppsmc_pptable->Mem0MaxCurrent = smc_dpm_table.mem0maxcurrent;
+ ppsmc_pptable->Mem0Offset = smc_dpm_table.mem0offset;
+ ppsmc_pptable->Padding_TelemetryMem0 = smc_dpm_table.padding_telemetrymem0;
+
+ ppsmc_pptable->Mem1MaxCurrent = smc_dpm_table.mem1maxcurrent;
+ ppsmc_pptable->Mem1Offset = smc_dpm_table.mem1offset;
+ ppsmc_pptable->Padding_TelemetryMem1 = smc_dpm_table.padding_telemetrymem1;
+
+ ppsmc_pptable->AcDcGpio = smc_dpm_table.acdcgpio;
+ ppsmc_pptable->AcDcPolarity = smc_dpm_table.acdcpolarity;
+ ppsmc_pptable->VR0HotGpio = smc_dpm_table.vr0hotgpio;
+ ppsmc_pptable->VR0HotPolarity = smc_dpm_table.vr0hotpolarity;
+
+ ppsmc_pptable->VR1HotGpio = smc_dpm_table.vr1hotgpio;
+ ppsmc_pptable->VR1HotPolarity = smc_dpm_table.vr1hotpolarity;
+ ppsmc_pptable->Padding1 = smc_dpm_table.padding1;
+ ppsmc_pptable->Padding2 = smc_dpm_table.padding2;
+
+ ppsmc_pptable->LedPin0 = smc_dpm_table.ledpin0;
+ ppsmc_pptable->LedPin1 = smc_dpm_table.ledpin1;
+ ppsmc_pptable->LedPin2 = smc_dpm_table.ledpin2;
+
+ ppsmc_pptable->PllGfxclkSpreadEnabled = smc_dpm_table.pllgfxclkspreadenabled;
+ ppsmc_pptable->PllGfxclkSpreadPercent = smc_dpm_table.pllgfxclkspreadpercent;
+ ppsmc_pptable->PllGfxclkSpreadFreq = smc_dpm_table.pllgfxclkspreadfreq;
+
+ ppsmc_pptable->UclkSpreadEnabled = 0;
+ ppsmc_pptable->UclkSpreadPercent = smc_dpm_table.uclkspreadpercent;
+ ppsmc_pptable->UclkSpreadFreq = smc_dpm_table.uclkspreadfreq;
+
+ ppsmc_pptable->SocclkSpreadEnabled = 0;
+ ppsmc_pptable->SocclkSpreadPercent = smc_dpm_table.socclkspreadpercent;
+ ppsmc_pptable->SocclkSpreadFreq = smc_dpm_table.socclkspreadfreq;
+
+ ppsmc_pptable->AcgGfxclkSpreadEnabled = smc_dpm_table.acggfxclkspreadenabled;
+ ppsmc_pptable->AcgGfxclkSpreadPercent = smc_dpm_table.acggfxclkspreadpercent;
+ ppsmc_pptable->AcgGfxclkSpreadFreq = smc_dpm_table.acggfxclkspreadfreq;
+
+ ppsmc_pptable->Vr2_I2C_address = smc_dpm_table.Vr2_I2C_address;
+
+ ppsmc_pptable->Vr2_I2C_address = smc_dpm_table.Vr2_I2C_address;
+
+ return 0;
+}
+
+#define VEGA12_ENGINECLOCK_HARDMAX 198000
+static int init_powerplay_table_information(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_Vega12_POWERPLAYTABLE *powerplay_table)
+{
+ struct phm_ppt_v3_information *pptable_information =
+ (struct phm_ppt_v3_information *)hwmgr->pptable;
+ uint32_t disable_power_control = 0;
+ int result;
+
+ hwmgr->thermal_controller.ucType = powerplay_table->ucThermalControllerType;
+ pptable_information->uc_thermal_controller_type = powerplay_table->ucThermalControllerType;
+
+ set_hw_cap(hwmgr,
+ ATOM_VEGA12_PP_THERMALCONTROLLER_NONE != hwmgr->thermal_controller.ucType,
+ PHM_PlatformCaps_ThermalController);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl);
+
+ if (le32_to_cpu(powerplay_table->ODSettingsMax[ATOM_VEGA12_ODSETTING_GFXCLKFMAX]) > VEGA12_ENGINECLOCK_HARDMAX)
+ hwmgr->platform_descriptor.overdriveLimit.engineClock = VEGA12_ENGINECLOCK_HARDMAX;
+ else
+ hwmgr->platform_descriptor.overdriveLimit.engineClock =
+ le32_to_cpu(powerplay_table->ODSettingsMax[ATOM_VEGA12_ODSETTING_GFXCLKFMAX]);
+ hwmgr->platform_descriptor.overdriveLimit.memoryClock =
+ le32_to_cpu(powerplay_table->ODSettingsMax[ATOM_VEGA12_ODSETTING_UCLKFMAX]);
+
+ phm_copy_overdrive_settings_limits_array(hwmgr,
+ &pptable_information->od_settings_max,
+ powerplay_table->ODSettingsMax,
+ ATOM_VEGA12_ODSETTING_COUNT);
+ phm_copy_overdrive_settings_limits_array(hwmgr,
+ &pptable_information->od_settings_min,
+ powerplay_table->ODSettingsMin,
+ ATOM_VEGA12_ODSETTING_COUNT);
+
+ /* hwmgr->platformDescriptor.minOverdriveVDDC = 0;
+ hwmgr->platformDescriptor.maxOverdriveVDDC = 0;
+ hwmgr->platformDescriptor.overdriveVDDCStep = 0; */
+
+ if (hwmgr->platform_descriptor.overdriveLimit.engineClock > 0
+ && hwmgr->platform_descriptor.overdriveLimit.memoryClock > 0)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_ACOverdriveSupport);
+
+ pptable_information->us_small_power_limit1 = le16_to_cpu(powerplay_table->usSmallPowerLimit1);
+ pptable_information->us_small_power_limit2 = le16_to_cpu(powerplay_table->usSmallPowerLimit2);
+ pptable_information->us_boost_power_limit = le16_to_cpu(powerplay_table->usBoostPowerLimit);
+ pptable_information->us_od_turbo_power_limit = le16_to_cpu(powerplay_table->usODTurboPowerLimit);
+ pptable_information->us_od_powersave_power_limit = le16_to_cpu(powerplay_table->usODPowerSavePowerLimit);
+
+ pptable_information->us_software_shutdown_temp = le16_to_cpu(powerplay_table->usSoftwareShutdownTemp);
+
+ hwmgr->platform_descriptor.TDPODLimit = le32_to_cpu(powerplay_table->ODSettingsMax[ATOM_VEGA12_ODSETTING_POWERPERCENTAGE]);
+
+ disable_power_control = 0;
+ if (!disable_power_control) {
+ /* enable TDP overdrive (PowerControl) feature as well if supported */
+ if (hwmgr->platform_descriptor.TDPODLimit)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PowerControl);
+ }
+
+ phm_copy_clock_limits_array(hwmgr, &pptable_information->power_saving_clock_max, powerplay_table->PowerSavingClockMax, ATOM_VEGA12_PPCLOCK_COUNT);
+ phm_copy_clock_limits_array(hwmgr, &pptable_information->power_saving_clock_min, powerplay_table->PowerSavingClockMin, ATOM_VEGA12_PPCLOCK_COUNT);
+
+ pptable_information->smc_pptable = kmalloc(sizeof(PPTable_t), GFP_KERNEL);
+ if (pptable_information->smc_pptable == NULL)
+ return -ENOMEM;
+
+ memcpy(pptable_information->smc_pptable, &(powerplay_table->smcPPTable), sizeof(PPTable_t));
+
+ result = append_vbios_pptable(hwmgr, (pptable_information->smc_pptable));
+
+ return result;
+}
+
+static int vega12_pp_tables_initialize(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+ const ATOM_Vega12_POWERPLAYTABLE *powerplay_table;
+
+ hwmgr->pptable = kzalloc(sizeof(struct phm_ppt_v3_information), GFP_KERNEL);
+ PP_ASSERT_WITH_CODE((hwmgr->pptable != NULL),
+ "Failed to allocate hwmgr->pptable!", return -ENOMEM);
+
+ powerplay_table = get_powerplay_table(hwmgr);
+ PP_ASSERT_WITH_CODE((powerplay_table != NULL),
+ "Missing PowerPlay Table!", return -1);
+
+ result = check_powerplay_tables(hwmgr, powerplay_table);
+ PP_ASSERT_WITH_CODE((result == 0),
+ "check_powerplay_tables failed", return result);
+
+ result = set_platform_caps(hwmgr,
+ le32_to_cpu(powerplay_table->ulPlatformCaps));
+ PP_ASSERT_WITH_CODE((result == 0),
+ "set_platform_caps failed", return result);
+
+ result = init_powerplay_table_information(hwmgr, powerplay_table);
+ PP_ASSERT_WITH_CODE((result == 0),
+ "init_powerplay_table_information failed", return result);
+
+ return result;
+}
+
+static int vega12_pp_tables_uninitialize(struct pp_hwmgr *hwmgr)
+{
+ struct phm_ppt_v3_information *pp_table_info =
+ (struct phm_ppt_v3_information *)(hwmgr->pptable);
+
+ kfree(pp_table_info->power_saving_clock_max);
+ pp_table_info->power_saving_clock_max = NULL;
+
+ kfree(pp_table_info->power_saving_clock_min);
+ pp_table_info->power_saving_clock_min = NULL;
+
+ kfree(pp_table_info->od_settings_max);
+ pp_table_info->od_settings_max = NULL;
+
+ kfree(pp_table_info->od_settings_min);
+ pp_table_info->od_settings_min = NULL;
+
+ kfree(pp_table_info->smc_pptable);
+ pp_table_info->smc_pptable = NULL;
+
+ kfree(hwmgr->pptable);
+ hwmgr->pptable = NULL;
+
+ return 0;
+}
+
+const struct pp_table_func vega12_pptable_funcs = {
+ .pptable_init = vega12_pp_tables_initialize,
+ .pptable_fini = vega12_pp_tables_uninitialize,
+};
+
+#if 0
+static uint32_t make_classification_flags(struct pp_hwmgr *hwmgr,
+ uint16_t classification, uint16_t classification2)
+{
+ uint32_t result = 0;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_BOOT)
+ result |= PP_StateClassificationFlag_Boot;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_THERMAL)
+ result |= PP_StateClassificationFlag_Thermal;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE)
+ result |= PP_StateClassificationFlag_LimitedPowerSource;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_REST)
+ result |= PP_StateClassificationFlag_Rest;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_FORCED)
+ result |= PP_StateClassificationFlag_Forced;
+
+ if (classification & ATOM_PPLIB_CLASSIFICATION_ACPI)
+ result |= PP_StateClassificationFlag_ACPI;
+
+ if (classification2 & ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2)
+ result |= PP_StateClassificationFlag_LimitedPowerSource_2;
+
+ return result;
+}
+
+int vega12_get_powerplay_table_entry(struct pp_hwmgr *hwmgr,
+ uint32_t entry_index, struct pp_power_state *power_state,
+ int (*call_back_func)(struct pp_hwmgr *, void *,
+ struct pp_power_state *, void *, uint32_t))
+{
+ int result = 0;
+ const ATOM_Vega12_State_Array *state_arrays;
+ const ATOM_Vega12_State *state_entry;
+ const ATOM_Vega12_POWERPLAYTABLE *pp_table =
+ get_powerplay_table(hwmgr);
+
+ PP_ASSERT_WITH_CODE(pp_table, "Missing PowerPlay Table!",
+ return -1;);
+ power_state->classification.bios_index = entry_index;
+
+ if (pp_table->sHeader.format_revision >=
+ ATOM_Vega12_TABLE_REVISION_VEGA12) {
+ state_arrays = (ATOM_Vega12_State_Array *)
+ (((unsigned long)pp_table) +
+ le16_to_cpu(pp_table->usStateArrayOffset));
+
+ PP_ASSERT_WITH_CODE(pp_table->usStateArrayOffset > 0,
+ "Invalid PowerPlay Table State Array Offset.",
+ return -1);
+ PP_ASSERT_WITH_CODE(state_arrays->ucNumEntries > 0,
+ "Invalid PowerPlay Table State Array.",
+ return -1);
+ PP_ASSERT_WITH_CODE((entry_index <= state_arrays->ucNumEntries),
+ "Invalid PowerPlay Table State Array Entry.",
+ return -1);
+
+ state_entry = &(state_arrays->states[entry_index]);
+
+ result = call_back_func(hwmgr, (void *)state_entry, power_state,
+ (void *)pp_table,
+ make_classification_flags(hwmgr,
+ le16_to_cpu(state_entry->usClassification),
+ le16_to_cpu(state_entry->usClassification2)));
+ }
+
+ if (!result && (power_state->classification.flags &
+ PP_StateClassificationFlag_Boot))
+ result = hwmgr->hwmgr_func->patch_boot_state(hwmgr, &(power_state->hardware));
+
+ return result;
+}
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.h
new file mode 100644
index 000000000000..65652ae65929
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 VEGA12_PROCESSPPTABLES_H
+#define VEGA12_PROCESSPPTABLES_H
+
+#include "hwmgr.h"
+
+enum Vega12_I2CLineID {
+ Vega12_I2CLineID_DDC1 = 0x90,
+ Vega12_I2CLineID_DDC2 = 0x91,
+ Vega12_I2CLineID_DDC3 = 0x92,
+ Vega12_I2CLineID_DDC4 = 0x93,
+ Vega12_I2CLineID_DDC5 = 0x94,
+ Vega12_I2CLineID_DDC6 = 0x95,
+ Vega12_I2CLineID_SCLSDA = 0x96,
+ Vega12_I2CLineID_DDCVGA = 0x97
+};
+
+#define Vega12_I2C_DDC1DATA 0
+#define Vega12_I2C_DDC1CLK 1
+#define Vega12_I2C_DDC2DATA 2
+#define Vega12_I2C_DDC2CLK 3
+#define Vega12_I2C_DDC3DATA 4
+#define Vega12_I2C_DDC3CLK 5
+#define Vega12_I2C_SDA 40
+#define Vega12_I2C_SCL 41
+#define Vega12_I2C_DDC4DATA 65
+#define Vega12_I2C_DDC4CLK 66
+#define Vega12_I2C_DDC5DATA 0x48
+#define Vega12_I2C_DDC5CLK 0x49
+#define Vega12_I2C_DDC6DATA 0x4a
+#define Vega12_I2C_DDC6CLK 0x4b
+#define Vega12_I2C_DDCVGADATA 0x4c
+#define Vega12_I2C_DDCVGACLK 0x4d
+
+extern const struct pp_table_func vega12_pptable_funcs;
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_thermal.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_thermal.c
new file mode 100644
index 000000000000..7ace439dcde7
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_thermal.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "vega12_thermal.h"
+#include "vega12_hwmgr.h"
+#include "vega12_smumgr.h"
+#include "vega12_ppsmc.h"
+#include "vega12_inc.h"
+#include "soc15_common.h"
+#include "pp_debug.h"
+
+static int vega12_get_current_rpm(struct pp_hwmgr *hwmgr, uint32_t *current_rpm)
+{
+ PP_ASSERT_WITH_CODE(!smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_GetCurrentRpm,
+ current_rpm),
+ "Attempt to get current RPM from SMC Failed!",
+ return -EINVAL);
+
+ return 0;
+}
+
+int vega12_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr,
+ struct phm_fan_speed_info *fan_speed_info)
+{
+ memset(fan_speed_info, 0, sizeof(*fan_speed_info));
+ fan_speed_info->supports_percent_read = false;
+ fan_speed_info->supports_percent_write = false;
+ fan_speed_info->supports_rpm_read = true;
+ fan_speed_info->supports_rpm_write = true;
+
+ return 0;
+}
+
+int vega12_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed)
+{
+ *speed = 0;
+
+ return vega12_get_current_rpm(hwmgr, speed);
+}
+
+/**
+ * @fn vega12_enable_fan_control_feature
+ * @brief Enables the SMC Fan Control Feature.
+ *
+ * @param hwmgr - the address of the powerplay hardware manager.
+ * @return 0 on success. -1 otherwise.
+ */
+static int vega12_enable_fan_control_feature(struct pp_hwmgr *hwmgr)
+{
+#if 0
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+
+ if (data->smu_features[GNLD_FAN_CONTROL].supported) {
+ PP_ASSERT_WITH_CODE(!vega12_enable_smc_features(
+ hwmgr, true,
+ data->smu_features[GNLD_FAN_CONTROL].
+ smu_feature_bitmap),
+ "Attempt to Enable FAN CONTROL feature Failed!",
+ return -1);
+ data->smu_features[GNLD_FAN_CONTROL].enabled = true;
+ }
+#endif
+ return 0;
+}
+
+static int vega12_disable_fan_control_feature(struct pp_hwmgr *hwmgr)
+{
+#if 0
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+
+ if (data->smu_features[GNLD_FAN_CONTROL].supported) {
+ PP_ASSERT_WITH_CODE(!vega12_enable_smc_features(
+ hwmgr, false,
+ data->smu_features[GNLD_FAN_CONTROL].
+ smu_feature_bitmap),
+ "Attempt to Enable FAN CONTROL feature Failed!",
+ return -1);
+ data->smu_features[GNLD_FAN_CONTROL].enabled = false;
+ }
+#endif
+ return 0;
+}
+
+int vega12_fan_ctrl_start_smc_fan_control(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+
+ if (data->smu_features[GNLD_FAN_CONTROL].supported)
+ PP_ASSERT_WITH_CODE(
+ !vega12_enable_fan_control_feature(hwmgr),
+ "Attempt to Enable SMC FAN CONTROL Feature Failed!",
+ return -1);
+
+ return 0;
+}
+
+
+int vega12_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+
+ if (data->smu_features[GNLD_FAN_CONTROL].supported)
+ PP_ASSERT_WITH_CODE(!vega12_disable_fan_control_feature(hwmgr),
+ "Attempt to Disable SMC FAN CONTROL Feature Failed!",
+ return -1);
+
+ return 0;
+}
+
+/**
+* Reset Fan Speed to default.
+* @param hwmgr the address of the powerplay hardware manager.
+* @exception Always succeeds.
+*/
+int vega12_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr)
+{
+ return vega12_fan_ctrl_start_smc_fan_control(hwmgr);
+}
+
+/**
+* Reads the remote temperature from the SIslands thermal controller.
+*
+* @param hwmgr The address of the hardware manager.
+*/
+int vega12_thermal_get_temperature(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ int temp = 0;
+
+ temp = RREG32_SOC15(THM, 0, mmCG_MULT_THERMAL_STATUS);
+
+ temp = (temp & CG_MULT_THERMAL_STATUS__CTF_TEMP_MASK) >>
+ CG_MULT_THERMAL_STATUS__CTF_TEMP__SHIFT;
+
+ temp = temp & 0x1ff;
+
+ temp *= PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ return temp;
+}
+
+/**
+* Set the requested temperature range for high and low alert signals
+*
+* @param hwmgr The address of the hardware manager.
+* @param range Temperature range to be programmed for
+* high and low alert signals
+* @exception PP_Result_BadInput if the input data is not valid.
+*/
+static int vega12_thermal_set_temperature_range(struct pp_hwmgr *hwmgr,
+ struct PP_TemperatureRange *range)
+{
+ struct phm_ppt_v3_information *pptable_information =
+ (struct phm_ppt_v3_information *)hwmgr->pptable;
+ struct amdgpu_device *adev = hwmgr->adev;
+ int low = VEGA12_THERMAL_MINIMUM_ALERT_TEMP;
+ int high = VEGA12_THERMAL_MAXIMUM_ALERT_TEMP;
+ uint32_t val;
+
+ /* compare them in unit celsius degree */
+ if (low < range->min / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)
+ low = range->min / PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ if (high > pptable_information->us_software_shutdown_temp)
+ high = pptable_information->us_software_shutdown_temp;
+
+ if (low > high)
+ return -EINVAL;
+
+ val = RREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_CTRL);
+
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, MAX_IH_CREDIT, 5);
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, THERM_IH_HW_ENA, 1);
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, DIG_THERM_INTH, high);
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, DIG_THERM_INTL, low);
+ val = val & (~THM_THERMAL_INT_CTRL__THERM_TRIGGER_MASK_MASK);
+
+ WREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_CTRL, val);
+
+ return 0;
+}
+
+/**
+* Enable thermal alerts on the RV770 thermal controller.
+*
+* @param hwmgr The address of the hardware manager.
+*/
+static int vega12_thermal_enable_alert(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t val = 0;
+
+ val |= (1 << THM_THERMAL_INT_ENA__THERM_INTH_CLR__SHIFT);
+ val |= (1 << THM_THERMAL_INT_ENA__THERM_INTL_CLR__SHIFT);
+ val |= (1 << THM_THERMAL_INT_ENA__THERM_TRIGGER_CLR__SHIFT);
+
+ WREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_ENA, val);
+
+ return 0;
+}
+
+/**
+* Disable thermal alerts on the RV770 thermal controller.
+* @param hwmgr The address of the hardware manager.
+*/
+int vega12_thermal_disable_alert(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ WREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_ENA, 0);
+
+ return 0;
+}
+
+/**
+* Uninitialize the thermal controller.
+* Currently just disables alerts.
+* @param hwmgr The address of the hardware manager.
+*/
+int vega12_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr)
+{
+ int result = vega12_thermal_disable_alert(hwmgr);
+
+ return result;
+}
+
+/**
+* Set up the fan table to control the fan using the SMC.
+* @param hwmgr the address of the powerplay hardware manager.
+* @param pInput the pointer to input data
+* @param pOutput the pointer to output data
+* @param pStorage the pointer to temporary storage
+* @param Result the last failure code
+* @return result from set temperature range routine
+*/
+static int vega12_thermal_setup_fan_table(struct pp_hwmgr *hwmgr)
+{
+ int ret;
+ struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
+ PPTable_t *table = &(data->smc_state_table.pp_table);
+
+ ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetFanTemperatureTarget,
+ (uint32_t)table->FanTargetTemperature,
+ NULL);
+
+ return ret;
+}
+
+/**
+* Start the fan control on the SMC.
+* @param hwmgr the address of the powerplay hardware manager.
+* @param pInput the pointer to input data
+* @param pOutput the pointer to output data
+* @param pStorage the pointer to temporary storage
+* @param Result the last failure code
+* @return result from set temperature range routine
+*/
+static int vega12_thermal_start_smc_fan_control(struct pp_hwmgr *hwmgr)
+{
+ /* If the fantable setup has failed we could have disabled
+ * PHM_PlatformCaps_MicrocodeFanControl even after
+ * this function was included in the table.
+ * Make sure that we still think controlling the fan is OK.
+ */
+ if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl))
+ vega12_fan_ctrl_start_smc_fan_control(hwmgr);
+
+ return 0;
+}
+
+
+int vega12_start_thermal_controller(struct pp_hwmgr *hwmgr,
+ struct PP_TemperatureRange *range)
+{
+ int ret = 0;
+
+ if (range == NULL)
+ return -EINVAL;
+
+ ret = vega12_thermal_set_temperature_range(hwmgr, range);
+ if (ret)
+ return -EINVAL;
+
+ vega12_thermal_enable_alert(hwmgr);
+ /* We should restrict performance levels to low before we halt the SMC.
+ * On the other hand we are still in boot state when we do this
+ * so it would be pointless.
+ * If this assumption changes we have to revisit this table.
+ */
+ ret = vega12_thermal_setup_fan_table(hwmgr);
+ if (ret)
+ return -EINVAL;
+
+ vega12_thermal_start_smc_fan_control(hwmgr);
+
+ return 0;
+};
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_thermal.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_thermal.h
new file mode 100644
index 000000000000..0d8ed039ab12
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_thermal.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 VEGA12_THERMAL_H
+#define VEGA12_THERMAL_H
+
+#include "hwmgr.h"
+
+struct vega12_temperature {
+ uint16_t edge_temp;
+ uint16_t hot_spot_temp;
+ uint16_t hbm_temp;
+ uint16_t vr_soc_temp;
+ uint16_t vr_mem_temp;
+ uint16_t liquid1_temp;
+ uint16_t liquid2_temp;
+ uint16_t plx_temp;
+};
+
+#define VEGA12_THERMAL_HIGH_ALERT_MASK 0x1
+#define VEGA12_THERMAL_LOW_ALERT_MASK 0x2
+
+#define VEGA12_THERMAL_MINIMUM_TEMP_READING -256
+#define VEGA12_THERMAL_MAXIMUM_TEMP_READING 255
+
+#define VEGA12_THERMAL_MINIMUM_ALERT_TEMP 0
+#define VEGA12_THERMAL_MAXIMUM_ALERT_TEMP 255
+
+#define FDO_PWM_MODE_STATIC 1
+#define FDO_PWM_MODE_STATIC_RPM 5
+
+extern int vega12_thermal_get_temperature(struct pp_hwmgr *hwmgr);
+extern int vega12_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr);
+extern int vega12_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr,
+ struct phm_fan_speed_info *fan_speed_info);
+extern int vega12_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr);
+extern int vega12_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr,
+ uint32_t *speed);
+extern int vega12_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr);
+extern int vega12_thermal_disable_alert(struct pp_hwmgr *hwmgr);
+extern int vega12_fan_ctrl_start_smc_fan_control(struct pp_hwmgr *hwmgr);
+extern int vega12_start_thermal_controller(struct pp_hwmgr *hwmgr,
+ struct PP_TemperatureRange *range);
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_baco.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_baco.c
new file mode 100644
index 000000000000..2a28c9df15a0
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_baco.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "amdgpu.h"
+#include "soc15.h"
+#include "soc15_hw_ip.h"
+#include "soc15_common.h"
+#include "vega20_inc.h"
+#include "vega20_ppsmc.h"
+#include "vega20_baco.h"
+#include "vega20_smumgr.h"
+
+#include "amdgpu_ras.h"
+
+static const struct soc15_baco_cmd_entry clean_baco_tbl[] =
+{
+ {CMD_WRITE, SOC15_REG_ENTRY(NBIF, 0, mmBIOS_SCRATCH_6), 0, 0, 0, 0},
+ {CMD_WRITE, SOC15_REG_ENTRY(NBIF, 0, mmBIOS_SCRATCH_7), 0, 0, 0, 0},
+};
+
+int vega20_baco_get_capability(struct pp_hwmgr *hwmgr, bool *cap)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
+ uint32_t reg;
+
+ *cap = false;
+ if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_BACO))
+ return 0;
+
+ if (((RREG32(0x17569) & 0x20000000) >> 29) == 0x1) {
+ reg = RREG32_SOC15(NBIF, 0, mmRCC_BIF_STRAP0);
+
+ if (reg & RCC_BIF_STRAP0__STRAP_PX_CAPABLE_MASK)
+ *cap = true;
+ }
+
+ return 0;
+}
+
+int vega20_baco_get_state(struct pp_hwmgr *hwmgr, enum BACO_STATE *state)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
+ uint32_t reg;
+
+ reg = RREG32_SOC15(NBIF, 0, mmBACO_CNTL);
+
+ if (reg & BACO_CNTL__BACO_MODE_MASK)
+ /* gfx has already entered BACO state */
+ *state = BACO_STATE_IN;
+ else
+ *state = BACO_STATE_OUT;
+ return 0;
+}
+
+int vega20_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
+ struct amdgpu_ras *ras = amdgpu_ras_get_context(adev);
+ enum BACO_STATE cur_state;
+ uint32_t data;
+
+ vega20_baco_get_state(hwmgr, &cur_state);
+
+ if (cur_state == state)
+ /* aisc already in the target state */
+ return 0;
+
+ if (state == BACO_STATE_IN) {
+ if (!ras || !ras->supported) {
+ data = RREG32_SOC15(THM, 0, mmTHM_BACO_CNTL);
+ data |= 0x80000000;
+ WREG32_SOC15(THM, 0, mmTHM_BACO_CNTL, data);
+
+ if(smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_EnterBaco, 0, NULL))
+ return -EINVAL;
+ } else {
+ if(smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_EnterBaco, 1, NULL))
+ return -EINVAL;
+ }
+
+ } else if (state == BACO_STATE_OUT) {
+ if (smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ExitBaco, NULL))
+ return -EINVAL;
+ if (!soc15_baco_program_registers(hwmgr, clean_baco_tbl,
+ ARRAY_SIZE(clean_baco_tbl)))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int vega20_baco_apply_vdci_flush_workaround(struct pp_hwmgr *hwmgr)
+{
+ int ret = 0;
+
+ ret = vega20_set_pptable_driver_address(hwmgr);
+ if (ret)
+ return ret;
+
+ return smum_send_msg_to_smc(hwmgr, PPSMC_MSG_BacoWorkAroundFlushVDCI, NULL);
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_baco.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_baco.h
new file mode 100644
index 000000000000..f06471e712dc
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_baco.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 __VEGA20_BACO_H__
+#define __VEGA20_BACO_H__
+#include "hwmgr.h"
+#include "common_baco.h"
+
+extern int vega20_baco_get_capability(struct pp_hwmgr *hwmgr, bool *cap);
+extern int vega20_baco_get_state(struct pp_hwmgr *hwmgr, enum BACO_STATE *state);
+extern int vega20_baco_set_state(struct pp_hwmgr *hwmgr, enum BACO_STATE state);
+extern int vega20_baco_apply_vdci_flush_workaround(struct pp_hwmgr *hwmgr);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c
new file mode 100644
index 000000000000..da84012b7fd5
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c
@@ -0,0 +1,4409 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "hwmgr.h"
+#include "amd_powerplay.h"
+#include "vega20_smumgr.h"
+#include "hardwaremanager.h"
+#include "ppatomfwctrl.h"
+#include "atomfirmware.h"
+#include "cgs_common.h"
+#include "vega20_powertune.h"
+#include "vega20_inc.h"
+#include "pppcielanes.h"
+#include "vega20_hwmgr.h"
+#include "vega20_processpptables.h"
+#include "vega20_pptable.h"
+#include "vega20_thermal.h"
+#include "vega20_ppsmc.h"
+#include "pp_debug.h"
+#include "amd_pcie_helpers.h"
+#include "ppinterrupt.h"
+#include "pp_overdriver.h"
+#include "pp_thermal.h"
+#include "soc15_common.h"
+#include "vega20_baco.h"
+#include "smuio/smuio_9_0_offset.h"
+#include "smuio/smuio_9_0_sh_mask.h"
+#include "nbio/nbio_7_4_sh_mask.h"
+
+#define smnPCIE_LC_SPEED_CNTL 0x11140290
+#define smnPCIE_LC_LINK_WIDTH_CNTL 0x11140288
+
+#define LINK_WIDTH_MAX 6
+#define LINK_SPEED_MAX 3
+static int link_width[] = {0, 1, 2, 4, 8, 12, 16};
+static int link_speed[] = {25, 50, 80, 160};
+
+static void vega20_set_default_registry_data(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+
+ data->gfxclk_average_alpha = PPVEGA20_VEGA20GFXCLKAVERAGEALPHA_DFLT;
+ data->socclk_average_alpha = PPVEGA20_VEGA20SOCCLKAVERAGEALPHA_DFLT;
+ data->uclk_average_alpha = PPVEGA20_VEGA20UCLKCLKAVERAGEALPHA_DFLT;
+ data->gfx_activity_average_alpha = PPVEGA20_VEGA20GFXACTIVITYAVERAGEALPHA_DFLT;
+ data->lowest_uclk_reserved_for_ulv = PPVEGA20_VEGA20LOWESTUCLKRESERVEDFORULV_DFLT;
+
+ data->display_voltage_mode = PPVEGA20_VEGA20DISPLAYVOLTAGEMODE_DFLT;
+ data->dcef_clk_quad_eqn_a = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
+ data->dcef_clk_quad_eqn_b = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
+ data->dcef_clk_quad_eqn_c = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
+ data->disp_clk_quad_eqn_a = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
+ data->disp_clk_quad_eqn_b = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
+ data->disp_clk_quad_eqn_c = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
+ data->pixel_clk_quad_eqn_a = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
+ data->pixel_clk_quad_eqn_b = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
+ data->pixel_clk_quad_eqn_c = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
+ data->phy_clk_quad_eqn_a = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
+ data->phy_clk_quad_eqn_b = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
+ data->phy_clk_quad_eqn_c = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT;
+
+ /*
+ * Disable the following features for now:
+ * GFXCLK DS
+ * SOCLK DS
+ * LCLK DS
+ * DCEFCLK DS
+ * FCLK DS
+ * MP1CLK DS
+ * MP0CLK DS
+ */
+ data->registry_data.disallowed_features = 0xE0041C00;
+ /* ECC feature should be disabled on old SMUs */
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetSmuVersion, &hwmgr->smu_version);
+ if (hwmgr->smu_version < 0x282100)
+ data->registry_data.disallowed_features |= FEATURE_ECC_MASK;
+
+ if (!(hwmgr->feature_mask & PP_PCIE_DPM_MASK))
+ data->registry_data.disallowed_features |= FEATURE_DPM_LINK_MASK;
+
+ if (!(hwmgr->feature_mask & PP_SCLK_DPM_MASK))
+ data->registry_data.disallowed_features |= FEATURE_DPM_GFXCLK_MASK;
+
+ if (!(hwmgr->feature_mask & PP_SOCCLK_DPM_MASK))
+ data->registry_data.disallowed_features |= FEATURE_DPM_SOCCLK_MASK;
+
+ if (!(hwmgr->feature_mask & PP_MCLK_DPM_MASK))
+ data->registry_data.disallowed_features |= FEATURE_DPM_UCLK_MASK;
+
+ if (!(hwmgr->feature_mask & PP_DCEFCLK_DPM_MASK))
+ data->registry_data.disallowed_features |= FEATURE_DPM_DCEFCLK_MASK;
+
+ if (!(hwmgr->feature_mask & PP_ULV_MASK))
+ data->registry_data.disallowed_features |= FEATURE_ULV_MASK;
+
+ if (!(hwmgr->feature_mask & PP_SCLK_DEEP_SLEEP_MASK))
+ data->registry_data.disallowed_features |= FEATURE_DS_GFXCLK_MASK;
+
+ data->registry_data.od_state_in_dc_support = 0;
+ data->registry_data.thermal_support = 1;
+ data->registry_data.skip_baco_hardware = 0;
+
+ data->registry_data.log_avfs_param = 0;
+ data->registry_data.sclk_throttle_low_notification = 1;
+ data->registry_data.force_dpm_high = 0;
+ data->registry_data.stable_pstate_sclk_dpm_percentage = 75;
+
+ data->registry_data.didt_support = 0;
+ if (data->registry_data.didt_support) {
+ data->registry_data.didt_mode = 6;
+ data->registry_data.sq_ramping_support = 1;
+ data->registry_data.db_ramping_support = 0;
+ data->registry_data.td_ramping_support = 0;
+ data->registry_data.tcp_ramping_support = 0;
+ data->registry_data.dbr_ramping_support = 0;
+ data->registry_data.edc_didt_support = 1;
+ data->registry_data.gc_didt_support = 0;
+ data->registry_data.psm_didt_support = 0;
+ }
+
+ data->registry_data.pcie_lane_override = 0xff;
+ data->registry_data.pcie_speed_override = 0xff;
+ data->registry_data.pcie_clock_override = 0xffffffff;
+ data->registry_data.regulator_hot_gpio_support = 1;
+ data->registry_data.ac_dc_switch_gpio_support = 0;
+ data->registry_data.quick_transition_support = 0;
+ data->registry_data.zrpm_start_temp = 0xffff;
+ data->registry_data.zrpm_stop_temp = 0xffff;
+ data->registry_data.od8_feature_enable = 1;
+ data->registry_data.disable_water_mark = 0;
+ data->registry_data.disable_pp_tuning = 0;
+ data->registry_data.disable_xlpp_tuning = 0;
+ data->registry_data.disable_workload_policy = 0;
+ data->registry_data.perf_ui_tuning_profile_turbo = 0x19190F0F;
+ data->registry_data.perf_ui_tuning_profile_powerSave = 0x19191919;
+ data->registry_data.perf_ui_tuning_profile_xl = 0x00000F0A;
+ data->registry_data.force_workload_policy_mask = 0;
+ data->registry_data.disable_3d_fs_detection = 0;
+ data->registry_data.fps_support = 1;
+ data->registry_data.disable_auto_wattman = 1;
+ data->registry_data.auto_wattman_debug = 0;
+ data->registry_data.auto_wattman_sample_period = 100;
+ data->registry_data.fclk_gfxclk_ratio = 0;
+ data->registry_data.auto_wattman_threshold = 50;
+ data->registry_data.gfxoff_controlled_by_driver = 1;
+ data->gfxoff_allowed = false;
+ data->counter_gfxoff = 0;
+}
+
+static int vega20_set_features_platform_caps(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ if (data->vddci_control == VEGA20_VOLTAGE_CONTROL_NONE)
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ControlVDDCI);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TablelessHardwareInterface);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_BACO);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EnableSMU7ThermalManagement);
+
+ if (adev->pg_flags & AMD_PG_SUPPORT_UVD)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UVDPowerGating);
+
+ if (adev->pg_flags & AMD_PG_SUPPORT_VCE)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_VCEPowerGating);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UnTabledHardwareInterface);
+
+ if (data->registry_data.od8_feature_enable)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_OD8inACSupport);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ActivityReporting);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_FanSpeedInTableIsRPM);
+
+ if (data->registry_data.od_state_in_dc_support) {
+ if (data->registry_data.od8_feature_enable)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_OD8inDCSupport);
+ }
+
+ if (data->registry_data.thermal_support &&
+ data->registry_data.fuzzy_fan_control_support &&
+ hwmgr->thermal_controller.advanceFanControlParameters.usTMax)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ODFuzzyFanControlSupport);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DynamicPowerManagement);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SMC);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ThermalPolicyDelay);
+
+ if (data->registry_data.force_dpm_high)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ExclusiveModeAlwaysHigh);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DynamicUVDState);
+
+ if (data->registry_data.sclk_throttle_low_notification)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkThrottleLowNotification);
+
+ /* power tune caps */
+ /* assume disabled */
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PowerContainment);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DiDtSupport);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SQRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DBRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TDRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TCPRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DBRRamping);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DiDtEDCEnable);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_GCEDC);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PSM);
+
+ if (data->registry_data.didt_support) {
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DiDtSupport);
+ if (data->registry_data.sq_ramping_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SQRamping);
+ if (data->registry_data.db_ramping_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DBRamping);
+ if (data->registry_data.td_ramping_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TDRamping);
+ if (data->registry_data.tcp_ramping_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_TCPRamping);
+ if (data->registry_data.dbr_ramping_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DBRRamping);
+ if (data->registry_data.edc_didt_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_DiDtEDCEnable);
+ if (data->registry_data.gc_didt_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_GCEDC);
+ if (data->registry_data.psm_didt_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PSM);
+ }
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_RegulatorHot);
+
+ if (data->registry_data.ac_dc_switch_gpio_support) {
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SMCtoPPLIBAcdcGpioScheme);
+ }
+
+ if (data->registry_data.quick_transition_support) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition);
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SMCtoPPLIBAcdcGpioScheme);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_Falcon_QuickTransition);
+ }
+
+ if (data->lowest_uclk_reserved_for_ulv != PPVEGA20_VEGA20LOWESTUCLKRESERVEDFORULV_DFLT) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_LowestUclkReservedForUlv);
+ if (data->lowest_uclk_reserved_for_ulv == 1)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_LowestUclkReservedForUlv);
+ }
+
+ if (data->registry_data.custom_fan_support)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_CustomFanControlSupport);
+
+ return 0;
+}
+
+static void vega20_init_dpm_defaults(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t top32, bottom32;
+ int i;
+
+ data->smu_features[GNLD_DPM_PREFETCHER].smu_feature_id =
+ FEATURE_DPM_PREFETCHER_BIT;
+ data->smu_features[GNLD_DPM_GFXCLK].smu_feature_id =
+ FEATURE_DPM_GFXCLK_BIT;
+ data->smu_features[GNLD_DPM_UCLK].smu_feature_id =
+ FEATURE_DPM_UCLK_BIT;
+ data->smu_features[GNLD_DPM_SOCCLK].smu_feature_id =
+ FEATURE_DPM_SOCCLK_BIT;
+ data->smu_features[GNLD_DPM_UVD].smu_feature_id =
+ FEATURE_DPM_UVD_BIT;
+ data->smu_features[GNLD_DPM_VCE].smu_feature_id =
+ FEATURE_DPM_VCE_BIT;
+ data->smu_features[GNLD_ULV].smu_feature_id =
+ FEATURE_ULV_BIT;
+ data->smu_features[GNLD_DPM_MP0CLK].smu_feature_id =
+ FEATURE_DPM_MP0CLK_BIT;
+ data->smu_features[GNLD_DPM_LINK].smu_feature_id =
+ FEATURE_DPM_LINK_BIT;
+ data->smu_features[GNLD_DPM_DCEFCLK].smu_feature_id =
+ FEATURE_DPM_DCEFCLK_BIT;
+ data->smu_features[GNLD_DS_GFXCLK].smu_feature_id =
+ FEATURE_DS_GFXCLK_BIT;
+ data->smu_features[GNLD_DS_SOCCLK].smu_feature_id =
+ FEATURE_DS_SOCCLK_BIT;
+ data->smu_features[GNLD_DS_LCLK].smu_feature_id =
+ FEATURE_DS_LCLK_BIT;
+ data->smu_features[GNLD_PPT].smu_feature_id =
+ FEATURE_PPT_BIT;
+ data->smu_features[GNLD_TDC].smu_feature_id =
+ FEATURE_TDC_BIT;
+ data->smu_features[GNLD_THERMAL].smu_feature_id =
+ FEATURE_THERMAL_BIT;
+ data->smu_features[GNLD_GFX_PER_CU_CG].smu_feature_id =
+ FEATURE_GFX_PER_CU_CG_BIT;
+ data->smu_features[GNLD_RM].smu_feature_id =
+ FEATURE_RM_BIT;
+ data->smu_features[GNLD_DS_DCEFCLK].smu_feature_id =
+ FEATURE_DS_DCEFCLK_BIT;
+ data->smu_features[GNLD_ACDC].smu_feature_id =
+ FEATURE_ACDC_BIT;
+ data->smu_features[GNLD_VR0HOT].smu_feature_id =
+ FEATURE_VR0HOT_BIT;
+ data->smu_features[GNLD_VR1HOT].smu_feature_id =
+ FEATURE_VR1HOT_BIT;
+ data->smu_features[GNLD_FW_CTF].smu_feature_id =
+ FEATURE_FW_CTF_BIT;
+ data->smu_features[GNLD_LED_DISPLAY].smu_feature_id =
+ FEATURE_LED_DISPLAY_BIT;
+ data->smu_features[GNLD_FAN_CONTROL].smu_feature_id =
+ FEATURE_FAN_CONTROL_BIT;
+ data->smu_features[GNLD_DIDT].smu_feature_id = FEATURE_GFX_EDC_BIT;
+ data->smu_features[GNLD_GFXOFF].smu_feature_id = FEATURE_GFXOFF_BIT;
+ data->smu_features[GNLD_CG].smu_feature_id = FEATURE_CG_BIT;
+ data->smu_features[GNLD_DPM_FCLK].smu_feature_id = FEATURE_DPM_FCLK_BIT;
+ data->smu_features[GNLD_DS_FCLK].smu_feature_id = FEATURE_DS_FCLK_BIT;
+ data->smu_features[GNLD_DS_MP1CLK].smu_feature_id = FEATURE_DS_MP1CLK_BIT;
+ data->smu_features[GNLD_DS_MP0CLK].smu_feature_id = FEATURE_DS_MP0CLK_BIT;
+ data->smu_features[GNLD_XGMI].smu_feature_id = FEATURE_XGMI_BIT;
+ data->smu_features[GNLD_ECC].smu_feature_id = FEATURE_ECC_BIT;
+
+ for (i = 0; i < GNLD_FEATURES_MAX; i++) {
+ data->smu_features[i].smu_feature_bitmap =
+ (uint64_t)(1ULL << data->smu_features[i].smu_feature_id);
+ data->smu_features[i].allowed =
+ ((data->registry_data.disallowed_features >> i) & 1) ?
+ false : true;
+ }
+
+ /* Get the SN to turn into a Unique ID */
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ReadSerialNumTop32, &top32);
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ReadSerialNumBottom32, &bottom32);
+
+ adev->unique_id = ((uint64_t)bottom32 << 32) | top32;
+}
+
+static int vega20_set_private_data_based_on_pptable(struct pp_hwmgr *hwmgr)
+{
+ return 0;
+}
+
+static int vega20_hwmgr_backend_fini(struct pp_hwmgr *hwmgr)
+{
+ kfree(hwmgr->backend);
+ hwmgr->backend = NULL;
+
+ return 0;
+}
+
+static int vega20_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data;
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ data = kzalloc(sizeof(struct vega20_hwmgr), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ hwmgr->backend = data;
+
+ hwmgr->workload_mask = 1 << hwmgr->workload_prority[PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT];
+ hwmgr->power_profile_mode = PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT;
+ hwmgr->default_power_profile_mode = PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT;
+
+ vega20_set_default_registry_data(hwmgr);
+
+ data->disable_dpm_mask = 0xff;
+
+ /* need to set voltage control types before EVV patching */
+ data->vddc_control = VEGA20_VOLTAGE_CONTROL_NONE;
+ data->mvdd_control = VEGA20_VOLTAGE_CONTROL_NONE;
+ data->vddci_control = VEGA20_VOLTAGE_CONTROL_NONE;
+
+ data->water_marks_bitmap = 0;
+ data->avfs_exist = false;
+
+ vega20_set_features_platform_caps(hwmgr);
+
+ vega20_init_dpm_defaults(hwmgr);
+
+ /* Parse pptable data read from VBIOS */
+ vega20_set_private_data_based_on_pptable(hwmgr);
+
+ data->is_tlu_enabled = false;
+
+ hwmgr->platform_descriptor.hardwareActivityPerformanceLevels =
+ VEGA20_MAX_HARDWARE_POWERLEVELS;
+ hwmgr->platform_descriptor.hardwarePerformanceLevels = 2;
+ hwmgr->platform_descriptor.minimumClocksReductionPercentage = 50;
+
+ hwmgr->platform_descriptor.vbiosInterruptId = 0x20000400; /* IRQ_SOURCE1_SW_INT */
+ /* The true clock step depends on the frequency, typically 4.5 or 9 MHz. Here we use 5. */
+ hwmgr->platform_descriptor.clockStep.engineClock = 500;
+ hwmgr->platform_descriptor.clockStep.memoryClock = 500;
+
+ data->total_active_cus = adev->gfx.cu_info.number;
+ data->is_custom_profile_set = false;
+
+ return 0;
+}
+
+static int vega20_init_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+
+ data->low_sclk_interrupt_threshold = 0;
+
+ return 0;
+}
+
+static int vega20_setup_asic_task(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
+ int ret = 0;
+ bool use_baco = (amdgpu_in_reset(adev) &&
+ (amdgpu_asic_reset_method(adev) == AMD_RESET_METHOD_BACO)) ||
+ (adev->in_runpm && amdgpu_asic_supports_baco(adev));
+
+ ret = vega20_init_sclk_threshold(hwmgr);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to init sclk threshold!",
+ return ret);
+
+ if (use_baco) {
+ ret = vega20_baco_apply_vdci_flush_workaround(hwmgr);
+ if (ret)
+ pr_err("Failed to apply vega20 baco workaround!\n");
+ }
+
+ return ret;
+}
+
+/*
+ * @fn vega20_init_dpm_state
+ * @brief Function to initialize all Soft Min/Max and Hard Min/Max to 0xff.
+ *
+ * @param dpm_state - the address of the DPM Table to initiailize.
+ * @return None.
+ */
+static void vega20_init_dpm_state(struct vega20_dpm_state *dpm_state)
+{
+ dpm_state->soft_min_level = 0x0;
+ dpm_state->soft_max_level = VG20_CLOCK_MAX_DEFAULT;
+ dpm_state->hard_min_level = 0x0;
+ dpm_state->hard_max_level = VG20_CLOCK_MAX_DEFAULT;
+}
+
+static int vega20_get_number_of_dpm_level(struct pp_hwmgr *hwmgr,
+ PPCLK_e clk_id, uint32_t *num_of_levels)
+{
+ int ret = 0;
+
+ ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_GetDpmFreqByIndex,
+ (clk_id << 16 | 0xFF),
+ num_of_levels);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[GetNumOfDpmLevel] failed to get dpm levels!",
+ return ret);
+
+ return ret;
+}
+
+static int vega20_get_dpm_frequency_by_index(struct pp_hwmgr *hwmgr,
+ PPCLK_e clk_id, uint32_t index, uint32_t *clk)
+{
+ int ret = 0;
+
+ ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_GetDpmFreqByIndex,
+ (clk_id << 16 | index),
+ clk);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[GetDpmFreqByIndex] failed to get dpm freq by index!",
+ return ret);
+
+ return ret;
+}
+
+static int vega20_setup_single_dpm_table(struct pp_hwmgr *hwmgr,
+ struct vega20_single_dpm_table *dpm_table, PPCLK_e clk_id)
+{
+ int ret = 0;
+ uint32_t i, num_of_levels, clk;
+
+ ret = vega20_get_number_of_dpm_level(hwmgr, clk_id, &num_of_levels);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupSingleDpmTable] failed to get clk levels!",
+ return ret);
+
+ dpm_table->count = num_of_levels;
+
+ for (i = 0; i < num_of_levels; i++) {
+ ret = vega20_get_dpm_frequency_by_index(hwmgr, clk_id, i, &clk);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupSingleDpmTable] failed to get clk of specific level!",
+ return ret);
+ dpm_table->dpm_levels[i].value = clk;
+ dpm_table->dpm_levels[i].enabled = true;
+ }
+
+ return ret;
+}
+
+static int vega20_setup_gfxclk_dpm_table(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_single_dpm_table *dpm_table;
+ int ret = 0;
+
+ dpm_table = &(data->dpm_table.gfx_table);
+ if (data->smu_features[GNLD_DPM_GFXCLK].enabled) {
+ ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_GFXCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get gfxclk dpm levels!",
+ return ret);
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = data->vbios_boot_state.gfx_clock / 100;
+ }
+
+ return ret;
+}
+
+static int vega20_setup_memclk_dpm_table(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_single_dpm_table *dpm_table;
+ int ret = 0;
+
+ dpm_table = &(data->dpm_table.mem_table);
+ if (data->smu_features[GNLD_DPM_UCLK].enabled) {
+ ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_UCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get memclk dpm levels!",
+ return ret);
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = data->vbios_boot_state.mem_clock / 100;
+ }
+
+ return ret;
+}
+
+/*
+ * This function is to initialize all DPM state tables
+ * for SMU based on the dependency table.
+ * Dynamic state patching function will then trim these
+ * state tables to the allowed range based
+ * on the power policy or external client requests,
+ * such as UVD request, etc.
+ */
+static int vega20_setup_default_dpm_tables(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_single_dpm_table *dpm_table;
+ int ret = 0;
+
+ memset(&data->dpm_table, 0, sizeof(data->dpm_table));
+
+ /* socclk */
+ dpm_table = &(data->dpm_table.soc_table);
+ if (data->smu_features[GNLD_DPM_SOCCLK].enabled) {
+ ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_SOCCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get socclk dpm levels!",
+ return ret);
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = data->vbios_boot_state.soc_clock / 100;
+ }
+ vega20_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* gfxclk */
+ dpm_table = &(data->dpm_table.gfx_table);
+ ret = vega20_setup_gfxclk_dpm_table(hwmgr);
+ if (ret)
+ return ret;
+ vega20_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* memclk */
+ dpm_table = &(data->dpm_table.mem_table);
+ ret = vega20_setup_memclk_dpm_table(hwmgr);
+ if (ret)
+ return ret;
+ vega20_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* eclk */
+ dpm_table = &(data->dpm_table.eclk_table);
+ if (data->smu_features[GNLD_DPM_VCE].enabled) {
+ ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_ECLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get eclk dpm levels!",
+ return ret);
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = data->vbios_boot_state.eclock / 100;
+ }
+ vega20_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* vclk */
+ dpm_table = &(data->dpm_table.vclk_table);
+ if (data->smu_features[GNLD_DPM_UVD].enabled) {
+ ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_VCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get vclk dpm levels!",
+ return ret);
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = data->vbios_boot_state.vclock / 100;
+ }
+ vega20_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* dclk */
+ dpm_table = &(data->dpm_table.dclk_table);
+ if (data->smu_features[GNLD_DPM_UVD].enabled) {
+ ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_DCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get dclk dpm levels!",
+ return ret);
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = data->vbios_boot_state.dclock / 100;
+ }
+ vega20_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* dcefclk */
+ dpm_table = &(data->dpm_table.dcef_table);
+ if (data->smu_features[GNLD_DPM_DCEFCLK].enabled) {
+ ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_DCEFCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get dcefclk dpm levels!",
+ return ret);
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = data->vbios_boot_state.dcef_clock / 100;
+ }
+ vega20_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* pixclk */
+ dpm_table = &(data->dpm_table.pixel_table);
+ if (data->smu_features[GNLD_DPM_DCEFCLK].enabled) {
+ ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_PIXCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get pixclk dpm levels!",
+ return ret);
+ } else
+ dpm_table->count = 0;
+ vega20_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* dispclk */
+ dpm_table = &(data->dpm_table.display_table);
+ if (data->smu_features[GNLD_DPM_DCEFCLK].enabled) {
+ ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_DISPCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get dispclk dpm levels!",
+ return ret);
+ } else
+ dpm_table->count = 0;
+ vega20_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* phyclk */
+ dpm_table = &(data->dpm_table.phy_table);
+ if (data->smu_features[GNLD_DPM_DCEFCLK].enabled) {
+ ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_PHYCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get phyclk dpm levels!",
+ return ret);
+ } else
+ dpm_table->count = 0;
+ vega20_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* fclk */
+ dpm_table = &(data->dpm_table.fclk_table);
+ if (data->smu_features[GNLD_DPM_FCLK].enabled) {
+ ret = vega20_setup_single_dpm_table(hwmgr, dpm_table, PPCLK_FCLK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetupDefaultDpmTable] failed to get fclk dpm levels!",
+ return ret);
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = data->vbios_boot_state.fclock / 100;
+ }
+ vega20_init_dpm_state(&(dpm_table->dpm_state));
+
+ /* save a copy of the default DPM table */
+ memcpy(&(data->golden_dpm_table), &(data->dpm_table),
+ sizeof(struct vega20_dpm_table));
+
+ return 0;
+}
+
+/**
+* Initializes the SMC table and uploads it
+*
+* @param hwmgr the address of the powerplay hardware manager.
+* @param pInput the pointer to input data (PowerState)
+* @return always 0
+*/
+static int vega20_init_smc_table(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+ struct pp_atomfwctrl_bios_boot_up_values boot_up_values;
+ struct phm_ppt_v3_information *pptable_information =
+ (struct phm_ppt_v3_information *)hwmgr->pptable;
+
+ result = pp_atomfwctrl_get_vbios_bootup_values(hwmgr, &boot_up_values);
+ PP_ASSERT_WITH_CODE(!result,
+ "[InitSMCTable] Failed to get vbios bootup values!",
+ return result);
+
+ data->vbios_boot_state.vddc = boot_up_values.usVddc;
+ data->vbios_boot_state.vddci = boot_up_values.usVddci;
+ data->vbios_boot_state.mvddc = boot_up_values.usMvddc;
+ data->vbios_boot_state.gfx_clock = boot_up_values.ulGfxClk;
+ data->vbios_boot_state.mem_clock = boot_up_values.ulUClk;
+ data->vbios_boot_state.soc_clock = boot_up_values.ulSocClk;
+ data->vbios_boot_state.dcef_clock = boot_up_values.ulDCEFClk;
+ data->vbios_boot_state.eclock = boot_up_values.ulEClk;
+ data->vbios_boot_state.vclock = boot_up_values.ulVClk;
+ data->vbios_boot_state.dclock = boot_up_values.ulDClk;
+ data->vbios_boot_state.fclock = boot_up_values.ulFClk;
+ data->vbios_boot_state.uc_cooling_id = boot_up_values.ucCoolingID;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetMinDeepSleepDcefclk,
+ (uint32_t)(data->vbios_boot_state.dcef_clock / 100),
+ NULL);
+
+ memcpy(pp_table, pptable_information->smc_pptable, sizeof(PPTable_t));
+
+ result = smum_smc_table_manager(hwmgr,
+ (uint8_t *)pp_table, TABLE_PPTABLE, false);
+ PP_ASSERT_WITH_CODE(!result,
+ "[InitSMCTable] Failed to upload PPtable!",
+ return result);
+
+ return 0;
+}
+
+/*
+ * Override PCIe link speed and link width for DPM Level 1. PPTable entries
+ * reflect the ASIC capabilities and not the system capabilities. For e.g.
+ * Vega20 board in a PCI Gen3 system. In this case, when SMU's tries to switch
+ * to DPM1, it fails as system doesn't support Gen4.
+ */
+static int vega20_override_pcie_parameters(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ uint32_t pcie_gen = 0, pcie_width = 0, smu_pcie_arg;
+ int ret;
+
+ if (adev->pm.pcie_gen_mask & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN4)
+ pcie_gen = 3;
+ else if (adev->pm.pcie_gen_mask & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN3)
+ pcie_gen = 2;
+ else if (adev->pm.pcie_gen_mask & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN2)
+ pcie_gen = 1;
+ else if (adev->pm.pcie_gen_mask & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN1)
+ pcie_gen = 0;
+
+ if (adev->pm.pcie_mlw_mask & CAIL_PCIE_LINK_WIDTH_SUPPORT_X16)
+ pcie_width = 6;
+ else if (adev->pm.pcie_mlw_mask & CAIL_PCIE_LINK_WIDTH_SUPPORT_X12)
+ pcie_width = 5;
+ else if (adev->pm.pcie_mlw_mask & CAIL_PCIE_LINK_WIDTH_SUPPORT_X8)
+ pcie_width = 4;
+ else if (adev->pm.pcie_mlw_mask & CAIL_PCIE_LINK_WIDTH_SUPPORT_X4)
+ pcie_width = 3;
+ else if (adev->pm.pcie_mlw_mask & CAIL_PCIE_LINK_WIDTH_SUPPORT_X2)
+ pcie_width = 2;
+ else if (adev->pm.pcie_mlw_mask & CAIL_PCIE_LINK_WIDTH_SUPPORT_X1)
+ pcie_width = 1;
+
+ /* Bit 31:16: LCLK DPM level. 0 is DPM0, and 1 is DPM1
+ * Bit 15:8: PCIE GEN, 0 to 3 corresponds to GEN1 to GEN4
+ * Bit 7:0: PCIE lane width, 1 to 7 corresponds is x1 to x32
+ */
+ smu_pcie_arg = (1 << 16) | (pcie_gen << 8) | pcie_width;
+ ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_OverridePcieParameters, smu_pcie_arg,
+ NULL);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[OverridePcieParameters] Attempt to override pcie params failed!",
+ return ret);
+
+ data->pcie_parameters_override = true;
+ data->pcie_gen_level1 = pcie_gen;
+ data->pcie_width_level1 = pcie_width;
+
+ return 0;
+}
+
+static int vega20_set_allowed_featuresmask(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ uint32_t allowed_features_low = 0, allowed_features_high = 0;
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < GNLD_FEATURES_MAX; i++)
+ if (data->smu_features[i].allowed)
+ data->smu_features[i].smu_feature_id > 31 ?
+ (allowed_features_high |=
+ ((data->smu_features[i].smu_feature_bitmap >> SMU_FEATURES_HIGH_SHIFT)
+ & 0xFFFFFFFF)) :
+ (allowed_features_low |=
+ ((data->smu_features[i].smu_feature_bitmap >> SMU_FEATURES_LOW_SHIFT)
+ & 0xFFFFFFFF));
+
+ ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetAllowedFeaturesMaskHigh, allowed_features_high, NULL);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetAllowedFeaturesMask] Attempt to set allowed features mask(high) failed!",
+ return ret);
+
+ ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetAllowedFeaturesMaskLow, allowed_features_low, NULL);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetAllowedFeaturesMask] Attempt to set allowed features mask (low) failed!",
+ return ret);
+
+ return 0;
+}
+
+static int vega20_run_btc(struct pp_hwmgr *hwmgr)
+{
+ return smum_send_msg_to_smc(hwmgr, PPSMC_MSG_RunBtc, NULL);
+}
+
+static int vega20_run_btc_afll(struct pp_hwmgr *hwmgr)
+{
+ return smum_send_msg_to_smc(hwmgr, PPSMC_MSG_RunAfllBtc, NULL);
+}
+
+static int vega20_enable_all_smu_features(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ uint64_t features_enabled;
+ int i;
+ bool enabled;
+ int ret = 0;
+
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_EnableAllSmuFeatures,
+ NULL)) == 0,
+ "[EnableAllSMUFeatures] Failed to enable all smu features!",
+ return ret);
+
+ ret = vega20_get_enabled_smc_features(hwmgr, &features_enabled);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[EnableAllSmuFeatures] Failed to get enabled smc features!",
+ return ret);
+
+ for (i = 0; i < GNLD_FEATURES_MAX; i++) {
+ enabled = (features_enabled & data->smu_features[i].smu_feature_bitmap) ?
+ true : false;
+ data->smu_features[i].enabled = enabled;
+ data->smu_features[i].supported = enabled;
+
+#if 0
+ if (data->smu_features[i].allowed && !enabled)
+ pr_info("[EnableAllSMUFeatures] feature %d is expected enabled!", i);
+ else if (!data->smu_features[i].allowed && enabled)
+ pr_info("[EnableAllSMUFeatures] feature %d is expected disabled!", i);
+#endif
+ }
+
+ return 0;
+}
+
+static int vega20_notify_smc_display_change(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+
+ if (data->smu_features[GNLD_DPM_UCLK].enabled)
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetUclkFastSwitch,
+ 1,
+ NULL);
+
+ return 0;
+}
+
+static int vega20_send_clock_ratio(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetFclkGfxClkRatio,
+ data->registry_data.fclk_gfxclk_ratio,
+ NULL);
+}
+
+static int vega20_disable_all_smu_features(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ int i, ret = 0;
+
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_DisableAllSmuFeatures,
+ NULL)) == 0,
+ "[DisableAllSMUFeatures] Failed to disable all smu features!",
+ return ret);
+
+ for (i = 0; i < GNLD_FEATURES_MAX; i++)
+ data->smu_features[i].enabled = 0;
+
+ return 0;
+}
+
+static int vega20_od8_set_feature_capabilities(
+ struct pp_hwmgr *hwmgr)
+{
+ struct phm_ppt_v3_information *pptable_information =
+ (struct phm_ppt_v3_information *)hwmgr->pptable;
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+ struct vega20_od8_settings *od_settings = &(data->od8_settings);
+
+ od_settings->overdrive8_capabilities = 0;
+
+ if (data->smu_features[GNLD_DPM_GFXCLK].enabled) {
+ if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_GFXCLK_LIMITS] &&
+ pptable_information->od_settings_max[OD8_SETTING_GFXCLK_FMAX] > 0 &&
+ pptable_information->od_settings_min[OD8_SETTING_GFXCLK_FMIN] > 0 &&
+ (pptable_information->od_settings_max[OD8_SETTING_GFXCLK_FMAX] >=
+ pptable_information->od_settings_min[OD8_SETTING_GFXCLK_FMIN]))
+ od_settings->overdrive8_capabilities |= OD8_GFXCLK_LIMITS;
+
+ if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_GFXCLK_CURVE] &&
+ (pptable_information->od_settings_min[OD8_SETTING_GFXCLK_VOLTAGE1] >=
+ pp_table->MinVoltageGfx / VOLTAGE_SCALE) &&
+ (pptable_information->od_settings_max[OD8_SETTING_GFXCLK_VOLTAGE3] <=
+ pp_table->MaxVoltageGfx / VOLTAGE_SCALE) &&
+ (pptable_information->od_settings_max[OD8_SETTING_GFXCLK_VOLTAGE3] >=
+ pptable_information->od_settings_min[OD8_SETTING_GFXCLK_VOLTAGE1]))
+ od_settings->overdrive8_capabilities |= OD8_GFXCLK_CURVE;
+ }
+
+ if (data->smu_features[GNLD_DPM_UCLK].enabled) {
+ pptable_information->od_settings_min[OD8_SETTING_UCLK_FMAX] =
+ data->dpm_table.mem_table.dpm_levels[data->dpm_table.mem_table.count - 2].value;
+ if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_UCLK_MAX] &&
+ pptable_information->od_settings_min[OD8_SETTING_UCLK_FMAX] > 0 &&
+ pptable_information->od_settings_max[OD8_SETTING_UCLK_FMAX] > 0 &&
+ (pptable_information->od_settings_max[OD8_SETTING_UCLK_FMAX] >=
+ pptable_information->od_settings_min[OD8_SETTING_UCLK_FMAX]))
+ od_settings->overdrive8_capabilities |= OD8_UCLK_MAX;
+ }
+
+ if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_POWER_LIMIT] &&
+ pptable_information->od_settings_max[OD8_SETTING_POWER_PERCENTAGE] > 0 &&
+ pptable_information->od_settings_max[OD8_SETTING_POWER_PERCENTAGE] <= 100 &&
+ pptable_information->od_settings_min[OD8_SETTING_POWER_PERCENTAGE] > 0 &&
+ pptable_information->od_settings_min[OD8_SETTING_POWER_PERCENTAGE] <= 100)
+ od_settings->overdrive8_capabilities |= OD8_POWER_LIMIT;
+
+ if (data->smu_features[GNLD_FAN_CONTROL].enabled) {
+ if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_FAN_ACOUSTIC_LIMIT] &&
+ pptable_information->od_settings_min[OD8_SETTING_FAN_ACOUSTIC_LIMIT] > 0 &&
+ pptable_information->od_settings_max[OD8_SETTING_FAN_ACOUSTIC_LIMIT] > 0 &&
+ (pptable_information->od_settings_max[OD8_SETTING_FAN_ACOUSTIC_LIMIT] >=
+ pptable_information->od_settings_min[OD8_SETTING_FAN_ACOUSTIC_LIMIT]))
+ od_settings->overdrive8_capabilities |= OD8_ACOUSTIC_LIMIT_SCLK;
+
+ if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_FAN_SPEED_MIN] &&
+ (pptable_information->od_settings_min[OD8_SETTING_FAN_MIN_SPEED] >=
+ (pp_table->FanPwmMin * pp_table->FanMaximumRpm / 100)) &&
+ pptable_information->od_settings_max[OD8_SETTING_FAN_MIN_SPEED] > 0 &&
+ (pptable_information->od_settings_max[OD8_SETTING_FAN_MIN_SPEED] >=
+ pptable_information->od_settings_min[OD8_SETTING_FAN_MIN_SPEED]))
+ od_settings->overdrive8_capabilities |= OD8_FAN_SPEED_MIN;
+ }
+
+ if (data->smu_features[GNLD_THERMAL].enabled) {
+ if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_TEMPERATURE_FAN] &&
+ pptable_information->od_settings_max[OD8_SETTING_FAN_TARGET_TEMP] > 0 &&
+ pptable_information->od_settings_min[OD8_SETTING_FAN_TARGET_TEMP] > 0 &&
+ (pptable_information->od_settings_max[OD8_SETTING_FAN_TARGET_TEMP] >=
+ pptable_information->od_settings_min[OD8_SETTING_FAN_TARGET_TEMP]))
+ od_settings->overdrive8_capabilities |= OD8_TEMPERATURE_FAN;
+
+ if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_TEMPERATURE_SYSTEM] &&
+ pptable_information->od_settings_max[OD8_SETTING_OPERATING_TEMP_MAX] > 0 &&
+ pptable_information->od_settings_min[OD8_SETTING_OPERATING_TEMP_MAX] > 0 &&
+ (pptable_information->od_settings_max[OD8_SETTING_OPERATING_TEMP_MAX] >=
+ pptable_information->od_settings_min[OD8_SETTING_OPERATING_TEMP_MAX]))
+ od_settings->overdrive8_capabilities |= OD8_TEMPERATURE_SYSTEM;
+ }
+
+ if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_MEMORY_TIMING_TUNE])
+ od_settings->overdrive8_capabilities |= OD8_MEMORY_TIMING_TUNE;
+
+ if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_FAN_ZERO_RPM_CONTROL] &&
+ pp_table->FanZeroRpmEnable)
+ od_settings->overdrive8_capabilities |= OD8_FAN_ZERO_RPM_CONTROL;
+
+ if (!od_settings->overdrive8_capabilities)
+ hwmgr->od_enabled = false;
+
+ return 0;
+}
+
+static int vega20_od8_set_feature_id(
+ struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_od8_settings *od_settings = &(data->od8_settings);
+
+ if (od_settings->overdrive8_capabilities & OD8_GFXCLK_LIMITS) {
+ od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMIN].feature_id =
+ OD8_GFXCLK_LIMITS;
+ od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMAX].feature_id =
+ OD8_GFXCLK_LIMITS;
+ } else {
+ od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMIN].feature_id =
+ 0;
+ od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMAX].feature_id =
+ 0;
+ }
+
+ if (od_settings->overdrive8_capabilities & OD8_GFXCLK_CURVE) {
+ od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ1].feature_id =
+ OD8_GFXCLK_CURVE;
+ od_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].feature_id =
+ OD8_GFXCLK_CURVE;
+ od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ2].feature_id =
+ OD8_GFXCLK_CURVE;
+ od_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].feature_id =
+ OD8_GFXCLK_CURVE;
+ od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ3].feature_id =
+ OD8_GFXCLK_CURVE;
+ od_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].feature_id =
+ OD8_GFXCLK_CURVE;
+ } else {
+ od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ1].feature_id =
+ 0;
+ od_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].feature_id =
+ 0;
+ od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ2].feature_id =
+ 0;
+ od_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].feature_id =
+ 0;
+ od_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ3].feature_id =
+ 0;
+ od_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].feature_id =
+ 0;
+ }
+
+ if (od_settings->overdrive8_capabilities & OD8_UCLK_MAX)
+ od_settings->od8_settings_array[OD8_SETTING_UCLK_FMAX].feature_id = OD8_UCLK_MAX;
+ else
+ od_settings->od8_settings_array[OD8_SETTING_UCLK_FMAX].feature_id = 0;
+
+ if (od_settings->overdrive8_capabilities & OD8_POWER_LIMIT)
+ od_settings->od8_settings_array[OD8_SETTING_POWER_PERCENTAGE].feature_id = OD8_POWER_LIMIT;
+ else
+ od_settings->od8_settings_array[OD8_SETTING_POWER_PERCENTAGE].feature_id = 0;
+
+ if (od_settings->overdrive8_capabilities & OD8_ACOUSTIC_LIMIT_SCLK)
+ od_settings->od8_settings_array[OD8_SETTING_FAN_ACOUSTIC_LIMIT].feature_id =
+ OD8_ACOUSTIC_LIMIT_SCLK;
+ else
+ od_settings->od8_settings_array[OD8_SETTING_FAN_ACOUSTIC_LIMIT].feature_id =
+ 0;
+
+ if (od_settings->overdrive8_capabilities & OD8_FAN_SPEED_MIN)
+ od_settings->od8_settings_array[OD8_SETTING_FAN_MIN_SPEED].feature_id =
+ OD8_FAN_SPEED_MIN;
+ else
+ od_settings->od8_settings_array[OD8_SETTING_FAN_MIN_SPEED].feature_id =
+ 0;
+
+ if (od_settings->overdrive8_capabilities & OD8_TEMPERATURE_FAN)
+ od_settings->od8_settings_array[OD8_SETTING_FAN_TARGET_TEMP].feature_id =
+ OD8_TEMPERATURE_FAN;
+ else
+ od_settings->od8_settings_array[OD8_SETTING_FAN_TARGET_TEMP].feature_id =
+ 0;
+
+ if (od_settings->overdrive8_capabilities & OD8_TEMPERATURE_SYSTEM)
+ od_settings->od8_settings_array[OD8_SETTING_OPERATING_TEMP_MAX].feature_id =
+ OD8_TEMPERATURE_SYSTEM;
+ else
+ od_settings->od8_settings_array[OD8_SETTING_OPERATING_TEMP_MAX].feature_id =
+ 0;
+
+ return 0;
+}
+
+static int vega20_od8_get_gfx_clock_base_voltage(
+ struct pp_hwmgr *hwmgr,
+ uint32_t *voltage,
+ uint32_t freq)
+{
+ int ret = 0;
+
+ ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_GetAVFSVoltageByDpm,
+ ((AVFS_CURVE << 24) | (OD8_HOTCURVE_TEMPERATURE << 16) | freq),
+ voltage);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[GetBaseVoltage] failed to get GFXCLK AVFS voltage from SMU!",
+ return ret);
+
+ *voltage = *voltage / VOLTAGE_SCALE;
+
+ return 0;
+}
+
+static int vega20_od8_initialize_default_settings(
+ struct pp_hwmgr *hwmgr)
+{
+ struct phm_ppt_v3_information *pptable_information =
+ (struct phm_ppt_v3_information *)hwmgr->pptable;
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_od8_settings *od8_settings = &(data->od8_settings);
+ OverDriveTable_t *od_table = &(data->smc_state_table.overdrive_table);
+ int i, ret = 0;
+
+ /* Set Feature Capabilities */
+ vega20_od8_set_feature_capabilities(hwmgr);
+
+ /* Map FeatureID to individual settings */
+ vega20_od8_set_feature_id(hwmgr);
+
+ /* Set default values */
+ ret = smum_smc_table_manager(hwmgr, (uint8_t *)od_table, TABLE_OVERDRIVE, true);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to export over drive table!",
+ return ret);
+
+ if (od8_settings->overdrive8_capabilities & OD8_GFXCLK_LIMITS) {
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMIN].default_value =
+ od_table->GfxclkFmin;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMAX].default_value =
+ od_table->GfxclkFmax;
+ } else {
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMIN].default_value =
+ 0;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FMAX].default_value =
+ 0;
+ }
+
+ if (od8_settings->overdrive8_capabilities & OD8_GFXCLK_CURVE) {
+ od_table->GfxclkFreq1 = od_table->GfxclkFmin;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ1].default_value =
+ od_table->GfxclkFreq1;
+
+ od_table->GfxclkFreq3 = od_table->GfxclkFmax;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ3].default_value =
+ od_table->GfxclkFreq3;
+
+ od_table->GfxclkFreq2 = (od_table->GfxclkFreq1 + od_table->GfxclkFreq3) / 2;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ2].default_value =
+ od_table->GfxclkFreq2;
+
+ PP_ASSERT_WITH_CODE(!vega20_od8_get_gfx_clock_base_voltage(hwmgr,
+ &(od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].default_value),
+ od_table->GfxclkFreq1),
+ "[PhwVega20_OD8_InitializeDefaultSettings] Failed to get Base clock voltage from SMU!",
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].default_value = 0);
+ od_table->GfxclkVolt1 = od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].default_value
+ * VOLTAGE_SCALE;
+
+ PP_ASSERT_WITH_CODE(!vega20_od8_get_gfx_clock_base_voltage(hwmgr,
+ &(od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].default_value),
+ od_table->GfxclkFreq2),
+ "[PhwVega20_OD8_InitializeDefaultSettings] Failed to get Base clock voltage from SMU!",
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].default_value = 0);
+ od_table->GfxclkVolt2 = od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].default_value
+ * VOLTAGE_SCALE;
+
+ PP_ASSERT_WITH_CODE(!vega20_od8_get_gfx_clock_base_voltage(hwmgr,
+ &(od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].default_value),
+ od_table->GfxclkFreq3),
+ "[PhwVega20_OD8_InitializeDefaultSettings] Failed to get Base clock voltage from SMU!",
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].default_value = 0);
+ od_table->GfxclkVolt3 = od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].default_value
+ * VOLTAGE_SCALE;
+ } else {
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ1].default_value =
+ 0;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE1].default_value =
+ 0;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ2].default_value =
+ 0;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE2].default_value =
+ 0;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_FREQ3].default_value =
+ 0;
+ od8_settings->od8_settings_array[OD8_SETTING_GFXCLK_VOLTAGE3].default_value =
+ 0;
+ }
+
+ if (od8_settings->overdrive8_capabilities & OD8_UCLK_MAX)
+ od8_settings->od8_settings_array[OD8_SETTING_UCLK_FMAX].default_value =
+ od_table->UclkFmax;
+ else
+ od8_settings->od8_settings_array[OD8_SETTING_UCLK_FMAX].default_value =
+ 0;
+
+ if (od8_settings->overdrive8_capabilities & OD8_POWER_LIMIT)
+ od8_settings->od8_settings_array[OD8_SETTING_POWER_PERCENTAGE].default_value =
+ od_table->OverDrivePct;
+ else
+ od8_settings->od8_settings_array[OD8_SETTING_POWER_PERCENTAGE].default_value =
+ 0;
+
+ if (od8_settings->overdrive8_capabilities & OD8_ACOUSTIC_LIMIT_SCLK)
+ od8_settings->od8_settings_array[OD8_SETTING_FAN_ACOUSTIC_LIMIT].default_value =
+ od_table->FanMaximumRpm;
+ else
+ od8_settings->od8_settings_array[OD8_SETTING_FAN_ACOUSTIC_LIMIT].default_value =
+ 0;
+
+ if (od8_settings->overdrive8_capabilities & OD8_FAN_SPEED_MIN)
+ od8_settings->od8_settings_array[OD8_SETTING_FAN_MIN_SPEED].default_value =
+ od_table->FanMinimumPwm * data->smc_state_table.pp_table.FanMaximumRpm / 100;
+ else
+ od8_settings->od8_settings_array[OD8_SETTING_FAN_MIN_SPEED].default_value =
+ 0;
+
+ if (od8_settings->overdrive8_capabilities & OD8_TEMPERATURE_FAN)
+ od8_settings->od8_settings_array[OD8_SETTING_FAN_TARGET_TEMP].default_value =
+ od_table->FanTargetTemperature;
+ else
+ od8_settings->od8_settings_array[OD8_SETTING_FAN_TARGET_TEMP].default_value =
+ 0;
+
+ if (od8_settings->overdrive8_capabilities & OD8_TEMPERATURE_SYSTEM)
+ od8_settings->od8_settings_array[OD8_SETTING_OPERATING_TEMP_MAX].default_value =
+ od_table->MaxOpTemp;
+ else
+ od8_settings->od8_settings_array[OD8_SETTING_OPERATING_TEMP_MAX].default_value =
+ 0;
+
+ for (i = 0; i < OD8_SETTING_COUNT; i++) {
+ if (od8_settings->od8_settings_array[i].feature_id) {
+ od8_settings->od8_settings_array[i].min_value =
+ pptable_information->od_settings_min[i];
+ od8_settings->od8_settings_array[i].max_value =
+ pptable_information->od_settings_max[i];
+ od8_settings->od8_settings_array[i].current_value =
+ od8_settings->od8_settings_array[i].default_value;
+ } else {
+ od8_settings->od8_settings_array[i].min_value =
+ 0;
+ od8_settings->od8_settings_array[i].max_value =
+ 0;
+ od8_settings->od8_settings_array[i].current_value =
+ 0;
+ }
+ }
+
+ ret = smum_smc_table_manager(hwmgr, (uint8_t *)od_table, TABLE_OVERDRIVE, false);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to import over drive table!",
+ return ret);
+
+ return 0;
+}
+
+static int vega20_od8_set_settings(
+ struct pp_hwmgr *hwmgr,
+ uint32_t index,
+ uint32_t value)
+{
+ OverDriveTable_t od_table;
+ int ret = 0;
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_od8_single_setting *od8_settings =
+ data->od8_settings.od8_settings_array;
+
+ ret = smum_smc_table_manager(hwmgr, (uint8_t *)(&od_table), TABLE_OVERDRIVE, true);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to export over drive table!",
+ return ret);
+
+ switch(index) {
+ case OD8_SETTING_GFXCLK_FMIN:
+ od_table.GfxclkFmin = (uint16_t)value;
+ break;
+ case OD8_SETTING_GFXCLK_FMAX:
+ if (value < od8_settings[OD8_SETTING_GFXCLK_FMAX].min_value ||
+ value > od8_settings[OD8_SETTING_GFXCLK_FMAX].max_value)
+ return -EINVAL;
+
+ od_table.GfxclkFmax = (uint16_t)value;
+ break;
+ case OD8_SETTING_GFXCLK_FREQ1:
+ od_table.GfxclkFreq1 = (uint16_t)value;
+ break;
+ case OD8_SETTING_GFXCLK_VOLTAGE1:
+ od_table.GfxclkVolt1 = (uint16_t)value;
+ break;
+ case OD8_SETTING_GFXCLK_FREQ2:
+ od_table.GfxclkFreq2 = (uint16_t)value;
+ break;
+ case OD8_SETTING_GFXCLK_VOLTAGE2:
+ od_table.GfxclkVolt2 = (uint16_t)value;
+ break;
+ case OD8_SETTING_GFXCLK_FREQ3:
+ od_table.GfxclkFreq3 = (uint16_t)value;
+ break;
+ case OD8_SETTING_GFXCLK_VOLTAGE3:
+ od_table.GfxclkVolt3 = (uint16_t)value;
+ break;
+ case OD8_SETTING_UCLK_FMAX:
+ if (value < od8_settings[OD8_SETTING_UCLK_FMAX].min_value ||
+ value > od8_settings[OD8_SETTING_UCLK_FMAX].max_value)
+ return -EINVAL;
+ od_table.UclkFmax = (uint16_t)value;
+ break;
+ case OD8_SETTING_POWER_PERCENTAGE:
+ od_table.OverDrivePct = (int16_t)value;
+ break;
+ case OD8_SETTING_FAN_ACOUSTIC_LIMIT:
+ od_table.FanMaximumRpm = (uint16_t)value;
+ break;
+ case OD8_SETTING_FAN_MIN_SPEED:
+ od_table.FanMinimumPwm = (uint16_t)value;
+ break;
+ case OD8_SETTING_FAN_TARGET_TEMP:
+ od_table.FanTargetTemperature = (uint16_t)value;
+ break;
+ case OD8_SETTING_OPERATING_TEMP_MAX:
+ od_table.MaxOpTemp = (uint16_t)value;
+ break;
+ }
+
+ ret = smum_smc_table_manager(hwmgr, (uint8_t *)(&od_table), TABLE_OVERDRIVE, false);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to import over drive table!",
+ return ret);
+
+ return 0;
+}
+
+static int vega20_get_sclk_od(
+ struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data = hwmgr->backend;
+ struct vega20_single_dpm_table *sclk_table =
+ &(data->dpm_table.gfx_table);
+ struct vega20_single_dpm_table *golden_sclk_table =
+ &(data->golden_dpm_table.gfx_table);
+ int value = sclk_table->dpm_levels[sclk_table->count - 1].value;
+ int golden_value = golden_sclk_table->dpm_levels
+ [golden_sclk_table->count - 1].value;
+
+ /* od percentage */
+ value -= golden_value;
+ value = DIV_ROUND_UP(value * 100, golden_value);
+
+ return value;
+}
+
+static int vega20_set_sclk_od(
+ struct pp_hwmgr *hwmgr, uint32_t value)
+{
+ struct vega20_hwmgr *data = hwmgr->backend;
+ struct vega20_single_dpm_table *golden_sclk_table =
+ &(data->golden_dpm_table.gfx_table);
+ uint32_t od_sclk;
+ int ret = 0;
+
+ od_sclk = golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value * value;
+ od_sclk /= 100;
+ od_sclk += golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value;
+
+ ret = vega20_od8_set_settings(hwmgr, OD8_SETTING_GFXCLK_FMAX, od_sclk);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetSclkOD] failed to set od gfxclk!",
+ return ret);
+
+ /* retrieve updated gfxclk table */
+ ret = vega20_setup_gfxclk_dpm_table(hwmgr);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetSclkOD] failed to refresh gfxclk table!",
+ return ret);
+
+ return 0;
+}
+
+static int vega20_get_mclk_od(
+ struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data = hwmgr->backend;
+ struct vega20_single_dpm_table *mclk_table =
+ &(data->dpm_table.mem_table);
+ struct vega20_single_dpm_table *golden_mclk_table =
+ &(data->golden_dpm_table.mem_table);
+ int value = mclk_table->dpm_levels[mclk_table->count - 1].value;
+ int golden_value = golden_mclk_table->dpm_levels
+ [golden_mclk_table->count - 1].value;
+
+ /* od percentage */
+ value -= golden_value;
+ value = DIV_ROUND_UP(value * 100, golden_value);
+
+ return value;
+}
+
+static int vega20_set_mclk_od(
+ struct pp_hwmgr *hwmgr, uint32_t value)
+{
+ struct vega20_hwmgr *data = hwmgr->backend;
+ struct vega20_single_dpm_table *golden_mclk_table =
+ &(data->golden_dpm_table.mem_table);
+ uint32_t od_mclk;
+ int ret = 0;
+
+ od_mclk = golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value * value;
+ od_mclk /= 100;
+ od_mclk += golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value;
+
+ ret = vega20_od8_set_settings(hwmgr, OD8_SETTING_UCLK_FMAX, od_mclk);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetMclkOD] failed to set od memclk!",
+ return ret);
+
+ /* retrieve updated memclk table */
+ ret = vega20_setup_memclk_dpm_table(hwmgr);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[SetMclkOD] failed to refresh memclk table!",
+ return ret);
+
+ return 0;
+}
+
+static int vega20_populate_umdpstate_clocks(
+ struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_single_dpm_table *gfx_table = &(data->dpm_table.gfx_table);
+ struct vega20_single_dpm_table *mem_table = &(data->dpm_table.mem_table);
+
+ hwmgr->pstate_sclk = gfx_table->dpm_levels[0].value;
+ hwmgr->pstate_mclk = mem_table->dpm_levels[0].value;
+
+ if (gfx_table->count > VEGA20_UMD_PSTATE_GFXCLK_LEVEL &&
+ mem_table->count > VEGA20_UMD_PSTATE_MCLK_LEVEL) {
+ hwmgr->pstate_sclk = gfx_table->dpm_levels[VEGA20_UMD_PSTATE_GFXCLK_LEVEL].value;
+ hwmgr->pstate_mclk = mem_table->dpm_levels[VEGA20_UMD_PSTATE_MCLK_LEVEL].value;
+ }
+
+ hwmgr->pstate_sclk = hwmgr->pstate_sclk * 100;
+ hwmgr->pstate_mclk = hwmgr->pstate_mclk * 100;
+
+ return 0;
+}
+
+static int vega20_get_max_sustainable_clock(struct pp_hwmgr *hwmgr,
+ PP_Clock *clock, PPCLK_e clock_select)
+{
+ int ret = 0;
+
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_GetDcModeMaxDpmFreq,
+ (clock_select << 16),
+ clock)) == 0,
+ "[GetMaxSustainableClock] Failed to get max DC clock from SMC!",
+ return ret);
+
+ /* if DC limit is zero, return AC limit */
+ if (*clock == 0) {
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_GetMaxDpmFreq,
+ (clock_select << 16),
+ clock)) == 0,
+ "[GetMaxSustainableClock] failed to get max AC clock from SMC!",
+ return ret);
+ }
+
+ return 0;
+}
+
+static int vega20_init_max_sustainable_clocks(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_max_sustainable_clocks *max_sustainable_clocks =
+ &(data->max_sustainable_clocks);
+ int ret = 0;
+
+ max_sustainable_clocks->uclock = data->vbios_boot_state.mem_clock / 100;
+ max_sustainable_clocks->soc_clock = data->vbios_boot_state.soc_clock / 100;
+ max_sustainable_clocks->dcef_clock = data->vbios_boot_state.dcef_clock / 100;
+ max_sustainable_clocks->display_clock = 0xFFFFFFFF;
+ max_sustainable_clocks->phy_clock = 0xFFFFFFFF;
+ max_sustainable_clocks->pixel_clock = 0xFFFFFFFF;
+
+ if (data->smu_features[GNLD_DPM_UCLK].enabled)
+ PP_ASSERT_WITH_CODE((ret = vega20_get_max_sustainable_clock(hwmgr,
+ &(max_sustainable_clocks->uclock),
+ PPCLK_UCLK)) == 0,
+ "[InitMaxSustainableClocks] failed to get max UCLK from SMC!",
+ return ret);
+
+ if (data->smu_features[GNLD_DPM_SOCCLK].enabled)
+ PP_ASSERT_WITH_CODE((ret = vega20_get_max_sustainable_clock(hwmgr,
+ &(max_sustainable_clocks->soc_clock),
+ PPCLK_SOCCLK)) == 0,
+ "[InitMaxSustainableClocks] failed to get max SOCCLK from SMC!",
+ return ret);
+
+ if (data->smu_features[GNLD_DPM_DCEFCLK].enabled) {
+ PP_ASSERT_WITH_CODE((ret = vega20_get_max_sustainable_clock(hwmgr,
+ &(max_sustainable_clocks->dcef_clock),
+ PPCLK_DCEFCLK)) == 0,
+ "[InitMaxSustainableClocks] failed to get max DCEFCLK from SMC!",
+ return ret);
+ PP_ASSERT_WITH_CODE((ret = vega20_get_max_sustainable_clock(hwmgr,
+ &(max_sustainable_clocks->display_clock),
+ PPCLK_DISPCLK)) == 0,
+ "[InitMaxSustainableClocks] failed to get max DISPCLK from SMC!",
+ return ret);
+ PP_ASSERT_WITH_CODE((ret = vega20_get_max_sustainable_clock(hwmgr,
+ &(max_sustainable_clocks->phy_clock),
+ PPCLK_PHYCLK)) == 0,
+ "[InitMaxSustainableClocks] failed to get max PHYCLK from SMC!",
+ return ret);
+ PP_ASSERT_WITH_CODE((ret = vega20_get_max_sustainable_clock(hwmgr,
+ &(max_sustainable_clocks->pixel_clock),
+ PPCLK_PIXCLK)) == 0,
+ "[InitMaxSustainableClocks] failed to get max PIXCLK from SMC!",
+ return ret);
+ }
+
+ if (max_sustainable_clocks->soc_clock < max_sustainable_clocks->uclock)
+ max_sustainable_clocks->uclock = max_sustainable_clocks->soc_clock;
+
+ return 0;
+}
+
+static int vega20_enable_mgpu_fan_boost(struct pp_hwmgr *hwmgr)
+{
+ int result;
+
+ result = smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_SetMGpuFanBoostLimitRpm,
+ NULL);
+ PP_ASSERT_WITH_CODE(!result,
+ "[EnableMgpuFan] Failed to enable mgpu fan boost!",
+ return result);
+
+ return 0;
+}
+
+static void vega20_init_powergate_state(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+
+ data->uvd_power_gated = true;
+ data->vce_power_gated = true;
+}
+
+static int vega20_enable_dpm_tasks(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_NumOfDisplays, 0, NULL);
+
+ result = vega20_set_allowed_featuresmask(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "[EnableDPMTasks] Failed to set allowed featuresmask!\n",
+ return result);
+
+ result = vega20_init_smc_table(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "[EnableDPMTasks] Failed to initialize SMC table!",
+ return result);
+
+ result = vega20_run_btc(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "[EnableDPMTasks] Failed to run btc!",
+ return result);
+
+ result = vega20_run_btc_afll(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "[EnableDPMTasks] Failed to run btc afll!",
+ return result);
+
+ result = vega20_enable_all_smu_features(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "[EnableDPMTasks] Failed to enable all smu features!",
+ return result);
+
+ result = vega20_override_pcie_parameters(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "[EnableDPMTasks] Failed to override pcie parameters!",
+ return result);
+
+ result = vega20_notify_smc_display_change(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "[EnableDPMTasks] Failed to notify smc display change!",
+ return result);
+
+ result = vega20_send_clock_ratio(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "[EnableDPMTasks] Failed to send clock ratio!",
+ return result);
+
+ /* Initialize UVD/VCE powergating state */
+ vega20_init_powergate_state(hwmgr);
+
+ result = vega20_setup_default_dpm_tables(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "[EnableDPMTasks] Failed to setup default DPM tables!",
+ return result);
+
+ result = vega20_init_max_sustainable_clocks(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "[EnableDPMTasks] Failed to get maximum sustainable clocks!",
+ return result);
+
+ result = vega20_power_control_set_level(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "[EnableDPMTasks] Failed to power control set level!",
+ return result);
+
+ result = vega20_od8_initialize_default_settings(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "[EnableDPMTasks] Failed to initialize odn settings!",
+ return result);
+
+ result = vega20_populate_umdpstate_clocks(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "[EnableDPMTasks] Failed to populate umdpstate clocks!",
+ return result);
+
+ result = smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_GetPptLimit,
+ POWER_SOURCE_AC << 16, &hwmgr->default_power_limit);
+ PP_ASSERT_WITH_CODE(!result,
+ "[GetPptLimit] get default PPT limit failed!",
+ return result);
+ hwmgr->power_limit =
+ hwmgr->default_power_limit;
+
+ return 0;
+}
+
+static uint32_t vega20_find_lowest_dpm_level(
+ struct vega20_single_dpm_table *table)
+{
+ uint32_t i;
+
+ for (i = 0; i < table->count; i++) {
+ if (table->dpm_levels[i].enabled)
+ break;
+ }
+ if (i >= table->count) {
+ i = 0;
+ table->dpm_levels[i].enabled = true;
+ }
+
+ return i;
+}
+
+static uint32_t vega20_find_highest_dpm_level(
+ struct vega20_single_dpm_table *table)
+{
+ int i = 0;
+
+ PP_ASSERT_WITH_CODE(table != NULL,
+ "[FindHighestDPMLevel] DPM Table does not exist!",
+ return 0);
+ PP_ASSERT_WITH_CODE(table->count > 0,
+ "[FindHighestDPMLevel] DPM Table has no entry!",
+ return 0);
+ PP_ASSERT_WITH_CODE(table->count <= MAX_REGULAR_DPM_NUMBER,
+ "[FindHighestDPMLevel] DPM Table has too many entries!",
+ return MAX_REGULAR_DPM_NUMBER - 1);
+
+ for (i = table->count - 1; i >= 0; i--) {
+ if (table->dpm_levels[i].enabled)
+ break;
+ }
+ if (i < 0) {
+ i = 0;
+ table->dpm_levels[i].enabled = true;
+ }
+
+ return i;
+}
+
+static int vega20_upload_dpm_min_level(struct pp_hwmgr *hwmgr, uint32_t feature_mask)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ uint32_t min_freq;
+ int ret = 0;
+
+ if (data->smu_features[GNLD_DPM_GFXCLK].enabled &&
+ (feature_mask & FEATURE_DPM_GFXCLK_MASK)) {
+ min_freq = data->dpm_table.gfx_table.dpm_state.soft_min_level;
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMinByFreq,
+ (PPCLK_GFXCLK << 16) | (min_freq & 0xffff),
+ NULL)),
+ "Failed to set soft min gfxclk !",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_UCLK].enabled &&
+ (feature_mask & FEATURE_DPM_UCLK_MASK)) {
+ min_freq = data->dpm_table.mem_table.dpm_state.soft_min_level;
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMinByFreq,
+ (PPCLK_UCLK << 16) | (min_freq & 0xffff),
+ NULL)),
+ "Failed to set soft min memclk !",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_UVD].enabled &&
+ (feature_mask & FEATURE_DPM_UVD_MASK)) {
+ min_freq = data->dpm_table.vclk_table.dpm_state.soft_min_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMinByFreq,
+ (PPCLK_VCLK << 16) | (min_freq & 0xffff),
+ NULL)),
+ "Failed to set soft min vclk!",
+ return ret);
+
+ min_freq = data->dpm_table.dclk_table.dpm_state.soft_min_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMinByFreq,
+ (PPCLK_DCLK << 16) | (min_freq & 0xffff),
+ NULL)),
+ "Failed to set soft min dclk!",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_VCE].enabled &&
+ (feature_mask & FEATURE_DPM_VCE_MASK)) {
+ min_freq = data->dpm_table.eclk_table.dpm_state.soft_min_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMinByFreq,
+ (PPCLK_ECLK << 16) | (min_freq & 0xffff),
+ NULL)),
+ "Failed to set soft min eclk!",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_SOCCLK].enabled &&
+ (feature_mask & FEATURE_DPM_SOCCLK_MASK)) {
+ min_freq = data->dpm_table.soc_table.dpm_state.soft_min_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMinByFreq,
+ (PPCLK_SOCCLK << 16) | (min_freq & 0xffff),
+ NULL)),
+ "Failed to set soft min socclk!",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_FCLK].enabled &&
+ (feature_mask & FEATURE_DPM_FCLK_MASK)) {
+ min_freq = data->dpm_table.fclk_table.dpm_state.soft_min_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMinByFreq,
+ (PPCLK_FCLK << 16) | (min_freq & 0xffff),
+ NULL)),
+ "Failed to set soft min fclk!",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_DCEFCLK].enabled &&
+ (feature_mask & FEATURE_DPM_DCEFCLK_MASK)) {
+ min_freq = data->dpm_table.dcef_table.dpm_state.hard_min_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetHardMinByFreq,
+ (PPCLK_DCEFCLK << 16) | (min_freq & 0xffff),
+ NULL)),
+ "Failed to set hard min dcefclk!",
+ return ret);
+ }
+
+ return ret;
+}
+
+static int vega20_upload_dpm_max_level(struct pp_hwmgr *hwmgr, uint32_t feature_mask)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ uint32_t max_freq;
+ int ret = 0;
+
+ if (data->smu_features[GNLD_DPM_GFXCLK].enabled &&
+ (feature_mask & FEATURE_DPM_GFXCLK_MASK)) {
+ max_freq = data->dpm_table.gfx_table.dpm_state.soft_max_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMaxByFreq,
+ (PPCLK_GFXCLK << 16) | (max_freq & 0xffff),
+ NULL)),
+ "Failed to set soft max gfxclk!",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_UCLK].enabled &&
+ (feature_mask & FEATURE_DPM_UCLK_MASK)) {
+ max_freq = data->dpm_table.mem_table.dpm_state.soft_max_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMaxByFreq,
+ (PPCLK_UCLK << 16) | (max_freq & 0xffff),
+ NULL)),
+ "Failed to set soft max memclk!",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_UVD].enabled &&
+ (feature_mask & FEATURE_DPM_UVD_MASK)) {
+ max_freq = data->dpm_table.vclk_table.dpm_state.soft_max_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMaxByFreq,
+ (PPCLK_VCLK << 16) | (max_freq & 0xffff),
+ NULL)),
+ "Failed to set soft max vclk!",
+ return ret);
+
+ max_freq = data->dpm_table.dclk_table.dpm_state.soft_max_level;
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMaxByFreq,
+ (PPCLK_DCLK << 16) | (max_freq & 0xffff),
+ NULL)),
+ "Failed to set soft max dclk!",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_VCE].enabled &&
+ (feature_mask & FEATURE_DPM_VCE_MASK)) {
+ max_freq = data->dpm_table.eclk_table.dpm_state.soft_max_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMaxByFreq,
+ (PPCLK_ECLK << 16) | (max_freq & 0xffff),
+ NULL)),
+ "Failed to set soft max eclk!",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_SOCCLK].enabled &&
+ (feature_mask & FEATURE_DPM_SOCCLK_MASK)) {
+ max_freq = data->dpm_table.soc_table.dpm_state.soft_max_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMaxByFreq,
+ (PPCLK_SOCCLK << 16) | (max_freq & 0xffff),
+ NULL)),
+ "Failed to set soft max socclk!",
+ return ret);
+ }
+
+ if (data->smu_features[GNLD_DPM_FCLK].enabled &&
+ (feature_mask & FEATURE_DPM_FCLK_MASK)) {
+ max_freq = data->dpm_table.fclk_table.dpm_state.soft_max_level;
+
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetSoftMaxByFreq,
+ (PPCLK_FCLK << 16) | (max_freq & 0xffff),
+ NULL)),
+ "Failed to set soft max fclk!",
+ return ret);
+ }
+
+ return ret;
+}
+
+static int vega20_enable_disable_vce_dpm(struct pp_hwmgr *hwmgr, bool enable)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ int ret = 0;
+
+ if (data->smu_features[GNLD_DPM_VCE].supported) {
+ if (data->smu_features[GNLD_DPM_VCE].enabled == enable) {
+ if (enable)
+ PP_DBG_LOG("[EnableDisableVCEDPM] feature VCE DPM already enabled!\n");
+ else
+ PP_DBG_LOG("[EnableDisableVCEDPM] feature VCE DPM already disabled!\n");
+ }
+
+ ret = vega20_enable_smc_features(hwmgr,
+ enable,
+ data->smu_features[GNLD_DPM_VCE].smu_feature_bitmap);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Attempt to Enable/Disable DPM VCE Failed!",
+ return ret);
+ data->smu_features[GNLD_DPM_VCE].enabled = enable;
+ }
+
+ return 0;
+}
+
+static int vega20_get_clock_ranges(struct pp_hwmgr *hwmgr,
+ uint32_t *clock,
+ PPCLK_e clock_select,
+ bool max)
+{
+ int ret;
+ *clock = 0;
+
+ if (max) {
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_GetMaxDpmFreq, (clock_select << 16),
+ clock)) == 0,
+ "[GetClockRanges] Failed to get max clock from SMC!",
+ return ret);
+ } else {
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_GetMinDpmFreq,
+ (clock_select << 16),
+ clock)) == 0,
+ "[GetClockRanges] Failed to get min clock from SMC!",
+ return ret);
+ }
+
+ return 0;
+}
+
+static uint32_t vega20_dpm_get_sclk(struct pp_hwmgr *hwmgr, bool low)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ uint32_t gfx_clk;
+ int ret = 0;
+
+ PP_ASSERT_WITH_CODE(data->smu_features[GNLD_DPM_GFXCLK].enabled,
+ "[GetSclks]: gfxclk dpm not enabled!\n",
+ return -EPERM);
+
+ if (low) {
+ ret = vega20_get_clock_ranges(hwmgr, &gfx_clk, PPCLK_GFXCLK, false);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[GetSclks]: fail to get min PPCLK_GFXCLK\n",
+ return ret);
+ } else {
+ ret = vega20_get_clock_ranges(hwmgr, &gfx_clk, PPCLK_GFXCLK, true);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[GetSclks]: fail to get max PPCLK_GFXCLK\n",
+ return ret);
+ }
+
+ return (gfx_clk * 100);
+}
+
+static uint32_t vega20_dpm_get_mclk(struct pp_hwmgr *hwmgr, bool low)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ uint32_t mem_clk;
+ int ret = 0;
+
+ PP_ASSERT_WITH_CODE(data->smu_features[GNLD_DPM_UCLK].enabled,
+ "[MemMclks]: memclk dpm not enabled!\n",
+ return -EPERM);
+
+ if (low) {
+ ret = vega20_get_clock_ranges(hwmgr, &mem_clk, PPCLK_UCLK, false);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[GetMclks]: fail to get min PPCLK_UCLK\n",
+ return ret);
+ } else {
+ ret = vega20_get_clock_ranges(hwmgr, &mem_clk, PPCLK_UCLK, true);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[GetMclks]: fail to get max PPCLK_UCLK\n",
+ return ret);
+ }
+
+ return (mem_clk * 100);
+}
+
+static int vega20_get_metrics_table(struct pp_hwmgr *hwmgr,
+ SmuMetrics_t *metrics_table,
+ bool bypass_cache)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ int ret = 0;
+
+ if (bypass_cache ||
+ !data->metrics_time ||
+ time_after(jiffies, data->metrics_time + msecs_to_jiffies(1))) {
+ ret = smum_smc_table_manager(hwmgr,
+ (uint8_t *)(&data->metrics_table),
+ TABLE_SMU_METRICS,
+ true);
+ if (ret) {
+ pr_info("Failed to export SMU metrics table!\n");
+ return ret;
+ }
+ data->metrics_time = jiffies;
+ }
+
+ if (metrics_table)
+ memcpy(metrics_table, &data->metrics_table, sizeof(SmuMetrics_t));
+
+ return ret;
+}
+
+static int vega20_get_gpu_power(struct pp_hwmgr *hwmgr,
+ uint32_t *query)
+{
+ int ret = 0;
+ SmuMetrics_t metrics_table;
+
+ ret = vega20_get_metrics_table(hwmgr, &metrics_table, false);
+ if (ret)
+ return ret;
+
+ /* For the 40.46 release, they changed the value name */
+ if (hwmgr->smu_version == 0x282e00)
+ *query = metrics_table.AverageSocketPower << 8;
+ else
+ *query = metrics_table.CurrSocketPower << 8;
+
+ return ret;
+}
+
+static int vega20_get_current_clk_freq(struct pp_hwmgr *hwmgr,
+ PPCLK_e clk_id, uint32_t *clk_freq)
+{
+ int ret = 0;
+
+ *clk_freq = 0;
+
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_GetDpmClockFreq, (clk_id << 16),
+ clk_freq)) == 0,
+ "[GetCurrentClkFreq] Attempt to get Current Frequency Failed!",
+ return ret);
+
+ *clk_freq = *clk_freq * 100;
+
+ return 0;
+}
+
+static int vega20_get_current_activity_percent(struct pp_hwmgr *hwmgr,
+ int idx,
+ uint32_t *activity_percent)
+{
+ int ret = 0;
+ SmuMetrics_t metrics_table;
+
+ ret = vega20_get_metrics_table(hwmgr, &metrics_table, false);
+ if (ret)
+ return ret;
+
+ switch (idx) {
+ case AMDGPU_PP_SENSOR_GPU_LOAD:
+ *activity_percent = metrics_table.AverageGfxActivity;
+ break;
+ case AMDGPU_PP_SENSOR_MEM_LOAD:
+ *activity_percent = metrics_table.AverageUclkActivity;
+ break;
+ default:
+ pr_err("Invalid index for retrieving clock activity\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int vega20_read_sensor(struct pp_hwmgr *hwmgr, int idx,
+ void *value, int *size)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+ SmuMetrics_t metrics_table;
+ uint32_t val_vid;
+ int ret = 0;
+
+ switch (idx) {
+ case AMDGPU_PP_SENSOR_GFX_SCLK:
+ ret = vega20_get_metrics_table(hwmgr, &metrics_table, false);
+ if (ret)
+ return ret;
+
+ *((uint32_t *)value) = metrics_table.AverageGfxclkFrequency * 100;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GFX_MCLK:
+ ret = vega20_get_current_clk_freq(hwmgr,
+ PPCLK_UCLK,
+ (uint32_t *)value);
+ if (!ret)
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GPU_LOAD:
+ case AMDGPU_PP_SENSOR_MEM_LOAD:
+ ret = vega20_get_current_activity_percent(hwmgr, idx, (uint32_t *)value);
+ if (!ret)
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_HOTSPOT_TEMP:
+ *((uint32_t *)value) = vega20_thermal_get_temperature(hwmgr);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_EDGE_TEMP:
+ ret = vega20_get_metrics_table(hwmgr, &metrics_table, false);
+ if (ret)
+ return ret;
+
+ *((uint32_t *)value) = metrics_table.TemperatureEdge *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_MEM_TEMP:
+ ret = vega20_get_metrics_table(hwmgr, &metrics_table, false);
+ if (ret)
+ return ret;
+
+ *((uint32_t *)value) = metrics_table.TemperatureHBM *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_UVD_POWER:
+ *((uint32_t *)value) = data->uvd_power_gated ? 0 : 1;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_VCE_POWER:
+ *((uint32_t *)value) = data->vce_power_gated ? 0 : 1;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GPU_POWER:
+ *size = 16;
+ ret = vega20_get_gpu_power(hwmgr, (uint32_t *)value);
+ break;
+ case AMDGPU_PP_SENSOR_VDDGFX:
+ val_vid = (RREG32_SOC15(SMUIO, 0, mmSMUSVI0_TEL_PLANE0) &
+ SMUSVI0_TEL_PLANE0__SVI0_PLANE0_VDDCOR_MASK) >>
+ SMUSVI0_TEL_PLANE0__SVI0_PLANE0_VDDCOR__SHIFT;
+ *((uint32_t *)value) =
+ (uint32_t)convert_to_vddc((uint8_t)val_vid);
+ break;
+ case AMDGPU_PP_SENSOR_ENABLED_SMC_FEATURES_MASK:
+ ret = vega20_get_enabled_smc_features(hwmgr, (uint64_t *)value);
+ if (!ret)
+ *size = 8;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int vega20_display_clock_voltage_request(struct pp_hwmgr *hwmgr,
+ struct pp_display_clock_request *clock_req)
+{
+ int result = 0;
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ enum amd_pp_clock_type clk_type = clock_req->clock_type;
+ uint32_t clk_freq = clock_req->clock_freq_in_khz / 1000;
+ PPCLK_e clk_select = 0;
+ uint32_t clk_request = 0;
+
+ if (data->smu_features[GNLD_DPM_DCEFCLK].enabled) {
+ switch (clk_type) {
+ case amd_pp_dcef_clock:
+ clk_select = PPCLK_DCEFCLK;
+ break;
+ case amd_pp_disp_clock:
+ clk_select = PPCLK_DISPCLK;
+ break;
+ case amd_pp_pixel_clock:
+ clk_select = PPCLK_PIXCLK;
+ break;
+ case amd_pp_phy_clock:
+ clk_select = PPCLK_PHYCLK;
+ break;
+ default:
+ pr_info("[DisplayClockVoltageRequest]Invalid Clock Type!");
+ result = -EINVAL;
+ break;
+ }
+
+ if (!result) {
+ clk_request = (clk_select << 16) | clk_freq;
+ result = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinByFreq,
+ clk_request,
+ NULL);
+ }
+ }
+
+ return result;
+}
+
+static int vega20_get_performance_level(struct pp_hwmgr *hwmgr, const struct pp_hw_power_state *state,
+ PHM_PerformanceLevelDesignation designation, uint32_t index,
+ PHM_PerformanceLevel *level)
+{
+ return 0;
+}
+
+static int vega20_notify_smc_display_config_after_ps_adjustment(
+ struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_single_dpm_table *dpm_table =
+ &data->dpm_table.mem_table;
+ struct PP_Clocks min_clocks = {0};
+ struct pp_display_clock_request clock_req;
+ int ret = 0;
+
+ min_clocks.dcefClock = hwmgr->display_config->min_dcef_set_clk;
+ min_clocks.dcefClockInSR = hwmgr->display_config->min_dcef_deep_sleep_set_clk;
+ min_clocks.memoryClock = hwmgr->display_config->min_mem_set_clock;
+
+ if (data->smu_features[GNLD_DPM_DCEFCLK].supported) {
+ clock_req.clock_type = amd_pp_dcef_clock;
+ clock_req.clock_freq_in_khz = min_clocks.dcefClock * 10;
+ if (!vega20_display_clock_voltage_request(hwmgr, &clock_req)) {
+ if (data->smu_features[GNLD_DS_DCEFCLK].supported)
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(
+ hwmgr, PPSMC_MSG_SetMinDeepSleepDcefclk,
+ min_clocks.dcefClockInSR / 100,
+ NULL)) == 0,
+ "Attempt to set divider for DCEFCLK Failed!",
+ return ret);
+ } else {
+ pr_info("Attempt to set Hard Min for DCEFCLK Failed!");
+ }
+ }
+
+ if (data->smu_features[GNLD_DPM_UCLK].enabled) {
+ dpm_table->dpm_state.hard_min_level = min_clocks.memoryClock / 100;
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinByFreq,
+ (PPCLK_UCLK << 16 ) | dpm_table->dpm_state.hard_min_level,
+ NULL)),
+ "[SetHardMinFreq] Set hard min uclk failed!",
+ return ret);
+ }
+
+ return 0;
+}
+
+static int vega20_force_dpm_highest(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ uint32_t soft_level;
+ int ret = 0;
+
+ soft_level = vega20_find_highest_dpm_level(&(data->dpm_table.gfx_table));
+
+ data->dpm_table.gfx_table.dpm_state.soft_min_level =
+ data->dpm_table.gfx_table.dpm_state.soft_max_level =
+ data->dpm_table.gfx_table.dpm_levels[soft_level].value;
+
+ soft_level = vega20_find_highest_dpm_level(&(data->dpm_table.mem_table));
+
+ data->dpm_table.mem_table.dpm_state.soft_min_level =
+ data->dpm_table.mem_table.dpm_state.soft_max_level =
+ data->dpm_table.mem_table.dpm_levels[soft_level].value;
+
+ soft_level = vega20_find_highest_dpm_level(&(data->dpm_table.soc_table));
+
+ data->dpm_table.soc_table.dpm_state.soft_min_level =
+ data->dpm_table.soc_table.dpm_state.soft_max_level =
+ data->dpm_table.soc_table.dpm_levels[soft_level].value;
+
+ ret = vega20_upload_dpm_min_level(hwmgr, FEATURE_DPM_GFXCLK_MASK |
+ FEATURE_DPM_UCLK_MASK |
+ FEATURE_DPM_SOCCLK_MASK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload boot level to highest!",
+ return ret);
+
+ ret = vega20_upload_dpm_max_level(hwmgr, FEATURE_DPM_GFXCLK_MASK |
+ FEATURE_DPM_UCLK_MASK |
+ FEATURE_DPM_SOCCLK_MASK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload dpm max level to highest!",
+ return ret);
+
+ return 0;
+}
+
+static int vega20_force_dpm_lowest(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ uint32_t soft_level;
+ int ret = 0;
+
+ soft_level = vega20_find_lowest_dpm_level(&(data->dpm_table.gfx_table));
+
+ data->dpm_table.gfx_table.dpm_state.soft_min_level =
+ data->dpm_table.gfx_table.dpm_state.soft_max_level =
+ data->dpm_table.gfx_table.dpm_levels[soft_level].value;
+
+ soft_level = vega20_find_lowest_dpm_level(&(data->dpm_table.mem_table));
+
+ data->dpm_table.mem_table.dpm_state.soft_min_level =
+ data->dpm_table.mem_table.dpm_state.soft_max_level =
+ data->dpm_table.mem_table.dpm_levels[soft_level].value;
+
+ soft_level = vega20_find_lowest_dpm_level(&(data->dpm_table.soc_table));
+
+ data->dpm_table.soc_table.dpm_state.soft_min_level =
+ data->dpm_table.soc_table.dpm_state.soft_max_level =
+ data->dpm_table.soc_table.dpm_levels[soft_level].value;
+
+ ret = vega20_upload_dpm_min_level(hwmgr, FEATURE_DPM_GFXCLK_MASK |
+ FEATURE_DPM_UCLK_MASK |
+ FEATURE_DPM_SOCCLK_MASK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload boot level to highest!",
+ return ret);
+
+ ret = vega20_upload_dpm_max_level(hwmgr, FEATURE_DPM_GFXCLK_MASK |
+ FEATURE_DPM_UCLK_MASK |
+ FEATURE_DPM_SOCCLK_MASK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload dpm max level to highest!",
+ return ret);
+
+ return 0;
+
+}
+
+static int vega20_unforce_dpm_levels(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ uint32_t soft_min_level, soft_max_level;
+ int ret = 0;
+
+ /* gfxclk soft min/max settings */
+ soft_min_level =
+ vega20_find_lowest_dpm_level(&(data->dpm_table.gfx_table));
+ soft_max_level =
+ vega20_find_highest_dpm_level(&(data->dpm_table.gfx_table));
+
+ data->dpm_table.gfx_table.dpm_state.soft_min_level =
+ data->dpm_table.gfx_table.dpm_levels[soft_min_level].value;
+ data->dpm_table.gfx_table.dpm_state.soft_max_level =
+ data->dpm_table.gfx_table.dpm_levels[soft_max_level].value;
+
+ /* uclk soft min/max settings */
+ soft_min_level =
+ vega20_find_lowest_dpm_level(&(data->dpm_table.mem_table));
+ soft_max_level =
+ vega20_find_highest_dpm_level(&(data->dpm_table.mem_table));
+
+ data->dpm_table.mem_table.dpm_state.soft_min_level =
+ data->dpm_table.mem_table.dpm_levels[soft_min_level].value;
+ data->dpm_table.mem_table.dpm_state.soft_max_level =
+ data->dpm_table.mem_table.dpm_levels[soft_max_level].value;
+
+ /* socclk soft min/max settings */
+ soft_min_level =
+ vega20_find_lowest_dpm_level(&(data->dpm_table.soc_table));
+ soft_max_level =
+ vega20_find_highest_dpm_level(&(data->dpm_table.soc_table));
+
+ data->dpm_table.soc_table.dpm_state.soft_min_level =
+ data->dpm_table.soc_table.dpm_levels[soft_min_level].value;
+ data->dpm_table.soc_table.dpm_state.soft_max_level =
+ data->dpm_table.soc_table.dpm_levels[soft_max_level].value;
+
+ ret = vega20_upload_dpm_min_level(hwmgr, FEATURE_DPM_GFXCLK_MASK |
+ FEATURE_DPM_UCLK_MASK |
+ FEATURE_DPM_SOCCLK_MASK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload DPM Bootup Levels!",
+ return ret);
+
+ ret = vega20_upload_dpm_max_level(hwmgr, FEATURE_DPM_GFXCLK_MASK |
+ FEATURE_DPM_UCLK_MASK |
+ FEATURE_DPM_SOCCLK_MASK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload DPM Max Levels!",
+ return ret);
+
+ return 0;
+}
+
+static int vega20_get_profiling_clk_mask(struct pp_hwmgr *hwmgr, enum amd_dpm_forced_level level,
+ uint32_t *sclk_mask, uint32_t *mclk_mask, uint32_t *soc_mask)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_single_dpm_table *gfx_dpm_table = &(data->dpm_table.gfx_table);
+ struct vega20_single_dpm_table *mem_dpm_table = &(data->dpm_table.mem_table);
+ struct vega20_single_dpm_table *soc_dpm_table = &(data->dpm_table.soc_table);
+
+ *sclk_mask = 0;
+ *mclk_mask = 0;
+ *soc_mask = 0;
+
+ if (gfx_dpm_table->count > VEGA20_UMD_PSTATE_GFXCLK_LEVEL &&
+ mem_dpm_table->count > VEGA20_UMD_PSTATE_MCLK_LEVEL &&
+ soc_dpm_table->count > VEGA20_UMD_PSTATE_SOCCLK_LEVEL) {
+ *sclk_mask = VEGA20_UMD_PSTATE_GFXCLK_LEVEL;
+ *mclk_mask = VEGA20_UMD_PSTATE_MCLK_LEVEL;
+ *soc_mask = VEGA20_UMD_PSTATE_SOCCLK_LEVEL;
+ }
+
+ if (level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK) {
+ *sclk_mask = 0;
+ } else if (level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK) {
+ *mclk_mask = 0;
+ } else if (level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ *sclk_mask = gfx_dpm_table->count - 1;
+ *mclk_mask = mem_dpm_table->count - 1;
+ *soc_mask = soc_dpm_table->count - 1;
+ }
+
+ return 0;
+}
+
+static int vega20_force_clock_level(struct pp_hwmgr *hwmgr,
+ enum pp_clock_type type, uint32_t mask)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ uint32_t soft_min_level, soft_max_level, hard_min_level;
+ int ret = 0;
+
+ switch (type) {
+ case PP_SCLK:
+ soft_min_level = mask ? (ffs(mask) - 1) : 0;
+ soft_max_level = mask ? (fls(mask) - 1) : 0;
+
+ if (soft_max_level >= data->dpm_table.gfx_table.count) {
+ pr_err("Clock level specified %d is over max allowed %d\n",
+ soft_max_level,
+ data->dpm_table.gfx_table.count - 1);
+ return -EINVAL;
+ }
+
+ data->dpm_table.gfx_table.dpm_state.soft_min_level =
+ data->dpm_table.gfx_table.dpm_levels[soft_min_level].value;
+ data->dpm_table.gfx_table.dpm_state.soft_max_level =
+ data->dpm_table.gfx_table.dpm_levels[soft_max_level].value;
+
+ ret = vega20_upload_dpm_min_level(hwmgr, FEATURE_DPM_GFXCLK_MASK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload boot level to lowest!",
+ return ret);
+
+ ret = vega20_upload_dpm_max_level(hwmgr, FEATURE_DPM_GFXCLK_MASK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload dpm max level to highest!",
+ return ret);
+ break;
+
+ case PP_MCLK:
+ soft_min_level = mask ? (ffs(mask) - 1) : 0;
+ soft_max_level = mask ? (fls(mask) - 1) : 0;
+
+ if (soft_max_level >= data->dpm_table.mem_table.count) {
+ pr_err("Clock level specified %d is over max allowed %d\n",
+ soft_max_level,
+ data->dpm_table.mem_table.count - 1);
+ return -EINVAL;
+ }
+
+ data->dpm_table.mem_table.dpm_state.soft_min_level =
+ data->dpm_table.mem_table.dpm_levels[soft_min_level].value;
+ data->dpm_table.mem_table.dpm_state.soft_max_level =
+ data->dpm_table.mem_table.dpm_levels[soft_max_level].value;
+
+ ret = vega20_upload_dpm_min_level(hwmgr, FEATURE_DPM_UCLK_MASK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload boot level to lowest!",
+ return ret);
+
+ ret = vega20_upload_dpm_max_level(hwmgr, FEATURE_DPM_UCLK_MASK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload dpm max level to highest!",
+ return ret);
+
+ break;
+
+ case PP_SOCCLK:
+ soft_min_level = mask ? (ffs(mask) - 1) : 0;
+ soft_max_level = mask ? (fls(mask) - 1) : 0;
+
+ if (soft_max_level >= data->dpm_table.soc_table.count) {
+ pr_err("Clock level specified %d is over max allowed %d\n",
+ soft_max_level,
+ data->dpm_table.soc_table.count - 1);
+ return -EINVAL;
+ }
+
+ data->dpm_table.soc_table.dpm_state.soft_min_level =
+ data->dpm_table.soc_table.dpm_levels[soft_min_level].value;
+ data->dpm_table.soc_table.dpm_state.soft_max_level =
+ data->dpm_table.soc_table.dpm_levels[soft_max_level].value;
+
+ ret = vega20_upload_dpm_min_level(hwmgr, FEATURE_DPM_SOCCLK_MASK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload boot level to lowest!",
+ return ret);
+
+ ret = vega20_upload_dpm_max_level(hwmgr, FEATURE_DPM_SOCCLK_MASK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload dpm max level to highest!",
+ return ret);
+
+ break;
+
+ case PP_FCLK:
+ soft_min_level = mask ? (ffs(mask) - 1) : 0;
+ soft_max_level = mask ? (fls(mask) - 1) : 0;
+
+ if (soft_max_level >= data->dpm_table.fclk_table.count) {
+ pr_err("Clock level specified %d is over max allowed %d\n",
+ soft_max_level,
+ data->dpm_table.fclk_table.count - 1);
+ return -EINVAL;
+ }
+
+ data->dpm_table.fclk_table.dpm_state.soft_min_level =
+ data->dpm_table.fclk_table.dpm_levels[soft_min_level].value;
+ data->dpm_table.fclk_table.dpm_state.soft_max_level =
+ data->dpm_table.fclk_table.dpm_levels[soft_max_level].value;
+
+ ret = vega20_upload_dpm_min_level(hwmgr, FEATURE_DPM_FCLK_MASK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload boot level to lowest!",
+ return ret);
+
+ ret = vega20_upload_dpm_max_level(hwmgr, FEATURE_DPM_FCLK_MASK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload dpm max level to highest!",
+ return ret);
+
+ break;
+
+ case PP_DCEFCLK:
+ hard_min_level = mask ? (ffs(mask) - 1) : 0;
+
+ if (hard_min_level >= data->dpm_table.dcef_table.count) {
+ pr_err("Clock level specified %d is over max allowed %d\n",
+ hard_min_level,
+ data->dpm_table.dcef_table.count - 1);
+ return -EINVAL;
+ }
+
+ data->dpm_table.dcef_table.dpm_state.hard_min_level =
+ data->dpm_table.dcef_table.dpm_levels[hard_min_level].value;
+
+ ret = vega20_upload_dpm_min_level(hwmgr, FEATURE_DPM_DCEFCLK_MASK);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to upload boot level to lowest!",
+ return ret);
+
+ //TODO: Setting DCEFCLK max dpm level is not supported
+
+ break;
+
+ case PP_PCIE:
+ soft_min_level = mask ? (ffs(mask) - 1) : 0;
+ soft_max_level = mask ? (fls(mask) - 1) : 0;
+ if (soft_min_level >= NUM_LINK_LEVELS ||
+ soft_max_level >= NUM_LINK_LEVELS)
+ return -EINVAL;
+
+ ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetMinLinkDpmByIndex, soft_min_level,
+ NULL);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to set min link dpm level!",
+ return ret);
+
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int vega20_dpm_force_dpm_level(struct pp_hwmgr *hwmgr,
+ enum amd_dpm_forced_level level)
+{
+ int ret = 0;
+ uint32_t sclk_mask, mclk_mask, soc_mask;
+
+ switch (level) {
+ case AMD_DPM_FORCED_LEVEL_HIGH:
+ ret = vega20_force_dpm_highest(hwmgr);
+ break;
+
+ case AMD_DPM_FORCED_LEVEL_LOW:
+ ret = vega20_force_dpm_lowest(hwmgr);
+ break;
+
+ case AMD_DPM_FORCED_LEVEL_AUTO:
+ ret = vega20_unforce_dpm_levels(hwmgr);
+ break;
+
+ case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+ ret = vega20_get_profiling_clk_mask(hwmgr, level, &sclk_mask, &mclk_mask, &soc_mask);
+ if (ret)
+ return ret;
+ vega20_force_clock_level(hwmgr, PP_SCLK, 1 << sclk_mask);
+ vega20_force_clock_level(hwmgr, PP_MCLK, 1 << mclk_mask);
+ vega20_force_clock_level(hwmgr, PP_SOCCLK, 1 << soc_mask);
+ break;
+
+ case AMD_DPM_FORCED_LEVEL_MANUAL:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_EXIT:
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static uint32_t vega20_get_fan_control_mode(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+
+ if (data->smu_features[GNLD_FAN_CONTROL].enabled == false)
+ return AMD_FAN_CTRL_MANUAL;
+ else
+ return AMD_FAN_CTRL_AUTO;
+}
+
+static void vega20_set_fan_control_mode(struct pp_hwmgr *hwmgr, uint32_t mode)
+{
+ switch (mode) {
+ case AMD_FAN_CTRL_NONE:
+ vega20_fan_ctrl_set_fan_speed_percent(hwmgr, 100);
+ break;
+ case AMD_FAN_CTRL_MANUAL:
+ if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl))
+ vega20_fan_ctrl_stop_smc_fan_control(hwmgr);
+ break;
+ case AMD_FAN_CTRL_AUTO:
+ if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl))
+ vega20_fan_ctrl_start_smc_fan_control(hwmgr);
+ break;
+ default:
+ break;
+ }
+}
+
+static int vega20_get_dal_power_level(struct pp_hwmgr *hwmgr,
+ struct amd_pp_simple_clock_info *info)
+{
+#if 0
+ struct phm_ppt_v2_information *table_info =
+ (struct phm_ppt_v2_information *)hwmgr->pptable;
+ struct phm_clock_and_voltage_limits *max_limits =
+ &table_info->max_clock_voltage_on_ac;
+
+ info->engine_max_clock = max_limits->sclk;
+ info->memory_max_clock = max_limits->mclk;
+#endif
+ return 0;
+}
+
+
+static int vega20_get_sclks(struct pp_hwmgr *hwmgr,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_single_dpm_table *dpm_table = &(data->dpm_table.gfx_table);
+ int i, count;
+
+ if (!data->smu_features[GNLD_DPM_GFXCLK].enabled)
+ return -1;
+
+ count = (dpm_table->count > MAX_NUM_CLOCKS) ? MAX_NUM_CLOCKS : dpm_table->count;
+ clocks->num_levels = count;
+
+ for (i = 0; i < count; i++) {
+ clocks->data[i].clocks_in_khz =
+ dpm_table->dpm_levels[i].value * 1000;
+ clocks->data[i].latency_in_us = 0;
+ }
+
+ return 0;
+}
+
+static uint32_t vega20_get_mem_latency(struct pp_hwmgr *hwmgr,
+ uint32_t clock)
+{
+ return 25;
+}
+
+static int vega20_get_memclocks(struct pp_hwmgr *hwmgr,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_single_dpm_table *dpm_table = &(data->dpm_table.mem_table);
+ int i, count;
+
+ if (!data->smu_features[GNLD_DPM_UCLK].enabled)
+ return -1;
+
+ count = (dpm_table->count > MAX_NUM_CLOCKS) ? MAX_NUM_CLOCKS : dpm_table->count;
+ clocks->num_levels = data->mclk_latency_table.count = count;
+
+ for (i = 0; i < count; i++) {
+ clocks->data[i].clocks_in_khz =
+ data->mclk_latency_table.entries[i].frequency =
+ dpm_table->dpm_levels[i].value * 1000;
+ clocks->data[i].latency_in_us =
+ data->mclk_latency_table.entries[i].latency =
+ vega20_get_mem_latency(hwmgr, dpm_table->dpm_levels[i].value);
+ }
+
+ return 0;
+}
+
+static int vega20_get_dcefclocks(struct pp_hwmgr *hwmgr,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_single_dpm_table *dpm_table = &(data->dpm_table.dcef_table);
+ int i, count;
+
+ if (!data->smu_features[GNLD_DPM_DCEFCLK].enabled)
+ return -1;
+
+ count = (dpm_table->count > MAX_NUM_CLOCKS) ? MAX_NUM_CLOCKS : dpm_table->count;
+ clocks->num_levels = count;
+
+ for (i = 0; i < count; i++) {
+ clocks->data[i].clocks_in_khz =
+ dpm_table->dpm_levels[i].value * 1000;
+ clocks->data[i].latency_in_us = 0;
+ }
+
+ return 0;
+}
+
+static int vega20_get_socclocks(struct pp_hwmgr *hwmgr,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_single_dpm_table *dpm_table = &(data->dpm_table.soc_table);
+ int i, count;
+
+ if (!data->smu_features[GNLD_DPM_SOCCLK].enabled)
+ return -1;
+
+ count = (dpm_table->count > MAX_NUM_CLOCKS) ? MAX_NUM_CLOCKS : dpm_table->count;
+ clocks->num_levels = count;
+
+ for (i = 0; i < count; i++) {
+ clocks->data[i].clocks_in_khz =
+ dpm_table->dpm_levels[i].value * 1000;
+ clocks->data[i].latency_in_us = 0;
+ }
+
+ return 0;
+
+}
+
+static int vega20_get_clock_by_type_with_latency(struct pp_hwmgr *hwmgr,
+ enum amd_pp_clock_type type,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ int ret;
+
+ switch (type) {
+ case amd_pp_sys_clock:
+ ret = vega20_get_sclks(hwmgr, clocks);
+ break;
+ case amd_pp_mem_clock:
+ ret = vega20_get_memclocks(hwmgr, clocks);
+ break;
+ case amd_pp_dcef_clock:
+ ret = vega20_get_dcefclocks(hwmgr, clocks);
+ break;
+ case amd_pp_soc_clock:
+ ret = vega20_get_socclocks(hwmgr, clocks);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int vega20_get_clock_by_type_with_voltage(struct pp_hwmgr *hwmgr,
+ enum amd_pp_clock_type type,
+ struct pp_clock_levels_with_voltage *clocks)
+{
+ clocks->num_levels = 0;
+
+ return 0;
+}
+
+static int vega20_set_watermarks_for_clocks_ranges(struct pp_hwmgr *hwmgr,
+ void *clock_ranges)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ Watermarks_t *table = &(data->smc_state_table.water_marks_table);
+ struct dm_pp_wm_sets_with_clock_ranges_soc15 *wm_with_clock_ranges = clock_ranges;
+
+ if (!data->registry_data.disable_water_mark &&
+ data->smu_features[GNLD_DPM_DCEFCLK].supported &&
+ data->smu_features[GNLD_DPM_SOCCLK].supported) {
+ smu_set_watermarks_for_clocks_ranges(table, wm_with_clock_ranges);
+ data->water_marks_bitmap |= WaterMarksExist;
+ data->water_marks_bitmap &= ~WaterMarksLoaded;
+ }
+
+ return 0;
+}
+
+static int vega20_odn_edit_dpm_table(struct pp_hwmgr *hwmgr,
+ enum PP_OD_DPM_TABLE_COMMAND type,
+ long *input, uint32_t size)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_od8_single_setting *od8_settings =
+ data->od8_settings.od8_settings_array;
+ OverDriveTable_t *od_table =
+ &(data->smc_state_table.overdrive_table);
+ int32_t input_index, input_clk, input_vol, i;
+ int od8_id;
+ int ret;
+
+ PP_ASSERT_WITH_CODE(input, "NULL user input for clock and voltage",
+ return -EINVAL);
+
+ switch (type) {
+ case PP_OD_EDIT_SCLK_VDDC_TABLE:
+ if (!(od8_settings[OD8_SETTING_GFXCLK_FMIN].feature_id &&
+ od8_settings[OD8_SETTING_GFXCLK_FMAX].feature_id)) {
+ pr_info("Sclk min/max frequency overdrive not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ for (i = 0; i < size; i += 2) {
+ if (i + 2 > size) {
+ pr_info("invalid number of input parameters %d\n",
+ size);
+ return -EINVAL;
+ }
+
+ input_index = input[i];
+ input_clk = input[i + 1];
+
+ if (input_index != 0 && input_index != 1) {
+ pr_info("Invalid index %d\n", input_index);
+ pr_info("Support min/max sclk frequency setting only which index by 0/1\n");
+ return -EINVAL;
+ }
+
+ if (input_clk < od8_settings[OD8_SETTING_GFXCLK_FMIN].min_value ||
+ input_clk > od8_settings[OD8_SETTING_GFXCLK_FMAX].max_value) {
+ pr_info("clock freq %d is not within allowed range [%d - %d]\n",
+ input_clk,
+ od8_settings[OD8_SETTING_GFXCLK_FMIN].min_value,
+ od8_settings[OD8_SETTING_GFXCLK_FMAX].max_value);
+ return -EINVAL;
+ }
+
+ if ((input_index == 0 && od_table->GfxclkFmin != input_clk) ||
+ (input_index == 1 && od_table->GfxclkFmax != input_clk))
+ data->gfxclk_overdrive = true;
+
+ if (input_index == 0)
+ od_table->GfxclkFmin = input_clk;
+ else
+ od_table->GfxclkFmax = input_clk;
+ }
+
+ break;
+
+ case PP_OD_EDIT_MCLK_VDDC_TABLE:
+ if (!od8_settings[OD8_SETTING_UCLK_FMAX].feature_id) {
+ pr_info("Mclk max frequency overdrive not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ for (i = 0; i < size; i += 2) {
+ if (i + 2 > size) {
+ pr_info("invalid number of input parameters %d\n",
+ size);
+ return -EINVAL;
+ }
+
+ input_index = input[i];
+ input_clk = input[i + 1];
+
+ if (input_index != 1) {
+ pr_info("Invalid index %d\n", input_index);
+ pr_info("Support max Mclk frequency setting only which index by 1\n");
+ return -EINVAL;
+ }
+
+ if (input_clk < od8_settings[OD8_SETTING_UCLK_FMAX].min_value ||
+ input_clk > od8_settings[OD8_SETTING_UCLK_FMAX].max_value) {
+ pr_info("clock freq %d is not within allowed range [%d - %d]\n",
+ input_clk,
+ od8_settings[OD8_SETTING_UCLK_FMAX].min_value,
+ od8_settings[OD8_SETTING_UCLK_FMAX].max_value);
+ return -EINVAL;
+ }
+
+ if (input_index == 1 && od_table->UclkFmax != input_clk)
+ data->memclk_overdrive = true;
+
+ od_table->UclkFmax = input_clk;
+ }
+
+ break;
+
+ case PP_OD_EDIT_VDDC_CURVE:
+ if (!(od8_settings[OD8_SETTING_GFXCLK_FREQ1].feature_id &&
+ od8_settings[OD8_SETTING_GFXCLK_FREQ2].feature_id &&
+ od8_settings[OD8_SETTING_GFXCLK_FREQ3].feature_id &&
+ od8_settings[OD8_SETTING_GFXCLK_VOLTAGE1].feature_id &&
+ od8_settings[OD8_SETTING_GFXCLK_VOLTAGE2].feature_id &&
+ od8_settings[OD8_SETTING_GFXCLK_VOLTAGE3].feature_id)) {
+ pr_info("Voltage curve calibrate not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ for (i = 0; i < size; i += 3) {
+ if (i + 3 > size) {
+ pr_info("invalid number of input parameters %d\n",
+ size);
+ return -EINVAL;
+ }
+
+ input_index = input[i];
+ input_clk = input[i + 1];
+ input_vol = input[i + 2];
+
+ if (input_index > 2) {
+ pr_info("Setting for point %d is not supported\n",
+ input_index + 1);
+ pr_info("Three supported points index by 0, 1, 2\n");
+ return -EINVAL;
+ }
+
+ od8_id = OD8_SETTING_GFXCLK_FREQ1 + 2 * input_index;
+ if (input_clk < od8_settings[od8_id].min_value ||
+ input_clk > od8_settings[od8_id].max_value) {
+ pr_info("clock freq %d is not within allowed range [%d - %d]\n",
+ input_clk,
+ od8_settings[od8_id].min_value,
+ od8_settings[od8_id].max_value);
+ return -EINVAL;
+ }
+
+ od8_id = OD8_SETTING_GFXCLK_VOLTAGE1 + 2 * input_index;
+ if (input_vol < od8_settings[od8_id].min_value ||
+ input_vol > od8_settings[od8_id].max_value) {
+ pr_info("clock voltage %d is not within allowed range [%d - %d]\n",
+ input_vol,
+ od8_settings[od8_id].min_value,
+ od8_settings[od8_id].max_value);
+ return -EINVAL;
+ }
+
+ switch (input_index) {
+ case 0:
+ od_table->GfxclkFreq1 = input_clk;
+ od_table->GfxclkVolt1 = input_vol * VOLTAGE_SCALE;
+ break;
+ case 1:
+ od_table->GfxclkFreq2 = input_clk;
+ od_table->GfxclkVolt2 = input_vol * VOLTAGE_SCALE;
+ break;
+ case 2:
+ od_table->GfxclkFreq3 = input_clk;
+ od_table->GfxclkVolt3 = input_vol * VOLTAGE_SCALE;
+ break;
+ }
+ }
+ break;
+
+ case PP_OD_RESTORE_DEFAULT_TABLE:
+ data->gfxclk_overdrive = false;
+ data->memclk_overdrive = false;
+
+ ret = smum_smc_table_manager(hwmgr,
+ (uint8_t *)od_table,
+ TABLE_OVERDRIVE, true);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to export overdrive table!",
+ return ret);
+ break;
+
+ case PP_OD_COMMIT_DPM_TABLE:
+ ret = smum_smc_table_manager(hwmgr,
+ (uint8_t *)od_table,
+ TABLE_OVERDRIVE, false);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Failed to import overdrive table!",
+ return ret);
+
+ /* retrieve updated gfxclk table */
+ if (data->gfxclk_overdrive) {
+ data->gfxclk_overdrive = false;
+
+ ret = vega20_setup_gfxclk_dpm_table(hwmgr);
+ if (ret)
+ return ret;
+ }
+
+ /* retrieve updated memclk table */
+ if (data->memclk_overdrive) {
+ data->memclk_overdrive = false;
+
+ ret = vega20_setup_memclk_dpm_table(hwmgr);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vega20_set_mp1_state(struct pp_hwmgr *hwmgr,
+ enum pp_mp1_state mp1_state)
+{
+ uint16_t msg;
+ int ret;
+
+ switch (mp1_state) {
+ case PP_MP1_STATE_SHUTDOWN:
+ msg = PPSMC_MSG_PrepareMp1ForShutdown;
+ break;
+ case PP_MP1_STATE_UNLOAD:
+ msg = PPSMC_MSG_PrepareMp1ForUnload;
+ break;
+ case PP_MP1_STATE_RESET:
+ msg = PPSMC_MSG_PrepareMp1ForReset;
+ break;
+ case PP_MP1_STATE_NONE:
+ default:
+ return 0;
+ }
+
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc(hwmgr, msg, NULL)) == 0,
+ "[PrepareMp1] Failed!",
+ return ret);
+
+ return 0;
+}
+
+static int vega20_get_ppfeature_status(struct pp_hwmgr *hwmgr, char *buf)
+{
+ static const char *ppfeature_name[] = {
+ "DPM_PREFETCHER",
+ "GFXCLK_DPM",
+ "UCLK_DPM",
+ "SOCCLK_DPM",
+ "UVD_DPM",
+ "VCE_DPM",
+ "ULV",
+ "MP0CLK_DPM",
+ "LINK_DPM",
+ "DCEFCLK_DPM",
+ "GFXCLK_DS",
+ "SOCCLK_DS",
+ "LCLK_DS",
+ "PPT",
+ "TDC",
+ "THERMAL",
+ "GFX_PER_CU_CG",
+ "RM",
+ "DCEFCLK_DS",
+ "ACDC",
+ "VR0HOT",
+ "VR1HOT",
+ "FW_CTF",
+ "LED_DISPLAY",
+ "FAN_CONTROL",
+ "GFX_EDC",
+ "GFXOFF",
+ "CG",
+ "FCLK_DPM",
+ "FCLK_DS",
+ "MP1CLK_DS",
+ "MP0CLK_DS",
+ "XGMI",
+ "ECC"};
+ static const char *output_title[] = {
+ "FEATURES",
+ "BITMASK",
+ "ENABLEMENT"};
+ uint64_t features_enabled;
+ int i;
+ int ret = 0;
+ int size = 0;
+
+ ret = vega20_get_enabled_smc_features(hwmgr, &features_enabled);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[EnableAllSmuFeatures] Failed to get enabled smc features!",
+ return ret);
+
+ size += sprintf(buf + size, "Current ppfeatures: 0x%016llx\n", features_enabled);
+ size += sprintf(buf + size, "%-19s %-22s %s\n",
+ output_title[0],
+ output_title[1],
+ output_title[2]);
+ for (i = 0; i < GNLD_FEATURES_MAX; i++) {
+ size += sprintf(buf + size, "%-19s 0x%016llx %6s\n",
+ ppfeature_name[i],
+ 1ULL << i,
+ (features_enabled & (1ULL << i)) ? "Y" : "N");
+ }
+
+ return size;
+}
+
+static int vega20_set_ppfeature_status(struct pp_hwmgr *hwmgr, uint64_t new_ppfeature_masks)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ uint64_t features_enabled, features_to_enable, features_to_disable;
+ int i, ret = 0;
+ bool enabled;
+
+ if (new_ppfeature_masks >= (1ULL << GNLD_FEATURES_MAX))
+ return -EINVAL;
+
+ ret = vega20_get_enabled_smc_features(hwmgr, &features_enabled);
+ if (ret)
+ return ret;
+
+ features_to_disable =
+ features_enabled & ~new_ppfeature_masks;
+ features_to_enable =
+ ~features_enabled & new_ppfeature_masks;
+
+ pr_debug("features_to_disable 0x%llx\n", features_to_disable);
+ pr_debug("features_to_enable 0x%llx\n", features_to_enable);
+
+ if (features_to_disable) {
+ ret = vega20_enable_smc_features(hwmgr, false, features_to_disable);
+ if (ret)
+ return ret;
+ }
+
+ if (features_to_enable) {
+ ret = vega20_enable_smc_features(hwmgr, true, features_to_enable);
+ if (ret)
+ return ret;
+ }
+
+ /* Update the cached feature enablement state */
+ ret = vega20_get_enabled_smc_features(hwmgr, &features_enabled);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < GNLD_FEATURES_MAX; i++) {
+ enabled = (features_enabled & data->smu_features[i].smu_feature_bitmap) ?
+ true : false;
+ data->smu_features[i].enabled = enabled;
+ }
+
+ return 0;
+}
+
+static int vega20_get_current_pcie_link_width_level(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ return (RREG32_PCIE(smnPCIE_LC_LINK_WIDTH_CNTL) &
+ PCIE_LC_LINK_WIDTH_CNTL__LC_LINK_WIDTH_RD_MASK)
+ >> PCIE_LC_LINK_WIDTH_CNTL__LC_LINK_WIDTH_RD__SHIFT;
+}
+
+static int vega20_get_current_pcie_link_width(struct pp_hwmgr *hwmgr)
+{
+ uint32_t width_level;
+
+ width_level = vega20_get_current_pcie_link_width_level(hwmgr);
+ if (width_level > LINK_WIDTH_MAX)
+ width_level = 0;
+
+ return link_width[width_level];
+}
+
+static int vega20_get_current_pcie_link_speed_level(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ return (RREG32_PCIE(smnPCIE_LC_SPEED_CNTL) &
+ PSWUSP0_PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE_MASK)
+ >> PSWUSP0_PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE__SHIFT;
+}
+
+static int vega20_get_current_pcie_link_speed(struct pp_hwmgr *hwmgr)
+{
+ uint32_t speed_level;
+
+ speed_level = vega20_get_current_pcie_link_speed_level(hwmgr);
+ if (speed_level > LINK_SPEED_MAX)
+ speed_level = 0;
+
+ return link_speed[speed_level];
+}
+
+static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr,
+ enum pp_clock_type type, char *buf)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_od8_single_setting *od8_settings =
+ data->od8_settings.od8_settings_array;
+ OverDriveTable_t *od_table =
+ &(data->smc_state_table.overdrive_table);
+ struct phm_ppt_v3_information *pptable_information =
+ (struct phm_ppt_v3_information *)hwmgr->pptable;
+ PPTable_t *pptable = (PPTable_t *)pptable_information->smc_pptable;
+ struct pp_clock_levels_with_latency clocks;
+ struct vega20_single_dpm_table *fclk_dpm_table =
+ &(data->dpm_table.fclk_table);
+ int i, now, size = 0;
+ int ret = 0;
+ uint32_t gen_speed, lane_width, current_gen_speed, current_lane_width;
+
+ switch (type) {
+ case PP_SCLK:
+ ret = vega20_get_current_clk_freq(hwmgr, PPCLK_GFXCLK, &now);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Attempt to get current gfx clk Failed!",
+ return ret);
+
+ if (vega20_get_sclks(hwmgr, &clocks)) {
+ size += sprintf(buf + size, "0: %uMhz * (DPM disabled)\n",
+ now / 100);
+ break;
+ }
+
+ for (i = 0; i < clocks.num_levels; i++)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ i, clocks.data[i].clocks_in_khz / 1000,
+ (clocks.data[i].clocks_in_khz == now * 10) ? "*" : "");
+ break;
+
+ case PP_MCLK:
+ ret = vega20_get_current_clk_freq(hwmgr, PPCLK_UCLK, &now);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Attempt to get current mclk freq Failed!",
+ return ret);
+
+ if (vega20_get_memclocks(hwmgr, &clocks)) {
+ size += sprintf(buf + size, "0: %uMhz * (DPM disabled)\n",
+ now / 100);
+ break;
+ }
+
+ for (i = 0; i < clocks.num_levels; i++)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ i, clocks.data[i].clocks_in_khz / 1000,
+ (clocks.data[i].clocks_in_khz == now * 10) ? "*" : "");
+ break;
+
+ case PP_SOCCLK:
+ ret = vega20_get_current_clk_freq(hwmgr, PPCLK_SOCCLK, &now);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Attempt to get current socclk freq Failed!",
+ return ret);
+
+ if (vega20_get_socclocks(hwmgr, &clocks)) {
+ size += sprintf(buf + size, "0: %uMhz * (DPM disabled)\n",
+ now / 100);
+ break;
+ }
+
+ for (i = 0; i < clocks.num_levels; i++)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ i, clocks.data[i].clocks_in_khz / 1000,
+ (clocks.data[i].clocks_in_khz == now * 10) ? "*" : "");
+ break;
+
+ case PP_FCLK:
+ ret = vega20_get_current_clk_freq(hwmgr, PPCLK_FCLK, &now);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Attempt to get current fclk freq Failed!",
+ return ret);
+
+ for (i = 0; i < fclk_dpm_table->count; i++)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ i, fclk_dpm_table->dpm_levels[i].value,
+ fclk_dpm_table->dpm_levels[i].value == (now / 100) ? "*" : "");
+ break;
+
+ case PP_DCEFCLK:
+ ret = vega20_get_current_clk_freq(hwmgr, PPCLK_DCEFCLK, &now);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Attempt to get current dcefclk freq Failed!",
+ return ret);
+
+ if (vega20_get_dcefclocks(hwmgr, &clocks)) {
+ size += sprintf(buf + size, "0: %uMhz * (DPM disabled)\n",
+ now / 100);
+ break;
+ }
+
+ for (i = 0; i < clocks.num_levels; i++)
+ size += sprintf(buf + size, "%d: %uMhz %s\n",
+ i, clocks.data[i].clocks_in_khz / 1000,
+ (clocks.data[i].clocks_in_khz == now * 10) ? "*" : "");
+ break;
+
+ case PP_PCIE:
+ current_gen_speed =
+ vega20_get_current_pcie_link_speed_level(hwmgr);
+ current_lane_width =
+ vega20_get_current_pcie_link_width_level(hwmgr);
+ for (i = 0; i < NUM_LINK_LEVELS; i++) {
+ if (i == 1 && data->pcie_parameters_override) {
+ gen_speed = data->pcie_gen_level1;
+ lane_width = data->pcie_width_level1;
+ } else {
+ gen_speed = pptable->PcieGenSpeed[i];
+ lane_width = pptable->PcieLaneCount[i];
+ }
+ size += sprintf(buf + size, "%d: %s %s %dMhz %s\n", i,
+ (gen_speed == 0) ? "2.5GT/s," :
+ (gen_speed == 1) ? "5.0GT/s," :
+ (gen_speed == 2) ? "8.0GT/s," :
+ (gen_speed == 3) ? "16.0GT/s," : "",
+ (lane_width == 1) ? "x1" :
+ (lane_width == 2) ? "x2" :
+ (lane_width == 3) ? "x4" :
+ (lane_width == 4) ? "x8" :
+ (lane_width == 5) ? "x12" :
+ (lane_width == 6) ? "x16" : "",
+ pptable->LclkFreq[i],
+ (current_gen_speed == gen_speed) &&
+ (current_lane_width == lane_width) ?
+ "*" : "");
+ }
+ break;
+
+ case OD_SCLK:
+ if (od8_settings[OD8_SETTING_GFXCLK_FMIN].feature_id &&
+ od8_settings[OD8_SETTING_GFXCLK_FMAX].feature_id) {
+ size = sprintf(buf, "%s:\n", "OD_SCLK");
+ size += sprintf(buf + size, "0: %10uMhz\n",
+ od_table->GfxclkFmin);
+ size += sprintf(buf + size, "1: %10uMhz\n",
+ od_table->GfxclkFmax);
+ }
+ break;
+
+ case OD_MCLK:
+ if (od8_settings[OD8_SETTING_UCLK_FMAX].feature_id) {
+ size = sprintf(buf, "%s:\n", "OD_MCLK");
+ size += sprintf(buf + size, "1: %10uMhz\n",
+ od_table->UclkFmax);
+ }
+
+ break;
+
+ case OD_VDDC_CURVE:
+ if (od8_settings[OD8_SETTING_GFXCLK_FREQ1].feature_id &&
+ od8_settings[OD8_SETTING_GFXCLK_FREQ2].feature_id &&
+ od8_settings[OD8_SETTING_GFXCLK_FREQ3].feature_id &&
+ od8_settings[OD8_SETTING_GFXCLK_VOLTAGE1].feature_id &&
+ od8_settings[OD8_SETTING_GFXCLK_VOLTAGE2].feature_id &&
+ od8_settings[OD8_SETTING_GFXCLK_VOLTAGE3].feature_id) {
+ size = sprintf(buf, "%s:\n", "OD_VDDC_CURVE");
+ size += sprintf(buf + size, "0: %10uMhz %10dmV\n",
+ od_table->GfxclkFreq1,
+ od_table->GfxclkVolt1 / VOLTAGE_SCALE);
+ size += sprintf(buf + size, "1: %10uMhz %10dmV\n",
+ od_table->GfxclkFreq2,
+ od_table->GfxclkVolt2 / VOLTAGE_SCALE);
+ size += sprintf(buf + size, "2: %10uMhz %10dmV\n",
+ od_table->GfxclkFreq3,
+ od_table->GfxclkVolt3 / VOLTAGE_SCALE);
+ }
+
+ break;
+
+ case OD_RANGE:
+ size = sprintf(buf, "%s:\n", "OD_RANGE");
+
+ if (od8_settings[OD8_SETTING_GFXCLK_FMIN].feature_id &&
+ od8_settings[OD8_SETTING_GFXCLK_FMAX].feature_id) {
+ size += sprintf(buf + size, "SCLK: %7uMhz %10uMhz\n",
+ od8_settings[OD8_SETTING_GFXCLK_FMIN].min_value,
+ od8_settings[OD8_SETTING_GFXCLK_FMAX].max_value);
+ }
+
+ if (od8_settings[OD8_SETTING_UCLK_FMAX].feature_id) {
+ size += sprintf(buf + size, "MCLK: %7uMhz %10uMhz\n",
+ od8_settings[OD8_SETTING_UCLK_FMAX].min_value,
+ od8_settings[OD8_SETTING_UCLK_FMAX].max_value);
+ }
+
+ if (od8_settings[OD8_SETTING_GFXCLK_FREQ1].feature_id &&
+ od8_settings[OD8_SETTING_GFXCLK_FREQ2].feature_id &&
+ od8_settings[OD8_SETTING_GFXCLK_FREQ3].feature_id &&
+ od8_settings[OD8_SETTING_GFXCLK_VOLTAGE1].feature_id &&
+ od8_settings[OD8_SETTING_GFXCLK_VOLTAGE2].feature_id &&
+ od8_settings[OD8_SETTING_GFXCLK_VOLTAGE3].feature_id) {
+ size += sprintf(buf + size, "VDDC_CURVE_SCLK[0]: %7uMhz %10uMhz\n",
+ od8_settings[OD8_SETTING_GFXCLK_FREQ1].min_value,
+ od8_settings[OD8_SETTING_GFXCLK_FREQ1].max_value);
+ size += sprintf(buf + size, "VDDC_CURVE_VOLT[0]: %7dmV %11dmV\n",
+ od8_settings[OD8_SETTING_GFXCLK_VOLTAGE1].min_value,
+ od8_settings[OD8_SETTING_GFXCLK_VOLTAGE1].max_value);
+ size += sprintf(buf + size, "VDDC_CURVE_SCLK[1]: %7uMhz %10uMhz\n",
+ od8_settings[OD8_SETTING_GFXCLK_FREQ2].min_value,
+ od8_settings[OD8_SETTING_GFXCLK_FREQ2].max_value);
+ size += sprintf(buf + size, "VDDC_CURVE_VOLT[1]: %7dmV %11dmV\n",
+ od8_settings[OD8_SETTING_GFXCLK_VOLTAGE2].min_value,
+ od8_settings[OD8_SETTING_GFXCLK_VOLTAGE2].max_value);
+ size += sprintf(buf + size, "VDDC_CURVE_SCLK[2]: %7uMhz %10uMhz\n",
+ od8_settings[OD8_SETTING_GFXCLK_FREQ3].min_value,
+ od8_settings[OD8_SETTING_GFXCLK_FREQ3].max_value);
+ size += sprintf(buf + size, "VDDC_CURVE_VOLT[2]: %7dmV %11dmV\n",
+ od8_settings[OD8_SETTING_GFXCLK_VOLTAGE3].min_value,
+ od8_settings[OD8_SETTING_GFXCLK_VOLTAGE3].max_value);
+ }
+
+ break;
+ default:
+ break;
+ }
+ return size;
+}
+
+static int vega20_set_uclk_to_highest_dpm_level(struct pp_hwmgr *hwmgr,
+ struct vega20_single_dpm_table *dpm_table)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ int ret = 0;
+
+ if (data->smu_features[GNLD_DPM_UCLK].enabled) {
+ PP_ASSERT_WITH_CODE(dpm_table->count > 0,
+ "[SetUclkToHightestDpmLevel] Dpm table has no entry!",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(dpm_table->count <= NUM_UCLK_DPM_LEVELS,
+ "[SetUclkToHightestDpmLevel] Dpm table has too many entries!",
+ return -EINVAL);
+
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetHardMinByFreq,
+ (PPCLK_UCLK << 16 ) | dpm_table->dpm_state.hard_min_level,
+ NULL)),
+ "[SetUclkToHightestDpmLevel] Set hard min uclk failed!",
+ return ret);
+ }
+
+ return ret;
+}
+
+static int vega20_set_fclk_to_highest_dpm_level(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_single_dpm_table *dpm_table = &(data->dpm_table.fclk_table);
+ int ret = 0;
+
+ if (data->smu_features[GNLD_DPM_FCLK].enabled) {
+ PP_ASSERT_WITH_CODE(dpm_table->count > 0,
+ "[SetFclkToHightestDpmLevel] Dpm table has no entry!",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(dpm_table->count <= NUM_FCLK_DPM_LEVELS,
+ "[SetFclkToHightestDpmLevel] Dpm table has too many entries!",
+ return -EINVAL);
+
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMinByFreq,
+ (PPCLK_FCLK << 16 ) | dpm_table->dpm_state.soft_min_level,
+ NULL)),
+ "[SetFclkToHightestDpmLevel] Set soft min fclk failed!",
+ return ret);
+ }
+
+ return ret;
+}
+
+static int vega20_pre_display_configuration_changed_task(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ int ret = 0;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_NumOfDisplays, 0, NULL);
+
+ ret = vega20_set_uclk_to_highest_dpm_level(hwmgr,
+ &data->dpm_table.mem_table);
+ if (ret)
+ return ret;
+
+ return vega20_set_fclk_to_highest_dpm_level(hwmgr);
+}
+
+static int vega20_display_configuration_changed_task(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ int result = 0;
+ Watermarks_t *wm_table = &(data->smc_state_table.water_marks_table);
+
+ if ((data->water_marks_bitmap & WaterMarksExist) &&
+ !(data->water_marks_bitmap & WaterMarksLoaded)) {
+ result = smum_smc_table_manager(hwmgr,
+ (uint8_t *)wm_table, TABLE_WATERMARKS, false);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to update WMTABLE!",
+ return result);
+ data->water_marks_bitmap |= WaterMarksLoaded;
+ }
+
+ if ((data->water_marks_bitmap & WaterMarksExist) &&
+ data->smu_features[GNLD_DPM_DCEFCLK].supported &&
+ data->smu_features[GNLD_DPM_SOCCLK].supported) {
+ result = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_NumOfDisplays,
+ hwmgr->display_config->num_display,
+ NULL);
+ }
+
+ return result;
+}
+
+static int vega20_enable_disable_uvd_dpm(struct pp_hwmgr *hwmgr, bool enable)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ int ret = 0;
+
+ if (data->smu_features[GNLD_DPM_UVD].supported) {
+ if (data->smu_features[GNLD_DPM_UVD].enabled == enable) {
+ if (enable)
+ PP_DBG_LOG("[EnableDisableUVDDPM] feature DPM UVD already enabled!\n");
+ else
+ PP_DBG_LOG("[EnableDisableUVDDPM] feature DPM UVD already disabled!\n");
+ }
+
+ ret = vega20_enable_smc_features(hwmgr,
+ enable,
+ data->smu_features[GNLD_DPM_UVD].smu_feature_bitmap);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[EnableDisableUVDDPM] Attempt to Enable/Disable DPM UVD Failed!",
+ return ret);
+ data->smu_features[GNLD_DPM_UVD].enabled = enable;
+ }
+
+ return 0;
+}
+
+static void vega20_power_gate_vce(struct pp_hwmgr *hwmgr, bool bgate)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+
+ if (data->vce_power_gated == bgate)
+ return ;
+
+ data->vce_power_gated = bgate;
+ if (bgate) {
+ vega20_enable_disable_vce_dpm(hwmgr, !bgate);
+ amdgpu_device_ip_set_powergating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_VCE,
+ AMD_PG_STATE_GATE);
+ } else {
+ amdgpu_device_ip_set_powergating_state(hwmgr->adev,
+ AMD_IP_BLOCK_TYPE_VCE,
+ AMD_PG_STATE_UNGATE);
+ vega20_enable_disable_vce_dpm(hwmgr, !bgate);
+ }
+
+}
+
+static void vega20_power_gate_uvd(struct pp_hwmgr *hwmgr, bool bgate)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+
+ if (data->uvd_power_gated == bgate)
+ return ;
+
+ data->uvd_power_gated = bgate;
+ vega20_enable_disable_uvd_dpm(hwmgr, !bgate);
+}
+
+static int vega20_apply_clocks_adjust_rules(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_single_dpm_table *dpm_table;
+ bool vblank_too_short = false;
+ bool disable_mclk_switching;
+ bool disable_fclk_switching;
+ uint32_t i, latency;
+
+ disable_mclk_switching = ((1 < hwmgr->display_config->num_display) &&
+ !hwmgr->display_config->multi_monitor_in_sync) ||
+ vblank_too_short;
+ latency = hwmgr->display_config->dce_tolerable_mclk_in_active_latency;
+
+ /* gfxclk */
+ dpm_table = &(data->dpm_table.gfx_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT;
+
+ if (PP_CAP(PHM_PlatformCaps_UMDPState)) {
+ if (VEGA20_UMD_PSTATE_GFXCLK_LEVEL < dpm_table->count) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_GFXCLK_LEVEL].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_GFXCLK_LEVEL].value;
+ }
+
+ if (hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[0].value;
+ }
+
+ if (hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ }
+ }
+
+ /* memclk */
+ dpm_table = &(data->dpm_table.mem_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT;
+
+ if (PP_CAP(PHM_PlatformCaps_UMDPState)) {
+ if (VEGA20_UMD_PSTATE_MCLK_LEVEL < dpm_table->count) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_MCLK_LEVEL].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_MCLK_LEVEL].value;
+ }
+
+ if (hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[0].value;
+ }
+
+ if (hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ }
+ }
+
+ /* honour DAL's UCLK Hardmin */
+ if (dpm_table->dpm_state.hard_min_level < (hwmgr->display_config->min_mem_set_clock / 100))
+ dpm_table->dpm_state.hard_min_level = hwmgr->display_config->min_mem_set_clock / 100;
+
+ /* Hardmin is dependent on displayconfig */
+ if (disable_mclk_switching) {
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ for (i = 0; i < data->mclk_latency_table.count - 1; i++) {
+ if (data->mclk_latency_table.entries[i].latency <= latency) {
+ if (dpm_table->dpm_levels[i].value >= (hwmgr->display_config->min_mem_set_clock / 100)) {
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[i].value;
+ break;
+ }
+ }
+ }
+ }
+
+ if (hwmgr->display_config->nb_pstate_switch_disable)
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+
+ if ((disable_mclk_switching &&
+ (dpm_table->dpm_state.hard_min_level == dpm_table->dpm_levels[dpm_table->count - 1].value)) ||
+ hwmgr->display_config->min_mem_set_clock / 100 >= dpm_table->dpm_levels[dpm_table->count - 1].value)
+ disable_fclk_switching = true;
+ else
+ disable_fclk_switching = false;
+
+ /* fclk */
+ dpm_table = &(data->dpm_table.fclk_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT;
+ if (hwmgr->display_config->nb_pstate_switch_disable || disable_fclk_switching)
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+
+ /* vclk */
+ dpm_table = &(data->dpm_table.vclk_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT;
+
+ if (PP_CAP(PHM_PlatformCaps_UMDPState)) {
+ if (VEGA20_UMD_PSTATE_UVDCLK_LEVEL < dpm_table->count) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_UVDCLK_LEVEL].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_UVDCLK_LEVEL].value;
+ }
+
+ if (hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ }
+ }
+
+ /* dclk */
+ dpm_table = &(data->dpm_table.dclk_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT;
+
+ if (PP_CAP(PHM_PlatformCaps_UMDPState)) {
+ if (VEGA20_UMD_PSTATE_UVDCLK_LEVEL < dpm_table->count) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_UVDCLK_LEVEL].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_UVDCLK_LEVEL].value;
+ }
+
+ if (hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ }
+ }
+
+ /* socclk */
+ dpm_table = &(data->dpm_table.soc_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT;
+
+ if (PP_CAP(PHM_PlatformCaps_UMDPState)) {
+ if (VEGA20_UMD_PSTATE_SOCCLK_LEVEL < dpm_table->count) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_SOCCLK_LEVEL].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_SOCCLK_LEVEL].value;
+ }
+
+ if (hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ }
+ }
+
+ /* eclk */
+ dpm_table = &(data->dpm_table.eclk_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT;
+
+ if (PP_CAP(PHM_PlatformCaps_UMDPState)) {
+ if (VEGA20_UMD_PSTATE_VCEMCLK_LEVEL < dpm_table->count) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_VCEMCLK_LEVEL].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[VEGA20_UMD_PSTATE_VCEMCLK_LEVEL].value;
+ }
+
+ if (hwmgr->dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) {
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ }
+ }
+
+ return 0;
+}
+
+static bool
+vega20_check_smc_update_required_for_display_configuration(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ bool is_update_required = false;
+
+ if (data->display_timing.num_existing_displays !=
+ hwmgr->display_config->num_display)
+ is_update_required = true;
+
+ if (data->registry_data.gfx_clk_deep_sleep_support &&
+ (data->display_timing.min_clock_in_sr !=
+ hwmgr->display_config->min_core_set_clock_in_sr))
+ is_update_required = true;
+
+ return is_update_required;
+}
+
+static int vega20_disable_dpm_tasks(struct pp_hwmgr *hwmgr)
+{
+ int ret = 0;
+
+ ret = vega20_disable_all_smu_features(hwmgr);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[DisableDpmTasks] Failed to disable all smu features!",
+ return ret);
+
+ return 0;
+}
+
+static int vega20_power_off_asic(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ int result;
+
+ result = vega20_disable_dpm_tasks(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "[PowerOffAsic] Failed to disable DPM!",
+ );
+ data->water_marks_bitmap &= ~(WaterMarksLoaded);
+
+ return result;
+}
+
+static int conv_power_profile_to_pplib_workload(int power_profile)
+{
+ int pplib_workload = 0;
+
+ switch (power_profile) {
+ case PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT:
+ pplib_workload = WORKLOAD_DEFAULT_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_FULLSCREEN3D:
+ pplib_workload = WORKLOAD_PPLIB_FULL_SCREEN_3D_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_POWERSAVING:
+ pplib_workload = WORKLOAD_PPLIB_POWER_SAVING_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_VIDEO:
+ pplib_workload = WORKLOAD_PPLIB_VIDEO_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_VR:
+ pplib_workload = WORKLOAD_PPLIB_VR_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_COMPUTE:
+ pplib_workload = WORKLOAD_PPLIB_COMPUTE_BIT;
+ break;
+ case PP_SMC_POWER_PROFILE_CUSTOM:
+ pplib_workload = WORKLOAD_PPLIB_CUSTOM_BIT;
+ break;
+ }
+
+ return pplib_workload;
+}
+
+static int vega20_get_power_profile_mode(struct pp_hwmgr *hwmgr, char *buf)
+{
+ DpmActivityMonitorCoeffInt_t activity_monitor;
+ uint32_t i, size = 0;
+ uint16_t workload_type = 0;
+ static const char *profile_name[] = {
+ "BOOTUP_DEFAULT",
+ "3D_FULL_SCREEN",
+ "POWER_SAVING",
+ "VIDEO",
+ "VR",
+ "COMPUTE",
+ "CUSTOM"};
+ static const char *title[] = {
+ "PROFILE_INDEX(NAME)",
+ "CLOCK_TYPE(NAME)",
+ "FPS",
+ "UseRlcBusy",
+ "MinActiveFreqType",
+ "MinActiveFreq",
+ "BoosterFreqType",
+ "BoosterFreq",
+ "PD_Data_limit_c",
+ "PD_Data_error_coeff",
+ "PD_Data_error_rate_coeff"};
+ int result = 0;
+
+ if (!buf)
+ return -EINVAL;
+
+ size += sprintf(buf + size, "%16s %s %s %s %s %s %s %s %s %s %s\n",
+ title[0], title[1], title[2], title[3], title[4], title[5],
+ title[6], title[7], title[8], title[9], title[10]);
+
+ for (i = 0; i <= PP_SMC_POWER_PROFILE_CUSTOM; i++) {
+ /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
+ workload_type = conv_power_profile_to_pplib_workload(i);
+ result = vega20_get_activity_monitor_coeff(hwmgr,
+ (uint8_t *)(&activity_monitor), workload_type);
+ PP_ASSERT_WITH_CODE(!result,
+ "[GetPowerProfile] Failed to get activity monitor!",
+ return result);
+
+ size += sprintf(buf + size, "%2d %14s%s:\n",
+ i, profile_name[i], (i == hwmgr->power_profile_mode) ? "*" : " ");
+
+ size += sprintf(buf + size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d %7d\n",
+ " ",
+ 0,
+ "GFXCLK",
+ activity_monitor.Gfx_FPS,
+ activity_monitor.Gfx_UseRlcBusy,
+ activity_monitor.Gfx_MinActiveFreqType,
+ activity_monitor.Gfx_MinActiveFreq,
+ activity_monitor.Gfx_BoosterFreqType,
+ activity_monitor.Gfx_BoosterFreq,
+ activity_monitor.Gfx_PD_Data_limit_c,
+ activity_monitor.Gfx_PD_Data_error_coeff,
+ activity_monitor.Gfx_PD_Data_error_rate_coeff);
+
+ size += sprintf(buf + size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d %7d\n",
+ " ",
+ 1,
+ "SOCCLK",
+ activity_monitor.Soc_FPS,
+ activity_monitor.Soc_UseRlcBusy,
+ activity_monitor.Soc_MinActiveFreqType,
+ activity_monitor.Soc_MinActiveFreq,
+ activity_monitor.Soc_BoosterFreqType,
+ activity_monitor.Soc_BoosterFreq,
+ activity_monitor.Soc_PD_Data_limit_c,
+ activity_monitor.Soc_PD_Data_error_coeff,
+ activity_monitor.Soc_PD_Data_error_rate_coeff);
+
+ size += sprintf(buf + size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d %7d\n",
+ " ",
+ 2,
+ "UCLK",
+ activity_monitor.Mem_FPS,
+ activity_monitor.Mem_UseRlcBusy,
+ activity_monitor.Mem_MinActiveFreqType,
+ activity_monitor.Mem_MinActiveFreq,
+ activity_monitor.Mem_BoosterFreqType,
+ activity_monitor.Mem_BoosterFreq,
+ activity_monitor.Mem_PD_Data_limit_c,
+ activity_monitor.Mem_PD_Data_error_coeff,
+ activity_monitor.Mem_PD_Data_error_rate_coeff);
+
+ size += sprintf(buf + size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d %7d\n",
+ " ",
+ 3,
+ "FCLK",
+ activity_monitor.Fclk_FPS,
+ activity_monitor.Fclk_UseRlcBusy,
+ activity_monitor.Fclk_MinActiveFreqType,
+ activity_monitor.Fclk_MinActiveFreq,
+ activity_monitor.Fclk_BoosterFreqType,
+ activity_monitor.Fclk_BoosterFreq,
+ activity_monitor.Fclk_PD_Data_limit_c,
+ activity_monitor.Fclk_PD_Data_error_coeff,
+ activity_monitor.Fclk_PD_Data_error_rate_coeff);
+ }
+
+ return size;
+}
+
+static int vega20_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, uint32_t size)
+{
+ DpmActivityMonitorCoeffInt_t activity_monitor;
+ int workload_type, result = 0;
+ uint32_t power_profile_mode = input[size];
+
+ if (power_profile_mode > PP_SMC_POWER_PROFILE_CUSTOM) {
+ pr_err("Invalid power profile mode %d\n", power_profile_mode);
+ return -EINVAL;
+ }
+
+ if (power_profile_mode == PP_SMC_POWER_PROFILE_CUSTOM) {
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ if (size == 0 && !data->is_custom_profile_set)
+ return -EINVAL;
+ if (size < 10 && size != 0)
+ return -EINVAL;
+
+ result = vega20_get_activity_monitor_coeff(hwmgr,
+ (uint8_t *)(&activity_monitor),
+ WORKLOAD_PPLIB_CUSTOM_BIT);
+ PP_ASSERT_WITH_CODE(!result,
+ "[SetPowerProfile] Failed to get activity monitor!",
+ return result);
+
+ /* If size==0, then we want to apply the already-configured
+ * CUSTOM profile again. Just apply it, since we checked its
+ * validity above
+ */
+ if (size == 0)
+ goto out;
+
+ switch (input[0]) {
+ case 0: /* Gfxclk */
+ activity_monitor.Gfx_FPS = input[1];
+ activity_monitor.Gfx_UseRlcBusy = input[2];
+ activity_monitor.Gfx_MinActiveFreqType = input[3];
+ activity_monitor.Gfx_MinActiveFreq = input[4];
+ activity_monitor.Gfx_BoosterFreqType = input[5];
+ activity_monitor.Gfx_BoosterFreq = input[6];
+ activity_monitor.Gfx_PD_Data_limit_c = input[7];
+ activity_monitor.Gfx_PD_Data_error_coeff = input[8];
+ activity_monitor.Gfx_PD_Data_error_rate_coeff = input[9];
+ break;
+ case 1: /* Socclk */
+ activity_monitor.Soc_FPS = input[1];
+ activity_monitor.Soc_UseRlcBusy = input[2];
+ activity_monitor.Soc_MinActiveFreqType = input[3];
+ activity_monitor.Soc_MinActiveFreq = input[4];
+ activity_monitor.Soc_BoosterFreqType = input[5];
+ activity_monitor.Soc_BoosterFreq = input[6];
+ activity_monitor.Soc_PD_Data_limit_c = input[7];
+ activity_monitor.Soc_PD_Data_error_coeff = input[8];
+ activity_monitor.Soc_PD_Data_error_rate_coeff = input[9];
+ break;
+ case 2: /* Uclk */
+ activity_monitor.Mem_FPS = input[1];
+ activity_monitor.Mem_UseRlcBusy = input[2];
+ activity_monitor.Mem_MinActiveFreqType = input[3];
+ activity_monitor.Mem_MinActiveFreq = input[4];
+ activity_monitor.Mem_BoosterFreqType = input[5];
+ activity_monitor.Mem_BoosterFreq = input[6];
+ activity_monitor.Mem_PD_Data_limit_c = input[7];
+ activity_monitor.Mem_PD_Data_error_coeff = input[8];
+ activity_monitor.Mem_PD_Data_error_rate_coeff = input[9];
+ break;
+ case 3: /* Fclk */
+ activity_monitor.Fclk_FPS = input[1];
+ activity_monitor.Fclk_UseRlcBusy = input[2];
+ activity_monitor.Fclk_MinActiveFreqType = input[3];
+ activity_monitor.Fclk_MinActiveFreq = input[4];
+ activity_monitor.Fclk_BoosterFreqType = input[5];
+ activity_monitor.Fclk_BoosterFreq = input[6];
+ activity_monitor.Fclk_PD_Data_limit_c = input[7];
+ activity_monitor.Fclk_PD_Data_error_coeff = input[8];
+ activity_monitor.Fclk_PD_Data_error_rate_coeff = input[9];
+ break;
+ }
+
+ result = vega20_set_activity_monitor_coeff(hwmgr,
+ (uint8_t *)(&activity_monitor),
+ WORKLOAD_PPLIB_CUSTOM_BIT);
+ data->is_custom_profile_set = true;
+ PP_ASSERT_WITH_CODE(!result,
+ "[SetPowerProfile] Failed to set activity monitor!",
+ return result);
+ }
+
+out:
+ /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
+ workload_type =
+ conv_power_profile_to_pplib_workload(power_profile_mode);
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_SetWorkloadMask,
+ 1 << workload_type,
+ NULL);
+
+ hwmgr->power_profile_mode = power_profile_mode;
+
+ return 0;
+}
+
+static int vega20_notify_cac_buffer_info(struct pp_hwmgr *hwmgr,
+ uint32_t virtual_addr_low,
+ uint32_t virtual_addr_hi,
+ uint32_t mc_addr_low,
+ uint32_t mc_addr_hi,
+ uint32_t size)
+{
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSystemVirtualDramAddrHigh,
+ virtual_addr_hi,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSystemVirtualDramAddrLow,
+ virtual_addr_low,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DramLogSetDramAddrHigh,
+ mc_addr_hi,
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DramLogSetDramAddrLow,
+ mc_addr_low,
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DramLogSetDramSize,
+ size,
+ NULL);
+ return 0;
+}
+
+static int vega20_get_thermal_temperature_range(struct pp_hwmgr *hwmgr,
+ struct PP_TemperatureRange *thermal_data)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+
+ memcpy(thermal_data, &SMU7ThermalWithDelayPolicy[0], sizeof(struct PP_TemperatureRange));
+
+ thermal_data->max = pp_table->TedgeLimit *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ thermal_data->edge_emergency_max = (pp_table->TedgeLimit + CTF_OFFSET_EDGE) *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ thermal_data->hotspot_crit_max = pp_table->ThotspotLimit *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ thermal_data->hotspot_emergency_max = (pp_table->ThotspotLimit + CTF_OFFSET_HOTSPOT) *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ thermal_data->mem_crit_max = pp_table->ThbmLimit *
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ thermal_data->mem_emergency_max = (pp_table->ThbmLimit + CTF_OFFSET_HBM)*
+ PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+ return 0;
+}
+
+static int vega20_smu_i2c_bus_access(struct pp_hwmgr *hwmgr, bool acquire)
+{
+ int res;
+
+ /* I2C bus access can happen very early, when SMU not loaded yet */
+ if (!vega20_is_smc_ram_running(hwmgr))
+ return 0;
+
+ res = smum_send_msg_to_smc_with_parameter(hwmgr,
+ (acquire ?
+ PPSMC_MSG_RequestI2CBus :
+ PPSMC_MSG_ReleaseI2CBus),
+ 0,
+ NULL);
+
+ PP_ASSERT_WITH_CODE(!res, "[SmuI2CAccessBus] Failed to access bus!", return res);
+ return res;
+}
+
+static int vega20_set_df_cstate(struct pp_hwmgr *hwmgr,
+ enum pp_df_cstate state)
+{
+ int ret;
+
+ /* PPSMC_MSG_DFCstateControl is supported with 40.50 and later fws */
+ if (hwmgr->smu_version < 0x283200) {
+ pr_err("Df cstate control is supported with 40.50 and later SMC fw!\n");
+ return -EINVAL;
+ }
+
+ ret = smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_DFCstateControl, state,
+ NULL);
+ if (ret)
+ pr_err("SetDfCstate failed!\n");
+
+ return ret;
+}
+
+static int vega20_set_xgmi_pstate(struct pp_hwmgr *hwmgr,
+ uint32_t pstate)
+{
+ int ret;
+
+ ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetXgmiMode,
+ pstate ? XGMI_MODE_PSTATE_D0 : XGMI_MODE_PSTATE_D3,
+ NULL);
+ if (ret)
+ pr_err("SetXgmiPstate failed!\n");
+
+ return ret;
+}
+
+static void vega20_init_gpu_metrics_v1_0(struct gpu_metrics_v1_0 *gpu_metrics)
+{
+ memset(gpu_metrics, 0xFF, sizeof(struct gpu_metrics_v1_0));
+
+ gpu_metrics->common_header.structure_size =
+ sizeof(struct gpu_metrics_v1_0);
+ gpu_metrics->common_header.format_revision = 1;
+ gpu_metrics->common_header.content_revision = 0;
+
+ gpu_metrics->system_clock_counter = ktime_get_boottime_ns();
+}
+
+static ssize_t vega20_get_gpu_metrics(struct pp_hwmgr *hwmgr,
+ void **table)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+ struct gpu_metrics_v1_0 *gpu_metrics =
+ &data->gpu_metrics_table;
+ SmuMetrics_t metrics;
+ uint32_t fan_speed_rpm;
+ int ret;
+
+ ret = vega20_get_metrics_table(hwmgr, &metrics, true);
+ if (ret)
+ return ret;
+
+ vega20_init_gpu_metrics_v1_0(gpu_metrics);
+
+ gpu_metrics->temperature_edge = metrics.TemperatureEdge;
+ gpu_metrics->temperature_hotspot = metrics.TemperatureHotspot;
+ gpu_metrics->temperature_mem = metrics.TemperatureHBM;
+ gpu_metrics->temperature_vrgfx = metrics.TemperatureVrGfx;
+ gpu_metrics->temperature_vrsoc = metrics.TemperatureVrSoc;
+ gpu_metrics->temperature_vrmem = metrics.TemperatureVrMem0;
+
+ gpu_metrics->average_gfx_activity = metrics.AverageGfxActivity;
+ gpu_metrics->average_umc_activity = metrics.AverageUclkActivity;
+
+ gpu_metrics->average_socket_power = metrics.AverageSocketPower;
+
+ gpu_metrics->average_gfxclk_frequency = metrics.AverageGfxclkFrequency;
+ gpu_metrics->average_socclk_frequency = metrics.AverageSocclkFrequency;
+ gpu_metrics->average_uclk_frequency = metrics.AverageUclkFrequency;
+
+ gpu_metrics->current_gfxclk = metrics.CurrClock[PPCLK_GFXCLK];
+ gpu_metrics->current_socclk = metrics.CurrClock[PPCLK_SOCCLK];
+ gpu_metrics->current_uclk = metrics.CurrClock[PPCLK_UCLK];
+ gpu_metrics->current_vclk0 = metrics.CurrClock[PPCLK_VCLK];
+ gpu_metrics->current_dclk0 = metrics.CurrClock[PPCLK_DCLK];
+
+ gpu_metrics->throttle_status = metrics.ThrottlerStatus;
+
+ vega20_fan_ctrl_get_fan_speed_rpm(hwmgr, &fan_speed_rpm);
+ gpu_metrics->current_fan_speed = (uint16_t)fan_speed_rpm;
+
+ gpu_metrics->pcie_link_width =
+ vega20_get_current_pcie_link_width(hwmgr);
+ gpu_metrics->pcie_link_speed =
+ vega20_get_current_pcie_link_speed(hwmgr);
+
+ *table = (void *)gpu_metrics;
+
+ return sizeof(struct gpu_metrics_v1_0);
+}
+
+static const struct pp_hwmgr_func vega20_hwmgr_funcs = {
+ /* init/fini related */
+ .backend_init = vega20_hwmgr_backend_init,
+ .backend_fini = vega20_hwmgr_backend_fini,
+ .asic_setup = vega20_setup_asic_task,
+ .power_off_asic = vega20_power_off_asic,
+ .dynamic_state_management_enable = vega20_enable_dpm_tasks,
+ .dynamic_state_management_disable = vega20_disable_dpm_tasks,
+ /* power state related */
+ .apply_clocks_adjust_rules = vega20_apply_clocks_adjust_rules,
+ .pre_display_config_changed = vega20_pre_display_configuration_changed_task,
+ .display_config_changed = vega20_display_configuration_changed_task,
+ .check_smc_update_required_for_display_configuration =
+ vega20_check_smc_update_required_for_display_configuration,
+ .notify_smc_display_config_after_ps_adjustment =
+ vega20_notify_smc_display_config_after_ps_adjustment,
+ /* export to DAL */
+ .get_sclk = vega20_dpm_get_sclk,
+ .get_mclk = vega20_dpm_get_mclk,
+ .get_dal_power_level = vega20_get_dal_power_level,
+ .get_clock_by_type_with_latency = vega20_get_clock_by_type_with_latency,
+ .get_clock_by_type_with_voltage = vega20_get_clock_by_type_with_voltage,
+ .set_watermarks_for_clocks_ranges = vega20_set_watermarks_for_clocks_ranges,
+ .display_clock_voltage_request = vega20_display_clock_voltage_request,
+ .get_performance_level = vega20_get_performance_level,
+ /* UMD pstate, profile related */
+ .force_dpm_level = vega20_dpm_force_dpm_level,
+ .get_power_profile_mode = vega20_get_power_profile_mode,
+ .set_power_profile_mode = vega20_set_power_profile_mode,
+ /* od related */
+ .set_power_limit = vega20_set_power_limit,
+ .get_sclk_od = vega20_get_sclk_od,
+ .set_sclk_od = vega20_set_sclk_od,
+ .get_mclk_od = vega20_get_mclk_od,
+ .set_mclk_od = vega20_set_mclk_od,
+ .odn_edit_dpm_table = vega20_odn_edit_dpm_table,
+ /* for sysfs to retrive/set gfxclk/memclk */
+ .force_clock_level = vega20_force_clock_level,
+ .print_clock_levels = vega20_print_clock_levels,
+ .read_sensor = vega20_read_sensor,
+ .get_ppfeature_status = vega20_get_ppfeature_status,
+ .set_ppfeature_status = vega20_set_ppfeature_status,
+ /* powergate related */
+ .powergate_uvd = vega20_power_gate_uvd,
+ .powergate_vce = vega20_power_gate_vce,
+ /* thermal related */
+ .start_thermal_controller = vega20_start_thermal_controller,
+ .stop_thermal_controller = vega20_thermal_stop_thermal_controller,
+ .get_thermal_temperature_range = vega20_get_thermal_temperature_range,
+ .register_irq_handlers = smu9_register_irq_handlers,
+ .disable_smc_firmware_ctf = vega20_thermal_disable_alert,
+ /* fan control related */
+ .get_fan_speed_percent = vega20_fan_ctrl_get_fan_speed_percent,
+ .set_fan_speed_percent = vega20_fan_ctrl_set_fan_speed_percent,
+ .get_fan_speed_info = vega20_fan_ctrl_get_fan_speed_info,
+ .get_fan_speed_rpm = vega20_fan_ctrl_get_fan_speed_rpm,
+ .set_fan_speed_rpm = vega20_fan_ctrl_set_fan_speed_rpm,
+ .get_fan_control_mode = vega20_get_fan_control_mode,
+ .set_fan_control_mode = vega20_set_fan_control_mode,
+ /* smu memory related */
+ .notify_cac_buffer_info = vega20_notify_cac_buffer_info,
+ .enable_mgpu_fan_boost = vega20_enable_mgpu_fan_boost,
+ /* BACO related */
+ .get_asic_baco_capability = vega20_baco_get_capability,
+ .get_asic_baco_state = vega20_baco_get_state,
+ .set_asic_baco_state = vega20_baco_set_state,
+ .set_mp1_state = vega20_set_mp1_state,
+ .smu_i2c_bus_access = vega20_smu_i2c_bus_access,
+ .set_df_cstate = vega20_set_df_cstate,
+ .set_xgmi_pstate = vega20_set_xgmi_pstate,
+ .get_gpu_metrics = vega20_get_gpu_metrics,
+};
+
+int vega20_hwmgr_init(struct pp_hwmgr *hwmgr)
+{
+ hwmgr->hwmgr_func = &vega20_hwmgr_funcs;
+ hwmgr->pptable_func = &vega20_pptable_funcs;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.h
new file mode 100644
index 000000000000..075c0094da9c
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.h
@@ -0,0 +1,590 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _VEGA20_HWMGR_H_
+#define _VEGA20_HWMGR_H_
+
+#include "hwmgr.h"
+#include "smu11_driver_if.h"
+#include "ppatomfwctrl.h"
+
+#define VEGA20_MAX_HARDWARE_POWERLEVELS 2
+
+#define WaterMarksExist 1
+#define WaterMarksLoaded 2
+
+#define VG20_PSUEDO_NUM_GFXCLK_DPM_LEVELS 8
+#define VG20_PSUEDO_NUM_SOCCLK_DPM_LEVELS 8
+#define VG20_PSUEDO_NUM_DCEFCLK_DPM_LEVELS 8
+#define VG20_PSUEDO_NUM_UCLK_DPM_LEVELS 4
+
+//OverDriver8 macro defs
+#define AVFS_CURVE 0
+#define OD8_HOTCURVE_TEMPERATURE 85
+
+#define VG20_CLOCK_MAX_DEFAULT 0xFFFF
+
+typedef uint32_t PP_Clock;
+
+enum {
+ GNLD_DPM_PREFETCHER = 0,
+ GNLD_DPM_GFXCLK,
+ GNLD_DPM_UCLK,
+ GNLD_DPM_SOCCLK,
+ GNLD_DPM_UVD,
+ GNLD_DPM_VCE,
+ GNLD_ULV,
+ GNLD_DPM_MP0CLK,
+ GNLD_DPM_LINK,
+ GNLD_DPM_DCEFCLK,
+ GNLD_DS_GFXCLK,
+ GNLD_DS_SOCCLK,
+ GNLD_DS_LCLK,
+ GNLD_PPT,
+ GNLD_TDC,
+ GNLD_THERMAL,
+ GNLD_GFX_PER_CU_CG,
+ GNLD_RM,
+ GNLD_DS_DCEFCLK,
+ GNLD_ACDC,
+ GNLD_VR0HOT,
+ GNLD_VR1HOT,
+ GNLD_FW_CTF,
+ GNLD_LED_DISPLAY,
+ GNLD_FAN_CONTROL,
+ GNLD_DIDT,
+ GNLD_GFXOFF,
+ GNLD_CG,
+ GNLD_DPM_FCLK,
+ GNLD_DS_FCLK,
+ GNLD_DS_MP1CLK,
+ GNLD_DS_MP0CLK,
+ GNLD_XGMI,
+ GNLD_ECC,
+
+ GNLD_FEATURES_MAX
+};
+
+
+#define GNLD_DPM_MAX (GNLD_DPM_DCEFCLK + 1)
+
+#define SMC_DPM_FEATURES 0x30F
+
+struct smu_features {
+ bool supported;
+ bool enabled;
+ bool allowed;
+ uint32_t smu_feature_id;
+ uint64_t smu_feature_bitmap;
+};
+
+struct vega20_performance_level {
+ uint32_t soc_clock;
+ uint32_t gfx_clock;
+ uint32_t mem_clock;
+};
+
+struct vega20_bacos {
+ uint32_t baco_flags;
+ /* struct vega20_performance_level performance_level; */
+};
+
+struct vega20_uvd_clocks {
+ uint32_t vclk;
+ uint32_t dclk;
+};
+
+struct vega20_vce_clocks {
+ uint32_t evclk;
+ uint32_t ecclk;
+};
+
+struct vega20_power_state {
+ uint32_t magic;
+ struct vega20_uvd_clocks uvd_clks;
+ struct vega20_vce_clocks vce_clks;
+ uint16_t performance_level_count;
+ bool dc_compatible;
+ uint32_t sclk_threshold;
+ struct vega20_performance_level performance_levels[VEGA20_MAX_HARDWARE_POWERLEVELS];
+};
+
+struct vega20_dpm_level {
+ bool enabled;
+ uint32_t value;
+ uint32_t param1;
+};
+
+#define VEGA20_MAX_DEEPSLEEP_DIVIDER_ID 5
+#define MAX_REGULAR_DPM_NUMBER 16
+#define MAX_PCIE_CONF 2
+#define VEGA20_MINIMUM_ENGINE_CLOCK 2500
+
+struct vega20_max_sustainable_clocks {
+ PP_Clock display_clock;
+ PP_Clock phy_clock;
+ PP_Clock pixel_clock;
+ PP_Clock uclock;
+ PP_Clock dcef_clock;
+ PP_Clock soc_clock;
+};
+
+struct vega20_dpm_state {
+ uint32_t soft_min_level;
+ uint32_t soft_max_level;
+ uint32_t hard_min_level;
+ uint32_t hard_max_level;
+};
+
+struct vega20_single_dpm_table {
+ uint32_t count;
+ struct vega20_dpm_state dpm_state;
+ struct vega20_dpm_level dpm_levels[MAX_REGULAR_DPM_NUMBER];
+};
+
+struct vega20_odn_dpm_control {
+ uint32_t count;
+ uint32_t entries[MAX_REGULAR_DPM_NUMBER];
+};
+
+struct vega20_pcie_table {
+ uint16_t count;
+ uint8_t pcie_gen[MAX_PCIE_CONF];
+ uint8_t pcie_lane[MAX_PCIE_CONF];
+ uint32_t lclk[MAX_PCIE_CONF];
+};
+
+struct vega20_dpm_table {
+ struct vega20_single_dpm_table soc_table;
+ struct vega20_single_dpm_table gfx_table;
+ struct vega20_single_dpm_table mem_table;
+ struct vega20_single_dpm_table eclk_table;
+ struct vega20_single_dpm_table vclk_table;
+ struct vega20_single_dpm_table dclk_table;
+ struct vega20_single_dpm_table dcef_table;
+ struct vega20_single_dpm_table pixel_table;
+ struct vega20_single_dpm_table display_table;
+ struct vega20_single_dpm_table phy_table;
+ struct vega20_single_dpm_table fclk_table;
+ struct vega20_pcie_table pcie_table;
+};
+
+#define VEGA20_MAX_LEAKAGE_COUNT 8
+struct vega20_leakage_voltage {
+ uint16_t count;
+ uint16_t leakage_id[VEGA20_MAX_LEAKAGE_COUNT];
+ uint16_t actual_voltage[VEGA20_MAX_LEAKAGE_COUNT];
+};
+
+struct vega20_display_timing {
+ uint32_t min_clock_in_sr;
+ uint32_t num_existing_displays;
+};
+
+struct vega20_dpmlevel_enable_mask {
+ uint32_t uvd_dpm_enable_mask;
+ uint32_t vce_dpm_enable_mask;
+ uint32_t samu_dpm_enable_mask;
+ uint32_t sclk_dpm_enable_mask;
+ uint32_t mclk_dpm_enable_mask;
+};
+
+struct vega20_vbios_boot_state {
+ uint8_t uc_cooling_id;
+ uint16_t vddc;
+ uint16_t vddci;
+ uint16_t mvddc;
+ uint16_t vdd_gfx;
+ uint32_t gfx_clock;
+ uint32_t mem_clock;
+ uint32_t soc_clock;
+ uint32_t dcef_clock;
+ uint32_t eclock;
+ uint32_t dclock;
+ uint32_t vclock;
+ uint32_t fclock;
+};
+
+#define DPMTABLE_OD_UPDATE_SCLK 0x00000001
+#define DPMTABLE_OD_UPDATE_MCLK 0x00000002
+#define DPMTABLE_UPDATE_SCLK 0x00000004
+#define DPMTABLE_UPDATE_MCLK 0x00000008
+#define DPMTABLE_OD_UPDATE_VDDC 0x00000010
+#define DPMTABLE_OD_UPDATE_SCLK_MASK 0x00000020
+#define DPMTABLE_OD_UPDATE_MCLK_MASK 0x00000040
+
+// To determine if sclk and mclk are in overdrive state
+#define SCLK_MASK_OVERDRIVE_ENABLED 0x00000008
+#define MCLK_MASK_OVERDRIVE_ENABLED 0x00000010
+#define SOCCLK_OVERDRIVE_ENABLED 0x00000020
+
+struct vega20_smc_state_table {
+ uint32_t soc_boot_level;
+ uint32_t gfx_boot_level;
+ uint32_t dcef_boot_level;
+ uint32_t mem_boot_level;
+ uint32_t uvd_boot_level;
+ uint32_t vce_boot_level;
+ uint32_t gfx_max_level;
+ uint32_t mem_max_level;
+ uint8_t vr_hot_gpio;
+ uint8_t ac_dc_gpio;
+ uint8_t therm_out_gpio;
+ uint8_t therm_out_polarity;
+ uint8_t therm_out_mode;
+ PPTable_t pp_table;
+ Watermarks_t water_marks_table;
+ AvfsDebugTable_t avfs_debug_table;
+ AvfsFuseOverride_t avfs_fuse_override_table;
+ SmuMetrics_t smu_metrics;
+ DriverSmuConfig_t driver_smu_config;
+ DpmActivityMonitorCoeffInt_t dpm_activity_monitor_coeffint;
+ OverDriveTable_t overdrive_table;
+};
+
+struct vega20_mclk_latency_entries {
+ uint32_t frequency;
+ uint32_t latency;
+};
+
+struct vega20_mclk_latency_table {
+ uint32_t count;
+ struct vega20_mclk_latency_entries entries[MAX_REGULAR_DPM_NUMBER];
+};
+
+struct vega20_registry_data {
+ uint64_t disallowed_features;
+ uint8_t ac_dc_switch_gpio_support;
+ uint8_t acg_loop_support;
+ uint8_t clock_stretcher_support;
+ uint8_t db_ramping_support;
+ uint8_t didt_mode;
+ uint8_t didt_support;
+ uint8_t edc_didt_support;
+ uint8_t force_dpm_high;
+ uint8_t fuzzy_fan_control_support;
+ uint8_t mclk_dpm_key_disabled;
+ uint8_t od_state_in_dc_support;
+ uint8_t pcie_lane_override;
+ uint8_t pcie_speed_override;
+ uint32_t pcie_clock_override;
+ uint8_t pcie_dpm_key_disabled;
+ uint8_t dcefclk_dpm_key_disabled;
+ uint8_t prefetcher_dpm_key_disabled;
+ uint8_t quick_transition_support;
+ uint8_t regulator_hot_gpio_support;
+ uint8_t master_deep_sleep_support;
+ uint8_t gfx_clk_deep_sleep_support;
+ uint8_t sclk_deep_sleep_support;
+ uint8_t lclk_deep_sleep_support;
+ uint8_t dce_fclk_deep_sleep_support;
+ uint8_t sclk_dpm_key_disabled;
+ uint8_t sclk_throttle_low_notification;
+ uint8_t skip_baco_hardware;
+ uint8_t socclk_dpm_key_disabled;
+ uint8_t sq_ramping_support;
+ uint8_t tcp_ramping_support;
+ uint8_t td_ramping_support;
+ uint8_t dbr_ramping_support;
+ uint8_t gc_didt_support;
+ uint8_t psm_didt_support;
+ uint8_t thermal_support;
+ uint8_t fw_ctf_enabled;
+ uint8_t led_dpm_enabled;
+ uint8_t fan_control_support;
+ uint8_t ulv_support;
+ uint8_t od8_feature_enable;
+ uint8_t disable_water_mark;
+ uint8_t disable_workload_policy;
+ uint32_t force_workload_policy_mask;
+ uint8_t disable_3d_fs_detection;
+ uint8_t disable_pp_tuning;
+ uint8_t disable_xlpp_tuning;
+ uint32_t perf_ui_tuning_profile_turbo;
+ uint32_t perf_ui_tuning_profile_powerSave;
+ uint32_t perf_ui_tuning_profile_xl;
+ uint16_t zrpm_stop_temp;
+ uint16_t zrpm_start_temp;
+ uint32_t stable_pstate_sclk_dpm_percentage;
+ uint8_t fps_support;
+ uint8_t vr0hot;
+ uint8_t vr1hot;
+ uint8_t disable_auto_wattman;
+ uint32_t auto_wattman_debug;
+ uint32_t auto_wattman_sample_period;
+ uint32_t fclk_gfxclk_ratio;
+ uint8_t auto_wattman_threshold;
+ uint8_t log_avfs_param;
+ uint8_t enable_enginess;
+ uint8_t custom_fan_support;
+ uint8_t disable_pcc_limit_control;
+ uint8_t gfxoff_controlled_by_driver;
+};
+
+struct vega20_odn_clock_voltage_dependency_table {
+ uint32_t count;
+ struct phm_ppt_v1_clock_voltage_dependency_record
+ entries[MAX_REGULAR_DPM_NUMBER];
+};
+
+struct vega20_odn_dpm_table {
+ struct vega20_odn_dpm_control control_gfxclk_state;
+ struct vega20_odn_dpm_control control_memclk_state;
+ struct phm_odn_clock_levels odn_core_clock_dpm_levels;
+ struct phm_odn_clock_levels odn_memory_clock_dpm_levels;
+ struct vega20_odn_clock_voltage_dependency_table vdd_dependency_on_sclk;
+ struct vega20_odn_clock_voltage_dependency_table vdd_dependency_on_mclk;
+ struct vega20_odn_clock_voltage_dependency_table vdd_dependency_on_socclk;
+ uint32_t odn_mclk_min_limit;
+};
+
+struct vega20_odn_fan_table {
+ uint32_t target_fan_speed;
+ uint32_t target_temperature;
+ uint32_t min_performance_clock;
+ uint32_t min_fan_limit;
+ bool force_fan_pwm;
+};
+
+struct vega20_odn_temp_table {
+ uint16_t target_operating_temp;
+ uint16_t default_target_operating_temp;
+ uint16_t operating_temp_min_limit;
+ uint16_t operating_temp_max_limit;
+ uint16_t operating_temp_step;
+};
+
+struct vega20_odn_data {
+ uint32_t apply_overdrive_next_settings_mask;
+ uint32_t overdrive_next_state;
+ uint32_t overdrive_next_capabilities;
+ uint32_t odn_sclk_dpm_enable_mask;
+ uint32_t odn_mclk_dpm_enable_mask;
+ struct vega20_odn_dpm_table odn_dpm_table;
+ struct vega20_odn_fan_table odn_fan_table;
+ struct vega20_odn_temp_table odn_temp_table;
+};
+
+enum OD8_FEATURE_ID
+{
+ OD8_GFXCLK_LIMITS = 1 << 0,
+ OD8_GFXCLK_CURVE = 1 << 1,
+ OD8_UCLK_MAX = 1 << 2,
+ OD8_POWER_LIMIT = 1 << 3,
+ OD8_ACOUSTIC_LIMIT_SCLK = 1 << 4, //FanMaximumRpm
+ OD8_FAN_SPEED_MIN = 1 << 5, //FanMinimumPwm
+ OD8_TEMPERATURE_FAN = 1 << 6, //FanTargetTemperature
+ OD8_TEMPERATURE_SYSTEM = 1 << 7, //MaxOpTemp
+ OD8_MEMORY_TIMING_TUNE = 1 << 8,
+ OD8_FAN_ZERO_RPM_CONTROL = 1 << 9
+};
+
+enum OD8_SETTING_ID
+{
+ OD8_SETTING_GFXCLK_FMIN = 0,
+ OD8_SETTING_GFXCLK_FMAX,
+ OD8_SETTING_GFXCLK_FREQ1,
+ OD8_SETTING_GFXCLK_VOLTAGE1,
+ OD8_SETTING_GFXCLK_FREQ2,
+ OD8_SETTING_GFXCLK_VOLTAGE2,
+ OD8_SETTING_GFXCLK_FREQ3,
+ OD8_SETTING_GFXCLK_VOLTAGE3,
+ OD8_SETTING_UCLK_FMAX,
+ OD8_SETTING_POWER_PERCENTAGE,
+ OD8_SETTING_FAN_ACOUSTIC_LIMIT,
+ OD8_SETTING_FAN_MIN_SPEED,
+ OD8_SETTING_FAN_TARGET_TEMP,
+ OD8_SETTING_OPERATING_TEMP_MAX,
+ OD8_SETTING_AC_TIMING,
+ OD8_SETTING_FAN_ZERO_RPM_CONTROL,
+ OD8_SETTING_COUNT
+};
+
+struct vega20_od8_single_setting {
+ uint32_t feature_id;
+ int32_t min_value;
+ int32_t max_value;
+ int32_t current_value;
+ int32_t default_value;
+};
+
+struct vega20_od8_settings {
+ uint32_t overdrive8_capabilities;
+ struct vega20_od8_single_setting od8_settings_array[OD8_SETTING_COUNT];
+};
+
+struct vega20_hwmgr {
+ struct vega20_dpm_table dpm_table;
+ struct vega20_dpm_table golden_dpm_table;
+ struct vega20_registry_data registry_data;
+ struct vega20_vbios_boot_state vbios_boot_state;
+ struct vega20_mclk_latency_table mclk_latency_table;
+
+ struct vega20_max_sustainable_clocks max_sustainable_clocks;
+
+ struct vega20_leakage_voltage vddc_leakage;
+
+ uint32_t vddc_control;
+ struct pp_atomfwctrl_voltage_table vddc_voltage_table;
+ uint32_t mvdd_control;
+ struct pp_atomfwctrl_voltage_table mvdd_voltage_table;
+ uint32_t vddci_control;
+ struct pp_atomfwctrl_voltage_table vddci_voltage_table;
+
+ uint32_t active_auto_throttle_sources;
+ struct vega20_bacos bacos;
+
+ /* ---- General data ---- */
+ uint8_t need_update_dpm_table;
+
+ bool cac_enabled;
+ bool battery_state;
+ bool is_tlu_enabled;
+ bool avfs_exist;
+
+ uint32_t low_sclk_interrupt_threshold;
+
+ uint32_t total_active_cus;
+
+ uint32_t water_marks_bitmap;
+
+ struct vega20_display_timing display_timing;
+
+ /* ---- Vega20 Dyn Register Settings ---- */
+
+ uint32_t debug_settings;
+ uint32_t lowest_uclk_reserved_for_ulv;
+ uint32_t gfxclk_average_alpha;
+ uint32_t socclk_average_alpha;
+ uint32_t uclk_average_alpha;
+ uint32_t gfx_activity_average_alpha;
+ uint32_t display_voltage_mode;
+ uint32_t dcef_clk_quad_eqn_a;
+ uint32_t dcef_clk_quad_eqn_b;
+ uint32_t dcef_clk_quad_eqn_c;
+ uint32_t disp_clk_quad_eqn_a;
+ uint32_t disp_clk_quad_eqn_b;
+ uint32_t disp_clk_quad_eqn_c;
+ uint32_t pixel_clk_quad_eqn_a;
+ uint32_t pixel_clk_quad_eqn_b;
+ uint32_t pixel_clk_quad_eqn_c;
+ uint32_t phy_clk_quad_eqn_a;
+ uint32_t phy_clk_quad_eqn_b;
+ uint32_t phy_clk_quad_eqn_c;
+
+ /* ---- Thermal Temperature Setting ---- */
+ struct vega20_dpmlevel_enable_mask dpm_level_enable_mask;
+
+ /* ---- Power Gating States ---- */
+ bool uvd_power_gated;
+ bool vce_power_gated;
+ bool samu_power_gated;
+ bool need_long_memory_training;
+
+ /* Internal settings to apply the application power optimization parameters */
+ bool apply_optimized_settings;
+ uint32_t disable_dpm_mask;
+
+ /* ---- Overdrive next setting ---- */
+ struct vega20_odn_data odn_data;
+ bool gfxclk_overdrive;
+ bool memclk_overdrive;
+
+ /* ---- Overdrive8 Setting ---- */
+ struct vega20_od8_settings od8_settings;
+
+ /* ---- Workload Mask ---- */
+ uint32_t workload_mask;
+
+ /* ---- SMU9 ---- */
+ uint32_t smu_version;
+ struct smu_features smu_features[GNLD_FEATURES_MAX];
+ struct vega20_smc_state_table smc_state_table;
+
+ /* ---- Gfxoff ---- */
+ bool gfxoff_allowed;
+ uint32_t counter_gfxoff;
+
+ unsigned long metrics_time;
+ SmuMetrics_t metrics_table;
+ struct gpu_metrics_v1_0 gpu_metrics_table;
+
+ bool pcie_parameters_override;
+ uint32_t pcie_gen_level1;
+ uint32_t pcie_width_level1;
+
+ bool is_custom_profile_set;
+};
+
+#define VEGA20_DPM2_NEAR_TDP_DEC 10
+#define VEGA20_DPM2_ABOVE_SAFE_INC 5
+#define VEGA20_DPM2_BELOW_SAFE_INC 20
+
+#define VEGA20_DPM2_LTA_WINDOW_SIZE 7
+
+#define VEGA20_DPM2_LTS_TRUNCATE 0
+
+#define VEGA20_DPM2_TDP_SAFE_LIMIT_PERCENT 80
+
+#define VEGA20_DPM2_MAXPS_PERCENT_M 90
+#define VEGA20_DPM2_MAXPS_PERCENT_H 90
+
+#define VEGA20_DPM2_PWREFFICIENCYRATIO_MARGIN 50
+
+#define VEGA20_DPM2_SQ_RAMP_MAX_POWER 0x3FFF
+#define VEGA20_DPM2_SQ_RAMP_MIN_POWER 0x12
+#define VEGA20_DPM2_SQ_RAMP_MAX_POWER_DELTA 0x15
+#define VEGA20_DPM2_SQ_RAMP_SHORT_TERM_INTERVAL_SIZE 0x1E
+#define VEGA20_DPM2_SQ_RAMP_LONG_TERM_INTERVAL_RATIO 0xF
+
+#define VEGA20_VOLTAGE_CONTROL_NONE 0x0
+#define VEGA20_VOLTAGE_CONTROL_BY_GPIO 0x1
+#define VEGA20_VOLTAGE_CONTROL_BY_SVID2 0x2
+#define VEGA20_VOLTAGE_CONTROL_MERGED 0x3
+/* To convert to Q8.8 format for firmware */
+#define VEGA20_Q88_FORMAT_CONVERSION_UNIT 256
+
+#define VEGA20_UNUSED_GPIO_PIN 0x7F
+
+#define VEGA20_THERM_OUT_MODE_DISABLE 0x0
+#define VEGA20_THERM_OUT_MODE_THERM_ONLY 0x1
+#define VEGA20_THERM_OUT_MODE_THERM_VRHOT 0x2
+
+#define PPVEGA20_VEGA20DISPLAYVOLTAGEMODE_DFLT 0xffffffff
+#define PPREGKEY_VEGA20QUADRATICEQUATION_DFLT 0xffffffff
+
+#define PPVEGA20_VEGA20GFXCLKAVERAGEALPHA_DFLT 25 /* 10% * 255 = 25 */
+#define PPVEGA20_VEGA20SOCCLKAVERAGEALPHA_DFLT 25 /* 10% * 255 = 25 */
+#define PPVEGA20_VEGA20UCLKCLKAVERAGEALPHA_DFLT 25 /* 10% * 255 = 25 */
+#define PPVEGA20_VEGA20GFXACTIVITYAVERAGEALPHA_DFLT 25 /* 10% * 255 = 25 */
+#define PPVEGA20_VEGA20LOWESTUCLKRESERVEDFORULV_DFLT 0xffffffff
+#define PPVEGA20_VEGA20DISPLAYVOLTAGEMODE_DFLT 0xffffffff
+#define PPREGKEY_VEGA20QUADRATICEQUATION_DFLT 0xffffffff
+
+#define VEGA20_UMD_PSTATE_GFXCLK_LEVEL 0x3
+#define VEGA20_UMD_PSTATE_SOCCLK_LEVEL 0x3
+#define VEGA20_UMD_PSTATE_MCLK_LEVEL 0x2
+#define VEGA20_UMD_PSTATE_UVDCLK_LEVEL 0x3
+#define VEGA20_UMD_PSTATE_VCEMCLK_LEVEL 0x3
+
+#endif /* _VEGA20_HWMGR_H_ */
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_inc.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_inc.h
new file mode 100644
index 000000000000..613cb1989b3d
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_inc.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 VEGA20_INC_H
+#define VEGA20_INC_H
+
+#include "asic_reg/thm/thm_11_0_2_offset.h"
+#include "asic_reg/thm/thm_11_0_2_sh_mask.h"
+
+#include "asic_reg/mp/mp_9_0_offset.h"
+#include "asic_reg/mp/mp_9_0_sh_mask.h"
+
+#include "asic_reg/nbio/nbio_7_4_offset.h"
+#include "asic_reg/nbio/nbio_7_4_sh_mask.h"
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_powertune.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_powertune.c
new file mode 100644
index 000000000000..d7cc3d2d9e17
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_powertune.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "hwmgr.h"
+#include "vega20_hwmgr.h"
+#include "vega20_powertune.h"
+#include "vega20_smumgr.h"
+#include "vega20_ppsmc.h"
+#include "vega20_inc.h"
+#include "pp_debug.h"
+
+int vega20_set_power_limit(struct pp_hwmgr *hwmgr, uint32_t n)
+{
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
+
+ if (data->smu_features[GNLD_PPT].enabled)
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetPptLimit, n,
+ NULL);
+
+ return 0;
+}
+
+int vega20_validate_power_level_request(struct pp_hwmgr *hwmgr,
+ uint32_t tdp_percentage_adjustment, uint32_t tdp_absolute_value_adjustment)
+{
+ return (tdp_percentage_adjustment > hwmgr->platform_descriptor.TDPLimit) ? -1 : 0;
+}
+
+static int vega20_set_overdrive_target_percentage(struct pp_hwmgr *hwmgr,
+ uint32_t adjust_percent)
+{
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_OverDriveSetPercentage, adjust_percent,
+ NULL);
+}
+
+int vega20_power_control_set_level(struct pp_hwmgr *hwmgr)
+{
+ int adjust_percent, result = 0;
+
+ if (PP_CAP(PHM_PlatformCaps_PowerContainment)) {
+ adjust_percent =
+ hwmgr->platform_descriptor.TDPAdjustmentPolarity ?
+ hwmgr->platform_descriptor.TDPAdjustment :
+ (-1 * hwmgr->platform_descriptor.TDPAdjustment);
+ result = vega20_set_overdrive_target_percentage(hwmgr,
+ (uint32_t)adjust_percent);
+ }
+ return result;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_powertune.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_powertune.h
new file mode 100644
index 000000000000..d68c734c0f4e
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_powertune.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _VEGA20_POWERTUNE_H_
+#define _VEGA20_POWERTUNE_H_
+
+int vega20_set_power_limit(struct pp_hwmgr *hwmgr, uint32_t n);
+int vega20_power_control_set_level(struct pp_hwmgr *hwmgr);
+int vega20_validate_power_level_request(struct pp_hwmgr *hwmgr,
+ uint32_t tdp_percentage_adjustment,
+ uint32_t tdp_absolute_value_adjustment);
+#endif /* _VEGA20_POWERTUNE_H_ */
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_pptable.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_pptable.h
new file mode 100644
index 000000000000..2222e29405c6
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_pptable.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _VEGA20_PPTABLE_H_
+#define _VEGA20_PPTABLE_H_
+
+#pragma pack(push, 1)
+
+#define ATOM_VEGA20_PP_THERMALCONTROLLER_NONE 0
+#define ATOM_VEGA20_PP_THERMALCONTROLLER_VEGA20 26
+
+#define ATOM_VEGA20_PP_PLATFORM_CAP_POWERPLAY 0x1
+#define ATOM_VEGA20_PP_PLATFORM_CAP_SBIOSPOWERSOURCE 0x2
+#define ATOM_VEGA20_PP_PLATFORM_CAP_HARDWAREDC 0x4
+#define ATOM_VEGA20_PP_PLATFORM_CAP_BACO 0x8
+#define ATOM_VEGA20_PP_PLATFORM_CAP_BAMACO 0x10
+#define ATOM_VEGA20_PP_PLATFORM_CAP_ENABLESHADOWPSTATE 0x20
+
+#define ATOM_VEGA20_TABLE_REVISION_VEGA20 11
+#define ATOM_VEGA20_ODFEATURE_MAX_COUNT 32
+#define ATOM_VEGA20_ODSETTING_MAX_COUNT 32
+#define ATOM_VEGA20_PPCLOCK_MAX_COUNT 16
+
+enum ATOM_VEGA20_ODFEATURE_ID {
+ ATOM_VEGA20_ODFEATURE_GFXCLK_LIMITS = 0,
+ ATOM_VEGA20_ODFEATURE_GFXCLK_CURVE,
+ ATOM_VEGA20_ODFEATURE_UCLK_MAX,
+ ATOM_VEGA20_ODFEATURE_POWER_LIMIT,
+ ATOM_VEGA20_ODFEATURE_FAN_ACOUSTIC_LIMIT, //FanMaximumRpm
+ ATOM_VEGA20_ODFEATURE_FAN_SPEED_MIN, //FanMinimumPwm
+ ATOM_VEGA20_ODFEATURE_TEMPERATURE_FAN, //FanTargetTemperature
+ ATOM_VEGA20_ODFEATURE_TEMPERATURE_SYSTEM, //MaxOpTemp
+ ATOM_VEGA20_ODFEATURE_MEMORY_TIMING_TUNE,
+ ATOM_VEGA20_ODFEATURE_FAN_ZERO_RPM_CONTROL,
+ ATOM_VEGA20_ODFEATURE_COUNT,
+};
+
+enum ATOM_VEGA20_ODSETTING_ID {
+ ATOM_VEGA20_ODSETTING_GFXCLKFMAX = 0,
+ ATOM_VEGA20_ODSETTING_GFXCLKFMIN,
+ ATOM_VEGA20_ODSETTING_VDDGFXCURVEFREQ_P1,
+ ATOM_VEGA20_ODSETTING_VDDGFXCURVEVOLTAGEOFFSET_P1,
+ ATOM_VEGA20_ODSETTING_VDDGFXCURVEFREQ_P2,
+ ATOM_VEGA20_ODSETTING_VDDGFXCURVEVOLTAGEOFFSET_P2,
+ ATOM_VEGA20_ODSETTING_VDDGFXCURVEFREQ_P3,
+ ATOM_VEGA20_ODSETTING_VDDGFXCURVEVOLTAGEOFFSET_P3,
+ ATOM_VEGA20_ODSETTING_UCLKFMAX,
+ ATOM_VEGA20_ODSETTING_POWERPERCENTAGE,
+ ATOM_VEGA20_ODSETTING_FANRPMMIN,
+ ATOM_VEGA20_ODSETTING_FANRPMACOUSTICLIMIT,
+ ATOM_VEGA20_ODSETTING_FANTARGETTEMPERATURE,
+ ATOM_VEGA20_ODSETTING_OPERATINGTEMPMAX,
+ ATOM_VEGA20_ODSETTING_COUNT,
+};
+typedef enum ATOM_VEGA20_ODSETTING_ID ATOM_VEGA20_ODSETTING_ID;
+
+typedef struct _ATOM_VEGA20_OVERDRIVE8_RECORD
+{
+ UCHAR ucODTableRevision;
+ ULONG ODFeatureCount;
+ UCHAR ODFeatureCapabilities [ATOM_VEGA20_ODFEATURE_MAX_COUNT]; //OD feature support flags
+ ULONG ODSettingCount;
+ ULONG ODSettingsMax [ATOM_VEGA20_ODSETTING_MAX_COUNT]; //Upper Limit for each OD Setting
+ ULONG ODSettingsMin [ATOM_VEGA20_ODSETTING_MAX_COUNT]; //Lower Limit for each OD Setting
+} ATOM_VEGA20_OVERDRIVE8_RECORD;
+
+enum ATOM_VEGA20_PPCLOCK_ID {
+ ATOM_VEGA20_PPCLOCK_GFXCLK = 0,
+ ATOM_VEGA20_PPCLOCK_VCLK,
+ ATOM_VEGA20_PPCLOCK_DCLK,
+ ATOM_VEGA20_PPCLOCK_ECLK,
+ ATOM_VEGA20_PPCLOCK_SOCCLK,
+ ATOM_VEGA20_PPCLOCK_UCLK,
+ ATOM_VEGA20_PPCLOCK_FCLK,
+ ATOM_VEGA20_PPCLOCK_DCEFCLK,
+ ATOM_VEGA20_PPCLOCK_DISPCLK,
+ ATOM_VEGA20_PPCLOCK_PIXCLK,
+ ATOM_VEGA20_PPCLOCK_PHYCLK,
+ ATOM_VEGA20_PPCLOCK_COUNT,
+};
+typedef enum ATOM_VEGA20_PPCLOCK_ID ATOM_VEGA20_PPCLOCK_ID;
+
+typedef struct _ATOM_VEGA20_POWER_SAVING_CLOCK_RECORD
+{
+ UCHAR ucTableRevision;
+ ULONG PowerSavingClockCount; // Count of PowerSavingClock Mode
+ ULONG PowerSavingClockMax [ATOM_VEGA20_PPCLOCK_MAX_COUNT]; // PowerSavingClock Mode Clock Maximum array In MHz
+ ULONG PowerSavingClockMin [ATOM_VEGA20_PPCLOCK_MAX_COUNT]; // PowerSavingClock Mode Clock Minimum array In MHz
+} ATOM_VEGA20_POWER_SAVING_CLOCK_RECORD;
+
+typedef struct _ATOM_VEGA20_POWERPLAYTABLE
+{
+ struct atom_common_table_header sHeader;
+ UCHAR ucTableRevision;
+ USHORT usTableSize;
+ ULONG ulGoldenPPID;
+ ULONG ulGoldenRevision;
+ USHORT usFormatID;
+
+ ULONG ulPlatformCaps;
+
+ UCHAR ucThermalControllerType;
+
+ USHORT usSmallPowerLimit1;
+ USHORT usSmallPowerLimit2;
+ USHORT usBoostPowerLimit;
+ USHORT usODTurboPowerLimit;
+ USHORT usODPowerSavePowerLimit;
+ USHORT usSoftwareShutdownTemp;
+
+ ATOM_VEGA20_POWER_SAVING_CLOCK_RECORD PowerSavingClockTable; //PowerSavingClock Mode Clock Min/Max array
+
+ ATOM_VEGA20_OVERDRIVE8_RECORD OverDrive8Table; //OverDrive8 Feature capabilities and Settings Range (Max and Min)
+
+ USHORT usReserve[5];
+
+ PPTable_t smcPPTable;
+
+} ATOM_Vega20_POWERPLAYTABLE;
+
+#pragma pack(pop)
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c
new file mode 100644
index 000000000000..1f9082539457
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c
@@ -0,0 +1,970 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <linux/module.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+
+#include "smu11_driver_if.h"
+#include "vega20_processpptables.h"
+#include "ppatomfwctrl.h"
+#include "atomfirmware.h"
+#include "pp_debug.h"
+#include "cgs_common.h"
+#include "vega20_pptable.h"
+
+#define VEGA20_FAN_TARGET_TEMPERATURE_OVERRIDE 105
+
+static void set_hw_cap(struct pp_hwmgr *hwmgr, bool enable,
+ enum phm_platform_caps cap)
+{
+ if (enable)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, cap);
+ else
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps, cap);
+}
+
+static const void *get_powerplay_table(struct pp_hwmgr *hwmgr)
+{
+ int index = GetIndexIntoMasterDataTable(powerplayinfo);
+
+ u16 size;
+ u8 frev, crev;
+ const void *table_address = hwmgr->soft_pp_table;
+
+ if (!table_address) {
+ table_address = (ATOM_Vega20_POWERPLAYTABLE *)
+ smu_atom_get_data_table(hwmgr->adev, index,
+ &size, &frev, &crev);
+
+ hwmgr->soft_pp_table = table_address;
+ hwmgr->soft_pp_table_size = size;
+ }
+
+ return table_address;
+}
+
+#if 0
+static void dump_pptable(PPTable_t *pptable)
+{
+ int i;
+
+ pr_info("Version = 0x%08x\n", pptable->Version);
+
+ pr_info("FeaturesToRun[0] = 0x%08x\n", pptable->FeaturesToRun[0]);
+ pr_info("FeaturesToRun[1] = 0x%08x\n", pptable->FeaturesToRun[1]);
+
+ pr_info("SocketPowerLimitAc0 = %d\n", pptable->SocketPowerLimitAc0);
+ pr_info("SocketPowerLimitAc0Tau = %d\n", pptable->SocketPowerLimitAc0Tau);
+ pr_info("SocketPowerLimitAc1 = %d\n", pptable->SocketPowerLimitAc1);
+ pr_info("SocketPowerLimitAc1Tau = %d\n", pptable->SocketPowerLimitAc1Tau);
+ pr_info("SocketPowerLimitAc2 = %d\n", pptable->SocketPowerLimitAc2);
+ pr_info("SocketPowerLimitAc2Tau = %d\n", pptable->SocketPowerLimitAc2Tau);
+ pr_info("SocketPowerLimitAc3 = %d\n", pptable->SocketPowerLimitAc3);
+ pr_info("SocketPowerLimitAc3Tau = %d\n", pptable->SocketPowerLimitAc3Tau);
+ pr_info("SocketPowerLimitDc = %d\n", pptable->SocketPowerLimitDc);
+ pr_info("SocketPowerLimitDcTau = %d\n", pptable->SocketPowerLimitDcTau);
+ pr_info("TdcLimitSoc = %d\n", pptable->TdcLimitSoc);
+ pr_info("TdcLimitSocTau = %d\n", pptable->TdcLimitSocTau);
+ pr_info("TdcLimitGfx = %d\n", pptable->TdcLimitGfx);
+ pr_info("TdcLimitGfxTau = %d\n", pptable->TdcLimitGfxTau);
+
+ pr_info("TedgeLimit = %d\n", pptable->TedgeLimit);
+ pr_info("ThotspotLimit = %d\n", pptable->ThotspotLimit);
+ pr_info("ThbmLimit = %d\n", pptable->ThbmLimit);
+ pr_info("Tvr_gfxLimit = %d\n", pptable->Tvr_gfxLimit);
+ pr_info("Tvr_memLimit = %d\n", pptable->Tvr_memLimit);
+ pr_info("Tliquid1Limit = %d\n", pptable->Tliquid1Limit);
+ pr_info("Tliquid2Limit = %d\n", pptable->Tliquid2Limit);
+ pr_info("TplxLimit = %d\n", pptable->TplxLimit);
+ pr_info("FitLimit = %d\n", pptable->FitLimit);
+
+ pr_info("PpmPowerLimit = %d\n", pptable->PpmPowerLimit);
+ pr_info("PpmTemperatureThreshold = %d\n", pptable->PpmTemperatureThreshold);
+
+ pr_info("MemoryOnPackage = 0x%02x\n", pptable->MemoryOnPackage);
+ pr_info("padding8_limits = 0x%02x\n", pptable->padding8_limits);
+ pr_info("Tvr_SocLimit = %d\n", pptable->Tvr_SocLimit);
+
+ pr_info("UlvVoltageOffsetSoc = %d\n", pptable->UlvVoltageOffsetSoc);
+ pr_info("UlvVoltageOffsetGfx = %d\n", pptable->UlvVoltageOffsetGfx);
+
+ pr_info("UlvSmnclkDid = %d\n", pptable->UlvSmnclkDid);
+ pr_info("UlvMp1clkDid = %d\n", pptable->UlvMp1clkDid);
+ pr_info("UlvGfxclkBypass = %d\n", pptable->UlvGfxclkBypass);
+ pr_info("Padding234 = 0x%02x\n", pptable->Padding234);
+
+ pr_info("MinVoltageGfx = %d\n", pptable->MinVoltageGfx);
+ pr_info("MinVoltageSoc = %d\n", pptable->MinVoltageSoc);
+ pr_info("MaxVoltageGfx = %d\n", pptable->MaxVoltageGfx);
+ pr_info("MaxVoltageSoc = %d\n", pptable->MaxVoltageSoc);
+
+ pr_info("LoadLineResistanceGfx = %d\n", pptable->LoadLineResistanceGfx);
+ pr_info("LoadLineResistanceSoc = %d\n", pptable->LoadLineResistanceSoc);
+
+ pr_info("[PPCLK_GFXCLK]\n"
+ " .VoltageMode = 0x%02x\n"
+ " .SnapToDiscrete = 0x%02x\n"
+ " .NumDiscreteLevels = 0x%02x\n"
+ " .padding = 0x%02x\n"
+ " .ConversionToAvfsClk{m = 0x%08x b = 0x%08x}\n"
+ " .SsCurve {a = 0x%08x b = 0x%08x c = 0x%08x}\n",
+ pptable->DpmDescriptor[PPCLK_GFXCLK].VoltageMode,
+ pptable->DpmDescriptor[PPCLK_GFXCLK].SnapToDiscrete,
+ pptable->DpmDescriptor[PPCLK_GFXCLK].NumDiscreteLevels,
+ pptable->DpmDescriptor[PPCLK_GFXCLK].padding,
+ pptable->DpmDescriptor[PPCLK_GFXCLK].ConversionToAvfsClk.m,
+ pptable->DpmDescriptor[PPCLK_GFXCLK].ConversionToAvfsClk.b,
+ pptable->DpmDescriptor[PPCLK_GFXCLK].SsCurve.a,
+ pptable->DpmDescriptor[PPCLK_GFXCLK].SsCurve.b,
+ pptable->DpmDescriptor[PPCLK_GFXCLK].SsCurve.c);
+
+ pr_info("[PPCLK_VCLK]\n"
+ " .VoltageMode = 0x%02x\n"
+ " .SnapToDiscrete = 0x%02x\n"
+ " .NumDiscreteLevels = 0x%02x\n"
+ " .padding = 0x%02x\n"
+ " .ConversionToAvfsClk{m = 0x%08x b = 0x%08x}\n"
+ " .SsCurve {a = 0x%08x b = 0x%08x c = 0x%08x}\n",
+ pptable->DpmDescriptor[PPCLK_VCLK].VoltageMode,
+ pptable->DpmDescriptor[PPCLK_VCLK].SnapToDiscrete,
+ pptable->DpmDescriptor[PPCLK_VCLK].NumDiscreteLevels,
+ pptable->DpmDescriptor[PPCLK_VCLK].padding,
+ pptable->DpmDescriptor[PPCLK_VCLK].ConversionToAvfsClk.m,
+ pptable->DpmDescriptor[PPCLK_VCLK].ConversionToAvfsClk.b,
+ pptable->DpmDescriptor[PPCLK_VCLK].SsCurve.a,
+ pptable->DpmDescriptor[PPCLK_VCLK].SsCurve.b,
+ pptable->DpmDescriptor[PPCLK_VCLK].SsCurve.c);
+
+ pr_info("[PPCLK_DCLK]\n"
+ " .VoltageMode = 0x%02x\n"
+ " .SnapToDiscrete = 0x%02x\n"
+ " .NumDiscreteLevels = 0x%02x\n"
+ " .padding = 0x%02x\n"
+ " .ConversionToAvfsClk{m = 0x%08x b = 0x%08x}\n"
+ " .SsCurve {a = 0x%08x b = 0x%08x c = 0x%08x}\n",
+ pptable->DpmDescriptor[PPCLK_DCLK].VoltageMode,
+ pptable->DpmDescriptor[PPCLK_DCLK].SnapToDiscrete,
+ pptable->DpmDescriptor[PPCLK_DCLK].NumDiscreteLevels,
+ pptable->DpmDescriptor[PPCLK_DCLK].padding,
+ pptable->DpmDescriptor[PPCLK_DCLK].ConversionToAvfsClk.m,
+ pptable->DpmDescriptor[PPCLK_DCLK].ConversionToAvfsClk.b,
+ pptable->DpmDescriptor[PPCLK_DCLK].SsCurve.a,
+ pptable->DpmDescriptor[PPCLK_DCLK].SsCurve.b,
+ pptable->DpmDescriptor[PPCLK_DCLK].SsCurve.c);
+
+ pr_info("[PPCLK_ECLK]\n"
+ " .VoltageMode = 0x%02x\n"
+ " .SnapToDiscrete = 0x%02x\n"
+ " .NumDiscreteLevels = 0x%02x\n"
+ " .padding = 0x%02x\n"
+ " .ConversionToAvfsClk{m = 0x%08x b = 0x%08x}\n"
+ " .SsCurve {a = 0x%08x b = 0x%08x c = 0x%08x}\n",
+ pptable->DpmDescriptor[PPCLK_ECLK].VoltageMode,
+ pptable->DpmDescriptor[PPCLK_ECLK].SnapToDiscrete,
+ pptable->DpmDescriptor[PPCLK_ECLK].NumDiscreteLevels,
+ pptable->DpmDescriptor[PPCLK_ECLK].padding,
+ pptable->DpmDescriptor[PPCLK_ECLK].ConversionToAvfsClk.m,
+ pptable->DpmDescriptor[PPCLK_ECLK].ConversionToAvfsClk.b,
+ pptable->DpmDescriptor[PPCLK_ECLK].SsCurve.a,
+ pptable->DpmDescriptor[PPCLK_ECLK].SsCurve.b,
+ pptable->DpmDescriptor[PPCLK_ECLK].SsCurve.c);
+
+ pr_info("[PPCLK_SOCCLK]\n"
+ " .VoltageMode = 0x%02x\n"
+ " .SnapToDiscrete = 0x%02x\n"
+ " .NumDiscreteLevels = 0x%02x\n"
+ " .padding = 0x%02x\n"
+ " .ConversionToAvfsClk{m = 0x%08x b = 0x%08x}\n"
+ " .SsCurve {a = 0x%08x b = 0x%08x c = 0x%08x}\n",
+ pptable->DpmDescriptor[PPCLK_SOCCLK].VoltageMode,
+ pptable->DpmDescriptor[PPCLK_SOCCLK].SnapToDiscrete,
+ pptable->DpmDescriptor[PPCLK_SOCCLK].NumDiscreteLevels,
+ pptable->DpmDescriptor[PPCLK_SOCCLK].padding,
+ pptable->DpmDescriptor[PPCLK_SOCCLK].ConversionToAvfsClk.m,
+ pptable->DpmDescriptor[PPCLK_SOCCLK].ConversionToAvfsClk.b,
+ pptable->DpmDescriptor[PPCLK_SOCCLK].SsCurve.a,
+ pptable->DpmDescriptor[PPCLK_SOCCLK].SsCurve.b,
+ pptable->DpmDescriptor[PPCLK_SOCCLK].SsCurve.c);
+
+ pr_info("[PPCLK_UCLK]\n"
+ " .VoltageMode = 0x%02x\n"
+ " .SnapToDiscrete = 0x%02x\n"
+ " .NumDiscreteLevels = 0x%02x\n"
+ " .padding = 0x%02x\n"
+ " .ConversionToAvfsClk{m = 0x%08x b = 0x%08x}\n"
+ " .SsCurve {a = 0x%08x b = 0x%08x c = 0x%08x}\n",
+ pptable->DpmDescriptor[PPCLK_UCLK].VoltageMode,
+ pptable->DpmDescriptor[PPCLK_UCLK].SnapToDiscrete,
+ pptable->DpmDescriptor[PPCLK_UCLK].NumDiscreteLevels,
+ pptable->DpmDescriptor[PPCLK_UCLK].padding,
+ pptable->DpmDescriptor[PPCLK_UCLK].ConversionToAvfsClk.m,
+ pptable->DpmDescriptor[PPCLK_UCLK].ConversionToAvfsClk.b,
+ pptable->DpmDescriptor[PPCLK_UCLK].SsCurve.a,
+ pptable->DpmDescriptor[PPCLK_UCLK].SsCurve.b,
+ pptable->DpmDescriptor[PPCLK_UCLK].SsCurve.c);
+
+ pr_info("[PPCLK_DCEFCLK]\n"
+ " .VoltageMode = 0x%02x\n"
+ " .SnapToDiscrete = 0x%02x\n"
+ " .NumDiscreteLevels = 0x%02x\n"
+ " .padding = 0x%02x\n"
+ " .ConversionToAvfsClk{m = 0x%08x b = 0x%08x}\n"
+ " .SsCurve {a = 0x%08x b = 0x%08x c = 0x%08x}\n",
+ pptable->DpmDescriptor[PPCLK_DCEFCLK].VoltageMode,
+ pptable->DpmDescriptor[PPCLK_DCEFCLK].SnapToDiscrete,
+ pptable->DpmDescriptor[PPCLK_DCEFCLK].NumDiscreteLevels,
+ pptable->DpmDescriptor[PPCLK_DCEFCLK].padding,
+ pptable->DpmDescriptor[PPCLK_DCEFCLK].ConversionToAvfsClk.m,
+ pptable->DpmDescriptor[PPCLK_DCEFCLK].ConversionToAvfsClk.b,
+ pptable->DpmDescriptor[PPCLK_DCEFCLK].SsCurve.a,
+ pptable->DpmDescriptor[PPCLK_DCEFCLK].SsCurve.b,
+ pptable->DpmDescriptor[PPCLK_DCEFCLK].SsCurve.c);
+
+ pr_info("[PPCLK_DISPCLK]\n"
+ " .VoltageMode = 0x%02x\n"
+ " .SnapToDiscrete = 0x%02x\n"
+ " .NumDiscreteLevels = 0x%02x\n"
+ " .padding = 0x%02x\n"
+ " .ConversionToAvfsClk{m = 0x%08x b = 0x%08x}\n"
+ " .SsCurve {a = 0x%08x b = 0x%08x c = 0x%08x}\n",
+ pptable->DpmDescriptor[PPCLK_DISPCLK].VoltageMode,
+ pptable->DpmDescriptor[PPCLK_DISPCLK].SnapToDiscrete,
+ pptable->DpmDescriptor[PPCLK_DISPCLK].NumDiscreteLevels,
+ pptable->DpmDescriptor[PPCLK_DISPCLK].padding,
+ pptable->DpmDescriptor[PPCLK_DISPCLK].ConversionToAvfsClk.m,
+ pptable->DpmDescriptor[PPCLK_DISPCLK].ConversionToAvfsClk.b,
+ pptable->DpmDescriptor[PPCLK_DISPCLK].SsCurve.a,
+ pptable->DpmDescriptor[PPCLK_DISPCLK].SsCurve.b,
+ pptable->DpmDescriptor[PPCLK_DISPCLK].SsCurve.c);
+
+ pr_info("[PPCLK_PIXCLK]\n"
+ " .VoltageMode = 0x%02x\n"
+ " .SnapToDiscrete = 0x%02x\n"
+ " .NumDiscreteLevels = 0x%02x\n"
+ " .padding = 0x%02x\n"
+ " .ConversionToAvfsClk{m = 0x%08x b = 0x%08x}\n"
+ " .SsCurve {a = 0x%08x b = 0x%08x c = 0x%08x}\n",
+ pptable->DpmDescriptor[PPCLK_PIXCLK].VoltageMode,
+ pptable->DpmDescriptor[PPCLK_PIXCLK].SnapToDiscrete,
+ pptable->DpmDescriptor[PPCLK_PIXCLK].NumDiscreteLevels,
+ pptable->DpmDescriptor[PPCLK_PIXCLK].padding,
+ pptable->DpmDescriptor[PPCLK_PIXCLK].ConversionToAvfsClk.m,
+ pptable->DpmDescriptor[PPCLK_PIXCLK].ConversionToAvfsClk.b,
+ pptable->DpmDescriptor[PPCLK_PIXCLK].SsCurve.a,
+ pptable->DpmDescriptor[PPCLK_PIXCLK].SsCurve.b,
+ pptable->DpmDescriptor[PPCLK_PIXCLK].SsCurve.c);
+
+ pr_info("[PPCLK_PHYCLK]\n"
+ " .VoltageMode = 0x%02x\n"
+ " .SnapToDiscrete = 0x%02x\n"
+ " .NumDiscreteLevels = 0x%02x\n"
+ " .padding = 0x%02x\n"
+ " .ConversionToAvfsClk{m = 0x%08x b = 0x%08x}\n"
+ " .SsCurve {a = 0x%08x b = 0x%08x c = 0x%08x}\n",
+ pptable->DpmDescriptor[PPCLK_PHYCLK].VoltageMode,
+ pptable->DpmDescriptor[PPCLK_PHYCLK].SnapToDiscrete,
+ pptable->DpmDescriptor[PPCLK_PHYCLK].NumDiscreteLevels,
+ pptable->DpmDescriptor[PPCLK_PHYCLK].padding,
+ pptable->DpmDescriptor[PPCLK_PHYCLK].ConversionToAvfsClk.m,
+ pptable->DpmDescriptor[PPCLK_PHYCLK].ConversionToAvfsClk.b,
+ pptable->DpmDescriptor[PPCLK_PHYCLK].SsCurve.a,
+ pptable->DpmDescriptor[PPCLK_PHYCLK].SsCurve.b,
+ pptable->DpmDescriptor[PPCLK_PHYCLK].SsCurve.c);
+
+ pr_info("[PPCLK_FCLK]\n"
+ " .VoltageMode = 0x%02x\n"
+ " .SnapToDiscrete = 0x%02x\n"
+ " .NumDiscreteLevels = 0x%02x\n"
+ " .padding = 0x%02x\n"
+ " .ConversionToAvfsClk{m = 0x%08x b = 0x%08x}\n"
+ " .SsCurve {a = 0x%08x b = 0x%08x c = 0x%08x}\n",
+ pptable->DpmDescriptor[PPCLK_FCLK].VoltageMode,
+ pptable->DpmDescriptor[PPCLK_FCLK].SnapToDiscrete,
+ pptable->DpmDescriptor[PPCLK_FCLK].NumDiscreteLevels,
+ pptable->DpmDescriptor[PPCLK_FCLK].padding,
+ pptable->DpmDescriptor[PPCLK_FCLK].ConversionToAvfsClk.m,
+ pptable->DpmDescriptor[PPCLK_FCLK].ConversionToAvfsClk.b,
+ pptable->DpmDescriptor[PPCLK_FCLK].SsCurve.a,
+ pptable->DpmDescriptor[PPCLK_FCLK].SsCurve.b,
+ pptable->DpmDescriptor[PPCLK_FCLK].SsCurve.c);
+
+
+ pr_info("FreqTableGfx\n");
+ for (i = 0; i < NUM_GFXCLK_DPM_LEVELS; i++)
+ pr_info(" .[%02d] = %d\n", i, pptable->FreqTableGfx[i]);
+
+ pr_info("FreqTableVclk\n");
+ for (i = 0; i < NUM_VCLK_DPM_LEVELS; i++)
+ pr_info(" .[%02d] = %d\n", i, pptable->FreqTableVclk[i]);
+
+ pr_info("FreqTableDclk\n");
+ for (i = 0; i < NUM_DCLK_DPM_LEVELS; i++)
+ pr_info(" .[%02d] = %d\n", i, pptable->FreqTableDclk[i]);
+
+ pr_info("FreqTableEclk\n");
+ for (i = 0; i < NUM_ECLK_DPM_LEVELS; i++)
+ pr_info(" .[%02d] = %d\n", i, pptable->FreqTableEclk[i]);
+
+ pr_info("FreqTableSocclk\n");
+ for (i = 0; i < NUM_SOCCLK_DPM_LEVELS; i++)
+ pr_info(" .[%02d] = %d\n", i, pptable->FreqTableSocclk[i]);
+
+ pr_info("FreqTableUclk\n");
+ for (i = 0; i < NUM_UCLK_DPM_LEVELS; i++)
+ pr_info(" .[%02d] = %d\n", i, pptable->FreqTableUclk[i]);
+
+ pr_info("FreqTableFclk\n");
+ for (i = 0; i < NUM_FCLK_DPM_LEVELS; i++)
+ pr_info(" .[%02d] = %d\n", i, pptable->FreqTableFclk[i]);
+
+ pr_info("FreqTableDcefclk\n");
+ for (i = 0; i < NUM_DCEFCLK_DPM_LEVELS; i++)
+ pr_info(" .[%02d] = %d\n", i, pptable->FreqTableDcefclk[i]);
+
+ pr_info("FreqTableDispclk\n");
+ for (i = 0; i < NUM_DISPCLK_DPM_LEVELS; i++)
+ pr_info(" .[%02d] = %d\n", i, pptable->FreqTableDispclk[i]);
+
+ pr_info("FreqTablePixclk\n");
+ for (i = 0; i < NUM_PIXCLK_DPM_LEVELS; i++)
+ pr_info(" .[%02d] = %d\n", i, pptable->FreqTablePixclk[i]);
+
+ pr_info("FreqTablePhyclk\n");
+ for (i = 0; i < NUM_PHYCLK_DPM_LEVELS; i++)
+ pr_info(" .[%02d] = %d\n", i, pptable->FreqTablePhyclk[i]);
+
+ pr_info("DcModeMaxFreq[PPCLK_GFXCLK] = %d\n", pptable->DcModeMaxFreq[PPCLK_GFXCLK]);
+ pr_info("DcModeMaxFreq[PPCLK_VCLK] = %d\n", pptable->DcModeMaxFreq[PPCLK_VCLK]);
+ pr_info("DcModeMaxFreq[PPCLK_DCLK] = %d\n", pptable->DcModeMaxFreq[PPCLK_DCLK]);
+ pr_info("DcModeMaxFreq[PPCLK_ECLK] = %d\n", pptable->DcModeMaxFreq[PPCLK_ECLK]);
+ pr_info("DcModeMaxFreq[PPCLK_SOCCLK] = %d\n", pptable->DcModeMaxFreq[PPCLK_SOCCLK]);
+ pr_info("DcModeMaxFreq[PPCLK_UCLK] = %d\n", pptable->DcModeMaxFreq[PPCLK_UCLK]);
+ pr_info("DcModeMaxFreq[PPCLK_DCEFCLK] = %d\n", pptable->DcModeMaxFreq[PPCLK_DCEFCLK]);
+ pr_info("DcModeMaxFreq[PPCLK_DISPCLK] = %d\n", pptable->DcModeMaxFreq[PPCLK_DISPCLK]);
+ pr_info("DcModeMaxFreq[PPCLK_PIXCLK] = %d\n", pptable->DcModeMaxFreq[PPCLK_PIXCLK]);
+ pr_info("DcModeMaxFreq[PPCLK_PHYCLK] = %d\n", pptable->DcModeMaxFreq[PPCLK_PHYCLK]);
+ pr_info("DcModeMaxFreq[PPCLK_FCLK] = %d\n", pptable->DcModeMaxFreq[PPCLK_FCLK]);
+ pr_info("Padding8_Clks = %d\n", pptable->Padding8_Clks);
+
+ pr_info("Mp0clkFreq\n");
+ for (i = 0; i < NUM_MP0CLK_DPM_LEVELS; i++)
+ pr_info(" .[%d] = %d\n", i, pptable->Mp0clkFreq[i]);
+
+ pr_info("Mp0DpmVoltage\n");
+ for (i = 0; i < NUM_MP0CLK_DPM_LEVELS; i++)
+ pr_info(" .[%d] = %d\n", i, pptable->Mp0DpmVoltage[i]);
+
+ pr_info("GfxclkFidle = 0x%x\n", pptable->GfxclkFidle);
+ pr_info("GfxclkSlewRate = 0x%x\n", pptable->GfxclkSlewRate);
+ pr_info("CksEnableFreq = 0x%x\n", pptable->CksEnableFreq);
+ pr_info("Padding789 = 0x%x\n", pptable->Padding789);
+ pr_info("CksVoltageOffset[a = 0x%08x b = 0x%08x c = 0x%08x]\n",
+ pptable->CksVoltageOffset.a,
+ pptable->CksVoltageOffset.b,
+ pptable->CksVoltageOffset.c);
+ pr_info("Padding567[0] = 0x%x\n", pptable->Padding567[0]);
+ pr_info("Padding567[1] = 0x%x\n", pptable->Padding567[1]);
+ pr_info("Padding567[2] = 0x%x\n", pptable->Padding567[2]);
+ pr_info("Padding567[3] = 0x%x\n", pptable->Padding567[3]);
+ pr_info("GfxclkDsMaxFreq = %d\n", pptable->GfxclkDsMaxFreq);
+ pr_info("GfxclkSource = 0x%x\n", pptable->GfxclkSource);
+ pr_info("Padding456 = 0x%x\n", pptable->Padding456);
+
+ pr_info("LowestUclkReservedForUlv = %d\n", pptable->LowestUclkReservedForUlv);
+ pr_info("Padding8_Uclk[0] = 0x%x\n", pptable->Padding8_Uclk[0]);
+ pr_info("Padding8_Uclk[1] = 0x%x\n", pptable->Padding8_Uclk[1]);
+ pr_info("Padding8_Uclk[2] = 0x%x\n", pptable->Padding8_Uclk[2]);
+
+ pr_info("PcieGenSpeed\n");
+ for (i = 0; i < NUM_LINK_LEVELS; i++)
+ pr_info(" .[%d] = %d\n", i, pptable->PcieGenSpeed[i]);
+
+ pr_info("PcieLaneCount\n");
+ for (i = 0; i < NUM_LINK_LEVELS; i++)
+ pr_info(" .[%d] = %d\n", i, pptable->PcieLaneCount[i]);
+
+ pr_info("LclkFreq\n");
+ for (i = 0; i < NUM_LINK_LEVELS; i++)
+ pr_info(" .[%d] = %d\n", i, pptable->LclkFreq[i]);
+
+ pr_info("EnableTdpm = %d\n", pptable->EnableTdpm);
+ pr_info("TdpmHighHystTemperature = %d\n", pptable->TdpmHighHystTemperature);
+ pr_info("TdpmLowHystTemperature = %d\n", pptable->TdpmLowHystTemperature);
+ pr_info("GfxclkFreqHighTempLimit = %d\n", pptable->GfxclkFreqHighTempLimit);
+
+ pr_info("FanStopTemp = %d\n", pptable->FanStopTemp);
+ pr_info("FanStartTemp = %d\n", pptable->FanStartTemp);
+
+ pr_info("FanGainEdge = %d\n", pptable->FanGainEdge);
+ pr_info("FanGainHotspot = %d\n", pptable->FanGainHotspot);
+ pr_info("FanGainLiquid = %d\n", pptable->FanGainLiquid);
+ pr_info("FanGainVrGfx = %d\n", pptable->FanGainVrGfx);
+ pr_info("FanGainVrSoc = %d\n", pptable->FanGainVrSoc);
+ pr_info("FanGainPlx = %d\n", pptable->FanGainPlx);
+ pr_info("FanGainHbm = %d\n", pptable->FanGainHbm);
+ pr_info("FanPwmMin = %d\n", pptable->FanPwmMin);
+ pr_info("FanAcousticLimitRpm = %d\n", pptable->FanAcousticLimitRpm);
+ pr_info("FanThrottlingRpm = %d\n", pptable->FanThrottlingRpm);
+ pr_info("FanMaximumRpm = %d\n", pptable->FanMaximumRpm);
+ pr_info("FanTargetTemperature = %d\n", pptable->FanTargetTemperature);
+ pr_info("FanTargetGfxclk = %d\n", pptable->FanTargetGfxclk);
+ pr_info("FanZeroRpmEnable = %d\n", pptable->FanZeroRpmEnable);
+ pr_info("FanTachEdgePerRev = %d\n", pptable->FanTachEdgePerRev);
+
+ pr_info("FuzzyFan_ErrorSetDelta = %d\n", pptable->FuzzyFan_ErrorSetDelta);
+ pr_info("FuzzyFan_ErrorRateSetDelta = %d\n", pptable->FuzzyFan_ErrorRateSetDelta);
+ pr_info("FuzzyFan_PwmSetDelta = %d\n", pptable->FuzzyFan_PwmSetDelta);
+ pr_info("FuzzyFan_Reserved = %d\n", pptable->FuzzyFan_Reserved);
+
+ pr_info("OverrideAvfsGb[AVFS_VOLTAGE_GFX] = 0x%x\n", pptable->OverrideAvfsGb[AVFS_VOLTAGE_GFX]);
+ pr_info("OverrideAvfsGb[AVFS_VOLTAGE_SOC] = 0x%x\n", pptable->OverrideAvfsGb[AVFS_VOLTAGE_SOC]);
+ pr_info("Padding8_Avfs[0] = %d\n", pptable->Padding8_Avfs[0]);
+ pr_info("Padding8_Avfs[1] = %d\n", pptable->Padding8_Avfs[1]);
+
+ pr_info("qAvfsGb[AVFS_VOLTAGE_GFX]{a = 0x%x b = 0x%x c = 0x%x}\n",
+ pptable->qAvfsGb[AVFS_VOLTAGE_GFX].a,
+ pptable->qAvfsGb[AVFS_VOLTAGE_GFX].b,
+ pptable->qAvfsGb[AVFS_VOLTAGE_GFX].c);
+ pr_info("qAvfsGb[AVFS_VOLTAGE_SOC]{a = 0x%x b = 0x%x c = 0x%x}\n",
+ pptable->qAvfsGb[AVFS_VOLTAGE_SOC].a,
+ pptable->qAvfsGb[AVFS_VOLTAGE_SOC].b,
+ pptable->qAvfsGb[AVFS_VOLTAGE_SOC].c);
+ pr_info("dBtcGbGfxCksOn{a = 0x%x b = 0x%x c = 0x%x}\n",
+ pptable->dBtcGbGfxCksOn.a,
+ pptable->dBtcGbGfxCksOn.b,
+ pptable->dBtcGbGfxCksOn.c);
+ pr_info("dBtcGbGfxCksOff{a = 0x%x b = 0x%x c = 0x%x}\n",
+ pptable->dBtcGbGfxCksOff.a,
+ pptable->dBtcGbGfxCksOff.b,
+ pptable->dBtcGbGfxCksOff.c);
+ pr_info("dBtcGbGfxAfll{a = 0x%x b = 0x%x c = 0x%x}\n",
+ pptable->dBtcGbGfxAfll.a,
+ pptable->dBtcGbGfxAfll.b,
+ pptable->dBtcGbGfxAfll.c);
+ pr_info("dBtcGbSoc{a = 0x%x b = 0x%x c = 0x%x}\n",
+ pptable->dBtcGbSoc.a,
+ pptable->dBtcGbSoc.b,
+ pptable->dBtcGbSoc.c);
+ pr_info("qAgingGb[AVFS_VOLTAGE_GFX]{m = 0x%x b = 0x%x}\n",
+ pptable->qAgingGb[AVFS_VOLTAGE_GFX].m,
+ pptable->qAgingGb[AVFS_VOLTAGE_GFX].b);
+ pr_info("qAgingGb[AVFS_VOLTAGE_SOC]{m = 0x%x b = 0x%x}\n",
+ pptable->qAgingGb[AVFS_VOLTAGE_SOC].m,
+ pptable->qAgingGb[AVFS_VOLTAGE_SOC].b);
+
+ pr_info("qStaticVoltageOffset[AVFS_VOLTAGE_GFX]{a = 0x%x b = 0x%x c = 0x%x}\n",
+ pptable->qStaticVoltageOffset[AVFS_VOLTAGE_GFX].a,
+ pptable->qStaticVoltageOffset[AVFS_VOLTAGE_GFX].b,
+ pptable->qStaticVoltageOffset[AVFS_VOLTAGE_GFX].c);
+ pr_info("qStaticVoltageOffset[AVFS_VOLTAGE_SOC]{a = 0x%x b = 0x%x c = 0x%x}\n",
+ pptable->qStaticVoltageOffset[AVFS_VOLTAGE_SOC].a,
+ pptable->qStaticVoltageOffset[AVFS_VOLTAGE_SOC].b,
+ pptable->qStaticVoltageOffset[AVFS_VOLTAGE_SOC].c);
+
+ pr_info("DcTol[AVFS_VOLTAGE_GFX] = 0x%x\n", pptable->DcTol[AVFS_VOLTAGE_GFX]);
+ pr_info("DcTol[AVFS_VOLTAGE_SOC] = 0x%x\n", pptable->DcTol[AVFS_VOLTAGE_SOC]);
+
+ pr_info("DcBtcEnabled[AVFS_VOLTAGE_GFX] = 0x%x\n", pptable->DcBtcEnabled[AVFS_VOLTAGE_GFX]);
+ pr_info("DcBtcEnabled[AVFS_VOLTAGE_SOC] = 0x%x\n", pptable->DcBtcEnabled[AVFS_VOLTAGE_SOC]);
+ pr_info("Padding8_GfxBtc[0] = 0x%x\n", pptable->Padding8_GfxBtc[0]);
+ pr_info("Padding8_GfxBtc[1] = 0x%x\n", pptable->Padding8_GfxBtc[1]);
+
+ pr_info("DcBtcMin[AVFS_VOLTAGE_GFX] = 0x%x\n", pptable->DcBtcMin[AVFS_VOLTAGE_GFX]);
+ pr_info("DcBtcMin[AVFS_VOLTAGE_SOC] = 0x%x\n", pptable->DcBtcMin[AVFS_VOLTAGE_SOC]);
+ pr_info("DcBtcMax[AVFS_VOLTAGE_GFX] = 0x%x\n", pptable->DcBtcMax[AVFS_VOLTAGE_GFX]);
+ pr_info("DcBtcMax[AVFS_VOLTAGE_SOC] = 0x%x\n", pptable->DcBtcMax[AVFS_VOLTAGE_SOC]);
+
+ pr_info("XgmiLinkSpeed\n");
+ for (i = 0; i < NUM_XGMI_LEVELS; i++)
+ pr_info(" .[%d] = %d\n", i, pptable->XgmiLinkSpeed[i]);
+ pr_info("XgmiLinkWidth\n");
+ for (i = 0; i < NUM_XGMI_LEVELS; i++)
+ pr_info(" .[%d] = %d\n", i, pptable->XgmiLinkWidth[i]);
+ pr_info("XgmiFclkFreq\n");
+ for (i = 0; i < NUM_XGMI_LEVELS; i++)
+ pr_info(" .[%d] = %d\n", i, pptable->XgmiFclkFreq[i]);
+ pr_info("XgmiUclkFreq\n");
+ for (i = 0; i < NUM_XGMI_LEVELS; i++)
+ pr_info(" .[%d] = %d\n", i, pptable->XgmiUclkFreq[i]);
+ pr_info("XgmiSocclkFreq\n");
+ for (i = 0; i < NUM_XGMI_LEVELS; i++)
+ pr_info(" .[%d] = %d\n", i, pptable->XgmiSocclkFreq[i]);
+ pr_info("XgmiSocVoltage\n");
+ for (i = 0; i < NUM_XGMI_LEVELS; i++)
+ pr_info(" .[%d] = %d\n", i, pptable->XgmiSocVoltage[i]);
+
+ pr_info("DebugOverrides = 0x%x\n", pptable->DebugOverrides);
+ pr_info("ReservedEquation0{a = 0x%x b = 0x%x c = 0x%x}\n",
+ pptable->ReservedEquation0.a,
+ pptable->ReservedEquation0.b,
+ pptable->ReservedEquation0.c);
+ pr_info("ReservedEquation1{a = 0x%x b = 0x%x c = 0x%x}\n",
+ pptable->ReservedEquation1.a,
+ pptable->ReservedEquation1.b,
+ pptable->ReservedEquation1.c);
+ pr_info("ReservedEquation2{a = 0x%x b = 0x%x c = 0x%x}\n",
+ pptable->ReservedEquation2.a,
+ pptable->ReservedEquation2.b,
+ pptable->ReservedEquation2.c);
+ pr_info("ReservedEquation3{a = 0x%x b = 0x%x c = 0x%x}\n",
+ pptable->ReservedEquation3.a,
+ pptable->ReservedEquation3.b,
+ pptable->ReservedEquation3.c);
+
+ pr_info("MinVoltageUlvGfx = %d\n", pptable->MinVoltageUlvGfx);
+ pr_info("MinVoltageUlvSoc = %d\n", pptable->MinVoltageUlvSoc);
+
+ pr_info("MGpuFanBoostLimitRpm = %d\n", pptable->MGpuFanBoostLimitRpm);
+ pr_info("padding16_Fan = %d\n", pptable->padding16_Fan);
+
+ pr_info("FanGainVrMem0 = %d\n", pptable->FanGainVrMem0);
+ pr_info("FanGainVrMem0 = %d\n", pptable->FanGainVrMem0);
+
+ pr_info("DcBtcGb[AVFS_VOLTAGE_GFX] = 0x%x\n", pptable->DcBtcGb[AVFS_VOLTAGE_GFX]);
+ pr_info("DcBtcGb[AVFS_VOLTAGE_SOC] = 0x%x\n", pptable->DcBtcGb[AVFS_VOLTAGE_SOC]);
+
+ for (i = 0; i < 11; i++)
+ pr_info("Reserved[%d] = 0x%x\n", i, pptable->Reserved[i]);
+
+ for (i = 0; i < 3; i++)
+ pr_info("Padding32[%d] = 0x%x\n", i, pptable->Padding32[i]);
+
+ pr_info("MaxVoltageStepGfx = 0x%x\n", pptable->MaxVoltageStepGfx);
+ pr_info("MaxVoltageStepSoc = 0x%x\n", pptable->MaxVoltageStepSoc);
+
+ pr_info("VddGfxVrMapping = 0x%x\n", pptable->VddGfxVrMapping);
+ pr_info("VddSocVrMapping = 0x%x\n", pptable->VddSocVrMapping);
+ pr_info("VddMem0VrMapping = 0x%x\n", pptable->VddMem0VrMapping);
+ pr_info("VddMem1VrMapping = 0x%x\n", pptable->VddMem1VrMapping);
+
+ pr_info("GfxUlvPhaseSheddingMask = 0x%x\n", pptable->GfxUlvPhaseSheddingMask);
+ pr_info("SocUlvPhaseSheddingMask = 0x%x\n", pptable->SocUlvPhaseSheddingMask);
+ pr_info("ExternalSensorPresent = 0x%x\n", pptable->ExternalSensorPresent);
+ pr_info("Padding8_V = 0x%x\n", pptable->Padding8_V);
+
+ pr_info("GfxMaxCurrent = 0x%x\n", pptable->GfxMaxCurrent);
+ pr_info("GfxOffset = 0x%x\n", pptable->GfxOffset);
+ pr_info("Padding_TelemetryGfx = 0x%x\n", pptable->Padding_TelemetryGfx);
+
+ pr_info("SocMaxCurrent = 0x%x\n", pptable->SocMaxCurrent);
+ pr_info("SocOffset = 0x%x\n", pptable->SocOffset);
+ pr_info("Padding_TelemetrySoc = 0x%x\n", pptable->Padding_TelemetrySoc);
+
+ pr_info("Mem0MaxCurrent = 0x%x\n", pptable->Mem0MaxCurrent);
+ pr_info("Mem0Offset = 0x%x\n", pptable->Mem0Offset);
+ pr_info("Padding_TelemetryMem0 = 0x%x\n", pptable->Padding_TelemetryMem0);
+
+ pr_info("Mem1MaxCurrent = 0x%x\n", pptable->Mem1MaxCurrent);
+ pr_info("Mem1Offset = 0x%x\n", pptable->Mem1Offset);
+ pr_info("Padding_TelemetryMem1 = 0x%x\n", pptable->Padding_TelemetryMem1);
+
+ pr_info("AcDcGpio = %d\n", pptable->AcDcGpio);
+ pr_info("AcDcPolarity = %d\n", pptable->AcDcPolarity);
+ pr_info("VR0HotGpio = %d\n", pptable->VR0HotGpio);
+ pr_info("VR0HotPolarity = %d\n", pptable->VR0HotPolarity);
+
+ pr_info("VR1HotGpio = %d\n", pptable->VR1HotGpio);
+ pr_info("VR1HotPolarity = %d\n", pptable->VR1HotPolarity);
+ pr_info("Padding1 = 0x%x\n", pptable->Padding1);
+ pr_info("Padding2 = 0x%x\n", pptable->Padding2);
+
+ pr_info("LedPin0 = %d\n", pptable->LedPin0);
+ pr_info("LedPin1 = %d\n", pptable->LedPin1);
+ pr_info("LedPin2 = %d\n", pptable->LedPin2);
+ pr_info("padding8_4 = 0x%x\n", pptable->padding8_4);
+
+ pr_info("PllGfxclkSpreadEnabled = %d\n", pptable->PllGfxclkSpreadEnabled);
+ pr_info("PllGfxclkSpreadPercent = %d\n", pptable->PllGfxclkSpreadPercent);
+ pr_info("PllGfxclkSpreadFreq = %d\n", pptable->PllGfxclkSpreadFreq);
+
+ pr_info("UclkSpreadEnabled = %d\n", pptable->UclkSpreadEnabled);
+ pr_info("UclkSpreadPercent = %d\n", pptable->UclkSpreadPercent);
+ pr_info("UclkSpreadFreq = %d\n", pptable->UclkSpreadFreq);
+
+ pr_info("FclkSpreadEnabled = %d\n", pptable->FclkSpreadEnabled);
+ pr_info("FclkSpreadPercent = %d\n", pptable->FclkSpreadPercent);
+ pr_info("FclkSpreadFreq = %d\n", pptable->FclkSpreadFreq);
+
+ pr_info("FllGfxclkSpreadEnabled = %d\n", pptable->FllGfxclkSpreadEnabled);
+ pr_info("FllGfxclkSpreadPercent = %d\n", pptable->FllGfxclkSpreadPercent);
+ pr_info("FllGfxclkSpreadFreq = %d\n", pptable->FllGfxclkSpreadFreq);
+
+ for (i = 0; i < I2C_CONTROLLER_NAME_COUNT; i++) {
+ pr_info("I2cControllers[%d]:\n", i);
+ pr_info(" .Enabled = %d\n",
+ pptable->I2cControllers[i].Enabled);
+ pr_info(" .SlaveAddress = 0x%x\n",
+ pptable->I2cControllers[i].SlaveAddress);
+ pr_info(" .ControllerPort = %d\n",
+ pptable->I2cControllers[i].ControllerPort);
+ pr_info(" .ControllerName = %d\n",
+ pptable->I2cControllers[i].ControllerName);
+ pr_info(" .ThermalThrottler = %d\n",
+ pptable->I2cControllers[i].ThermalThrottler);
+ pr_info(" .I2cProtocol = %d\n",
+ pptable->I2cControllers[i].I2cProtocol);
+ pr_info(" .I2cSpeed = %d\n",
+ pptable->I2cControllers[i].I2cSpeed);
+ }
+
+ for (i = 0; i < 10; i++)
+ pr_info("BoardReserved[%d] = 0x%x\n", i, pptable->BoardReserved[i]);
+
+ for (i = 0; i < 8; i++)
+ pr_info("MmHubPadding[%d] = 0x%x\n", i, pptable->MmHubPadding[i]);
+}
+#endif
+
+static int check_powerplay_tables(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_Vega20_POWERPLAYTABLE *powerplay_table)
+{
+ PP_ASSERT_WITH_CODE((powerplay_table->sHeader.format_revision >=
+ ATOM_VEGA20_TABLE_REVISION_VEGA20),
+ "Unsupported PPTable format!", return -1);
+ PP_ASSERT_WITH_CODE(powerplay_table->sHeader.structuresize > 0,
+ "Invalid PowerPlay Table!", return -1);
+
+ if (powerplay_table->smcPPTable.Version != PPTABLE_V20_SMU_VERSION) {
+ pr_info("Unmatch PPTable version: "
+ "pptable from VBIOS is V%d while driver supported is V%d!",
+ powerplay_table->smcPPTable.Version,
+ PPTABLE_V20_SMU_VERSION);
+ return -EINVAL;
+ }
+
+ //dump_pptable(&powerplay_table->smcPPTable);
+
+ return 0;
+}
+
+static int set_platform_caps(struct pp_hwmgr *hwmgr, uint32_t powerplay_caps)
+{
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_VEGA20_PP_PLATFORM_CAP_POWERPLAY),
+ PHM_PlatformCaps_PowerPlaySupport);
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_VEGA20_PP_PLATFORM_CAP_SBIOSPOWERSOURCE),
+ PHM_PlatformCaps_BiosPowerSourceControl);
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_VEGA20_PP_PLATFORM_CAP_BACO),
+ PHM_PlatformCaps_BACO);
+
+ set_hw_cap(
+ hwmgr,
+ 0 != (powerplay_caps & ATOM_VEGA20_PP_PLATFORM_CAP_BAMACO),
+ PHM_PlatformCaps_BAMACO);
+
+ return 0;
+}
+
+static int copy_overdrive_feature_capabilities_array(
+ struct pp_hwmgr *hwmgr,
+ uint8_t **pptable_info_array,
+ const uint8_t *pptable_array,
+ uint8_t od_feature_count)
+{
+ uint32_t array_size, i;
+ uint8_t *table;
+ bool od_supported = false;
+
+ array_size = sizeof(uint8_t) * od_feature_count;
+ table = kzalloc(array_size, GFP_KERNEL);
+ if (NULL == table)
+ return -ENOMEM;
+
+ for (i = 0; i < od_feature_count; i++) {
+ table[i] = le32_to_cpu(pptable_array[i]);
+ if (table[i])
+ od_supported = true;
+ }
+
+ *pptable_info_array = table;
+
+ if (od_supported)
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ACOverdriveSupport);
+
+ return 0;
+}
+
+static int append_vbios_pptable(struct pp_hwmgr *hwmgr, PPTable_t *ppsmc_pptable)
+{
+ struct atom_smc_dpm_info_v4_4 *smc_dpm_table;
+ int index = GetIndexIntoMasterDataTable(smc_dpm_info);
+ int i;
+
+ PP_ASSERT_WITH_CODE(
+ smc_dpm_table = smu_atom_get_data_table(hwmgr->adev, index, NULL, NULL, NULL),
+ "[appendVbiosPPTable] Failed to retrieve Smc Dpm Table from VBIOS!",
+ return -1);
+
+ ppsmc_pptable->MaxVoltageStepGfx = smc_dpm_table->maxvoltagestepgfx;
+ ppsmc_pptable->MaxVoltageStepSoc = smc_dpm_table->maxvoltagestepsoc;
+
+ ppsmc_pptable->VddGfxVrMapping = smc_dpm_table->vddgfxvrmapping;
+ ppsmc_pptable->VddSocVrMapping = smc_dpm_table->vddsocvrmapping;
+ ppsmc_pptable->VddMem0VrMapping = smc_dpm_table->vddmem0vrmapping;
+ ppsmc_pptable->VddMem1VrMapping = smc_dpm_table->vddmem1vrmapping;
+
+ ppsmc_pptable->GfxUlvPhaseSheddingMask = smc_dpm_table->gfxulvphasesheddingmask;
+ ppsmc_pptable->SocUlvPhaseSheddingMask = smc_dpm_table->soculvphasesheddingmask;
+ ppsmc_pptable->ExternalSensorPresent = smc_dpm_table->externalsensorpresent;
+
+ ppsmc_pptable->GfxMaxCurrent = smc_dpm_table->gfxmaxcurrent;
+ ppsmc_pptable->GfxOffset = smc_dpm_table->gfxoffset;
+ ppsmc_pptable->Padding_TelemetryGfx = smc_dpm_table->padding_telemetrygfx;
+
+ ppsmc_pptable->SocMaxCurrent = smc_dpm_table->socmaxcurrent;
+ ppsmc_pptable->SocOffset = smc_dpm_table->socoffset;
+ ppsmc_pptable->Padding_TelemetrySoc = smc_dpm_table->padding_telemetrysoc;
+
+ ppsmc_pptable->Mem0MaxCurrent = smc_dpm_table->mem0maxcurrent;
+ ppsmc_pptable->Mem0Offset = smc_dpm_table->mem0offset;
+ ppsmc_pptable->Padding_TelemetryMem0 = smc_dpm_table->padding_telemetrymem0;
+
+ ppsmc_pptable->Mem1MaxCurrent = smc_dpm_table->mem1maxcurrent;
+ ppsmc_pptable->Mem1Offset = smc_dpm_table->mem1offset;
+ ppsmc_pptable->Padding_TelemetryMem1 = smc_dpm_table->padding_telemetrymem1;
+
+ ppsmc_pptable->AcDcGpio = smc_dpm_table->acdcgpio;
+ ppsmc_pptable->AcDcPolarity = smc_dpm_table->acdcpolarity;
+ ppsmc_pptable->VR0HotGpio = smc_dpm_table->vr0hotgpio;
+ ppsmc_pptable->VR0HotPolarity = smc_dpm_table->vr0hotpolarity;
+
+ ppsmc_pptable->VR1HotGpio = smc_dpm_table->vr1hotgpio;
+ ppsmc_pptable->VR1HotPolarity = smc_dpm_table->vr1hotpolarity;
+ ppsmc_pptable->Padding1 = smc_dpm_table->padding1;
+ ppsmc_pptable->Padding2 = smc_dpm_table->padding2;
+
+ ppsmc_pptable->LedPin0 = smc_dpm_table->ledpin0;
+ ppsmc_pptable->LedPin1 = smc_dpm_table->ledpin1;
+ ppsmc_pptable->LedPin2 = smc_dpm_table->ledpin2;
+
+ ppsmc_pptable->PllGfxclkSpreadEnabled = smc_dpm_table->pllgfxclkspreadenabled;
+ ppsmc_pptable->PllGfxclkSpreadPercent = smc_dpm_table->pllgfxclkspreadpercent;
+ ppsmc_pptable->PllGfxclkSpreadFreq = smc_dpm_table->pllgfxclkspreadfreq;
+
+ ppsmc_pptable->UclkSpreadEnabled = 0;
+ ppsmc_pptable->UclkSpreadPercent = smc_dpm_table->uclkspreadpercent;
+ ppsmc_pptable->UclkSpreadFreq = smc_dpm_table->uclkspreadfreq;
+
+ ppsmc_pptable->FclkSpreadEnabled = smc_dpm_table->fclkspreadenabled;
+ ppsmc_pptable->FclkSpreadPercent = smc_dpm_table->fclkspreadpercent;
+ ppsmc_pptable->FclkSpreadFreq = smc_dpm_table->fclkspreadfreq;
+
+ ppsmc_pptable->FllGfxclkSpreadEnabled = smc_dpm_table->fllgfxclkspreadenabled;
+ ppsmc_pptable->FllGfxclkSpreadPercent = smc_dpm_table->fllgfxclkspreadpercent;
+ ppsmc_pptable->FllGfxclkSpreadFreq = smc_dpm_table->fllgfxclkspreadfreq;
+
+ for (i = 0; i < I2C_CONTROLLER_NAME_COUNT; i++) {
+ ppsmc_pptable->I2cControllers[i].Enabled =
+ smc_dpm_table->i2ccontrollers[i].enabled;
+ ppsmc_pptable->I2cControllers[i].SlaveAddress =
+ smc_dpm_table->i2ccontrollers[i].slaveaddress;
+ ppsmc_pptable->I2cControllers[i].ControllerPort =
+ smc_dpm_table->i2ccontrollers[i].controllerport;
+ ppsmc_pptable->I2cControllers[i].ThermalThrottler =
+ smc_dpm_table->i2ccontrollers[i].thermalthrottler;
+ ppsmc_pptable->I2cControllers[i].I2cProtocol =
+ smc_dpm_table->i2ccontrollers[i].i2cprotocol;
+ ppsmc_pptable->I2cControllers[i].I2cSpeed =
+ smc_dpm_table->i2ccontrollers[i].i2cspeed;
+ }
+
+ return 0;
+}
+
+static int override_powerplay_table_fantargettemperature(struct pp_hwmgr *hwmgr)
+{
+ struct phm_ppt_v3_information *pptable_information =
+ (struct phm_ppt_v3_information *)hwmgr->pptable;
+ PPTable_t *ppsmc_pptable = (PPTable_t *)(pptable_information->smc_pptable);
+
+ ppsmc_pptable->FanTargetTemperature = VEGA20_FAN_TARGET_TEMPERATURE_OVERRIDE;
+
+ return 0;
+}
+
+#define VEGA20_ENGINECLOCK_HARDMAX 198000
+static int init_powerplay_table_information(
+ struct pp_hwmgr *hwmgr,
+ const ATOM_Vega20_POWERPLAYTABLE *powerplay_table)
+{
+ struct phm_ppt_v3_information *pptable_information =
+ (struct phm_ppt_v3_information *)hwmgr->pptable;
+ uint32_t disable_power_control = 0;
+ uint32_t od_feature_count, od_setting_count, power_saving_clock_count;
+ int result;
+
+ hwmgr->thermal_controller.ucType = powerplay_table->ucThermalControllerType;
+ pptable_information->uc_thermal_controller_type = powerplay_table->ucThermalControllerType;
+ hwmgr->thermal_controller.fanInfo.ulMinRPM = 0;
+ hwmgr->thermal_controller.fanInfo.ulMaxRPM = powerplay_table->smcPPTable.FanMaximumRpm;
+
+ set_hw_cap(hwmgr,
+ ATOM_VEGA20_PP_THERMALCONTROLLER_NONE != hwmgr->thermal_controller.ucType,
+ PHM_PlatformCaps_ThermalController);
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl);
+
+ if (powerplay_table->OverDrive8Table.ucODTableRevision == 1) {
+ od_feature_count =
+ (le32_to_cpu(powerplay_table->OverDrive8Table.ODFeatureCount) >
+ ATOM_VEGA20_ODFEATURE_COUNT) ?
+ ATOM_VEGA20_ODFEATURE_COUNT :
+ le32_to_cpu(powerplay_table->OverDrive8Table.ODFeatureCount);
+ od_setting_count =
+ (le32_to_cpu(powerplay_table->OverDrive8Table.ODSettingCount) >
+ ATOM_VEGA20_ODSETTING_COUNT) ?
+ ATOM_VEGA20_ODSETTING_COUNT :
+ le32_to_cpu(powerplay_table->OverDrive8Table.ODSettingCount);
+
+ copy_overdrive_feature_capabilities_array(hwmgr,
+ &pptable_information->od_feature_capabilities,
+ powerplay_table->OverDrive8Table.ODFeatureCapabilities,
+ od_feature_count);
+ phm_copy_overdrive_settings_limits_array(hwmgr,
+ &pptable_information->od_settings_max,
+ powerplay_table->OverDrive8Table.ODSettingsMax,
+ od_setting_count);
+ phm_copy_overdrive_settings_limits_array(hwmgr,
+ &pptable_information->od_settings_min,
+ powerplay_table->OverDrive8Table.ODSettingsMin,
+ od_setting_count);
+ }
+
+ pptable_information->us_small_power_limit1 = le16_to_cpu(powerplay_table->usSmallPowerLimit1);
+ pptable_information->us_small_power_limit2 = le16_to_cpu(powerplay_table->usSmallPowerLimit2);
+ pptable_information->us_boost_power_limit = le16_to_cpu(powerplay_table->usBoostPowerLimit);
+ pptable_information->us_od_turbo_power_limit = le16_to_cpu(powerplay_table->usODTurboPowerLimit);
+ pptable_information->us_od_powersave_power_limit = le16_to_cpu(powerplay_table->usODPowerSavePowerLimit);
+
+ pptable_information->us_software_shutdown_temp = le16_to_cpu(powerplay_table->usSoftwareShutdownTemp);
+
+ hwmgr->platform_descriptor.TDPODLimit = le32_to_cpu(powerplay_table->OverDrive8Table.ODSettingsMax[ATOM_VEGA20_ODSETTING_POWERPERCENTAGE]);
+
+ disable_power_control = 0;
+ if (!disable_power_control && hwmgr->platform_descriptor.TDPODLimit)
+ /* enable TDP overdrive (PowerControl) feature as well if supported */
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_PowerControl);
+
+ if (powerplay_table->PowerSavingClockTable.ucTableRevision == 1) {
+ power_saving_clock_count =
+ (le32_to_cpu(powerplay_table->PowerSavingClockTable.PowerSavingClockCount) >=
+ ATOM_VEGA20_PPCLOCK_COUNT) ?
+ ATOM_VEGA20_PPCLOCK_COUNT :
+ le32_to_cpu(powerplay_table->PowerSavingClockTable.PowerSavingClockCount);
+ phm_copy_clock_limits_array(hwmgr,
+ &pptable_information->power_saving_clock_max,
+ powerplay_table->PowerSavingClockTable.PowerSavingClockMax,
+ power_saving_clock_count);
+ phm_copy_clock_limits_array(hwmgr,
+ &pptable_information->power_saving_clock_min,
+ powerplay_table->PowerSavingClockTable.PowerSavingClockMin,
+ power_saving_clock_count);
+ }
+
+ pptable_information->smc_pptable = kmemdup(&(powerplay_table->smcPPTable),
+ sizeof(PPTable_t),
+ GFP_KERNEL);
+ if (pptable_information->smc_pptable == NULL)
+ return -ENOMEM;
+
+
+ result = append_vbios_pptable(hwmgr, (pptable_information->smc_pptable));
+ if (result)
+ return result;
+
+ result = override_powerplay_table_fantargettemperature(hwmgr);
+
+ return result;
+}
+
+static int vega20_pp_tables_initialize(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+ const ATOM_Vega20_POWERPLAYTABLE *powerplay_table;
+
+ hwmgr->pptable = kzalloc(sizeof(struct phm_ppt_v3_information), GFP_KERNEL);
+ PP_ASSERT_WITH_CODE((hwmgr->pptable != NULL),
+ "Failed to allocate hwmgr->pptable!", return -ENOMEM);
+
+ powerplay_table = get_powerplay_table(hwmgr);
+ PP_ASSERT_WITH_CODE((powerplay_table != NULL),
+ "Missing PowerPlay Table!", return -1);
+
+ result = check_powerplay_tables(hwmgr, powerplay_table);
+ PP_ASSERT_WITH_CODE((result == 0),
+ "check_powerplay_tables failed", return result);
+
+ result = set_platform_caps(hwmgr,
+ le32_to_cpu(powerplay_table->ulPlatformCaps));
+ PP_ASSERT_WITH_CODE((result == 0),
+ "set_platform_caps failed", return result);
+
+ result = init_powerplay_table_information(hwmgr, powerplay_table);
+ PP_ASSERT_WITH_CODE((result == 0),
+ "init_powerplay_table_information failed", return result);
+
+ return result;
+}
+
+static int vega20_pp_tables_uninitialize(struct pp_hwmgr *hwmgr)
+{
+ struct phm_ppt_v3_information *pp_table_info =
+ (struct phm_ppt_v3_information *)(hwmgr->pptable);
+
+ kfree(pp_table_info->power_saving_clock_max);
+ pp_table_info->power_saving_clock_max = NULL;
+
+ kfree(pp_table_info->power_saving_clock_min);
+ pp_table_info->power_saving_clock_min = NULL;
+
+ kfree(pp_table_info->od_feature_capabilities);
+ pp_table_info->od_feature_capabilities = NULL;
+
+ kfree(pp_table_info->od_settings_max);
+ pp_table_info->od_settings_max = NULL;
+
+ kfree(pp_table_info->od_settings_min);
+ pp_table_info->od_settings_min = NULL;
+
+ kfree(pp_table_info->smc_pptable);
+ pp_table_info->smc_pptable = NULL;
+
+ kfree(hwmgr->pptable);
+ hwmgr->pptable = NULL;
+
+ return 0;
+}
+
+const struct pp_table_func vega20_pptable_funcs = {
+ .pptable_init = vega20_pp_tables_initialize,
+ .pptable_fini = vega20_pp_tables_uninitialize,
+};
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.h
new file mode 100644
index 000000000000..846c2cb40b35
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 VEGA20_PROCESSPPTABLES_H
+#define VEGA20_PROCESSPPTABLES_H
+
+#include "hwmgr.h"
+
+extern const struct pp_table_func vega20_pptable_funcs;
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_thermal.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_thermal.c
new file mode 100644
index 000000000000..364162ddaa9c
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_thermal.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "vega20_thermal.h"
+#include "vega20_hwmgr.h"
+#include "vega20_smumgr.h"
+#include "vega20_ppsmc.h"
+#include "vega20_inc.h"
+#include "soc15_common.h"
+#include "pp_debug.h"
+
+static int vega20_disable_fan_control_feature(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data = hwmgr->backend;
+ int ret = 0;
+
+ if (data->smu_features[GNLD_FAN_CONTROL].supported) {
+ ret = vega20_enable_smc_features(
+ hwmgr, false,
+ data->smu_features[GNLD_FAN_CONTROL].
+ smu_feature_bitmap);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Disable FAN CONTROL feature Failed!",
+ return ret);
+ data->smu_features[GNLD_FAN_CONTROL].enabled = false;
+ }
+
+ return ret;
+}
+
+int vega20_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data = hwmgr->backend;
+
+ if (data->smu_features[GNLD_FAN_CONTROL].supported)
+ return vega20_disable_fan_control_feature(hwmgr);
+
+ return 0;
+}
+
+static int vega20_enable_fan_control_feature(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data = hwmgr->backend;
+ int ret = 0;
+
+ if (data->smu_features[GNLD_FAN_CONTROL].supported) {
+ ret = vega20_enable_smc_features(
+ hwmgr, true,
+ data->smu_features[GNLD_FAN_CONTROL].
+ smu_feature_bitmap);
+ PP_ASSERT_WITH_CODE(!ret,
+ "Enable FAN CONTROL feature Failed!",
+ return ret);
+ data->smu_features[GNLD_FAN_CONTROL].enabled = true;
+ }
+
+ return ret;
+}
+
+int vega20_fan_ctrl_start_smc_fan_control(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data = hwmgr->backend;
+
+ if (data->smu_features[GNLD_FAN_CONTROL].supported)
+ return vega20_enable_fan_control_feature(hwmgr);
+
+ return 0;
+}
+
+static int vega20_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr, uint32_t mode)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ WREG32_SOC15(THM, 0, mmCG_FDO_CTRL2,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL2),
+ CG_FDO_CTRL2, TMIN, 0));
+ WREG32_SOC15(THM, 0, mmCG_FDO_CTRL2,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL2),
+ CG_FDO_CTRL2, FDO_PWM_MODE, mode));
+
+ return 0;
+}
+
+static int vega20_get_current_rpm(struct pp_hwmgr *hwmgr, uint32_t *current_rpm)
+{
+ int ret = 0;
+
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_GetCurrentRpm,
+ current_rpm)) == 0,
+ "Attempt to get current RPM from SMC Failed!",
+ return ret);
+
+ return 0;
+}
+
+int vega20_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr,
+ uint32_t *speed)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ PPTable_t *pp_table = &(data->smc_state_table.pp_table);
+ uint32_t current_rpm, percent = 0;
+ int ret = 0;
+
+ ret = vega20_get_current_rpm(hwmgr, &current_rpm);
+ if (ret)
+ return ret;
+
+ percent = current_rpm * 100 / pp_table->FanMaximumRpm;
+
+ *speed = percent > 100 ? 100 : percent;
+
+ return 0;
+}
+
+int vega20_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr,
+ uint32_t speed)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t duty100;
+ uint32_t duty;
+ uint64_t tmp64;
+
+ if (speed > 100)
+ speed = 100;
+
+ if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl))
+ vega20_fan_ctrl_stop_smc_fan_control(hwmgr);
+
+ duty100 = REG_GET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL1),
+ CG_FDO_CTRL1, FMAX_DUTY100);
+
+ if (duty100 == 0)
+ return -EINVAL;
+
+ tmp64 = (uint64_t)speed * duty100;
+ do_div(tmp64, 100);
+ duty = (uint32_t)tmp64;
+
+ WREG32_SOC15(THM, 0, mmCG_FDO_CTRL0,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL0),
+ CG_FDO_CTRL0, FDO_STATIC_DUTY, duty));
+
+ return vega20_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
+}
+
+int vega20_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr,
+ struct phm_fan_speed_info *fan_speed_info)
+{
+ memset(fan_speed_info, 0, sizeof(*fan_speed_info));
+ fan_speed_info->supports_percent_read = true;
+ fan_speed_info->supports_percent_write = true;
+ fan_speed_info->supports_rpm_read = true;
+ fan_speed_info->supports_rpm_write = true;
+
+ return 0;
+}
+
+int vega20_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed)
+{
+ *speed = 0;
+
+ return vega20_get_current_rpm(hwmgr, speed);
+}
+
+int vega20_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t tach_period, crystal_clock_freq;
+ int result = 0;
+
+ if (!speed)
+ return -EINVAL;
+
+ if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl)) {
+ result = vega20_fan_ctrl_stop_smc_fan_control(hwmgr);
+ if (result)
+ return result;
+ }
+
+ crystal_clock_freq = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev);
+ tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed);
+ WREG32_SOC15(THM, 0, mmCG_TACH_CTRL,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_TACH_CTRL),
+ CG_TACH_CTRL, TARGET_PERIOD,
+ tach_period));
+
+ return vega20_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC_RPM);
+}
+
+/**
+* Reads the remote temperature from the SIslands thermal controller.
+*
+* @param hwmgr The address of the hardware manager.
+*/
+int vega20_thermal_get_temperature(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ int temp = 0;
+
+ temp = RREG32_SOC15(THM, 0, mmCG_MULT_THERMAL_STATUS);
+
+ temp = (temp & CG_MULT_THERMAL_STATUS__CTF_TEMP_MASK) >>
+ CG_MULT_THERMAL_STATUS__CTF_TEMP__SHIFT;
+
+ temp = temp & 0x1ff;
+
+ temp *= PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ return temp;
+}
+
+/**
+* Set the requested temperature range for high and low alert signals
+*
+* @param hwmgr The address of the hardware manager.
+* @param range Temperature range to be programmed for
+* high and low alert signals
+* @exception PP_Result_BadInput if the input data is not valid.
+*/
+static int vega20_thermal_set_temperature_range(struct pp_hwmgr *hwmgr,
+ struct PP_TemperatureRange *range)
+{
+ struct phm_ppt_v3_information *pptable_information =
+ (struct phm_ppt_v3_information *)hwmgr->pptable;
+ struct amdgpu_device *adev = hwmgr->adev;
+ int low = VEGA20_THERMAL_MINIMUM_ALERT_TEMP;
+ int high = VEGA20_THERMAL_MAXIMUM_ALERT_TEMP;
+ uint32_t val;
+
+ /* compare them in unit celsius degree */
+ if (low < range->min / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)
+ low = range->min / PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ if (high > pptable_information->us_software_shutdown_temp)
+ high = pptable_information->us_software_shutdown_temp;
+
+ if (low > high)
+ return -EINVAL;
+
+ val = RREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_CTRL);
+
+ val = CGS_REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, MAX_IH_CREDIT, 5);
+ val = CGS_REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, THERM_IH_HW_ENA, 1);
+ val = CGS_REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, DIG_THERM_INTH, high);
+ val = CGS_REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, DIG_THERM_INTL, low);
+ val = val & (~THM_THERMAL_INT_CTRL__THERM_TRIGGER_MASK_MASK);
+
+ WREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_CTRL, val);
+
+ return 0;
+}
+
+/**
+* Enable thermal alerts on the RV770 thermal controller.
+*
+* @param hwmgr The address of the hardware manager.
+*/
+static int vega20_thermal_enable_alert(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t val = 0;
+
+ val |= (1 << THM_THERMAL_INT_ENA__THERM_INTH_CLR__SHIFT);
+ val |= (1 << THM_THERMAL_INT_ENA__THERM_INTL_CLR__SHIFT);
+ val |= (1 << THM_THERMAL_INT_ENA__THERM_TRIGGER_CLR__SHIFT);
+
+ WREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_ENA, val);
+
+ return 0;
+}
+
+/**
+* Disable thermal alerts on the RV770 thermal controller.
+* @param hwmgr The address of the hardware manager.
+*/
+int vega20_thermal_disable_alert(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ WREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_ENA, 0);
+
+ return 0;
+}
+
+/**
+* Uninitialize the thermal controller.
+* Currently just disables alerts.
+* @param hwmgr The address of the hardware manager.
+*/
+int vega20_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr)
+{
+ int result = vega20_thermal_disable_alert(hwmgr);
+
+ return result;
+}
+
+/**
+* Set up the fan table to control the fan using the SMC.
+* @param hwmgr the address of the powerplay hardware manager.
+* @param pInput the pointer to input data
+* @param pOutput the pointer to output data
+* @param pStorage the pointer to temporary storage
+* @param Result the last failure code
+* @return result from set temperature range routine
+*/
+static int vega20_thermal_setup_fan_table(struct pp_hwmgr *hwmgr)
+{
+ int ret;
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ PPTable_t *table = &(data->smc_state_table.pp_table);
+
+ ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetFanTemperatureTarget,
+ (uint32_t)table->FanTargetTemperature,
+ NULL);
+
+ return ret;
+}
+
+int vega20_start_thermal_controller(struct pp_hwmgr *hwmgr,
+ struct PP_TemperatureRange *range)
+{
+ int ret = 0;
+
+ if (range == NULL)
+ return -EINVAL;
+
+ ret = vega20_thermal_set_temperature_range(hwmgr, range);
+ if (ret)
+ return ret;
+
+ ret = vega20_thermal_enable_alert(hwmgr);
+ if (ret)
+ return ret;
+
+ ret = vega20_thermal_setup_fan_table(hwmgr);
+
+ return ret;
+};
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_thermal.h b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_thermal.h
new file mode 100644
index 000000000000..2d1769bbd24e
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_thermal.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 VEGA20_THERMAL_H
+#define VEGA20_THERMAL_H
+
+#include "hwmgr.h"
+
+struct vega20_temperature {
+ uint16_t edge_temp;
+ uint16_t hot_spot_temp;
+ uint16_t hbm_temp;
+ uint16_t vr_soc_temp;
+ uint16_t vr_mem_temp;
+ uint16_t liquid1_temp;
+ uint16_t liquid2_temp;
+ uint16_t plx_temp;
+};
+
+#define VEGA20_THERMAL_HIGH_ALERT_MASK 0x1
+#define VEGA20_THERMAL_LOW_ALERT_MASK 0x2
+
+#define VEGA20_THERMAL_MINIMUM_TEMP_READING -256
+#define VEGA20_THERMAL_MAXIMUM_TEMP_READING 255
+
+#define VEGA20_THERMAL_MINIMUM_ALERT_TEMP 0
+#define VEGA20_THERMAL_MAXIMUM_ALERT_TEMP 255
+
+#define FDO_PWM_MODE_STATIC 1
+#define FDO_PWM_MODE_STATIC_RPM 5
+
+extern int vega20_thermal_get_temperature(struct pp_hwmgr *hwmgr);
+extern int vega20_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr,
+ struct phm_fan_speed_info *fan_speed_info);
+extern int vega20_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr,
+ uint32_t *speed);
+extern int vega20_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr,
+ uint32_t speed);
+extern int vega20_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr,
+ uint32_t *speed);
+extern int vega20_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr,
+ uint32_t speed);
+extern int vega20_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr);
+extern int vega20_fan_ctrl_start_smc_fan_control(struct pp_hwmgr *hwmgr);
+extern int vega20_thermal_disable_alert(struct pp_hwmgr *hwmgr);
+extern int vega20_start_thermal_controller(struct pp_hwmgr *hwmgr,
+ struct PP_TemperatureRange *range);
+extern int vega20_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr);
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/kv_dpm.c b/drivers/gpu/drm/amd/pm/powerplay/kv_dpm.c
new file mode 100644
index 000000000000..4b3faaccecb9
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/kv_dpm.c
@@ -0,0 +1,3382 @@
+/*
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "amdgpu.h"
+#include "amdgpu_pm.h"
+#include "cikd.h"
+#include "atom.h"
+#include "amdgpu_atombios.h"
+#include "amdgpu_dpm.h"
+#include "kv_dpm.h"
+#include "gfx_v7_0.h"
+#include <linux/seq_file.h>
+
+#include "smu/smu_7_0_0_d.h"
+#include "smu/smu_7_0_0_sh_mask.h"
+
+#include "gca/gfx_7_2_d.h"
+#include "gca/gfx_7_2_sh_mask.h"
+
+#define KV_MAX_DEEPSLEEP_DIVIDER_ID 5
+#define KV_MINIMUM_ENGINE_CLOCK 800
+#define SMC_RAM_END 0x40000
+
+static const struct amd_pm_funcs kv_dpm_funcs;
+
+static void kv_dpm_set_irq_funcs(struct amdgpu_device *adev);
+static int kv_enable_nb_dpm(struct amdgpu_device *adev,
+ bool enable);
+static void kv_init_graphics_levels(struct amdgpu_device *adev);
+static int kv_calculate_ds_divider(struct amdgpu_device *adev);
+static int kv_calculate_nbps_level_settings(struct amdgpu_device *adev);
+static int kv_calculate_dpm_settings(struct amdgpu_device *adev);
+static void kv_enable_new_levels(struct amdgpu_device *adev);
+static void kv_program_nbps_index_settings(struct amdgpu_device *adev,
+ struct amdgpu_ps *new_rps);
+static int kv_set_enabled_level(struct amdgpu_device *adev, u32 level);
+static int kv_set_enabled_levels(struct amdgpu_device *adev);
+static int kv_force_dpm_highest(struct amdgpu_device *adev);
+static int kv_force_dpm_lowest(struct amdgpu_device *adev);
+static void kv_apply_state_adjust_rules(struct amdgpu_device *adev,
+ struct amdgpu_ps *new_rps,
+ struct amdgpu_ps *old_rps);
+static int kv_set_thermal_temperature_range(struct amdgpu_device *adev,
+ int min_temp, int max_temp);
+static int kv_init_fps_limits(struct amdgpu_device *adev);
+
+static void kv_dpm_powergate_samu(struct amdgpu_device *adev, bool gate);
+static void kv_dpm_powergate_acp(struct amdgpu_device *adev, bool gate);
+
+
+static u32 kv_convert_vid2_to_vid7(struct amdgpu_device *adev,
+ struct sumo_vid_mapping_table *vid_mapping_table,
+ u32 vid_2bit)
+{
+ struct amdgpu_clock_voltage_dependency_table *vddc_sclk_table =
+ &adev->pm.dpm.dyn_state.vddc_dependency_on_sclk;
+ u32 i;
+
+ if (vddc_sclk_table && vddc_sclk_table->count) {
+ if (vid_2bit < vddc_sclk_table->count)
+ return vddc_sclk_table->entries[vid_2bit].v;
+ else
+ return vddc_sclk_table->entries[vddc_sclk_table->count - 1].v;
+ } else {
+ for (i = 0; i < vid_mapping_table->num_entries; i++) {
+ if (vid_mapping_table->entries[i].vid_2bit == vid_2bit)
+ return vid_mapping_table->entries[i].vid_7bit;
+ }
+ return vid_mapping_table->entries[vid_mapping_table->num_entries - 1].vid_7bit;
+ }
+}
+
+static u32 kv_convert_vid7_to_vid2(struct amdgpu_device *adev,
+ struct sumo_vid_mapping_table *vid_mapping_table,
+ u32 vid_7bit)
+{
+ struct amdgpu_clock_voltage_dependency_table *vddc_sclk_table =
+ &adev->pm.dpm.dyn_state.vddc_dependency_on_sclk;
+ u32 i;
+
+ if (vddc_sclk_table && vddc_sclk_table->count) {
+ for (i = 0; i < vddc_sclk_table->count; i++) {
+ if (vddc_sclk_table->entries[i].v == vid_7bit)
+ return i;
+ }
+ return vddc_sclk_table->count - 1;
+ } else {
+ for (i = 0; i < vid_mapping_table->num_entries; i++) {
+ if (vid_mapping_table->entries[i].vid_7bit == vid_7bit)
+ return vid_mapping_table->entries[i].vid_2bit;
+ }
+
+ return vid_mapping_table->entries[vid_mapping_table->num_entries - 1].vid_2bit;
+ }
+}
+
+static void sumo_take_smu_control(struct amdgpu_device *adev, bool enable)
+{
+/* This bit selects who handles display phy powergating.
+ * Clear the bit to let atom handle it.
+ * Set it to let the driver handle it.
+ * For now we just let atom handle it.
+ */
+#if 0
+ u32 v = RREG32(mmDOUT_SCRATCH3);
+
+ if (enable)
+ v |= 0x4;
+ else
+ v &= 0xFFFFFFFB;
+
+ WREG32(mmDOUT_SCRATCH3, v);
+#endif
+}
+
+static void sumo_construct_sclk_voltage_mapping_table(struct amdgpu_device *adev,
+ struct sumo_sclk_voltage_mapping_table *sclk_voltage_mapping_table,
+ ATOM_AVAILABLE_SCLK_LIST *table)
+{
+ u32 i;
+ u32 n = 0;
+ u32 prev_sclk = 0;
+
+ for (i = 0; i < SUMO_MAX_HARDWARE_POWERLEVELS; i++) {
+ if (table[i].ulSupportedSCLK > prev_sclk) {
+ sclk_voltage_mapping_table->entries[n].sclk_frequency =
+ table[i].ulSupportedSCLK;
+ sclk_voltage_mapping_table->entries[n].vid_2bit =
+ table[i].usVoltageIndex;
+ prev_sclk = table[i].ulSupportedSCLK;
+ n++;
+ }
+ }
+
+ sclk_voltage_mapping_table->num_max_dpm_entries = n;
+}
+
+static void sumo_construct_vid_mapping_table(struct amdgpu_device *adev,
+ struct sumo_vid_mapping_table *vid_mapping_table,
+ ATOM_AVAILABLE_SCLK_LIST *table)
+{
+ u32 i, j;
+
+ for (i = 0; i < SUMO_MAX_HARDWARE_POWERLEVELS; i++) {
+ if (table[i].ulSupportedSCLK != 0) {
+ vid_mapping_table->entries[table[i].usVoltageIndex].vid_7bit =
+ table[i].usVoltageID;
+ vid_mapping_table->entries[table[i].usVoltageIndex].vid_2bit =
+ table[i].usVoltageIndex;
+ }
+ }
+
+ for (i = 0; i < SUMO_MAX_NUMBER_VOLTAGES; i++) {
+ if (vid_mapping_table->entries[i].vid_7bit == 0) {
+ for (j = i + 1; j < SUMO_MAX_NUMBER_VOLTAGES; j++) {
+ if (vid_mapping_table->entries[j].vid_7bit != 0) {
+ vid_mapping_table->entries[i] =
+ vid_mapping_table->entries[j];
+ vid_mapping_table->entries[j].vid_7bit = 0;
+ break;
+ }
+ }
+
+ if (j == SUMO_MAX_NUMBER_VOLTAGES)
+ break;
+ }
+ }
+
+ vid_mapping_table->num_entries = i;
+}
+
+#if 0
+static const struct kv_lcac_config_values sx_local_cac_cfg_kv[] =
+{
+ { 0, 4, 1 },
+ { 1, 4, 1 },
+ { 2, 5, 1 },
+ { 3, 4, 2 },
+ { 4, 1, 1 },
+ { 5, 5, 2 },
+ { 6, 6, 1 },
+ { 7, 9, 2 },
+ { 0xffffffff }
+};
+
+static const struct kv_lcac_config_values mc0_local_cac_cfg_kv[] =
+{
+ { 0, 4, 1 },
+ { 0xffffffff }
+};
+
+static const struct kv_lcac_config_values mc1_local_cac_cfg_kv[] =
+{
+ { 0, 4, 1 },
+ { 0xffffffff }
+};
+
+static const struct kv_lcac_config_values mc2_local_cac_cfg_kv[] =
+{
+ { 0, 4, 1 },
+ { 0xffffffff }
+};
+
+static const struct kv_lcac_config_values mc3_local_cac_cfg_kv[] =
+{
+ { 0, 4, 1 },
+ { 0xffffffff }
+};
+
+static const struct kv_lcac_config_values cpl_local_cac_cfg_kv[] =
+{
+ { 0, 4, 1 },
+ { 1, 4, 1 },
+ { 2, 5, 1 },
+ { 3, 4, 1 },
+ { 4, 1, 1 },
+ { 5, 5, 1 },
+ { 6, 6, 1 },
+ { 7, 9, 1 },
+ { 8, 4, 1 },
+ { 9, 2, 1 },
+ { 10, 3, 1 },
+ { 11, 6, 1 },
+ { 12, 8, 2 },
+ { 13, 1, 1 },
+ { 14, 2, 1 },
+ { 15, 3, 1 },
+ { 16, 1, 1 },
+ { 17, 4, 1 },
+ { 18, 3, 1 },
+ { 19, 1, 1 },
+ { 20, 8, 1 },
+ { 21, 5, 1 },
+ { 22, 1, 1 },
+ { 23, 1, 1 },
+ { 24, 4, 1 },
+ { 27, 6, 1 },
+ { 28, 1, 1 },
+ { 0xffffffff }
+};
+
+static const struct kv_lcac_config_reg sx0_cac_config_reg[] =
+{
+ { 0xc0400d00, 0x003e0000, 17, 0x3fc00000, 22, 0x0001fffe, 1, 0x00000001, 0 }
+};
+
+static const struct kv_lcac_config_reg mc0_cac_config_reg[] =
+{
+ { 0xc0400d30, 0x003e0000, 17, 0x3fc00000, 22, 0x0001fffe, 1, 0x00000001, 0 }
+};
+
+static const struct kv_lcac_config_reg mc1_cac_config_reg[] =
+{
+ { 0xc0400d3c, 0x003e0000, 17, 0x3fc00000, 22, 0x0001fffe, 1, 0x00000001, 0 }
+};
+
+static const struct kv_lcac_config_reg mc2_cac_config_reg[] =
+{
+ { 0xc0400d48, 0x003e0000, 17, 0x3fc00000, 22, 0x0001fffe, 1, 0x00000001, 0 }
+};
+
+static const struct kv_lcac_config_reg mc3_cac_config_reg[] =
+{
+ { 0xc0400d54, 0x003e0000, 17, 0x3fc00000, 22, 0x0001fffe, 1, 0x00000001, 0 }
+};
+
+static const struct kv_lcac_config_reg cpl_cac_config_reg[] =
+{
+ { 0xc0400d80, 0x003e0000, 17, 0x3fc00000, 22, 0x0001fffe, 1, 0x00000001, 0 }
+};
+#endif
+
+static const struct kv_pt_config_reg didt_config_kv[] =
+{
+ { 0x10, 0x000000ff, 0, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x10, 0x0000ff00, 8, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x10, 0x00ff0000, 16, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x10, 0xff000000, 24, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x11, 0x000000ff, 0, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x11, 0x0000ff00, 8, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x11, 0x00ff0000, 16, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x11, 0xff000000, 24, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x12, 0x000000ff, 0, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x12, 0x0000ff00, 8, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x12, 0x00ff0000, 16, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x12, 0xff000000, 24, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x2, 0x00003fff, 0, 0x4, KV_CONFIGREG_DIDT_IND },
+ { 0x2, 0x03ff0000, 16, 0x80, KV_CONFIGREG_DIDT_IND },
+ { 0x2, 0x78000000, 27, 0x3, KV_CONFIGREG_DIDT_IND },
+ { 0x1, 0x0000ffff, 0, 0x3FFF, KV_CONFIGREG_DIDT_IND },
+ { 0x1, 0xffff0000, 16, 0x3FFF, KV_CONFIGREG_DIDT_IND },
+ { 0x0, 0x00000001, 0, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x30, 0x000000ff, 0, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x30, 0x0000ff00, 8, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x30, 0x00ff0000, 16, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x30, 0xff000000, 24, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x31, 0x000000ff, 0, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x31, 0x0000ff00, 8, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x31, 0x00ff0000, 16, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x31, 0xff000000, 24, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x32, 0x000000ff, 0, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x32, 0x0000ff00, 8, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x32, 0x00ff0000, 16, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x32, 0xff000000, 24, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x22, 0x00003fff, 0, 0x4, KV_CONFIGREG_DIDT_IND },
+ { 0x22, 0x03ff0000, 16, 0x80, KV_CONFIGREG_DIDT_IND },
+ { 0x22, 0x78000000, 27, 0x3, KV_CONFIGREG_DIDT_IND },
+ { 0x21, 0x0000ffff, 0, 0x3FFF, KV_CONFIGREG_DIDT_IND },
+ { 0x21, 0xffff0000, 16, 0x3FFF, KV_CONFIGREG_DIDT_IND },
+ { 0x20, 0x00000001, 0, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x50, 0x000000ff, 0, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x50, 0x0000ff00, 8, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x50, 0x00ff0000, 16, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x50, 0xff000000, 24, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x51, 0x000000ff, 0, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x51, 0x0000ff00, 8, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x51, 0x00ff0000, 16, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x51, 0xff000000, 24, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x52, 0x000000ff, 0, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x52, 0x0000ff00, 8, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x52, 0x00ff0000, 16, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x52, 0xff000000, 24, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x42, 0x00003fff, 0, 0x4, KV_CONFIGREG_DIDT_IND },
+ { 0x42, 0x03ff0000, 16, 0x80, KV_CONFIGREG_DIDT_IND },
+ { 0x42, 0x78000000, 27, 0x3, KV_CONFIGREG_DIDT_IND },
+ { 0x41, 0x0000ffff, 0, 0x3FFF, KV_CONFIGREG_DIDT_IND },
+ { 0x41, 0xffff0000, 16, 0x3FFF, KV_CONFIGREG_DIDT_IND },
+ { 0x40, 0x00000001, 0, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x70, 0x000000ff, 0, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x70, 0x0000ff00, 8, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x70, 0x00ff0000, 16, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x70, 0xff000000, 24, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x71, 0x000000ff, 0, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x71, 0x0000ff00, 8, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x71, 0x00ff0000, 16, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x71, 0xff000000, 24, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x72, 0x000000ff, 0, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x72, 0x0000ff00, 8, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x72, 0x00ff0000, 16, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x72, 0xff000000, 24, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0x62, 0x00003fff, 0, 0x4, KV_CONFIGREG_DIDT_IND },
+ { 0x62, 0x03ff0000, 16, 0x80, KV_CONFIGREG_DIDT_IND },
+ { 0x62, 0x78000000, 27, 0x3, KV_CONFIGREG_DIDT_IND },
+ { 0x61, 0x0000ffff, 0, 0x3FFF, KV_CONFIGREG_DIDT_IND },
+ { 0x61, 0xffff0000, 16, 0x3FFF, KV_CONFIGREG_DIDT_IND },
+ { 0x60, 0x00000001, 0, 0x0, KV_CONFIGREG_DIDT_IND },
+ { 0xFFFFFFFF }
+};
+
+static struct kv_ps *kv_get_ps(struct amdgpu_ps *rps)
+{
+ struct kv_ps *ps = rps->ps_priv;
+
+ return ps;
+}
+
+static struct kv_power_info *kv_get_pi(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = adev->pm.dpm.priv;
+
+ return pi;
+}
+
+#if 0
+static void kv_program_local_cac_table(struct amdgpu_device *adev,
+ const struct kv_lcac_config_values *local_cac_table,
+ const struct kv_lcac_config_reg *local_cac_reg)
+{
+ u32 i, count, data;
+ const struct kv_lcac_config_values *values = local_cac_table;
+
+ while (values->block_id != 0xffffffff) {
+ count = values->signal_id;
+ for (i = 0; i < count; i++) {
+ data = ((values->block_id << local_cac_reg->block_shift) &
+ local_cac_reg->block_mask);
+ data |= ((i << local_cac_reg->signal_shift) &
+ local_cac_reg->signal_mask);
+ data |= ((values->t << local_cac_reg->t_shift) &
+ local_cac_reg->t_mask);
+ data |= ((1 << local_cac_reg->enable_shift) &
+ local_cac_reg->enable_mask);
+ WREG32_SMC(local_cac_reg->cntl, data);
+ }
+ values++;
+ }
+}
+#endif
+
+static int kv_program_pt_config_registers(struct amdgpu_device *adev,
+ const struct kv_pt_config_reg *cac_config_regs)
+{
+ const struct kv_pt_config_reg *config_regs = cac_config_regs;
+ u32 data;
+ u32 cache = 0;
+
+ if (config_regs == NULL)
+ return -EINVAL;
+
+ while (config_regs->offset != 0xFFFFFFFF) {
+ if (config_regs->type == KV_CONFIGREG_CACHE) {
+ cache |= ((config_regs->value << config_regs->shift) & config_regs->mask);
+ } else {
+ switch (config_regs->type) {
+ case KV_CONFIGREG_SMC_IND:
+ data = RREG32_SMC(config_regs->offset);
+ break;
+ case KV_CONFIGREG_DIDT_IND:
+ data = RREG32_DIDT(config_regs->offset);
+ break;
+ default:
+ data = RREG32(config_regs->offset);
+ break;
+ }
+
+ data &= ~config_regs->mask;
+ data |= ((config_regs->value << config_regs->shift) & config_regs->mask);
+ data |= cache;
+ cache = 0;
+
+ switch (config_regs->type) {
+ case KV_CONFIGREG_SMC_IND:
+ WREG32_SMC(config_regs->offset, data);
+ break;
+ case KV_CONFIGREG_DIDT_IND:
+ WREG32_DIDT(config_regs->offset, data);
+ break;
+ default:
+ WREG32(config_regs->offset, data);
+ break;
+ }
+ }
+ config_regs++;
+ }
+
+ return 0;
+}
+
+static void kv_do_enable_didt(struct amdgpu_device *adev, bool enable)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u32 data;
+
+ if (pi->caps_sq_ramping) {
+ data = RREG32_DIDT(ixDIDT_SQ_CTRL0);
+ if (enable)
+ data |= DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK;
+ else
+ data &= ~DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK;
+ WREG32_DIDT(ixDIDT_SQ_CTRL0, data);
+ }
+
+ if (pi->caps_db_ramping) {
+ data = RREG32_DIDT(ixDIDT_DB_CTRL0);
+ if (enable)
+ data |= DIDT_DB_CTRL0__DIDT_CTRL_EN_MASK;
+ else
+ data &= ~DIDT_DB_CTRL0__DIDT_CTRL_EN_MASK;
+ WREG32_DIDT(ixDIDT_DB_CTRL0, data);
+ }
+
+ if (pi->caps_td_ramping) {
+ data = RREG32_DIDT(ixDIDT_TD_CTRL0);
+ if (enable)
+ data |= DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK;
+ else
+ data &= ~DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK;
+ WREG32_DIDT(ixDIDT_TD_CTRL0, data);
+ }
+
+ if (pi->caps_tcp_ramping) {
+ data = RREG32_DIDT(ixDIDT_TCP_CTRL0);
+ if (enable)
+ data |= DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK;
+ else
+ data &= ~DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK;
+ WREG32_DIDT(ixDIDT_TCP_CTRL0, data);
+ }
+}
+
+static int kv_enable_didt(struct amdgpu_device *adev, bool enable)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ int ret;
+
+ if (pi->caps_sq_ramping ||
+ pi->caps_db_ramping ||
+ pi->caps_td_ramping ||
+ pi->caps_tcp_ramping) {
+ amdgpu_gfx_rlc_enter_safe_mode(adev);
+
+ if (enable) {
+ ret = kv_program_pt_config_registers(adev, didt_config_kv);
+ if (ret) {
+ amdgpu_gfx_rlc_exit_safe_mode(adev);
+ return ret;
+ }
+ }
+
+ kv_do_enable_didt(adev, enable);
+
+ amdgpu_gfx_rlc_exit_safe_mode(adev);
+ }
+
+ return 0;
+}
+
+#if 0
+static void kv_initialize_hardware_cac_manager(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ if (pi->caps_cac) {
+ WREG32_SMC(ixLCAC_SX0_OVR_SEL, 0);
+ WREG32_SMC(ixLCAC_SX0_OVR_VAL, 0);
+ kv_program_local_cac_table(adev, sx_local_cac_cfg_kv, sx0_cac_config_reg);
+
+ WREG32_SMC(ixLCAC_MC0_OVR_SEL, 0);
+ WREG32_SMC(ixLCAC_MC0_OVR_VAL, 0);
+ kv_program_local_cac_table(adev, mc0_local_cac_cfg_kv, mc0_cac_config_reg);
+
+ WREG32_SMC(ixLCAC_MC1_OVR_SEL, 0);
+ WREG32_SMC(ixLCAC_MC1_OVR_VAL, 0);
+ kv_program_local_cac_table(adev, mc1_local_cac_cfg_kv, mc1_cac_config_reg);
+
+ WREG32_SMC(ixLCAC_MC2_OVR_SEL, 0);
+ WREG32_SMC(ixLCAC_MC2_OVR_VAL, 0);
+ kv_program_local_cac_table(adev, mc2_local_cac_cfg_kv, mc2_cac_config_reg);
+
+ WREG32_SMC(ixLCAC_MC3_OVR_SEL, 0);
+ WREG32_SMC(ixLCAC_MC3_OVR_VAL, 0);
+ kv_program_local_cac_table(adev, mc3_local_cac_cfg_kv, mc3_cac_config_reg);
+
+ WREG32_SMC(ixLCAC_CPL_OVR_SEL, 0);
+ WREG32_SMC(ixLCAC_CPL_OVR_VAL, 0);
+ kv_program_local_cac_table(adev, cpl_local_cac_cfg_kv, cpl_cac_config_reg);
+ }
+}
+#endif
+
+static int kv_enable_smc_cac(struct amdgpu_device *adev, bool enable)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ int ret = 0;
+
+ if (pi->caps_cac) {
+ if (enable) {
+ ret = amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_EnableCac);
+ if (ret)
+ pi->cac_enabled = false;
+ else
+ pi->cac_enabled = true;
+ } else if (pi->cac_enabled) {
+ amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_DisableCac);
+ pi->cac_enabled = false;
+ }
+ }
+
+ return ret;
+}
+
+static int kv_process_firmware_header(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u32 tmp;
+ int ret;
+
+ ret = amdgpu_kv_read_smc_sram_dword(adev, SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU7_Firmware_Header, DpmTable),
+ &tmp, pi->sram_end);
+
+ if (ret == 0)
+ pi->dpm_table_start = tmp;
+
+ ret = amdgpu_kv_read_smc_sram_dword(adev, SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU7_Firmware_Header, SoftRegisters),
+ &tmp, pi->sram_end);
+
+ if (ret == 0)
+ pi->soft_regs_start = tmp;
+
+ return ret;
+}
+
+static int kv_enable_dpm_voltage_scaling(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ int ret;
+
+ pi->graphics_voltage_change_enable = 1;
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, GraphicsVoltageChangeEnable),
+ &pi->graphics_voltage_change_enable,
+ sizeof(u8), pi->sram_end);
+
+ return ret;
+}
+
+static int kv_set_dpm_interval(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ int ret;
+
+ pi->graphics_interval = 1;
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, GraphicsInterval),
+ &pi->graphics_interval,
+ sizeof(u8), pi->sram_end);
+
+ return ret;
+}
+
+static int kv_set_dpm_boot_state(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ int ret;
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, GraphicsBootLevel),
+ &pi->graphics_boot_level,
+ sizeof(u8), pi->sram_end);
+
+ return ret;
+}
+
+static void kv_program_vc(struct amdgpu_device *adev)
+{
+ WREG32_SMC(ixCG_FREQ_TRAN_VOTING_0, 0x3FFFC100);
+}
+
+static void kv_clear_vc(struct amdgpu_device *adev)
+{
+ WREG32_SMC(ixCG_FREQ_TRAN_VOTING_0, 0);
+}
+
+static int kv_set_divider_value(struct amdgpu_device *adev,
+ u32 index, u32 sclk)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ struct atom_clock_dividers dividers;
+ int ret;
+
+ ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_ENGINE_PLL_PARAM,
+ sclk, false, &dividers);
+ if (ret)
+ return ret;
+
+ pi->graphics_level[index].SclkDid = (u8)dividers.post_div;
+ pi->graphics_level[index].SclkFrequency = cpu_to_be32(sclk);
+
+ return 0;
+}
+
+static u16 kv_convert_8bit_index_to_voltage(struct amdgpu_device *adev,
+ u16 voltage)
+{
+ return 6200 - (voltage * 25);
+}
+
+static u16 kv_convert_2bit_index_to_voltage(struct amdgpu_device *adev,
+ u32 vid_2bit)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u32 vid_8bit = kv_convert_vid2_to_vid7(adev,
+ &pi->sys_info.vid_mapping_table,
+ vid_2bit);
+
+ return kv_convert_8bit_index_to_voltage(adev, (u16)vid_8bit);
+}
+
+
+static int kv_set_vid(struct amdgpu_device *adev, u32 index, u32 vid)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ pi->graphics_level[index].VoltageDownH = (u8)pi->voltage_drop_t;
+ pi->graphics_level[index].MinVddNb =
+ cpu_to_be32(kv_convert_2bit_index_to_voltage(adev, vid));
+
+ return 0;
+}
+
+static int kv_set_at(struct amdgpu_device *adev, u32 index, u32 at)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ pi->graphics_level[index].AT = cpu_to_be16((u16)at);
+
+ return 0;
+}
+
+static void kv_dpm_power_level_enable(struct amdgpu_device *adev,
+ u32 index, bool enable)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ pi->graphics_level[index].EnabledForActivity = enable ? 1 : 0;
+}
+
+static void kv_start_dpm(struct amdgpu_device *adev)
+{
+ u32 tmp = RREG32_SMC(ixGENERAL_PWRMGT);
+
+ tmp |= GENERAL_PWRMGT__GLOBAL_PWRMGT_EN_MASK;
+ WREG32_SMC(ixGENERAL_PWRMGT, tmp);
+
+ amdgpu_kv_smc_dpm_enable(adev, true);
+}
+
+static void kv_stop_dpm(struct amdgpu_device *adev)
+{
+ amdgpu_kv_smc_dpm_enable(adev, false);
+}
+
+static void kv_start_am(struct amdgpu_device *adev)
+{
+ u32 sclk_pwrmgt_cntl = RREG32_SMC(ixSCLK_PWRMGT_CNTL);
+
+ sclk_pwrmgt_cntl &= ~(SCLK_PWRMGT_CNTL__RESET_SCLK_CNT_MASK |
+ SCLK_PWRMGT_CNTL__RESET_BUSY_CNT_MASK);
+ sclk_pwrmgt_cntl |= SCLK_PWRMGT_CNTL__DYNAMIC_PM_EN_MASK;
+
+ WREG32_SMC(ixSCLK_PWRMGT_CNTL, sclk_pwrmgt_cntl);
+}
+
+static void kv_reset_am(struct amdgpu_device *adev)
+{
+ u32 sclk_pwrmgt_cntl = RREG32_SMC(ixSCLK_PWRMGT_CNTL);
+
+ sclk_pwrmgt_cntl |= (SCLK_PWRMGT_CNTL__RESET_SCLK_CNT_MASK |
+ SCLK_PWRMGT_CNTL__RESET_BUSY_CNT_MASK);
+
+ WREG32_SMC(ixSCLK_PWRMGT_CNTL, sclk_pwrmgt_cntl);
+}
+
+static int kv_freeze_sclk_dpm(struct amdgpu_device *adev, bool freeze)
+{
+ return amdgpu_kv_notify_message_to_smu(adev, freeze ?
+ PPSMC_MSG_SCLKDPM_FreezeLevel : PPSMC_MSG_SCLKDPM_UnfreezeLevel);
+}
+
+static int kv_force_lowest_valid(struct amdgpu_device *adev)
+{
+ return kv_force_dpm_lowest(adev);
+}
+
+static int kv_unforce_levels(struct amdgpu_device *adev)
+{
+ if (adev->asic_type == CHIP_KABINI || adev->asic_type == CHIP_MULLINS)
+ return amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_NoForcedLevel);
+ else
+ return kv_set_enabled_levels(adev);
+}
+
+static int kv_update_sclk_t(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u32 low_sclk_interrupt_t = 0;
+ int ret = 0;
+
+ if (pi->caps_sclk_throttle_low_notification) {
+ low_sclk_interrupt_t = cpu_to_be32(pi->low_sclk_interrupt_t);
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, LowSclkInterruptT),
+ (u8 *)&low_sclk_interrupt_t,
+ sizeof(u32), pi->sram_end);
+ }
+ return ret;
+}
+
+static int kv_program_bootup_state(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u32 i;
+ struct amdgpu_clock_voltage_dependency_table *table =
+ &adev->pm.dpm.dyn_state.vddc_dependency_on_sclk;
+
+ if (table && table->count) {
+ for (i = pi->graphics_dpm_level_count - 1; i > 0; i--) {
+ if (table->entries[i].clk == pi->boot_pl.sclk)
+ break;
+ }
+
+ pi->graphics_boot_level = (u8)i;
+ kv_dpm_power_level_enable(adev, i, true);
+ } else {
+ struct sumo_sclk_voltage_mapping_table *table =
+ &pi->sys_info.sclk_voltage_mapping_table;
+
+ if (table->num_max_dpm_entries == 0)
+ return -EINVAL;
+
+ for (i = pi->graphics_dpm_level_count - 1; i > 0; i--) {
+ if (table->entries[i].sclk_frequency == pi->boot_pl.sclk)
+ break;
+ }
+
+ pi->graphics_boot_level = (u8)i;
+ kv_dpm_power_level_enable(adev, i, true);
+ }
+ return 0;
+}
+
+static int kv_enable_auto_thermal_throttling(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ int ret;
+
+ pi->graphics_therm_throttle_enable = 1;
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, GraphicsThermThrottleEnable),
+ &pi->graphics_therm_throttle_enable,
+ sizeof(u8), pi->sram_end);
+
+ return ret;
+}
+
+static int kv_upload_dpm_settings(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ int ret;
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, GraphicsLevel),
+ (u8 *)&pi->graphics_level,
+ sizeof(SMU7_Fusion_GraphicsLevel) * SMU7_MAX_LEVELS_GRAPHICS,
+ pi->sram_end);
+
+ if (ret)
+ return ret;
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, GraphicsDpmLevelCount),
+ &pi->graphics_dpm_level_count,
+ sizeof(u8), pi->sram_end);
+
+ return ret;
+}
+
+static u32 kv_get_clock_difference(u32 a, u32 b)
+{
+ return (a >= b) ? a - b : b - a;
+}
+
+static u32 kv_get_clk_bypass(struct amdgpu_device *adev, u32 clk)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u32 value;
+
+ if (pi->caps_enable_dfs_bypass) {
+ if (kv_get_clock_difference(clk, 40000) < 200)
+ value = 3;
+ else if (kv_get_clock_difference(clk, 30000) < 200)
+ value = 2;
+ else if (kv_get_clock_difference(clk, 20000) < 200)
+ value = 7;
+ else if (kv_get_clock_difference(clk, 15000) < 200)
+ value = 6;
+ else if (kv_get_clock_difference(clk, 10000) < 200)
+ value = 8;
+ else
+ value = 0;
+ } else {
+ value = 0;
+ }
+
+ return value;
+}
+
+static int kv_populate_uvd_table(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ struct amdgpu_uvd_clock_voltage_dependency_table *table =
+ &adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table;
+ struct atom_clock_dividers dividers;
+ int ret;
+ u32 i;
+
+ if (table == NULL || table->count == 0)
+ return 0;
+
+ pi->uvd_level_count = 0;
+ for (i = 0; i < table->count; i++) {
+ if (pi->high_voltage_t &&
+ (pi->high_voltage_t < table->entries[i].v))
+ break;
+
+ pi->uvd_level[i].VclkFrequency = cpu_to_be32(table->entries[i].vclk);
+ pi->uvd_level[i].DclkFrequency = cpu_to_be32(table->entries[i].dclk);
+ pi->uvd_level[i].MinVddNb = cpu_to_be16(table->entries[i].v);
+
+ pi->uvd_level[i].VClkBypassCntl =
+ (u8)kv_get_clk_bypass(adev, table->entries[i].vclk);
+ pi->uvd_level[i].DClkBypassCntl =
+ (u8)kv_get_clk_bypass(adev, table->entries[i].dclk);
+
+ ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_ENGINE_PLL_PARAM,
+ table->entries[i].vclk, false, &dividers);
+ if (ret)
+ return ret;
+ pi->uvd_level[i].VclkDivider = (u8)dividers.post_div;
+
+ ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_ENGINE_PLL_PARAM,
+ table->entries[i].dclk, false, &dividers);
+ if (ret)
+ return ret;
+ pi->uvd_level[i].DclkDivider = (u8)dividers.post_div;
+
+ pi->uvd_level_count++;
+ }
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, UvdLevelCount),
+ (u8 *)&pi->uvd_level_count,
+ sizeof(u8), pi->sram_end);
+ if (ret)
+ return ret;
+
+ pi->uvd_interval = 1;
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, UVDInterval),
+ &pi->uvd_interval,
+ sizeof(u8), pi->sram_end);
+ if (ret)
+ return ret;
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, UvdLevel),
+ (u8 *)&pi->uvd_level,
+ sizeof(SMU7_Fusion_UvdLevel) * SMU7_MAX_LEVELS_UVD,
+ pi->sram_end);
+
+ return ret;
+
+}
+
+static int kv_populate_vce_table(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ int ret;
+ u32 i;
+ struct amdgpu_vce_clock_voltage_dependency_table *table =
+ &adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table;
+ struct atom_clock_dividers dividers;
+
+ if (table == NULL || table->count == 0)
+ return 0;
+
+ pi->vce_level_count = 0;
+ for (i = 0; i < table->count; i++) {
+ if (pi->high_voltage_t &&
+ pi->high_voltage_t < table->entries[i].v)
+ break;
+
+ pi->vce_level[i].Frequency = cpu_to_be32(table->entries[i].evclk);
+ pi->vce_level[i].MinVoltage = cpu_to_be16(table->entries[i].v);
+
+ pi->vce_level[i].ClkBypassCntl =
+ (u8)kv_get_clk_bypass(adev, table->entries[i].evclk);
+
+ ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_ENGINE_PLL_PARAM,
+ table->entries[i].evclk, false, &dividers);
+ if (ret)
+ return ret;
+ pi->vce_level[i].Divider = (u8)dividers.post_div;
+
+ pi->vce_level_count++;
+ }
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, VceLevelCount),
+ (u8 *)&pi->vce_level_count,
+ sizeof(u8),
+ pi->sram_end);
+ if (ret)
+ return ret;
+
+ pi->vce_interval = 1;
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, VCEInterval),
+ (u8 *)&pi->vce_interval,
+ sizeof(u8),
+ pi->sram_end);
+ if (ret)
+ return ret;
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, VceLevel),
+ (u8 *)&pi->vce_level,
+ sizeof(SMU7_Fusion_ExtClkLevel) * SMU7_MAX_LEVELS_VCE,
+ pi->sram_end);
+
+ return ret;
+}
+
+static int kv_populate_samu_table(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ struct amdgpu_clock_voltage_dependency_table *table =
+ &adev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table;
+ struct atom_clock_dividers dividers;
+ int ret;
+ u32 i;
+
+ if (table == NULL || table->count == 0)
+ return 0;
+
+ pi->samu_level_count = 0;
+ for (i = 0; i < table->count; i++) {
+ if (pi->high_voltage_t &&
+ pi->high_voltage_t < table->entries[i].v)
+ break;
+
+ pi->samu_level[i].Frequency = cpu_to_be32(table->entries[i].clk);
+ pi->samu_level[i].MinVoltage = cpu_to_be16(table->entries[i].v);
+
+ pi->samu_level[i].ClkBypassCntl =
+ (u8)kv_get_clk_bypass(adev, table->entries[i].clk);
+
+ ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_ENGINE_PLL_PARAM,
+ table->entries[i].clk, false, &dividers);
+ if (ret)
+ return ret;
+ pi->samu_level[i].Divider = (u8)dividers.post_div;
+
+ pi->samu_level_count++;
+ }
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, SamuLevelCount),
+ (u8 *)&pi->samu_level_count,
+ sizeof(u8),
+ pi->sram_end);
+ if (ret)
+ return ret;
+
+ pi->samu_interval = 1;
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, SAMUInterval),
+ (u8 *)&pi->samu_interval,
+ sizeof(u8),
+ pi->sram_end);
+ if (ret)
+ return ret;
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, SamuLevel),
+ (u8 *)&pi->samu_level,
+ sizeof(SMU7_Fusion_ExtClkLevel) * SMU7_MAX_LEVELS_SAMU,
+ pi->sram_end);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+
+static int kv_populate_acp_table(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ struct amdgpu_clock_voltage_dependency_table *table =
+ &adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table;
+ struct atom_clock_dividers dividers;
+ int ret;
+ u32 i;
+
+ if (table == NULL || table->count == 0)
+ return 0;
+
+ pi->acp_level_count = 0;
+ for (i = 0; i < table->count; i++) {
+ pi->acp_level[i].Frequency = cpu_to_be32(table->entries[i].clk);
+ pi->acp_level[i].MinVoltage = cpu_to_be16(table->entries[i].v);
+
+ ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_ENGINE_PLL_PARAM,
+ table->entries[i].clk, false, &dividers);
+ if (ret)
+ return ret;
+ pi->acp_level[i].Divider = (u8)dividers.post_div;
+
+ pi->acp_level_count++;
+ }
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, AcpLevelCount),
+ (u8 *)&pi->acp_level_count,
+ sizeof(u8),
+ pi->sram_end);
+ if (ret)
+ return ret;
+
+ pi->acp_interval = 1;
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, ACPInterval),
+ (u8 *)&pi->acp_interval,
+ sizeof(u8),
+ pi->sram_end);
+ if (ret)
+ return ret;
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, AcpLevel),
+ (u8 *)&pi->acp_level,
+ sizeof(SMU7_Fusion_ExtClkLevel) * SMU7_MAX_LEVELS_ACP,
+ pi->sram_end);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static void kv_calculate_dfs_bypass_settings(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u32 i;
+ struct amdgpu_clock_voltage_dependency_table *table =
+ &adev->pm.dpm.dyn_state.vddc_dependency_on_sclk;
+
+ if (table && table->count) {
+ for (i = 0; i < pi->graphics_dpm_level_count; i++) {
+ if (pi->caps_enable_dfs_bypass) {
+ if (kv_get_clock_difference(table->entries[i].clk, 40000) < 200)
+ pi->graphics_level[i].ClkBypassCntl = 3;
+ else if (kv_get_clock_difference(table->entries[i].clk, 30000) < 200)
+ pi->graphics_level[i].ClkBypassCntl = 2;
+ else if (kv_get_clock_difference(table->entries[i].clk, 26600) < 200)
+ pi->graphics_level[i].ClkBypassCntl = 7;
+ else if (kv_get_clock_difference(table->entries[i].clk , 20000) < 200)
+ pi->graphics_level[i].ClkBypassCntl = 6;
+ else if (kv_get_clock_difference(table->entries[i].clk , 10000) < 200)
+ pi->graphics_level[i].ClkBypassCntl = 8;
+ else
+ pi->graphics_level[i].ClkBypassCntl = 0;
+ } else {
+ pi->graphics_level[i].ClkBypassCntl = 0;
+ }
+ }
+ } else {
+ struct sumo_sclk_voltage_mapping_table *table =
+ &pi->sys_info.sclk_voltage_mapping_table;
+ for (i = 0; i < pi->graphics_dpm_level_count; i++) {
+ if (pi->caps_enable_dfs_bypass) {
+ if (kv_get_clock_difference(table->entries[i].sclk_frequency, 40000) < 200)
+ pi->graphics_level[i].ClkBypassCntl = 3;
+ else if (kv_get_clock_difference(table->entries[i].sclk_frequency, 30000) < 200)
+ pi->graphics_level[i].ClkBypassCntl = 2;
+ else if (kv_get_clock_difference(table->entries[i].sclk_frequency, 26600) < 200)
+ pi->graphics_level[i].ClkBypassCntl = 7;
+ else if (kv_get_clock_difference(table->entries[i].sclk_frequency, 20000) < 200)
+ pi->graphics_level[i].ClkBypassCntl = 6;
+ else if (kv_get_clock_difference(table->entries[i].sclk_frequency, 10000) < 200)
+ pi->graphics_level[i].ClkBypassCntl = 8;
+ else
+ pi->graphics_level[i].ClkBypassCntl = 0;
+ } else {
+ pi->graphics_level[i].ClkBypassCntl = 0;
+ }
+ }
+ }
+}
+
+static int kv_enable_ulv(struct amdgpu_device *adev, bool enable)
+{
+ return amdgpu_kv_notify_message_to_smu(adev, enable ?
+ PPSMC_MSG_EnableULV : PPSMC_MSG_DisableULV);
+}
+
+static void kv_reset_acp_boot_level(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ pi->acp_boot_level = 0xff;
+}
+
+static void kv_update_current_ps(struct amdgpu_device *adev,
+ struct amdgpu_ps *rps)
+{
+ struct kv_ps *new_ps = kv_get_ps(rps);
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ pi->current_rps = *rps;
+ pi->current_ps = *new_ps;
+ pi->current_rps.ps_priv = &pi->current_ps;
+ adev->pm.dpm.current_ps = &pi->current_rps;
+}
+
+static void kv_update_requested_ps(struct amdgpu_device *adev,
+ struct amdgpu_ps *rps)
+{
+ struct kv_ps *new_ps = kv_get_ps(rps);
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ pi->requested_rps = *rps;
+ pi->requested_ps = *new_ps;
+ pi->requested_rps.ps_priv = &pi->requested_ps;
+ adev->pm.dpm.requested_ps = &pi->requested_rps;
+}
+
+static void kv_dpm_enable_bapm(void *handle, bool enable)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct kv_power_info *pi = kv_get_pi(adev);
+ int ret;
+
+ if (pi->bapm_enable) {
+ ret = amdgpu_kv_smc_bapm_enable(adev, enable);
+ if (ret)
+ DRM_ERROR("amdgpu_kv_smc_bapm_enable failed\n");
+ }
+}
+
+static int kv_dpm_enable(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ int ret;
+
+ ret = kv_process_firmware_header(adev);
+ if (ret) {
+ DRM_ERROR("kv_process_firmware_header failed\n");
+ return ret;
+ }
+ kv_init_fps_limits(adev);
+ kv_init_graphics_levels(adev);
+ ret = kv_program_bootup_state(adev);
+ if (ret) {
+ DRM_ERROR("kv_program_bootup_state failed\n");
+ return ret;
+ }
+ kv_calculate_dfs_bypass_settings(adev);
+ ret = kv_upload_dpm_settings(adev);
+ if (ret) {
+ DRM_ERROR("kv_upload_dpm_settings failed\n");
+ return ret;
+ }
+ ret = kv_populate_uvd_table(adev);
+ if (ret) {
+ DRM_ERROR("kv_populate_uvd_table failed\n");
+ return ret;
+ }
+ ret = kv_populate_vce_table(adev);
+ if (ret) {
+ DRM_ERROR("kv_populate_vce_table failed\n");
+ return ret;
+ }
+ ret = kv_populate_samu_table(adev);
+ if (ret) {
+ DRM_ERROR("kv_populate_samu_table failed\n");
+ return ret;
+ }
+ ret = kv_populate_acp_table(adev);
+ if (ret) {
+ DRM_ERROR("kv_populate_acp_table failed\n");
+ return ret;
+ }
+ kv_program_vc(adev);
+#if 0
+ kv_initialize_hardware_cac_manager(adev);
+#endif
+ kv_start_am(adev);
+ if (pi->enable_auto_thermal_throttling) {
+ ret = kv_enable_auto_thermal_throttling(adev);
+ if (ret) {
+ DRM_ERROR("kv_enable_auto_thermal_throttling failed\n");
+ return ret;
+ }
+ }
+ ret = kv_enable_dpm_voltage_scaling(adev);
+ if (ret) {
+ DRM_ERROR("kv_enable_dpm_voltage_scaling failed\n");
+ return ret;
+ }
+ ret = kv_set_dpm_interval(adev);
+ if (ret) {
+ DRM_ERROR("kv_set_dpm_interval failed\n");
+ return ret;
+ }
+ ret = kv_set_dpm_boot_state(adev);
+ if (ret) {
+ DRM_ERROR("kv_set_dpm_boot_state failed\n");
+ return ret;
+ }
+ ret = kv_enable_ulv(adev, true);
+ if (ret) {
+ DRM_ERROR("kv_enable_ulv failed\n");
+ return ret;
+ }
+ kv_start_dpm(adev);
+ ret = kv_enable_didt(adev, true);
+ if (ret) {
+ DRM_ERROR("kv_enable_didt failed\n");
+ return ret;
+ }
+ ret = kv_enable_smc_cac(adev, true);
+ if (ret) {
+ DRM_ERROR("kv_enable_smc_cac failed\n");
+ return ret;
+ }
+
+ kv_reset_acp_boot_level(adev);
+
+ ret = amdgpu_kv_smc_bapm_enable(adev, false);
+ if (ret) {
+ DRM_ERROR("amdgpu_kv_smc_bapm_enable failed\n");
+ return ret;
+ }
+
+ if (adev->irq.installed &&
+ amdgpu_is_internal_thermal_sensor(adev->pm.int_thermal_type)) {
+ ret = kv_set_thermal_temperature_range(adev, KV_TEMP_RANGE_MIN, KV_TEMP_RANGE_MAX);
+ if (ret) {
+ DRM_ERROR("kv_set_thermal_temperature_range failed\n");
+ return ret;
+ }
+ amdgpu_irq_get(adev, &adev->pm.dpm.thermal.irq,
+ AMDGPU_THERMAL_IRQ_LOW_TO_HIGH);
+ amdgpu_irq_get(adev, &adev->pm.dpm.thermal.irq,
+ AMDGPU_THERMAL_IRQ_HIGH_TO_LOW);
+ }
+
+ return ret;
+}
+
+static void kv_dpm_disable(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ amdgpu_irq_put(adev, &adev->pm.dpm.thermal.irq,
+ AMDGPU_THERMAL_IRQ_LOW_TO_HIGH);
+ amdgpu_irq_put(adev, &adev->pm.dpm.thermal.irq,
+ AMDGPU_THERMAL_IRQ_HIGH_TO_LOW);
+
+ amdgpu_kv_smc_bapm_enable(adev, false);
+
+ if (adev->asic_type == CHIP_MULLINS)
+ kv_enable_nb_dpm(adev, false);
+
+ /* powerup blocks */
+ kv_dpm_powergate_acp(adev, false);
+ kv_dpm_powergate_samu(adev, false);
+ if (pi->caps_vce_pg) /* power on the VCE block */
+ amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_VCEPowerON);
+ if (pi->caps_uvd_pg) /* power on the UVD block */
+ amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_UVDPowerON);
+
+ kv_enable_smc_cac(adev, false);
+ kv_enable_didt(adev, false);
+ kv_clear_vc(adev);
+ kv_stop_dpm(adev);
+ kv_enable_ulv(adev, false);
+ kv_reset_am(adev);
+
+ kv_update_current_ps(adev, adev->pm.dpm.boot_ps);
+}
+
+#if 0
+static int kv_write_smc_soft_register(struct amdgpu_device *adev,
+ u16 reg_offset, u32 value)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ return amdgpu_kv_copy_bytes_to_smc(adev, pi->soft_regs_start + reg_offset,
+ (u8 *)&value, sizeof(u16), pi->sram_end);
+}
+
+static int kv_read_smc_soft_register(struct amdgpu_device *adev,
+ u16 reg_offset, u32 *value)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ return amdgpu_kv_read_smc_sram_dword(adev, pi->soft_regs_start + reg_offset,
+ value, pi->sram_end);
+}
+#endif
+
+static void kv_init_sclk_t(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ pi->low_sclk_interrupt_t = 0;
+}
+
+static int kv_init_fps_limits(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ int ret = 0;
+
+ if (pi->caps_fps) {
+ u16 tmp;
+
+ tmp = 45;
+ pi->fps_high_t = cpu_to_be16(tmp);
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, FpsHighT),
+ (u8 *)&pi->fps_high_t,
+ sizeof(u16), pi->sram_end);
+
+ tmp = 30;
+ pi->fps_low_t = cpu_to_be16(tmp);
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, FpsLowT),
+ (u8 *)&pi->fps_low_t,
+ sizeof(u16), pi->sram_end);
+
+ }
+ return ret;
+}
+
+static void kv_init_powergate_state(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ pi->uvd_power_gated = false;
+ pi->vce_power_gated = false;
+ pi->samu_power_gated = false;
+ pi->acp_power_gated = false;
+
+}
+
+static int kv_enable_uvd_dpm(struct amdgpu_device *adev, bool enable)
+{
+ return amdgpu_kv_notify_message_to_smu(adev, enable ?
+ PPSMC_MSG_UVDDPM_Enable : PPSMC_MSG_UVDDPM_Disable);
+}
+
+static int kv_enable_vce_dpm(struct amdgpu_device *adev, bool enable)
+{
+ return amdgpu_kv_notify_message_to_smu(adev, enable ?
+ PPSMC_MSG_VCEDPM_Enable : PPSMC_MSG_VCEDPM_Disable);
+}
+
+static int kv_enable_samu_dpm(struct amdgpu_device *adev, bool enable)
+{
+ return amdgpu_kv_notify_message_to_smu(adev, enable ?
+ PPSMC_MSG_SAMUDPM_Enable : PPSMC_MSG_SAMUDPM_Disable);
+}
+
+static int kv_enable_acp_dpm(struct amdgpu_device *adev, bool enable)
+{
+ return amdgpu_kv_notify_message_to_smu(adev, enable ?
+ PPSMC_MSG_ACPDPM_Enable : PPSMC_MSG_ACPDPM_Disable);
+}
+
+static int kv_update_uvd_dpm(struct amdgpu_device *adev, bool gate)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ struct amdgpu_uvd_clock_voltage_dependency_table *table =
+ &adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table;
+ int ret;
+ u32 mask;
+
+ if (!gate) {
+ if (table->count)
+ pi->uvd_boot_level = table->count - 1;
+ else
+ pi->uvd_boot_level = 0;
+
+ if (!pi->caps_uvd_dpm || pi->caps_stable_p_state) {
+ mask = 1 << pi->uvd_boot_level;
+ } else {
+ mask = 0x1f;
+ }
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, UvdBootLevel),
+ (uint8_t *)&pi->uvd_boot_level,
+ sizeof(u8), pi->sram_end);
+ if (ret)
+ return ret;
+
+ amdgpu_kv_send_msg_to_smc_with_parameter(adev,
+ PPSMC_MSG_UVDDPM_SetEnabledMask,
+ mask);
+ }
+
+ return kv_enable_uvd_dpm(adev, !gate);
+}
+
+static u8 kv_get_vce_boot_level(struct amdgpu_device *adev, u32 evclk)
+{
+ u8 i;
+ struct amdgpu_vce_clock_voltage_dependency_table *table =
+ &adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table;
+
+ for (i = 0; i < table->count; i++) {
+ if (table->entries[i].evclk >= evclk)
+ break;
+ }
+
+ return i;
+}
+
+static int kv_update_vce_dpm(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_new_state,
+ struct amdgpu_ps *amdgpu_current_state)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ struct amdgpu_vce_clock_voltage_dependency_table *table =
+ &adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table;
+ int ret;
+
+ if (amdgpu_new_state->evclk > 0 && amdgpu_current_state->evclk == 0) {
+ if (pi->caps_stable_p_state)
+ pi->vce_boot_level = table->count - 1;
+ else
+ pi->vce_boot_level = kv_get_vce_boot_level(adev, amdgpu_new_state->evclk);
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, VceBootLevel),
+ (u8 *)&pi->vce_boot_level,
+ sizeof(u8),
+ pi->sram_end);
+ if (ret)
+ return ret;
+
+ if (pi->caps_stable_p_state)
+ amdgpu_kv_send_msg_to_smc_with_parameter(adev,
+ PPSMC_MSG_VCEDPM_SetEnabledMask,
+ (1 << pi->vce_boot_level));
+ kv_enable_vce_dpm(adev, true);
+ } else if (amdgpu_new_state->evclk == 0 && amdgpu_current_state->evclk > 0) {
+ kv_enable_vce_dpm(adev, false);
+ }
+
+ return 0;
+}
+
+static int kv_update_samu_dpm(struct amdgpu_device *adev, bool gate)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ struct amdgpu_clock_voltage_dependency_table *table =
+ &adev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table;
+ int ret;
+
+ if (!gate) {
+ if (pi->caps_stable_p_state)
+ pi->samu_boot_level = table->count - 1;
+ else
+ pi->samu_boot_level = 0;
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, SamuBootLevel),
+ (u8 *)&pi->samu_boot_level,
+ sizeof(u8),
+ pi->sram_end);
+ if (ret)
+ return ret;
+
+ if (pi->caps_stable_p_state)
+ amdgpu_kv_send_msg_to_smc_with_parameter(adev,
+ PPSMC_MSG_SAMUDPM_SetEnabledMask,
+ (1 << pi->samu_boot_level));
+ }
+
+ return kv_enable_samu_dpm(adev, !gate);
+}
+
+static u8 kv_get_acp_boot_level(struct amdgpu_device *adev)
+{
+ u8 i;
+ struct amdgpu_clock_voltage_dependency_table *table =
+ &adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table;
+
+ for (i = 0; i < table->count; i++) {
+ if (table->entries[i].clk >= 0) /* XXX */
+ break;
+ }
+
+ if (i >= table->count)
+ i = table->count - 1;
+
+ return i;
+}
+
+static void kv_update_acp_boot_level(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u8 acp_boot_level;
+
+ if (!pi->caps_stable_p_state) {
+ acp_boot_level = kv_get_acp_boot_level(adev);
+ if (acp_boot_level != pi->acp_boot_level) {
+ pi->acp_boot_level = acp_boot_level;
+ amdgpu_kv_send_msg_to_smc_with_parameter(adev,
+ PPSMC_MSG_ACPDPM_SetEnabledMask,
+ (1 << pi->acp_boot_level));
+ }
+ }
+}
+
+static int kv_update_acp_dpm(struct amdgpu_device *adev, bool gate)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ struct amdgpu_clock_voltage_dependency_table *table =
+ &adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table;
+ int ret;
+
+ if (!gate) {
+ if (pi->caps_stable_p_state)
+ pi->acp_boot_level = table->count - 1;
+ else
+ pi->acp_boot_level = kv_get_acp_boot_level(adev);
+
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, AcpBootLevel),
+ (u8 *)&pi->acp_boot_level,
+ sizeof(u8),
+ pi->sram_end);
+ if (ret)
+ return ret;
+
+ if (pi->caps_stable_p_state)
+ amdgpu_kv_send_msg_to_smc_with_parameter(adev,
+ PPSMC_MSG_ACPDPM_SetEnabledMask,
+ (1 << pi->acp_boot_level));
+ }
+
+ return kv_enable_acp_dpm(adev, !gate);
+}
+
+static void kv_dpm_powergate_uvd(void *handle, bool gate)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct kv_power_info *pi = kv_get_pi(adev);
+ int ret;
+
+ pi->uvd_power_gated = gate;
+
+ if (gate) {
+ /* stop the UVD block */
+ ret = amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_UVD,
+ AMD_PG_STATE_GATE);
+ kv_update_uvd_dpm(adev, gate);
+ if (pi->caps_uvd_pg)
+ /* power off the UVD block */
+ amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_UVDPowerOFF);
+ } else {
+ if (pi->caps_uvd_pg)
+ /* power on the UVD block */
+ amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_UVDPowerON);
+ /* re-init the UVD block */
+ kv_update_uvd_dpm(adev, gate);
+
+ ret = amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_UVD,
+ AMD_PG_STATE_UNGATE);
+ }
+}
+
+static void kv_dpm_powergate_vce(void *handle, bool gate)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct kv_power_info *pi = kv_get_pi(adev);
+ int ret;
+
+ pi->vce_power_gated = gate;
+
+ if (gate) {
+ /* stop the VCE block */
+ ret = amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VCE,
+ AMD_PG_STATE_GATE);
+ kv_enable_vce_dpm(adev, false);
+ if (pi->caps_vce_pg) /* power off the VCE block */
+ amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_VCEPowerOFF);
+ } else {
+ if (pi->caps_vce_pg) /* power on the VCE block */
+ amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_VCEPowerON);
+ kv_enable_vce_dpm(adev, true);
+ /* re-init the VCE block */
+ ret = amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VCE,
+ AMD_PG_STATE_UNGATE);
+ }
+}
+
+
+static void kv_dpm_powergate_samu(struct amdgpu_device *adev, bool gate)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ if (pi->samu_power_gated == gate)
+ return;
+
+ pi->samu_power_gated = gate;
+
+ if (gate) {
+ kv_update_samu_dpm(adev, true);
+ if (pi->caps_samu_pg)
+ amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_SAMPowerOFF);
+ } else {
+ if (pi->caps_samu_pg)
+ amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_SAMPowerON);
+ kv_update_samu_dpm(adev, false);
+ }
+}
+
+static void kv_dpm_powergate_acp(struct amdgpu_device *adev, bool gate)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ if (pi->acp_power_gated == gate)
+ return;
+
+ if (adev->asic_type == CHIP_KABINI || adev->asic_type == CHIP_MULLINS)
+ return;
+
+ pi->acp_power_gated = gate;
+
+ if (gate) {
+ kv_update_acp_dpm(adev, true);
+ if (pi->caps_acp_pg)
+ amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_ACPPowerOFF);
+ } else {
+ if (pi->caps_acp_pg)
+ amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_ACPPowerON);
+ kv_update_acp_dpm(adev, false);
+ }
+}
+
+static void kv_set_valid_clock_range(struct amdgpu_device *adev,
+ struct amdgpu_ps *new_rps)
+{
+ struct kv_ps *new_ps = kv_get_ps(new_rps);
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u32 i;
+ struct amdgpu_clock_voltage_dependency_table *table =
+ &adev->pm.dpm.dyn_state.vddc_dependency_on_sclk;
+
+ if (table && table->count) {
+ for (i = 0; i < pi->graphics_dpm_level_count; i++) {
+ if ((table->entries[i].clk >= new_ps->levels[0].sclk) ||
+ (i == (pi->graphics_dpm_level_count - 1))) {
+ pi->lowest_valid = i;
+ break;
+ }
+ }
+
+ for (i = pi->graphics_dpm_level_count - 1; i > 0; i--) {
+ if (table->entries[i].clk <= new_ps->levels[new_ps->num_levels - 1].sclk)
+ break;
+ }
+ pi->highest_valid = i;
+
+ if (pi->lowest_valid > pi->highest_valid) {
+ if ((new_ps->levels[0].sclk - table->entries[pi->highest_valid].clk) >
+ (table->entries[pi->lowest_valid].clk - new_ps->levels[new_ps->num_levels - 1].sclk))
+ pi->highest_valid = pi->lowest_valid;
+ else
+ pi->lowest_valid = pi->highest_valid;
+ }
+ } else {
+ struct sumo_sclk_voltage_mapping_table *table =
+ &pi->sys_info.sclk_voltage_mapping_table;
+
+ for (i = 0; i < (int)pi->graphics_dpm_level_count; i++) {
+ if (table->entries[i].sclk_frequency >= new_ps->levels[0].sclk ||
+ i == (int)(pi->graphics_dpm_level_count - 1)) {
+ pi->lowest_valid = i;
+ break;
+ }
+ }
+
+ for (i = pi->graphics_dpm_level_count - 1; i > 0; i--) {
+ if (table->entries[i].sclk_frequency <=
+ new_ps->levels[new_ps->num_levels - 1].sclk)
+ break;
+ }
+ pi->highest_valid = i;
+
+ if (pi->lowest_valid > pi->highest_valid) {
+ if ((new_ps->levels[0].sclk -
+ table->entries[pi->highest_valid].sclk_frequency) >
+ (table->entries[pi->lowest_valid].sclk_frequency -
+ new_ps->levels[new_ps->num_levels -1].sclk))
+ pi->highest_valid = pi->lowest_valid;
+ else
+ pi->lowest_valid = pi->highest_valid;
+ }
+ }
+}
+
+static int kv_update_dfs_bypass_settings(struct amdgpu_device *adev,
+ struct amdgpu_ps *new_rps)
+{
+ struct kv_ps *new_ps = kv_get_ps(new_rps);
+ struct kv_power_info *pi = kv_get_pi(adev);
+ int ret = 0;
+ u8 clk_bypass_cntl;
+
+ if (pi->caps_enable_dfs_bypass) {
+ clk_bypass_cntl = new_ps->need_dfs_bypass ?
+ pi->graphics_level[pi->graphics_boot_level].ClkBypassCntl : 0;
+ ret = amdgpu_kv_copy_bytes_to_smc(adev,
+ (pi->dpm_table_start +
+ offsetof(SMU7_Fusion_DpmTable, GraphicsLevel) +
+ (pi->graphics_boot_level * sizeof(SMU7_Fusion_GraphicsLevel)) +
+ offsetof(SMU7_Fusion_GraphicsLevel, ClkBypassCntl)),
+ &clk_bypass_cntl,
+ sizeof(u8), pi->sram_end);
+ }
+
+ return ret;
+}
+
+static int kv_enable_nb_dpm(struct amdgpu_device *adev,
+ bool enable)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ int ret = 0;
+
+ if (enable) {
+ if (pi->enable_nb_dpm && !pi->nb_dpm_enabled) {
+ ret = amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_NBDPM_Enable);
+ if (ret == 0)
+ pi->nb_dpm_enabled = true;
+ }
+ } else {
+ if (pi->enable_nb_dpm && pi->nb_dpm_enabled) {
+ ret = amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_NBDPM_Disable);
+ if (ret == 0)
+ pi->nb_dpm_enabled = false;
+ }
+ }
+
+ return ret;
+}
+
+static int kv_dpm_force_performance_level(void *handle,
+ enum amd_dpm_forced_level level)
+{
+ int ret;
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ if (level == AMD_DPM_FORCED_LEVEL_HIGH) {
+ ret = kv_force_dpm_highest(adev);
+ if (ret)
+ return ret;
+ } else if (level == AMD_DPM_FORCED_LEVEL_LOW) {
+ ret = kv_force_dpm_lowest(adev);
+ if (ret)
+ return ret;
+ } else if (level == AMD_DPM_FORCED_LEVEL_AUTO) {
+ ret = kv_unforce_levels(adev);
+ if (ret)
+ return ret;
+ }
+
+ adev->pm.dpm.forced_level = level;
+
+ return 0;
+}
+
+static int kv_dpm_pre_set_power_state(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct kv_power_info *pi = kv_get_pi(adev);
+ struct amdgpu_ps requested_ps = *adev->pm.dpm.requested_ps;
+ struct amdgpu_ps *new_ps = &requested_ps;
+
+ kv_update_requested_ps(adev, new_ps);
+
+ kv_apply_state_adjust_rules(adev,
+ &pi->requested_rps,
+ &pi->current_rps);
+
+ return 0;
+}
+
+static int kv_dpm_set_power_state(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct kv_power_info *pi = kv_get_pi(adev);
+ struct amdgpu_ps *new_ps = &pi->requested_rps;
+ struct amdgpu_ps *old_ps = &pi->current_rps;
+ int ret;
+
+ if (pi->bapm_enable) {
+ ret = amdgpu_kv_smc_bapm_enable(adev, adev->pm.ac_power);
+ if (ret) {
+ DRM_ERROR("amdgpu_kv_smc_bapm_enable failed\n");
+ return ret;
+ }
+ }
+
+ if (adev->asic_type == CHIP_KABINI || adev->asic_type == CHIP_MULLINS) {
+ if (pi->enable_dpm) {
+ kv_set_valid_clock_range(adev, new_ps);
+ kv_update_dfs_bypass_settings(adev, new_ps);
+ ret = kv_calculate_ds_divider(adev);
+ if (ret) {
+ DRM_ERROR("kv_calculate_ds_divider failed\n");
+ return ret;
+ }
+ kv_calculate_nbps_level_settings(adev);
+ kv_calculate_dpm_settings(adev);
+ kv_force_lowest_valid(adev);
+ kv_enable_new_levels(adev);
+ kv_upload_dpm_settings(adev);
+ kv_program_nbps_index_settings(adev, new_ps);
+ kv_unforce_levels(adev);
+ kv_set_enabled_levels(adev);
+ kv_force_lowest_valid(adev);
+ kv_unforce_levels(adev);
+
+ ret = kv_update_vce_dpm(adev, new_ps, old_ps);
+ if (ret) {
+ DRM_ERROR("kv_update_vce_dpm failed\n");
+ return ret;
+ }
+ kv_update_sclk_t(adev);
+ if (adev->asic_type == CHIP_MULLINS)
+ kv_enable_nb_dpm(adev, true);
+ }
+ } else {
+ if (pi->enable_dpm) {
+ kv_set_valid_clock_range(adev, new_ps);
+ kv_update_dfs_bypass_settings(adev, new_ps);
+ ret = kv_calculate_ds_divider(adev);
+ if (ret) {
+ DRM_ERROR("kv_calculate_ds_divider failed\n");
+ return ret;
+ }
+ kv_calculate_nbps_level_settings(adev);
+ kv_calculate_dpm_settings(adev);
+ kv_freeze_sclk_dpm(adev, true);
+ kv_upload_dpm_settings(adev);
+ kv_program_nbps_index_settings(adev, new_ps);
+ kv_freeze_sclk_dpm(adev, false);
+ kv_set_enabled_levels(adev);
+ ret = kv_update_vce_dpm(adev, new_ps, old_ps);
+ if (ret) {
+ DRM_ERROR("kv_update_vce_dpm failed\n");
+ return ret;
+ }
+ kv_update_acp_boot_level(adev);
+ kv_update_sclk_t(adev);
+ kv_enable_nb_dpm(adev, true);
+ }
+ }
+
+ return 0;
+}
+
+static void kv_dpm_post_set_power_state(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct kv_power_info *pi = kv_get_pi(adev);
+ struct amdgpu_ps *new_ps = &pi->requested_rps;
+
+ kv_update_current_ps(adev, new_ps);
+}
+
+static void kv_dpm_setup_asic(struct amdgpu_device *adev)
+{
+ sumo_take_smu_control(adev, true);
+ kv_init_powergate_state(adev);
+ kv_init_sclk_t(adev);
+}
+
+#if 0
+static void kv_dpm_reset_asic(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ if (adev->asic_type == CHIP_KABINI || adev->asic_type == CHIP_MULLINS) {
+ kv_force_lowest_valid(adev);
+ kv_init_graphics_levels(adev);
+ kv_program_bootup_state(adev);
+ kv_upload_dpm_settings(adev);
+ kv_force_lowest_valid(adev);
+ kv_unforce_levels(adev);
+ } else {
+ kv_init_graphics_levels(adev);
+ kv_program_bootup_state(adev);
+ kv_freeze_sclk_dpm(adev, true);
+ kv_upload_dpm_settings(adev);
+ kv_freeze_sclk_dpm(adev, false);
+ kv_set_enabled_level(adev, pi->graphics_boot_level);
+ }
+}
+#endif
+
+static void kv_construct_max_power_limits_table(struct amdgpu_device *adev,
+ struct amdgpu_clock_and_voltage_limits *table)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ if (pi->sys_info.sclk_voltage_mapping_table.num_max_dpm_entries > 0) {
+ int idx = pi->sys_info.sclk_voltage_mapping_table.num_max_dpm_entries - 1;
+ table->sclk =
+ pi->sys_info.sclk_voltage_mapping_table.entries[idx].sclk_frequency;
+ table->vddc =
+ kv_convert_2bit_index_to_voltage(adev,
+ pi->sys_info.sclk_voltage_mapping_table.entries[idx].vid_2bit);
+ }
+
+ table->mclk = pi->sys_info.nbp_memory_clock[0];
+}
+
+static void kv_patch_voltage_values(struct amdgpu_device *adev)
+{
+ int i;
+ struct amdgpu_uvd_clock_voltage_dependency_table *uvd_table =
+ &adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table;
+ struct amdgpu_vce_clock_voltage_dependency_table *vce_table =
+ &adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table;
+ struct amdgpu_clock_voltage_dependency_table *samu_table =
+ &adev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table;
+ struct amdgpu_clock_voltage_dependency_table *acp_table =
+ &adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table;
+
+ if (uvd_table->count) {
+ for (i = 0; i < uvd_table->count; i++)
+ uvd_table->entries[i].v =
+ kv_convert_8bit_index_to_voltage(adev,
+ uvd_table->entries[i].v);
+ }
+
+ if (vce_table->count) {
+ for (i = 0; i < vce_table->count; i++)
+ vce_table->entries[i].v =
+ kv_convert_8bit_index_to_voltage(adev,
+ vce_table->entries[i].v);
+ }
+
+ if (samu_table->count) {
+ for (i = 0; i < samu_table->count; i++)
+ samu_table->entries[i].v =
+ kv_convert_8bit_index_to_voltage(adev,
+ samu_table->entries[i].v);
+ }
+
+ if (acp_table->count) {
+ for (i = 0; i < acp_table->count; i++)
+ acp_table->entries[i].v =
+ kv_convert_8bit_index_to_voltage(adev,
+ acp_table->entries[i].v);
+ }
+
+}
+
+static void kv_construct_boot_state(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ pi->boot_pl.sclk = pi->sys_info.bootup_sclk;
+ pi->boot_pl.vddc_index = pi->sys_info.bootup_nb_voltage_index;
+ pi->boot_pl.ds_divider_index = 0;
+ pi->boot_pl.ss_divider_index = 0;
+ pi->boot_pl.allow_gnb_slow = 1;
+ pi->boot_pl.force_nbp_state = 0;
+ pi->boot_pl.display_wm = 0;
+ pi->boot_pl.vce_wm = 0;
+}
+
+static int kv_force_dpm_highest(struct amdgpu_device *adev)
+{
+ int ret;
+ u32 enable_mask, i;
+
+ ret = amdgpu_kv_dpm_get_enable_mask(adev, &enable_mask);
+ if (ret)
+ return ret;
+
+ for (i = SMU7_MAX_LEVELS_GRAPHICS - 1; i > 0; i--) {
+ if (enable_mask & (1 << i))
+ break;
+ }
+
+ if (adev->asic_type == CHIP_KABINI || adev->asic_type == CHIP_MULLINS)
+ return amdgpu_kv_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_DPM_ForceState, i);
+ else
+ return kv_set_enabled_level(adev, i);
+}
+
+static int kv_force_dpm_lowest(struct amdgpu_device *adev)
+{
+ int ret;
+ u32 enable_mask, i;
+
+ ret = amdgpu_kv_dpm_get_enable_mask(adev, &enable_mask);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < SMU7_MAX_LEVELS_GRAPHICS; i++) {
+ if (enable_mask & (1 << i))
+ break;
+ }
+
+ if (adev->asic_type == CHIP_KABINI || adev->asic_type == CHIP_MULLINS)
+ return amdgpu_kv_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_DPM_ForceState, i);
+ else
+ return kv_set_enabled_level(adev, i);
+}
+
+static u8 kv_get_sleep_divider_id_from_clock(struct amdgpu_device *adev,
+ u32 sclk, u32 min_sclk_in_sr)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u32 i;
+ u32 temp;
+ u32 min = max(min_sclk_in_sr, (u32)KV_MINIMUM_ENGINE_CLOCK);
+
+ if (sclk < min)
+ return 0;
+
+ if (!pi->caps_sclk_ds)
+ return 0;
+
+ for (i = KV_MAX_DEEPSLEEP_DIVIDER_ID; i > 0; i--) {
+ temp = sclk >> i;
+ if (temp >= min)
+ break;
+ }
+
+ return (u8)i;
+}
+
+static int kv_get_high_voltage_limit(struct amdgpu_device *adev, int *limit)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ struct amdgpu_clock_voltage_dependency_table *table =
+ &adev->pm.dpm.dyn_state.vddc_dependency_on_sclk;
+ int i;
+
+ if (table && table->count) {
+ for (i = table->count - 1; i >= 0; i--) {
+ if (pi->high_voltage_t &&
+ (kv_convert_8bit_index_to_voltage(adev, table->entries[i].v) <=
+ pi->high_voltage_t)) {
+ *limit = i;
+ return 0;
+ }
+ }
+ } else {
+ struct sumo_sclk_voltage_mapping_table *table =
+ &pi->sys_info.sclk_voltage_mapping_table;
+
+ for (i = table->num_max_dpm_entries - 1; i >= 0; i--) {
+ if (pi->high_voltage_t &&
+ (kv_convert_2bit_index_to_voltage(adev, table->entries[i].vid_2bit) <=
+ pi->high_voltage_t)) {
+ *limit = i;
+ return 0;
+ }
+ }
+ }
+
+ *limit = 0;
+ return 0;
+}
+
+static void kv_apply_state_adjust_rules(struct amdgpu_device *adev,
+ struct amdgpu_ps *new_rps,
+ struct amdgpu_ps *old_rps)
+{
+ struct kv_ps *ps = kv_get_ps(new_rps);
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u32 min_sclk = 10000; /* ??? */
+ u32 sclk, mclk = 0;
+ int i, limit;
+ bool force_high;
+ struct amdgpu_clock_voltage_dependency_table *table =
+ &adev->pm.dpm.dyn_state.vddc_dependency_on_sclk;
+ u32 stable_p_state_sclk = 0;
+ struct amdgpu_clock_and_voltage_limits *max_limits =
+ &adev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+
+ if (new_rps->vce_active) {
+ new_rps->evclk = adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].evclk;
+ new_rps->ecclk = adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].ecclk;
+ } else {
+ new_rps->evclk = 0;
+ new_rps->ecclk = 0;
+ }
+
+ mclk = max_limits->mclk;
+ sclk = min_sclk;
+
+ if (pi->caps_stable_p_state) {
+ stable_p_state_sclk = (max_limits->sclk * 75) / 100;
+
+ for (i = table->count - 1; i >= 0; i--) {
+ if (stable_p_state_sclk >= table->entries[i].clk) {
+ stable_p_state_sclk = table->entries[i].clk;
+ break;
+ }
+ }
+
+ if (i > 0)
+ stable_p_state_sclk = table->entries[0].clk;
+
+ sclk = stable_p_state_sclk;
+ }
+
+ if (new_rps->vce_active) {
+ if (sclk < adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].sclk)
+ sclk = adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].sclk;
+ }
+
+ ps->need_dfs_bypass = true;
+
+ for (i = 0; i < ps->num_levels; i++) {
+ if (ps->levels[i].sclk < sclk)
+ ps->levels[i].sclk = sclk;
+ }
+
+ if (table && table->count) {
+ for (i = 0; i < ps->num_levels; i++) {
+ if (pi->high_voltage_t &&
+ (pi->high_voltage_t <
+ kv_convert_8bit_index_to_voltage(adev, ps->levels[i].vddc_index))) {
+ kv_get_high_voltage_limit(adev, &limit);
+ ps->levels[i].sclk = table->entries[limit].clk;
+ }
+ }
+ } else {
+ struct sumo_sclk_voltage_mapping_table *table =
+ &pi->sys_info.sclk_voltage_mapping_table;
+
+ for (i = 0; i < ps->num_levels; i++) {
+ if (pi->high_voltage_t &&
+ (pi->high_voltage_t <
+ kv_convert_8bit_index_to_voltage(adev, ps->levels[i].vddc_index))) {
+ kv_get_high_voltage_limit(adev, &limit);
+ ps->levels[i].sclk = table->entries[limit].sclk_frequency;
+ }
+ }
+ }
+
+ if (pi->caps_stable_p_state) {
+ for (i = 0; i < ps->num_levels; i++) {
+ ps->levels[i].sclk = stable_p_state_sclk;
+ }
+ }
+
+ pi->video_start = new_rps->dclk || new_rps->vclk ||
+ new_rps->evclk || new_rps->ecclk;
+
+ if ((new_rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) ==
+ ATOM_PPLIB_CLASSIFICATION_UI_BATTERY)
+ pi->battery_state = true;
+ else
+ pi->battery_state = false;
+
+ if (adev->asic_type == CHIP_KABINI || adev->asic_type == CHIP_MULLINS) {
+ ps->dpm0_pg_nb_ps_lo = 0x1;
+ ps->dpm0_pg_nb_ps_hi = 0x0;
+ ps->dpmx_nb_ps_lo = 0x1;
+ ps->dpmx_nb_ps_hi = 0x0;
+ } else {
+ ps->dpm0_pg_nb_ps_lo = 0x3;
+ ps->dpm0_pg_nb_ps_hi = 0x0;
+ ps->dpmx_nb_ps_lo = 0x3;
+ ps->dpmx_nb_ps_hi = 0x0;
+
+ if (pi->sys_info.nb_dpm_enable) {
+ force_high = (mclk >= pi->sys_info.nbp_memory_clock[3]) ||
+ pi->video_start || (adev->pm.dpm.new_active_crtc_count >= 3) ||
+ pi->disable_nb_ps3_in_battery;
+ ps->dpm0_pg_nb_ps_lo = force_high ? 0x2 : 0x3;
+ ps->dpm0_pg_nb_ps_hi = 0x2;
+ ps->dpmx_nb_ps_lo = force_high ? 0x2 : 0x3;
+ ps->dpmx_nb_ps_hi = 0x2;
+ }
+ }
+}
+
+static void kv_dpm_power_level_enabled_for_throttle(struct amdgpu_device *adev,
+ u32 index, bool enable)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ pi->graphics_level[index].EnabledForThrottle = enable ? 1 : 0;
+}
+
+static int kv_calculate_ds_divider(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u32 sclk_in_sr = 10000; /* ??? */
+ u32 i;
+
+ if (pi->lowest_valid > pi->highest_valid)
+ return -EINVAL;
+
+ for (i = pi->lowest_valid; i <= pi->highest_valid; i++) {
+ pi->graphics_level[i].DeepSleepDivId =
+ kv_get_sleep_divider_id_from_clock(adev,
+ be32_to_cpu(pi->graphics_level[i].SclkFrequency),
+ sclk_in_sr);
+ }
+ return 0;
+}
+
+static int kv_calculate_nbps_level_settings(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u32 i;
+ bool force_high;
+ struct amdgpu_clock_and_voltage_limits *max_limits =
+ &adev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+ u32 mclk = max_limits->mclk;
+
+ if (pi->lowest_valid > pi->highest_valid)
+ return -EINVAL;
+
+ if (adev->asic_type == CHIP_KABINI || adev->asic_type == CHIP_MULLINS) {
+ for (i = pi->lowest_valid; i <= pi->highest_valid; i++) {
+ pi->graphics_level[i].GnbSlow = 1;
+ pi->graphics_level[i].ForceNbPs1 = 0;
+ pi->graphics_level[i].UpH = 0;
+ }
+
+ if (!pi->sys_info.nb_dpm_enable)
+ return 0;
+
+ force_high = ((mclk >= pi->sys_info.nbp_memory_clock[3]) ||
+ (adev->pm.dpm.new_active_crtc_count >= 3) || pi->video_start);
+
+ if (force_high) {
+ for (i = pi->lowest_valid; i <= pi->highest_valid; i++)
+ pi->graphics_level[i].GnbSlow = 0;
+ } else {
+ if (pi->battery_state)
+ pi->graphics_level[0].ForceNbPs1 = 1;
+
+ pi->graphics_level[1].GnbSlow = 0;
+ pi->graphics_level[2].GnbSlow = 0;
+ pi->graphics_level[3].GnbSlow = 0;
+ pi->graphics_level[4].GnbSlow = 0;
+ }
+ } else {
+ for (i = pi->lowest_valid; i <= pi->highest_valid; i++) {
+ pi->graphics_level[i].GnbSlow = 1;
+ pi->graphics_level[i].ForceNbPs1 = 0;
+ pi->graphics_level[i].UpH = 0;
+ }
+
+ if (pi->sys_info.nb_dpm_enable && pi->battery_state) {
+ pi->graphics_level[pi->lowest_valid].UpH = 0x28;
+ pi->graphics_level[pi->lowest_valid].GnbSlow = 0;
+ if (pi->lowest_valid != pi->highest_valid)
+ pi->graphics_level[pi->lowest_valid].ForceNbPs1 = 1;
+ }
+ }
+ return 0;
+}
+
+static int kv_calculate_dpm_settings(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u32 i;
+
+ if (pi->lowest_valid > pi->highest_valid)
+ return -EINVAL;
+
+ for (i = pi->lowest_valid; i <= pi->highest_valid; i++)
+ pi->graphics_level[i].DisplayWatermark = (i == pi->highest_valid) ? 1 : 0;
+
+ return 0;
+}
+
+static void kv_init_graphics_levels(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u32 i;
+ struct amdgpu_clock_voltage_dependency_table *table =
+ &adev->pm.dpm.dyn_state.vddc_dependency_on_sclk;
+
+ if (table && table->count) {
+ u32 vid_2bit;
+
+ pi->graphics_dpm_level_count = 0;
+ for (i = 0; i < table->count; i++) {
+ if (pi->high_voltage_t &&
+ (pi->high_voltage_t <
+ kv_convert_8bit_index_to_voltage(adev, table->entries[i].v)))
+ break;
+
+ kv_set_divider_value(adev, i, table->entries[i].clk);
+ vid_2bit = kv_convert_vid7_to_vid2(adev,
+ &pi->sys_info.vid_mapping_table,
+ table->entries[i].v);
+ kv_set_vid(adev, i, vid_2bit);
+ kv_set_at(adev, i, pi->at[i]);
+ kv_dpm_power_level_enabled_for_throttle(adev, i, true);
+ pi->graphics_dpm_level_count++;
+ }
+ } else {
+ struct sumo_sclk_voltage_mapping_table *table =
+ &pi->sys_info.sclk_voltage_mapping_table;
+
+ pi->graphics_dpm_level_count = 0;
+ for (i = 0; i < table->num_max_dpm_entries; i++) {
+ if (pi->high_voltage_t &&
+ pi->high_voltage_t <
+ kv_convert_2bit_index_to_voltage(adev, table->entries[i].vid_2bit))
+ break;
+
+ kv_set_divider_value(adev, i, table->entries[i].sclk_frequency);
+ kv_set_vid(adev, i, table->entries[i].vid_2bit);
+ kv_set_at(adev, i, pi->at[i]);
+ kv_dpm_power_level_enabled_for_throttle(adev, i, true);
+ pi->graphics_dpm_level_count++;
+ }
+ }
+
+ for (i = 0; i < SMU7_MAX_LEVELS_GRAPHICS; i++)
+ kv_dpm_power_level_enable(adev, i, false);
+}
+
+static void kv_enable_new_levels(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u32 i;
+
+ for (i = 0; i < SMU7_MAX_LEVELS_GRAPHICS; i++) {
+ if (i >= pi->lowest_valid && i <= pi->highest_valid)
+ kv_dpm_power_level_enable(adev, i, true);
+ }
+}
+
+static int kv_set_enabled_level(struct amdgpu_device *adev, u32 level)
+{
+ u32 new_mask = (1 << level);
+
+ return amdgpu_kv_send_msg_to_smc_with_parameter(adev,
+ PPSMC_MSG_SCLKDPM_SetEnabledMask,
+ new_mask);
+}
+
+static int kv_set_enabled_levels(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u32 i, new_mask = 0;
+
+ for (i = pi->lowest_valid; i <= pi->highest_valid; i++)
+ new_mask |= (1 << i);
+
+ return amdgpu_kv_send_msg_to_smc_with_parameter(adev,
+ PPSMC_MSG_SCLKDPM_SetEnabledMask,
+ new_mask);
+}
+
+static void kv_program_nbps_index_settings(struct amdgpu_device *adev,
+ struct amdgpu_ps *new_rps)
+{
+ struct kv_ps *new_ps = kv_get_ps(new_rps);
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u32 nbdpmconfig1;
+
+ if (adev->asic_type == CHIP_KABINI || adev->asic_type == CHIP_MULLINS)
+ return;
+
+ if (pi->sys_info.nb_dpm_enable) {
+ nbdpmconfig1 = RREG32_SMC(ixNB_DPM_CONFIG_1);
+ nbdpmconfig1 &= ~(NB_DPM_CONFIG_1__Dpm0PgNbPsLo_MASK |
+ NB_DPM_CONFIG_1__Dpm0PgNbPsHi_MASK |
+ NB_DPM_CONFIG_1__DpmXNbPsLo_MASK |
+ NB_DPM_CONFIG_1__DpmXNbPsHi_MASK);
+ nbdpmconfig1 |= (new_ps->dpm0_pg_nb_ps_lo << NB_DPM_CONFIG_1__Dpm0PgNbPsLo__SHIFT) |
+ (new_ps->dpm0_pg_nb_ps_hi << NB_DPM_CONFIG_1__Dpm0PgNbPsHi__SHIFT) |
+ (new_ps->dpmx_nb_ps_lo << NB_DPM_CONFIG_1__DpmXNbPsLo__SHIFT) |
+ (new_ps->dpmx_nb_ps_hi << NB_DPM_CONFIG_1__DpmXNbPsHi__SHIFT);
+ WREG32_SMC(ixNB_DPM_CONFIG_1, nbdpmconfig1);
+ }
+}
+
+static int kv_set_thermal_temperature_range(struct amdgpu_device *adev,
+ int min_temp, int max_temp)
+{
+ int low_temp = 0 * 1000;
+ int high_temp = 255 * 1000;
+ u32 tmp;
+
+ if (low_temp < min_temp)
+ low_temp = min_temp;
+ if (high_temp > max_temp)
+ high_temp = max_temp;
+ if (high_temp < low_temp) {
+ DRM_ERROR("invalid thermal range: %d - %d\n", low_temp, high_temp);
+ return -EINVAL;
+ }
+
+ tmp = RREG32_SMC(ixCG_THERMAL_INT_CTRL);
+ tmp &= ~(CG_THERMAL_INT_CTRL__DIG_THERM_INTH_MASK |
+ CG_THERMAL_INT_CTRL__DIG_THERM_INTL_MASK);
+ tmp |= ((49 + (high_temp / 1000)) << CG_THERMAL_INT_CTRL__DIG_THERM_INTH__SHIFT) |
+ ((49 + (low_temp / 1000)) << CG_THERMAL_INT_CTRL__DIG_THERM_INTL__SHIFT);
+ WREG32_SMC(ixCG_THERMAL_INT_CTRL, tmp);
+
+ adev->pm.dpm.thermal.min_temp = low_temp;
+ adev->pm.dpm.thermal.max_temp = high_temp;
+
+ return 0;
+}
+
+union igp_info {
+ struct _ATOM_INTEGRATED_SYSTEM_INFO info;
+ struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2;
+ struct _ATOM_INTEGRATED_SYSTEM_INFO_V5 info_5;
+ struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 info_6;
+ struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 info_7;
+ struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_8 info_8;
+};
+
+static int kv_parse_sys_info_table(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ struct amdgpu_mode_info *mode_info = &adev->mode_info;
+ int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo);
+ union igp_info *igp_info;
+ u8 frev, crev;
+ u16 data_offset;
+ int i;
+
+ if (amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL,
+ &frev, &crev, &data_offset)) {
+ igp_info = (union igp_info *)(mode_info->atom_context->bios +
+ data_offset);
+
+ if (crev != 8) {
+ DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev);
+ return -EINVAL;
+ }
+ pi->sys_info.bootup_sclk = le32_to_cpu(igp_info->info_8.ulBootUpEngineClock);
+ pi->sys_info.bootup_uma_clk = le32_to_cpu(igp_info->info_8.ulBootUpUMAClock);
+ pi->sys_info.bootup_nb_voltage_index =
+ le16_to_cpu(igp_info->info_8.usBootUpNBVoltage);
+ if (igp_info->info_8.ucHtcTmpLmt == 0)
+ pi->sys_info.htc_tmp_lmt = 203;
+ else
+ pi->sys_info.htc_tmp_lmt = igp_info->info_8.ucHtcTmpLmt;
+ if (igp_info->info_8.ucHtcHystLmt == 0)
+ pi->sys_info.htc_hyst_lmt = 5;
+ else
+ pi->sys_info.htc_hyst_lmt = igp_info->info_8.ucHtcHystLmt;
+ if (pi->sys_info.htc_tmp_lmt <= pi->sys_info.htc_hyst_lmt) {
+ DRM_ERROR("The htcTmpLmt should be larger than htcHystLmt.\n");
+ }
+
+ if (le32_to_cpu(igp_info->info_8.ulSystemConfig) & (1 << 3))
+ pi->sys_info.nb_dpm_enable = true;
+ else
+ pi->sys_info.nb_dpm_enable = false;
+
+ for (i = 0; i < KV_NUM_NBPSTATES; i++) {
+ pi->sys_info.nbp_memory_clock[i] =
+ le32_to_cpu(igp_info->info_8.ulNbpStateMemclkFreq[i]);
+ pi->sys_info.nbp_n_clock[i] =
+ le32_to_cpu(igp_info->info_8.ulNbpStateNClkFreq[i]);
+ }
+ if (le32_to_cpu(igp_info->info_8.ulGPUCapInfo) &
+ SYS_INFO_GPUCAPS__ENABEL_DFS_BYPASS)
+ pi->caps_enable_dfs_bypass = true;
+
+ sumo_construct_sclk_voltage_mapping_table(adev,
+ &pi->sys_info.sclk_voltage_mapping_table,
+ igp_info->info_8.sAvail_SCLK);
+
+ sumo_construct_vid_mapping_table(adev,
+ &pi->sys_info.vid_mapping_table,
+ igp_info->info_8.sAvail_SCLK);
+
+ kv_construct_max_power_limits_table(adev,
+ &adev->pm.dpm.dyn_state.max_clock_voltage_on_ac);
+ }
+ return 0;
+}
+
+union power_info {
+ struct _ATOM_POWERPLAY_INFO info;
+ struct _ATOM_POWERPLAY_INFO_V2 info_2;
+ struct _ATOM_POWERPLAY_INFO_V3 info_3;
+ struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
+ struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
+ struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+};
+
+union pplib_clock_info {
+ struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
+ struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
+ struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
+ struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+};
+
+union pplib_power_state {
+ struct _ATOM_PPLIB_STATE v1;
+ struct _ATOM_PPLIB_STATE_V2 v2;
+};
+
+static void kv_patch_boot_state(struct amdgpu_device *adev,
+ struct kv_ps *ps)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ ps->num_levels = 1;
+ ps->levels[0] = pi->boot_pl;
+}
+
+static void kv_parse_pplib_non_clock_info(struct amdgpu_device *adev,
+ struct amdgpu_ps *rps,
+ struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info,
+ u8 table_rev)
+{
+ struct kv_ps *ps = kv_get_ps(rps);
+
+ rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
+ rps->class = le16_to_cpu(non_clock_info->usClassification);
+ rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
+
+ if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) {
+ rps->vclk = le32_to_cpu(non_clock_info->ulVCLK);
+ rps->dclk = le32_to_cpu(non_clock_info->ulDCLK);
+ } else {
+ rps->vclk = 0;
+ rps->dclk = 0;
+ }
+
+ if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
+ adev->pm.dpm.boot_ps = rps;
+ kv_patch_boot_state(adev, ps);
+ }
+ if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
+ adev->pm.dpm.uvd_ps = rps;
+}
+
+static void kv_parse_pplib_clock_info(struct amdgpu_device *adev,
+ struct amdgpu_ps *rps, int index,
+ union pplib_clock_info *clock_info)
+{
+ struct kv_power_info *pi = kv_get_pi(adev);
+ struct kv_ps *ps = kv_get_ps(rps);
+ struct kv_pl *pl = &ps->levels[index];
+ u32 sclk;
+
+ sclk = le16_to_cpu(clock_info->sumo.usEngineClockLow);
+ sclk |= clock_info->sumo.ucEngineClockHigh << 16;
+ pl->sclk = sclk;
+ pl->vddc_index = clock_info->sumo.vddcIndex;
+
+ ps->num_levels = index + 1;
+
+ if (pi->caps_sclk_ds) {
+ pl->ds_divider_index = 5;
+ pl->ss_divider_index = 5;
+ }
+}
+
+static int kv_parse_power_table(struct amdgpu_device *adev)
+{
+ struct amdgpu_mode_info *mode_info = &adev->mode_info;
+ struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
+ union pplib_power_state *power_state;
+ int i, j, k, non_clock_array_index, clock_array_index;
+ union pplib_clock_info *clock_info;
+ struct _StateArray *state_array;
+ struct _ClockInfoArray *clock_info_array;
+ struct _NonClockInfoArray *non_clock_info_array;
+ union power_info *power_info;
+ int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
+ u16 data_offset;
+ u8 frev, crev;
+ u8 *power_state_offset;
+ struct kv_ps *ps;
+
+ if (!amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL,
+ &frev, &crev, &data_offset))
+ return -EINVAL;
+ power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
+
+ amdgpu_add_thermal_controller(adev);
+
+ state_array = (struct _StateArray *)
+ (mode_info->atom_context->bios + data_offset +
+ le16_to_cpu(power_info->pplib.usStateArrayOffset));
+ clock_info_array = (struct _ClockInfoArray *)
+ (mode_info->atom_context->bios + data_offset +
+ le16_to_cpu(power_info->pplib.usClockInfoArrayOffset));
+ non_clock_info_array = (struct _NonClockInfoArray *)
+ (mode_info->atom_context->bios + data_offset +
+ le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset));
+
+ adev->pm.dpm.ps = kcalloc(state_array->ucNumEntries,
+ sizeof(struct amdgpu_ps),
+ GFP_KERNEL);
+ if (!adev->pm.dpm.ps)
+ return -ENOMEM;
+ power_state_offset = (u8 *)state_array->states;
+ for (i = 0; i < state_array->ucNumEntries; i++) {
+ u8 *idx;
+ power_state = (union pplib_power_state *)power_state_offset;
+ non_clock_array_index = power_state->v2.nonClockInfoIndex;
+ non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
+ &non_clock_info_array->nonClockInfo[non_clock_array_index];
+ ps = kzalloc(sizeof(struct kv_ps), GFP_KERNEL);
+ if (ps == NULL) {
+ kfree(adev->pm.dpm.ps);
+ return -ENOMEM;
+ }
+ adev->pm.dpm.ps[i].ps_priv = ps;
+ k = 0;
+ idx = (u8 *)&power_state->v2.clockInfoIndex[0];
+ for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) {
+ clock_array_index = idx[j];
+ if (clock_array_index >= clock_info_array->ucNumEntries)
+ continue;
+ if (k >= SUMO_MAX_HARDWARE_POWERLEVELS)
+ break;
+ clock_info = (union pplib_clock_info *)
+ ((u8 *)&clock_info_array->clockInfo[0] +
+ (clock_array_index * clock_info_array->ucEntrySize));
+ kv_parse_pplib_clock_info(adev,
+ &adev->pm.dpm.ps[i], k,
+ clock_info);
+ k++;
+ }
+ kv_parse_pplib_non_clock_info(adev, &adev->pm.dpm.ps[i],
+ non_clock_info,
+ non_clock_info_array->ucEntrySize);
+ power_state_offset += 2 + power_state->v2.ucNumDPMLevels;
+ }
+ adev->pm.dpm.num_ps = state_array->ucNumEntries;
+
+ /* fill in the vce power states */
+ for (i = 0; i < adev->pm.dpm.num_of_vce_states; i++) {
+ u32 sclk;
+ clock_array_index = adev->pm.dpm.vce_states[i].clk_idx;
+ clock_info = (union pplib_clock_info *)
+ &clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize];
+ sclk = le16_to_cpu(clock_info->sumo.usEngineClockLow);
+ sclk |= clock_info->sumo.ucEngineClockHigh << 16;
+ adev->pm.dpm.vce_states[i].sclk = sclk;
+ adev->pm.dpm.vce_states[i].mclk = 0;
+ }
+
+ return 0;
+}
+
+static int kv_dpm_init(struct amdgpu_device *adev)
+{
+ struct kv_power_info *pi;
+ int ret, i;
+
+ pi = kzalloc(sizeof(struct kv_power_info), GFP_KERNEL);
+ if (pi == NULL)
+ return -ENOMEM;
+ adev->pm.dpm.priv = pi;
+
+ ret = amdgpu_get_platform_caps(adev);
+ if (ret)
+ return ret;
+
+ ret = amdgpu_parse_extended_power_table(adev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < SUMO_MAX_HARDWARE_POWERLEVELS; i++)
+ pi->at[i] = TRINITY_AT_DFLT;
+
+ pi->sram_end = SMC_RAM_END;
+
+ pi->enable_nb_dpm = true;
+
+ pi->caps_power_containment = true;
+ pi->caps_cac = true;
+ pi->enable_didt = false;
+ if (pi->enable_didt) {
+ pi->caps_sq_ramping = true;
+ pi->caps_db_ramping = true;
+ pi->caps_td_ramping = true;
+ pi->caps_tcp_ramping = true;
+ }
+
+ if (adev->pm.pp_feature & PP_SCLK_DEEP_SLEEP_MASK)
+ pi->caps_sclk_ds = true;
+ else
+ pi->caps_sclk_ds = false;
+
+ pi->enable_auto_thermal_throttling = true;
+ pi->disable_nb_ps3_in_battery = false;
+ if (amdgpu_bapm == 0)
+ pi->bapm_enable = false;
+ else
+ pi->bapm_enable = true;
+ pi->voltage_drop_t = 0;
+ pi->caps_sclk_throttle_low_notification = false;
+ pi->caps_fps = false; /* true? */
+ pi->caps_uvd_pg = (adev->pg_flags & AMD_PG_SUPPORT_UVD) ? true : false;
+ pi->caps_uvd_dpm = true;
+ pi->caps_vce_pg = (adev->pg_flags & AMD_PG_SUPPORT_VCE) ? true : false;
+ pi->caps_samu_pg = (adev->pg_flags & AMD_PG_SUPPORT_SAMU) ? true : false;
+ pi->caps_acp_pg = (adev->pg_flags & AMD_PG_SUPPORT_ACP) ? true : false;
+ pi->caps_stable_p_state = false;
+
+ ret = kv_parse_sys_info_table(adev);
+ if (ret)
+ return ret;
+
+ kv_patch_voltage_values(adev);
+ kv_construct_boot_state(adev);
+
+ ret = kv_parse_power_table(adev);
+ if (ret)
+ return ret;
+
+ pi->enable_dpm = true;
+
+ return 0;
+}
+
+static void
+kv_dpm_debugfs_print_current_performance_level(void *handle,
+ struct seq_file *m)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct kv_power_info *pi = kv_get_pi(adev);
+ u32 current_index =
+ (RREG32_SMC(ixTARGET_AND_CURRENT_PROFILE_INDEX) &
+ TARGET_AND_CURRENT_PROFILE_INDEX__CURR_SCLK_INDEX_MASK) >>
+ TARGET_AND_CURRENT_PROFILE_INDEX__CURR_SCLK_INDEX__SHIFT;
+ u32 sclk, tmp;
+ u16 vddc;
+
+ if (current_index >= SMU__NUM_SCLK_DPM_STATE) {
+ seq_printf(m, "invalid dpm profile %d\n", current_index);
+ } else {
+ sclk = be32_to_cpu(pi->graphics_level[current_index].SclkFrequency);
+ tmp = (RREG32_SMC(ixSMU_VOLTAGE_STATUS) &
+ SMU_VOLTAGE_STATUS__SMU_VOLTAGE_CURRENT_LEVEL_MASK) >>
+ SMU_VOLTAGE_STATUS__SMU_VOLTAGE_CURRENT_LEVEL__SHIFT;
+ vddc = kv_convert_8bit_index_to_voltage(adev, (u16)tmp);
+ seq_printf(m, "uvd %sabled\n", pi->uvd_power_gated ? "dis" : "en");
+ seq_printf(m, "vce %sabled\n", pi->vce_power_gated ? "dis" : "en");
+ seq_printf(m, "power level %d sclk: %u vddc: %u\n",
+ current_index, sclk, vddc);
+ }
+}
+
+static void
+kv_dpm_print_power_state(void *handle, void *request_ps)
+{
+ int i;
+ struct amdgpu_ps *rps = (struct amdgpu_ps *)request_ps;
+ struct kv_ps *ps = kv_get_ps(rps);
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ amdgpu_dpm_print_class_info(rps->class, rps->class2);
+ amdgpu_dpm_print_cap_info(rps->caps);
+ printk("\tuvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+ for (i = 0; i < ps->num_levels; i++) {
+ struct kv_pl *pl = &ps->levels[i];
+ printk("\t\tpower level %d sclk: %u vddc: %u\n",
+ i, pl->sclk,
+ kv_convert_8bit_index_to_voltage(adev, pl->vddc_index));
+ }
+ amdgpu_dpm_print_ps_status(adev, rps);
+}
+
+static void kv_dpm_fini(struct amdgpu_device *adev)
+{
+ int i;
+
+ for (i = 0; i < adev->pm.dpm.num_ps; i++) {
+ kfree(adev->pm.dpm.ps[i].ps_priv);
+ }
+ kfree(adev->pm.dpm.ps);
+ kfree(adev->pm.dpm.priv);
+ amdgpu_free_extended_power_table(adev);
+}
+
+static void kv_dpm_display_configuration_changed(void *handle)
+{
+
+}
+
+static u32 kv_dpm_get_sclk(void *handle, bool low)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct kv_power_info *pi = kv_get_pi(adev);
+ struct kv_ps *requested_state = kv_get_ps(&pi->requested_rps);
+
+ if (low)
+ return requested_state->levels[0].sclk;
+ else
+ return requested_state->levels[requested_state->num_levels - 1].sclk;
+}
+
+static u32 kv_dpm_get_mclk(void *handle, bool low)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct kv_power_info *pi = kv_get_pi(adev);
+
+ return pi->sys_info.bootup_uma_clk;
+}
+
+/* get temperature in millidegrees */
+static int kv_dpm_get_temp(void *handle)
+{
+ u32 temp;
+ int actual_temp = 0;
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ temp = RREG32_SMC(0xC0300E0C);
+
+ if (temp)
+ actual_temp = (temp / 8) - 49;
+ else
+ actual_temp = 0;
+
+ actual_temp = actual_temp * 1000;
+
+ return actual_temp;
+}
+
+static int kv_dpm_early_init(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ adev->powerplay.pp_funcs = &kv_dpm_funcs;
+ adev->powerplay.pp_handle = adev;
+ kv_dpm_set_irq_funcs(adev);
+
+ return 0;
+}
+
+static int kv_dpm_late_init(void *handle)
+{
+ /* powerdown unused blocks for now */
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ if (!adev->pm.dpm_enabled)
+ return 0;
+
+ kv_dpm_powergate_acp(adev, true);
+ kv_dpm_powergate_samu(adev, true);
+
+ return 0;
+}
+
+static int kv_dpm_sw_init(void *handle)
+{
+ int ret;
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ ret = amdgpu_irq_add_id(adev, AMDGPU_IRQ_CLIENTID_LEGACY, 230,
+ &adev->pm.dpm.thermal.irq);
+ if (ret)
+ return ret;
+
+ ret = amdgpu_irq_add_id(adev, AMDGPU_IRQ_CLIENTID_LEGACY, 231,
+ &adev->pm.dpm.thermal.irq);
+ if (ret)
+ return ret;
+
+ /* default to balanced state */
+ adev->pm.dpm.state = POWER_STATE_TYPE_BALANCED;
+ adev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED;
+ adev->pm.dpm.forced_level = AMD_DPM_FORCED_LEVEL_AUTO;
+ adev->pm.default_sclk = adev->clock.default_sclk;
+ adev->pm.default_mclk = adev->clock.default_mclk;
+ adev->pm.current_sclk = adev->clock.default_sclk;
+ adev->pm.current_mclk = adev->clock.default_mclk;
+ adev->pm.int_thermal_type = THERMAL_TYPE_NONE;
+
+ if (amdgpu_dpm == 0)
+ return 0;
+
+ INIT_WORK(&adev->pm.dpm.thermal.work, amdgpu_dpm_thermal_work_handler);
+ mutex_lock(&adev->pm.mutex);
+ ret = kv_dpm_init(adev);
+ if (ret)
+ goto dpm_failed;
+ adev->pm.dpm.current_ps = adev->pm.dpm.requested_ps = adev->pm.dpm.boot_ps;
+ if (amdgpu_dpm == 1)
+ amdgpu_pm_print_power_states(adev);
+ mutex_unlock(&adev->pm.mutex);
+ DRM_INFO("amdgpu: dpm initialized\n");
+
+ return 0;
+
+dpm_failed:
+ kv_dpm_fini(adev);
+ mutex_unlock(&adev->pm.mutex);
+ DRM_ERROR("amdgpu: dpm initialization failed\n");
+ return ret;
+}
+
+static int kv_dpm_sw_fini(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ flush_work(&adev->pm.dpm.thermal.work);
+
+ mutex_lock(&adev->pm.mutex);
+ kv_dpm_fini(adev);
+ mutex_unlock(&adev->pm.mutex);
+
+ return 0;
+}
+
+static int kv_dpm_hw_init(void *handle)
+{
+ int ret;
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ if (!amdgpu_dpm)
+ return 0;
+
+ mutex_lock(&adev->pm.mutex);
+ kv_dpm_setup_asic(adev);
+ ret = kv_dpm_enable(adev);
+ if (ret)
+ adev->pm.dpm_enabled = false;
+ else
+ adev->pm.dpm_enabled = true;
+ mutex_unlock(&adev->pm.mutex);
+ amdgpu_pm_compute_clocks(adev);
+ return ret;
+}
+
+static int kv_dpm_hw_fini(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ if (adev->pm.dpm_enabled) {
+ mutex_lock(&adev->pm.mutex);
+ kv_dpm_disable(adev);
+ mutex_unlock(&adev->pm.mutex);
+ }
+
+ return 0;
+}
+
+static int kv_dpm_suspend(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ if (adev->pm.dpm_enabled) {
+ mutex_lock(&adev->pm.mutex);
+ /* disable dpm */
+ kv_dpm_disable(adev);
+ /* reset the power state */
+ adev->pm.dpm.current_ps = adev->pm.dpm.requested_ps = adev->pm.dpm.boot_ps;
+ mutex_unlock(&adev->pm.mutex);
+ }
+ return 0;
+}
+
+static int kv_dpm_resume(void *handle)
+{
+ int ret;
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ if (adev->pm.dpm_enabled) {
+ /* asic init will reset to the boot state */
+ mutex_lock(&adev->pm.mutex);
+ kv_dpm_setup_asic(adev);
+ ret = kv_dpm_enable(adev);
+ if (ret)
+ adev->pm.dpm_enabled = false;
+ else
+ adev->pm.dpm_enabled = true;
+ mutex_unlock(&adev->pm.mutex);
+ if (adev->pm.dpm_enabled)
+ amdgpu_pm_compute_clocks(adev);
+ }
+ return 0;
+}
+
+static bool kv_dpm_is_idle(void *handle)
+{
+ return true;
+}
+
+static int kv_dpm_wait_for_idle(void *handle)
+{
+ return 0;
+}
+
+
+static int kv_dpm_soft_reset(void *handle)
+{
+ return 0;
+}
+
+static int kv_dpm_set_interrupt_state(struct amdgpu_device *adev,
+ struct amdgpu_irq_src *src,
+ unsigned type,
+ enum amdgpu_interrupt_state state)
+{
+ u32 cg_thermal_int;
+
+ switch (type) {
+ case AMDGPU_THERMAL_IRQ_LOW_TO_HIGH:
+ switch (state) {
+ case AMDGPU_IRQ_STATE_DISABLE:
+ cg_thermal_int = RREG32_SMC(ixCG_THERMAL_INT_CTRL);
+ cg_thermal_int &= ~CG_THERMAL_INT_CTRL__THERM_INTH_MASK_MASK;
+ WREG32_SMC(ixCG_THERMAL_INT_CTRL, cg_thermal_int);
+ break;
+ case AMDGPU_IRQ_STATE_ENABLE:
+ cg_thermal_int = RREG32_SMC(ixCG_THERMAL_INT_CTRL);
+ cg_thermal_int |= CG_THERMAL_INT_CTRL__THERM_INTH_MASK_MASK;
+ WREG32_SMC(ixCG_THERMAL_INT_CTRL, cg_thermal_int);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case AMDGPU_THERMAL_IRQ_HIGH_TO_LOW:
+ switch (state) {
+ case AMDGPU_IRQ_STATE_DISABLE:
+ cg_thermal_int = RREG32_SMC(ixCG_THERMAL_INT_CTRL);
+ cg_thermal_int &= ~CG_THERMAL_INT_CTRL__THERM_INTL_MASK_MASK;
+ WREG32_SMC(ixCG_THERMAL_INT_CTRL, cg_thermal_int);
+ break;
+ case AMDGPU_IRQ_STATE_ENABLE:
+ cg_thermal_int = RREG32_SMC(ixCG_THERMAL_INT_CTRL);
+ cg_thermal_int |= CG_THERMAL_INT_CTRL__THERM_INTL_MASK_MASK;
+ WREG32_SMC(ixCG_THERMAL_INT_CTRL, cg_thermal_int);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int kv_dpm_process_interrupt(struct amdgpu_device *adev,
+ struct amdgpu_irq_src *source,
+ struct amdgpu_iv_entry *entry)
+{
+ bool queue_thermal = false;
+
+ if (entry == NULL)
+ return -EINVAL;
+
+ switch (entry->src_id) {
+ case 230: /* thermal low to high */
+ DRM_DEBUG("IH: thermal low to high\n");
+ adev->pm.dpm.thermal.high_to_low = false;
+ queue_thermal = true;
+ break;
+ case 231: /* thermal high to low */
+ DRM_DEBUG("IH: thermal high to low\n");
+ adev->pm.dpm.thermal.high_to_low = true;
+ queue_thermal = true;
+ break;
+ default:
+ break;
+ }
+
+ if (queue_thermal)
+ schedule_work(&adev->pm.dpm.thermal.work);
+
+ return 0;
+}
+
+static int kv_dpm_set_clockgating_state(void *handle,
+ enum amd_clockgating_state state)
+{
+ return 0;
+}
+
+static int kv_dpm_set_powergating_state(void *handle,
+ enum amd_powergating_state state)
+{
+ return 0;
+}
+
+static inline bool kv_are_power_levels_equal(const struct kv_pl *kv_cpl1,
+ const struct kv_pl *kv_cpl2)
+{
+ return ((kv_cpl1->sclk == kv_cpl2->sclk) &&
+ (kv_cpl1->vddc_index == kv_cpl2->vddc_index) &&
+ (kv_cpl1->ds_divider_index == kv_cpl2->ds_divider_index) &&
+ (kv_cpl1->force_nbp_state == kv_cpl2->force_nbp_state));
+}
+
+static int kv_check_state_equal(void *handle,
+ void *current_ps,
+ void *request_ps,
+ bool *equal)
+{
+ struct kv_ps *kv_cps;
+ struct kv_ps *kv_rps;
+ int i;
+ struct amdgpu_ps *cps = (struct amdgpu_ps *)current_ps;
+ struct amdgpu_ps *rps = (struct amdgpu_ps *)request_ps;
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ if (adev == NULL || cps == NULL || rps == NULL || equal == NULL)
+ return -EINVAL;
+
+ kv_cps = kv_get_ps(cps);
+ kv_rps = kv_get_ps(rps);
+
+ if (kv_cps == NULL) {
+ *equal = false;
+ return 0;
+ }
+
+ if (kv_cps->num_levels != kv_rps->num_levels) {
+ *equal = false;
+ return 0;
+ }
+
+ for (i = 0; i < kv_cps->num_levels; i++) {
+ if (!kv_are_power_levels_equal(&(kv_cps->levels[i]),
+ &(kv_rps->levels[i]))) {
+ *equal = false;
+ return 0;
+ }
+ }
+
+ /* If all performance levels are the same try to use the UVD clocks to break the tie.*/
+ *equal = ((cps->vclk == rps->vclk) && (cps->dclk == rps->dclk));
+ *equal &= ((cps->evclk == rps->evclk) && (cps->ecclk == rps->ecclk));
+
+ return 0;
+}
+
+static int kv_dpm_read_sensor(void *handle, int idx,
+ void *value, int *size)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct kv_power_info *pi = kv_get_pi(adev);
+ uint32_t sclk;
+ u32 pl_index =
+ (RREG32_SMC(ixTARGET_AND_CURRENT_PROFILE_INDEX) &
+ TARGET_AND_CURRENT_PROFILE_INDEX__CURR_SCLK_INDEX_MASK) >>
+ TARGET_AND_CURRENT_PROFILE_INDEX__CURR_SCLK_INDEX__SHIFT;
+
+ /* size must be at least 4 bytes for all sensors */
+ if (*size < 4)
+ return -EINVAL;
+
+ switch (idx) {
+ case AMDGPU_PP_SENSOR_GFX_SCLK:
+ if (pl_index < SMU__NUM_SCLK_DPM_STATE) {
+ sclk = be32_to_cpu(
+ pi->graphics_level[pl_index].SclkFrequency);
+ *((uint32_t *)value) = sclk;
+ *size = 4;
+ return 0;
+ }
+ return -EINVAL;
+ case AMDGPU_PP_SENSOR_GPU_TEMP:
+ *((uint32_t *)value) = kv_dpm_get_temp(adev);
+ *size = 4;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int kv_set_powergating_by_smu(void *handle,
+ uint32_t block_type, bool gate)
+{
+ switch (block_type) {
+ case AMD_IP_BLOCK_TYPE_UVD:
+ kv_dpm_powergate_uvd(handle, gate);
+ break;
+ case AMD_IP_BLOCK_TYPE_VCE:
+ kv_dpm_powergate_vce(handle, gate);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const struct amd_ip_funcs kv_dpm_ip_funcs = {
+ .name = "kv_dpm",
+ .early_init = kv_dpm_early_init,
+ .late_init = kv_dpm_late_init,
+ .sw_init = kv_dpm_sw_init,
+ .sw_fini = kv_dpm_sw_fini,
+ .hw_init = kv_dpm_hw_init,
+ .hw_fini = kv_dpm_hw_fini,
+ .suspend = kv_dpm_suspend,
+ .resume = kv_dpm_resume,
+ .is_idle = kv_dpm_is_idle,
+ .wait_for_idle = kv_dpm_wait_for_idle,
+ .soft_reset = kv_dpm_soft_reset,
+ .set_clockgating_state = kv_dpm_set_clockgating_state,
+ .set_powergating_state = kv_dpm_set_powergating_state,
+};
+
+const struct amdgpu_ip_block_version kv_smu_ip_block =
+{
+ .type = AMD_IP_BLOCK_TYPE_SMC,
+ .major = 1,
+ .minor = 0,
+ .rev = 0,
+ .funcs = &kv_dpm_ip_funcs,
+};
+
+static const struct amd_pm_funcs kv_dpm_funcs = {
+ .pre_set_power_state = &kv_dpm_pre_set_power_state,
+ .set_power_state = &kv_dpm_set_power_state,
+ .post_set_power_state = &kv_dpm_post_set_power_state,
+ .display_configuration_changed = &kv_dpm_display_configuration_changed,
+ .get_sclk = &kv_dpm_get_sclk,
+ .get_mclk = &kv_dpm_get_mclk,
+ .print_power_state = &kv_dpm_print_power_state,
+ .debugfs_print_current_performance_level = &kv_dpm_debugfs_print_current_performance_level,
+ .force_performance_level = &kv_dpm_force_performance_level,
+ .set_powergating_by_smu = kv_set_powergating_by_smu,
+ .enable_bapm = &kv_dpm_enable_bapm,
+ .get_vce_clock_state = amdgpu_get_vce_clock_state,
+ .check_state_equal = kv_check_state_equal,
+ .read_sensor = &kv_dpm_read_sensor,
+};
+
+static const struct amdgpu_irq_src_funcs kv_dpm_irq_funcs = {
+ .set = kv_dpm_set_interrupt_state,
+ .process = kv_dpm_process_interrupt,
+};
+
+static void kv_dpm_set_irq_funcs(struct amdgpu_device *adev)
+{
+ adev->pm.dpm.thermal.irq.num_types = AMDGPU_THERMAL_IRQ_LAST;
+ adev->pm.dpm.thermal.irq.funcs = &kv_dpm_irq_funcs;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/kv_dpm.h b/drivers/gpu/drm/amd/pm/powerplay/kv_dpm.h
new file mode 100644
index 000000000000..6df0ed41317c
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/kv_dpm.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 __KV_DPM_H__
+#define __KV_DPM_H__
+
+#define SMU__NUM_SCLK_DPM_STATE 8
+#define SMU__NUM_MCLK_DPM_LEVELS 4
+#define SMU__NUM_LCLK_DPM_LEVELS 8
+#define SMU__NUM_PCIE_DPM_LEVELS 0 /* ??? */
+#include "smu7_fusion.h"
+#include "ppsmc.h"
+
+#define SUMO_MAX_HARDWARE_POWERLEVELS 5
+
+#define SUMO_MAX_NUMBER_VOLTAGES 4
+
+struct sumo_vid_mapping_entry {
+ u16 vid_2bit;
+ u16 vid_7bit;
+};
+
+struct sumo_vid_mapping_table {
+ u32 num_entries;
+ struct sumo_vid_mapping_entry entries[SUMO_MAX_NUMBER_VOLTAGES];
+};
+
+struct sumo_sclk_voltage_mapping_entry {
+ u32 sclk_frequency;
+ u16 vid_2bit;
+ u16 rsv;
+};
+
+struct sumo_sclk_voltage_mapping_table {
+ u32 num_max_dpm_entries;
+ struct sumo_sclk_voltage_mapping_entry entries[SUMO_MAX_HARDWARE_POWERLEVELS];
+};
+
+#define TRINITY_AT_DFLT 30
+
+#define KV_NUM_NBPSTATES 4
+
+enum kv_pt_config_reg_type {
+ KV_CONFIGREG_MMR = 0,
+ KV_CONFIGREG_SMC_IND,
+ KV_CONFIGREG_DIDT_IND,
+ KV_CONFIGREG_CACHE,
+ KV_CONFIGREG_MAX
+};
+
+struct kv_pt_config_reg {
+ u32 offset;
+ u32 mask;
+ u32 shift;
+ u32 value;
+ enum kv_pt_config_reg_type type;
+};
+
+struct kv_lcac_config_values {
+ u32 block_id;
+ u32 signal_id;
+ u32 t;
+};
+
+struct kv_lcac_config_reg {
+ u32 cntl;
+ u32 block_mask;
+ u32 block_shift;
+ u32 signal_mask;
+ u32 signal_shift;
+ u32 t_mask;
+ u32 t_shift;
+ u32 enable_mask;
+ u32 enable_shift;
+};
+
+struct kv_pl {
+ u32 sclk;
+ u8 vddc_index;
+ u8 ds_divider_index;
+ u8 ss_divider_index;
+ u8 allow_gnb_slow;
+ u8 force_nbp_state;
+ u8 display_wm;
+ u8 vce_wm;
+};
+
+struct kv_ps {
+ struct kv_pl levels[SUMO_MAX_HARDWARE_POWERLEVELS];
+ u32 num_levels;
+ bool need_dfs_bypass;
+ u8 dpm0_pg_nb_ps_lo;
+ u8 dpm0_pg_nb_ps_hi;
+ u8 dpmx_nb_ps_lo;
+ u8 dpmx_nb_ps_hi;
+};
+
+struct kv_sys_info {
+ u32 bootup_uma_clk;
+ u32 bootup_sclk;
+ u32 dentist_vco_freq;
+ u32 nb_dpm_enable;
+ u32 nbp_memory_clock[KV_NUM_NBPSTATES];
+ u32 nbp_n_clock[KV_NUM_NBPSTATES];
+ u16 bootup_nb_voltage_index;
+ u8 htc_tmp_lmt;
+ u8 htc_hyst_lmt;
+ struct sumo_sclk_voltage_mapping_table sclk_voltage_mapping_table;
+ struct sumo_vid_mapping_table vid_mapping_table;
+ u32 uma_channel_number;
+};
+
+struct kv_power_info {
+ u32 at[SUMO_MAX_HARDWARE_POWERLEVELS];
+ u32 voltage_drop_t;
+ struct kv_sys_info sys_info;
+ struct kv_pl boot_pl;
+ bool enable_nb_ps_policy;
+ bool disable_nb_ps3_in_battery;
+ bool video_start;
+ bool battery_state;
+ u32 lowest_valid;
+ u32 highest_valid;
+ u16 high_voltage_t;
+ bool cac_enabled;
+ bool bapm_enable;
+ /* smc offsets */
+ u32 sram_end;
+ u32 dpm_table_start;
+ u32 soft_regs_start;
+ /* dpm SMU tables */
+ u8 graphics_dpm_level_count;
+ u8 uvd_level_count;
+ u8 vce_level_count;
+ u8 acp_level_count;
+ u8 samu_level_count;
+ u16 fps_high_t;
+ SMU7_Fusion_GraphicsLevel graphics_level[SMU__NUM_SCLK_DPM_STATE];
+ SMU7_Fusion_ACPILevel acpi_level;
+ SMU7_Fusion_UvdLevel uvd_level[SMU7_MAX_LEVELS_UVD];
+ SMU7_Fusion_ExtClkLevel vce_level[SMU7_MAX_LEVELS_VCE];
+ SMU7_Fusion_ExtClkLevel acp_level[SMU7_MAX_LEVELS_ACP];
+ SMU7_Fusion_ExtClkLevel samu_level[SMU7_MAX_LEVELS_SAMU];
+ u8 uvd_boot_level;
+ u8 vce_boot_level;
+ u8 acp_boot_level;
+ u8 samu_boot_level;
+ u8 uvd_interval;
+ u8 vce_interval;
+ u8 acp_interval;
+ u8 samu_interval;
+ u8 graphics_boot_level;
+ u8 graphics_interval;
+ u8 graphics_therm_throttle_enable;
+ u8 graphics_voltage_change_enable;
+ u8 graphics_clk_slow_enable;
+ u8 graphics_clk_slow_divider;
+ u8 fps_low_t;
+ u32 low_sclk_interrupt_t;
+ bool uvd_power_gated;
+ bool vce_power_gated;
+ bool acp_power_gated;
+ bool samu_power_gated;
+ bool nb_dpm_enabled;
+ /* flags */
+ bool enable_didt;
+ bool enable_dpm;
+ bool enable_auto_thermal_throttling;
+ bool enable_nb_dpm;
+ /* caps */
+ bool caps_cac;
+ bool caps_power_containment;
+ bool caps_sq_ramping;
+ bool caps_db_ramping;
+ bool caps_td_ramping;
+ bool caps_tcp_ramping;
+ bool caps_sclk_throttle_low_notification;
+ bool caps_fps;
+ bool caps_uvd_dpm;
+ bool caps_uvd_pg;
+ bool caps_vce_pg;
+ bool caps_samu_pg;
+ bool caps_acp_pg;
+ bool caps_stable_p_state;
+ bool caps_enable_dfs_bypass;
+ bool caps_sclk_ds;
+ struct amdgpu_ps current_rps;
+ struct kv_ps current_ps;
+ struct amdgpu_ps requested_rps;
+ struct kv_ps requested_ps;
+};
+
+/* XXX are these ok? */
+#define KV_TEMP_RANGE_MIN (90 * 1000)
+#define KV_TEMP_RANGE_MAX (120 * 1000)
+
+/* kv_smc.c */
+int amdgpu_kv_notify_message_to_smu(struct amdgpu_device *adev, u32 id);
+int amdgpu_kv_dpm_get_enable_mask(struct amdgpu_device *adev, u32 *enable_mask);
+int amdgpu_kv_send_msg_to_smc_with_parameter(struct amdgpu_device *adev,
+ PPSMC_Msg msg, u32 parameter);
+int amdgpu_kv_read_smc_sram_dword(struct amdgpu_device *adev, u32 smc_address,
+ u32 *value, u32 limit);
+int amdgpu_kv_smc_dpm_enable(struct amdgpu_device *adev, bool enable);
+int amdgpu_kv_smc_bapm_enable(struct amdgpu_device *adev, bool enable);
+int amdgpu_kv_copy_bytes_to_smc(struct amdgpu_device *adev,
+ u32 smc_start_address,
+ const u8 *src, u32 byte_count, u32 limit);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/kv_smc.c b/drivers/gpu/drm/amd/pm/powerplay/kv_smc.c
new file mode 100644
index 000000000000..2d9ab6b8be66
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/kv_smc.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Alex Deucher
+ */
+
+#include "amdgpu.h"
+#include "cikd.h"
+#include "kv_dpm.h"
+
+#include "smu/smu_7_0_0_d.h"
+#include "smu/smu_7_0_0_sh_mask.h"
+
+int amdgpu_kv_notify_message_to_smu(struct amdgpu_device *adev, u32 id)
+{
+ u32 i;
+ u32 tmp = 0;
+
+ WREG32(mmSMC_MESSAGE_0, id & SMC_MESSAGE_0__SMC_MSG_MASK);
+
+ for (i = 0; i < adev->usec_timeout; i++) {
+ if ((RREG32(mmSMC_RESP_0) & SMC_RESP_0__SMC_RESP_MASK) != 0)
+ break;
+ udelay(1);
+ }
+ tmp = RREG32(mmSMC_RESP_0) & SMC_RESP_0__SMC_RESP_MASK;
+
+ if (tmp != 1) {
+ if (tmp == 0xFF)
+ return -EINVAL;
+ else if (tmp == 0xFE)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int amdgpu_kv_dpm_get_enable_mask(struct amdgpu_device *adev, u32 *enable_mask)
+{
+ int ret;
+
+ ret = amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_SCLKDPM_GetEnabledMask);
+
+ if (ret == 0)
+ *enable_mask = RREG32_SMC(ixSMC_SYSCON_MSG_ARG_0);
+
+ return ret;
+}
+
+int amdgpu_kv_send_msg_to_smc_with_parameter(struct amdgpu_device *adev,
+ PPSMC_Msg msg, u32 parameter)
+{
+
+ WREG32(mmSMC_MSG_ARG_0, parameter);
+
+ return amdgpu_kv_notify_message_to_smu(adev, msg);
+}
+
+static int kv_set_smc_sram_address(struct amdgpu_device *adev,
+ u32 smc_address, u32 limit)
+{
+ if (smc_address & 3)
+ return -EINVAL;
+ if ((smc_address + 3) > limit)
+ return -EINVAL;
+
+ WREG32(mmSMC_IND_INDEX_0, smc_address);
+ WREG32_P(mmSMC_IND_ACCESS_CNTL, 0,
+ ~SMC_IND_ACCESS_CNTL__AUTO_INCREMENT_IND_0_MASK);
+
+ return 0;
+}
+
+int amdgpu_kv_read_smc_sram_dword(struct amdgpu_device *adev, u32 smc_address,
+ u32 *value, u32 limit)
+{
+ int ret;
+
+ ret = kv_set_smc_sram_address(adev, smc_address, limit);
+ if (ret)
+ return ret;
+
+ *value = RREG32(mmSMC_IND_DATA_0);
+ return 0;
+}
+
+int amdgpu_kv_smc_dpm_enable(struct amdgpu_device *adev, bool enable)
+{
+ if (enable)
+ return amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_DPM_Enable);
+ else
+ return amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_DPM_Disable);
+}
+
+int amdgpu_kv_smc_bapm_enable(struct amdgpu_device *adev, bool enable)
+{
+ if (enable)
+ return amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_EnableBAPM);
+ else
+ return amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_DisableBAPM);
+}
+
+int amdgpu_kv_copy_bytes_to_smc(struct amdgpu_device *adev,
+ u32 smc_start_address,
+ const u8 *src, u32 byte_count, u32 limit)
+{
+ int ret;
+ u32 data, original_data, addr, extra_shift, t_byte, count, mask;
+
+ if ((smc_start_address + byte_count) > limit)
+ return -EINVAL;
+
+ addr = smc_start_address;
+ t_byte = addr & 3;
+
+ /* RMW for the initial bytes */
+ if (t_byte != 0) {
+ addr -= t_byte;
+
+ ret = kv_set_smc_sram_address(adev, addr, limit);
+ if (ret)
+ return ret;
+
+ original_data = RREG32(mmSMC_IND_DATA_0);
+
+ data = 0;
+ mask = 0;
+ count = 4;
+ while (count > 0) {
+ if (t_byte > 0) {
+ mask = (mask << 8) | 0xff;
+ t_byte--;
+ } else if (byte_count > 0) {
+ data = (data << 8) + *src++;
+ byte_count--;
+ mask <<= 8;
+ } else {
+ data <<= 8;
+ mask = (mask << 8) | 0xff;
+ }
+ count--;
+ }
+
+ data |= original_data & mask;
+
+ ret = kv_set_smc_sram_address(adev, addr, limit);
+ if (ret)
+ return ret;
+
+ WREG32(mmSMC_IND_DATA_0, data);
+
+ addr += 4;
+ }
+
+ while (byte_count >= 4) {
+ /* SMC address space is BE */
+ data = (src[0] << 24) + (src[1] << 16) + (src[2] << 8) + src[3];
+
+ ret = kv_set_smc_sram_address(adev, addr, limit);
+ if (ret)
+ return ret;
+
+ WREG32(mmSMC_IND_DATA_0, data);
+
+ src += 4;
+ byte_count -= 4;
+ addr += 4;
+ }
+
+ /* RMW for the final bytes */
+ if (byte_count > 0) {
+ data = 0;
+
+ ret = kv_set_smc_sram_address(adev, addr, limit);
+ if (ret)
+ return ret;
+
+ original_data = RREG32(mmSMC_IND_DATA_0);
+
+ extra_shift = 8 * (4 - byte_count);
+
+ while (byte_count > 0) {
+ /* SMC address space is BE */
+ data = (data << 8) + *src++;
+ byte_count--;
+ }
+
+ data <<= extra_shift;
+
+ data |= (original_data & ~((~0UL) << extra_shift));
+
+ ret = kv_set_smc_sram_address(adev, addr, limit);
+ if (ret)
+ return ret;
+
+ WREG32(mmSMC_IND_DATA_0, data);
+ }
+ return 0;
+}
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/ppsmc.h b/drivers/gpu/drm/amd/pm/powerplay/ppsmc.h
new file mode 100644
index 000000000000..8463245f424f
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/ppsmc.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 PP_SMC_H
+#define PP_SMC_H
+
+#pragma pack(push, 1)
+
+#define PPSMC_SWSTATE_FLAG_DC 0x01
+#define PPSMC_SWSTATE_FLAG_UVD 0x02
+#define PPSMC_SWSTATE_FLAG_VCE 0x04
+#define PPSMC_SWSTATE_FLAG_PCIE_X1 0x08
+
+#define PPSMC_THERMAL_PROTECT_TYPE_INTERNAL 0x00
+#define PPSMC_THERMAL_PROTECT_TYPE_EXTERNAL 0x01
+#define PPSMC_THERMAL_PROTECT_TYPE_NONE 0xff
+
+#define PPSMC_SYSTEMFLAG_GPIO_DC 0x01
+#define PPSMC_SYSTEMFLAG_STEPVDDC 0x02
+#define PPSMC_SYSTEMFLAG_GDDR5 0x04
+#define PPSMC_SYSTEMFLAG_DISABLE_BABYSTEP 0x08
+#define PPSMC_SYSTEMFLAG_REGULATOR_HOT 0x10
+#define PPSMC_SYSTEMFLAG_REGULATOR_HOT_ANALOG 0x20
+#define PPSMC_SYSTEMFLAG_REGULATOR_HOT_PROG_GPIO 0x40
+
+#define PPSMC_EXTRAFLAGS_AC2DC_ACTION_MASK 0x07
+#define PPSMC_EXTRAFLAGS_AC2DC_DONT_WAIT_FOR_VBLANK 0x08
+#define PPSMC_EXTRAFLAGS_AC2DC_ACTION_GOTODPMLOWSTATE 0x00
+#define PPSMC_EXTRAFLAGS_AC2DC_ACTION_GOTOINITIALSTATE 0x01
+#define PPSMC_EXTRAFLAGS_AC2DC_GPIO5_POLARITY_HIGH 0x02
+
+#define PPSMC_DISPLAY_WATERMARK_LOW 0
+#define PPSMC_DISPLAY_WATERMARK_HIGH 1
+
+#define PPSMC_STATEFLAG_AUTO_PULSE_SKIP 0x01
+#define PPSMC_STATEFLAG_POWERBOOST 0x02
+#define PPSMC_STATEFLAG_DEEPSLEEP_THROTTLE 0x20
+#define PPSMC_STATEFLAG_DEEPSLEEP_BYPASS 0x40
+
+#define FDO_MODE_HARDWARE 0
+#define FDO_MODE_PIECE_WISE_LINEAR 1
+
+enum FAN_CONTROL {
+ FAN_CONTROL_FUZZY,
+ FAN_CONTROL_TABLE
+};
+
+#define PPSMC_Result_OK ((uint8_t)0x01)
+#define PPSMC_Result_Failed ((uint8_t)0xFF)
+
+typedef uint8_t PPSMC_Result;
+
+#define PPSMC_MSG_Halt ((uint8_t)0x10)
+#define PPSMC_MSG_Resume ((uint8_t)0x11)
+#define PPSMC_MSG_ZeroLevelsDisabled ((uint8_t)0x13)
+#define PPSMC_MSG_OneLevelsDisabled ((uint8_t)0x14)
+#define PPSMC_MSG_TwoLevelsDisabled ((uint8_t)0x15)
+#define PPSMC_MSG_EnableThermalInterrupt ((uint8_t)0x16)
+#define PPSMC_MSG_RunningOnAC ((uint8_t)0x17)
+#define PPSMC_MSG_SwitchToSwState ((uint8_t)0x20)
+#define PPSMC_MSG_SwitchToInitialState ((uint8_t)0x40)
+#define PPSMC_MSG_NoForcedLevel ((uint8_t)0x41)
+#define PPSMC_MSG_ForceHigh ((uint8_t)0x42)
+#define PPSMC_MSG_ForceMediumOrHigh ((uint8_t)0x43)
+#define PPSMC_MSG_SwitchToMinimumPower ((uint8_t)0x51)
+#define PPSMC_MSG_ResumeFromMinimumPower ((uint8_t)0x52)
+#define PPSMC_MSG_EnableCac ((uint8_t)0x53)
+#define PPSMC_MSG_DisableCac ((uint8_t)0x54)
+#define PPSMC_TDPClampingActive ((uint8_t)0x59)
+#define PPSMC_TDPClampingInactive ((uint8_t)0x5A)
+#define PPSMC_StartFanControl ((uint8_t)0x5B)
+#define PPSMC_StopFanControl ((uint8_t)0x5C)
+#define PPSMC_MSG_NoDisplay ((uint8_t)0x5D)
+#define PPSMC_NoDisplay ((uint8_t)0x5D)
+#define PPSMC_MSG_HasDisplay ((uint8_t)0x5E)
+#define PPSMC_HasDisplay ((uint8_t)0x5E)
+#define PPSMC_MSG_UVDPowerOFF ((uint8_t)0x60)
+#define PPSMC_MSG_UVDPowerON ((uint8_t)0x61)
+#define PPSMC_MSG_EnableULV ((uint8_t)0x62)
+#define PPSMC_MSG_DisableULV ((uint8_t)0x63)
+#define PPSMC_MSG_EnterULV ((uint8_t)0x64)
+#define PPSMC_MSG_ExitULV ((uint8_t)0x65)
+#define PPSMC_CACLongTermAvgEnable ((uint8_t)0x6E)
+#define PPSMC_CACLongTermAvgDisable ((uint8_t)0x6F)
+#define PPSMC_MSG_CollectCAC_PowerCorreln ((uint8_t)0x7A)
+#define PPSMC_FlushDataCache ((uint8_t)0x80)
+#define PPSMC_MSG_SetEnabledLevels ((uint8_t)0x82)
+#define PPSMC_MSG_SetForcedLevels ((uint8_t)0x83)
+#define PPSMC_MSG_ResetToDefaults ((uint8_t)0x84)
+#define PPSMC_MSG_EnableDTE ((uint8_t)0x87)
+#define PPSMC_MSG_DisableDTE ((uint8_t)0x88)
+#define PPSMC_MSG_ThrottleOVRDSCLKDS ((uint8_t)0x96)
+#define PPSMC_MSG_CancelThrottleOVRDSCLKDS ((uint8_t)0x97)
+#define PPSMC_MSG_EnableACDCGPIOInterrupt ((uint16_t) 0x149)
+
+/* CI/KV/KB */
+#define PPSMC_MSG_UVDDPM_SetEnabledMask ((uint16_t) 0x12D)
+#define PPSMC_MSG_VCEDPM_SetEnabledMask ((uint16_t) 0x12E)
+#define PPSMC_MSG_ACPDPM_SetEnabledMask ((uint16_t) 0x12F)
+#define PPSMC_MSG_SAMUDPM_SetEnabledMask ((uint16_t) 0x130)
+#define PPSMC_MSG_MCLKDPM_ForceState ((uint16_t) 0x131)
+#define PPSMC_MSG_MCLKDPM_NoForcedLevel ((uint16_t) 0x132)
+#define PPSMC_MSG_Thermal_Cntl_Disable ((uint16_t) 0x133)
+#define PPSMC_MSG_Voltage_Cntl_Disable ((uint16_t) 0x135)
+#define PPSMC_MSG_PCIeDPM_Enable ((uint16_t) 0x136)
+#define PPSMC_MSG_PCIeDPM_Disable ((uint16_t) 0x13d)
+#define PPSMC_MSG_ACPPowerOFF ((uint16_t) 0x137)
+#define PPSMC_MSG_ACPPowerON ((uint16_t) 0x138)
+#define PPSMC_MSG_SAMPowerOFF ((uint16_t) 0x139)
+#define PPSMC_MSG_SAMPowerON ((uint16_t) 0x13a)
+#define PPSMC_MSG_PCIeDPM_Disable ((uint16_t) 0x13d)
+#define PPSMC_MSG_NBDPM_Enable ((uint16_t) 0x140)
+#define PPSMC_MSG_NBDPM_Disable ((uint16_t) 0x141)
+#define PPSMC_MSG_SCLKDPM_SetEnabledMask ((uint16_t) 0x145)
+#define PPSMC_MSG_MCLKDPM_SetEnabledMask ((uint16_t) 0x146)
+#define PPSMC_MSG_PCIeDPM_ForceLevel ((uint16_t) 0x147)
+#define PPSMC_MSG_PCIeDPM_UnForceLevel ((uint16_t) 0x148)
+#define PPSMC_MSG_EnableVRHotGPIOInterrupt ((uint16_t) 0x14a)
+#define PPSMC_MSG_DPM_Enable ((uint16_t) 0x14e)
+#define PPSMC_MSG_DPM_Disable ((uint16_t) 0x14f)
+#define PPSMC_MSG_MCLKDPM_Enable ((uint16_t) 0x150)
+#define PPSMC_MSG_MCLKDPM_Disable ((uint16_t) 0x151)
+#define PPSMC_MSG_UVDDPM_Enable ((uint16_t) 0x154)
+#define PPSMC_MSG_UVDDPM_Disable ((uint16_t) 0x155)
+#define PPSMC_MSG_SAMUDPM_Enable ((uint16_t) 0x156)
+#define PPSMC_MSG_SAMUDPM_Disable ((uint16_t) 0x157)
+#define PPSMC_MSG_ACPDPM_Enable ((uint16_t) 0x158)
+#define PPSMC_MSG_ACPDPM_Disable ((uint16_t) 0x159)
+#define PPSMC_MSG_VCEDPM_Enable ((uint16_t) 0x15a)
+#define PPSMC_MSG_VCEDPM_Disable ((uint16_t) 0x15b)
+#define PPSMC_MSG_VddC_Request ((uint16_t) 0x15f)
+#define PPSMC_MSG_SCLKDPM_GetEnabledMask ((uint16_t) 0x162)
+#define PPSMC_MSG_PCIeDPM_SetEnabledMask ((uint16_t) 0x167)
+#define PPSMC_MSG_TDCLimitEnable ((uint16_t) 0x169)
+#define PPSMC_MSG_TDCLimitDisable ((uint16_t) 0x16a)
+#define PPSMC_MSG_PkgPwrLimitEnable ((uint16_t) 0x185)
+#define PPSMC_MSG_PkgPwrLimitDisable ((uint16_t) 0x186)
+#define PPSMC_MSG_PkgPwrSetLimit ((uint16_t) 0x187)
+#define PPSMC_MSG_OverDriveSetTargetTdp ((uint16_t) 0x188)
+#define PPSMC_MSG_SCLKDPM_FreezeLevel ((uint16_t) 0x189)
+#define PPSMC_MSG_SCLKDPM_UnfreezeLevel ((uint16_t) 0x18A)
+#define PPSMC_MSG_MCLKDPM_FreezeLevel ((uint16_t) 0x18B)
+#define PPSMC_MSG_MCLKDPM_UnfreezeLevel ((uint16_t) 0x18C)
+#define PPSMC_MSG_MASTER_DeepSleep_ON ((uint16_t) 0x18F)
+#define PPSMC_MSG_MASTER_DeepSleep_OFF ((uint16_t) 0x190)
+#define PPSMC_MSG_Remove_DC_Clamp ((uint16_t) 0x191)
+#define PPSMC_MSG_SetFanPwmMax ((uint16_t) 0x19A)
+#define PPSMC_MSG_SetFanRpmMax ((uint16_t) 0x205)
+
+#define PPSMC_MSG_ENABLE_THERMAL_DPM ((uint16_t) 0x19C)
+#define PPSMC_MSG_DISABLE_THERMAL_DPM ((uint16_t) 0x19D)
+
+#define PPSMC_MSG_API_GetSclkFrequency ((uint16_t) 0x200)
+#define PPSMC_MSG_API_GetMclkFrequency ((uint16_t) 0x201)
+
+/* TN */
+#define PPSMC_MSG_DPM_Config ((uint32_t) 0x102)
+#define PPSMC_MSG_DPM_ForceState ((uint32_t) 0x104)
+#define PPSMC_MSG_PG_SIMD_Config ((uint32_t) 0x108)
+#define PPSMC_MSG_Voltage_Cntl_Enable ((uint32_t) 0x109)
+#define PPSMC_MSG_Thermal_Cntl_Enable ((uint32_t) 0x10a)
+#define PPSMC_MSG_VCEPowerOFF ((uint32_t) 0x10e)
+#define PPSMC_MSG_VCEPowerON ((uint32_t) 0x10f)
+#define PPSMC_MSG_DPM_N_LevelsDisabled ((uint32_t) 0x112)
+#define PPSMC_MSG_DCE_RemoveVoltageAdjustment ((uint32_t) 0x11d)
+#define PPSMC_MSG_DCE_AllowVoltageAdjustment ((uint32_t) 0x11e)
+#define PPSMC_MSG_EnableBAPM ((uint32_t) 0x120)
+#define PPSMC_MSG_DisableBAPM ((uint32_t) 0x121)
+#define PPSMC_MSG_UVD_DPM_Config ((uint32_t) 0x124)
+
+#define PPSMC_MSG_DRV_DRAM_ADDR_HI ((uint16_t) 0x250)
+#define PPSMC_MSG_DRV_DRAM_ADDR_LO ((uint16_t) 0x251)
+#define PPSMC_MSG_SMU_DRAM_ADDR_HI ((uint16_t) 0x252)
+#define PPSMC_MSG_SMU_DRAM_ADDR_LO ((uint16_t) 0x253)
+#define PPSMC_MSG_LoadUcodes ((uint16_t) 0x254)
+
+typedef uint16_t PPSMC_Msg;
+
+#pragma pack(pop)
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/r600_dpm.h b/drivers/gpu/drm/amd/pm/powerplay/r600_dpm.h
new file mode 100644
index 000000000000..055321f61ca7
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/r600_dpm.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 __R600_DPM_H__
+#define __R600_DPM_H__
+
+#define R600_ASI_DFLT 10000
+#define R600_BSP_DFLT 0x41EB
+#define R600_BSU_DFLT 0x2
+#define R600_AH_DFLT 5
+#define R600_RLP_DFLT 25
+#define R600_RMP_DFLT 65
+#define R600_LHP_DFLT 40
+#define R600_LMP_DFLT 15
+#define R600_TD_DFLT 0
+#define R600_UTC_DFLT_00 0x24
+#define R600_UTC_DFLT_01 0x22
+#define R600_UTC_DFLT_02 0x22
+#define R600_UTC_DFLT_03 0x22
+#define R600_UTC_DFLT_04 0x22
+#define R600_UTC_DFLT_05 0x22
+#define R600_UTC_DFLT_06 0x22
+#define R600_UTC_DFLT_07 0x22
+#define R600_UTC_DFLT_08 0x22
+#define R600_UTC_DFLT_09 0x22
+#define R600_UTC_DFLT_10 0x22
+#define R600_UTC_DFLT_11 0x22
+#define R600_UTC_DFLT_12 0x22
+#define R600_UTC_DFLT_13 0x22
+#define R600_UTC_DFLT_14 0x22
+#define R600_DTC_DFLT_00 0x24
+#define R600_DTC_DFLT_01 0x22
+#define R600_DTC_DFLT_02 0x22
+#define R600_DTC_DFLT_03 0x22
+#define R600_DTC_DFLT_04 0x22
+#define R600_DTC_DFLT_05 0x22
+#define R600_DTC_DFLT_06 0x22
+#define R600_DTC_DFLT_07 0x22
+#define R600_DTC_DFLT_08 0x22
+#define R600_DTC_DFLT_09 0x22
+#define R600_DTC_DFLT_10 0x22
+#define R600_DTC_DFLT_11 0x22
+#define R600_DTC_DFLT_12 0x22
+#define R600_DTC_DFLT_13 0x22
+#define R600_DTC_DFLT_14 0x22
+#define R600_VRC_DFLT 0x0000C003
+#define R600_VOLTAGERESPONSETIME_DFLT 1000
+#define R600_BACKBIASRESPONSETIME_DFLT 1000
+#define R600_VRU_DFLT 0x3
+#define R600_SPLLSTEPTIME_DFLT 0x1000
+#define R600_SPLLSTEPUNIT_DFLT 0x3
+#define R600_TPU_DFLT 0
+#define R600_TPC_DFLT 0x200
+#define R600_SSTU_DFLT 0
+#define R600_SST_DFLT 0x00C8
+#define R600_GICST_DFLT 0x200
+#define R600_FCT_DFLT 0x0400
+#define R600_FCTU_DFLT 0
+#define R600_CTXCGTT3DRPHC_DFLT 0x20
+#define R600_CTXCGTT3DRSDC_DFLT 0x40
+#define R600_VDDC3DOORPHC_DFLT 0x100
+#define R600_VDDC3DOORSDC_DFLT 0x7
+#define R600_VDDC3DOORSU_DFLT 0
+#define R600_MPLLLOCKTIME_DFLT 100
+#define R600_MPLLRESETTIME_DFLT 150
+#define R600_VCOSTEPPCT_DFLT 20
+#define R600_ENDINGVCOSTEPPCT_DFLT 5
+#define R600_REFERENCEDIVIDER_DFLT 4
+
+#define R600_PM_NUMBER_OF_TC 15
+#define R600_PM_NUMBER_OF_SCLKS 20
+#define R600_PM_NUMBER_OF_MCLKS 4
+#define R600_PM_NUMBER_OF_VOLTAGE_LEVELS 4
+#define R600_PM_NUMBER_OF_ACTIVITY_LEVELS 3
+
+/* XXX are these ok? */
+#define R600_TEMP_RANGE_MIN (90 * 1000)
+#define R600_TEMP_RANGE_MAX (120 * 1000)
+
+#define FDO_PWM_MODE_STATIC 1
+#define FDO_PWM_MODE_STATIC_RPM 5
+
+enum r600_power_level {
+ R600_POWER_LEVEL_LOW = 0,
+ R600_POWER_LEVEL_MEDIUM = 1,
+ R600_POWER_LEVEL_HIGH = 2,
+ R600_POWER_LEVEL_CTXSW = 3,
+};
+
+enum r600_td {
+ R600_TD_AUTO,
+ R600_TD_UP,
+ R600_TD_DOWN,
+};
+
+enum r600_display_watermark {
+ R600_DISPLAY_WATERMARK_LOW = 0,
+ R600_DISPLAY_WATERMARK_HIGH = 1,
+};
+
+enum r600_display_gap
+{
+ R600_PM_DISPLAY_GAP_VBLANK_OR_WM = 0,
+ R600_PM_DISPLAY_GAP_VBLANK = 1,
+ R600_PM_DISPLAY_GAP_WATERMARK = 2,
+ R600_PM_DISPLAY_GAP_IGNORE = 3,
+};
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/si_dpm.c b/drivers/gpu/drm/amd/pm/powerplay/si_dpm.c
new file mode 100644
index 000000000000..b5986d19dc08
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/si_dpm.c
@@ -0,0 +1,8079 @@
+/*
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <linux/module.h>
+#include <linux/pci.h>
+
+#include "amdgpu.h"
+#include "amdgpu_pm.h"
+#include "amdgpu_dpm.h"
+#include "amdgpu_atombios.h"
+#include "amd_pcie.h"
+#include "sid.h"
+#include "r600_dpm.h"
+#include "si_dpm.h"
+#include "atom.h"
+#include "../include/pptable.h"
+#include <linux/math64.h>
+#include <linux/seq_file.h>
+#include <linux/firmware.h>
+
+#define MC_CG_ARB_FREQ_F0 0x0a
+#define MC_CG_ARB_FREQ_F1 0x0b
+#define MC_CG_ARB_FREQ_F2 0x0c
+#define MC_CG_ARB_FREQ_F3 0x0d
+
+#define SMC_RAM_END 0x20000
+
+#define SCLK_MIN_DEEPSLEEP_FREQ 1350
+
+
+/* sizeof(ATOM_PPLIB_EXTENDEDHEADER) */
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2 12
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3 14
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4 16
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V5 18
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6 20
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7 22
+
+#define BIOS_SCRATCH_4 0x5cd
+
+MODULE_FIRMWARE("amdgpu/tahiti_smc.bin");
+MODULE_FIRMWARE("amdgpu/pitcairn_smc.bin");
+MODULE_FIRMWARE("amdgpu/pitcairn_k_smc.bin");
+MODULE_FIRMWARE("amdgpu/verde_smc.bin");
+MODULE_FIRMWARE("amdgpu/verde_k_smc.bin");
+MODULE_FIRMWARE("amdgpu/oland_smc.bin");
+MODULE_FIRMWARE("amdgpu/oland_k_smc.bin");
+MODULE_FIRMWARE("amdgpu/hainan_smc.bin");
+MODULE_FIRMWARE("amdgpu/hainan_k_smc.bin");
+MODULE_FIRMWARE("amdgpu/banks_k_2_smc.bin");
+
+static const struct amd_pm_funcs si_dpm_funcs;
+
+union power_info {
+ struct _ATOM_POWERPLAY_INFO info;
+ struct _ATOM_POWERPLAY_INFO_V2 info_2;
+ struct _ATOM_POWERPLAY_INFO_V3 info_3;
+ struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
+ struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
+ struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+ struct _ATOM_PPLIB_POWERPLAYTABLE4 pplib4;
+ struct _ATOM_PPLIB_POWERPLAYTABLE5 pplib5;
+};
+
+union fan_info {
+ struct _ATOM_PPLIB_FANTABLE fan;
+ struct _ATOM_PPLIB_FANTABLE2 fan2;
+ struct _ATOM_PPLIB_FANTABLE3 fan3;
+};
+
+union pplib_clock_info {
+ struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
+ struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
+ struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
+ struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+ struct _ATOM_PPLIB_SI_CLOCK_INFO si;
+};
+
+static const u32 r600_utc[R600_PM_NUMBER_OF_TC] =
+{
+ R600_UTC_DFLT_00,
+ R600_UTC_DFLT_01,
+ R600_UTC_DFLT_02,
+ R600_UTC_DFLT_03,
+ R600_UTC_DFLT_04,
+ R600_UTC_DFLT_05,
+ R600_UTC_DFLT_06,
+ R600_UTC_DFLT_07,
+ R600_UTC_DFLT_08,
+ R600_UTC_DFLT_09,
+ R600_UTC_DFLT_10,
+ R600_UTC_DFLT_11,
+ R600_UTC_DFLT_12,
+ R600_UTC_DFLT_13,
+ R600_UTC_DFLT_14,
+};
+
+static const u32 r600_dtc[R600_PM_NUMBER_OF_TC] =
+{
+ R600_DTC_DFLT_00,
+ R600_DTC_DFLT_01,
+ R600_DTC_DFLT_02,
+ R600_DTC_DFLT_03,
+ R600_DTC_DFLT_04,
+ R600_DTC_DFLT_05,
+ R600_DTC_DFLT_06,
+ R600_DTC_DFLT_07,
+ R600_DTC_DFLT_08,
+ R600_DTC_DFLT_09,
+ R600_DTC_DFLT_10,
+ R600_DTC_DFLT_11,
+ R600_DTC_DFLT_12,
+ R600_DTC_DFLT_13,
+ R600_DTC_DFLT_14,
+};
+
+static const struct si_cac_config_reg cac_weights_tahiti[] =
+{
+ { 0x0, 0x0000ffff, 0, 0xc, SISLANDS_CACCONFIG_CGIND },
+ { 0x0, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0x0000ffff, 0, 0x101, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0xffff0000, 16, 0xc, SISLANDS_CACCONFIG_CGIND },
+ { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0x0000ffff, 0, 0x8fc, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0x0000ffff, 0, 0x95, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0xffff0000, 16, 0x34e, SISLANDS_CACCONFIG_CGIND },
+ { 0x18f, 0x0000ffff, 0, 0x1a1, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0x0000ffff, 0, 0xda, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0xffff0000, 16, 0x46, SISLANDS_CACCONFIG_CGIND },
+ { 0x9, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0xa, 0x0000ffff, 0, 0x208, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0x0000ffff, 0, 0xe7, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0xffff0000, 16, 0x948, SISLANDS_CACCONFIG_CGIND },
+ { 0xc, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0xe, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0x0000ffff, 0, 0x167, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x12, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+ { 0x14, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0x0000ffff, 0, 0x31, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x20, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x6d, 0x0000ffff, 0, 0x18e, SISLANDS_CACCONFIG_CGIND },
+ { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_tahiti[] =
+{
+ { 0x143, 0x0001fffe, 1, 0x3, SISLANDS_CACCONFIG_CGIND },
+ { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x146, 0x0001fffe, 1, 0x3, SISLANDS_CACCONFIG_CGIND },
+ { 0x146, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x149, 0x0001fffe, 1, 0x3, SISLANDS_CACCONFIG_CGIND },
+ { 0x149, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x14c, 0x0001fffe, 1, 0x3, SISLANDS_CACCONFIG_CGIND },
+ { 0x14c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x9b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x9b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x9e, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x9e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x101, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x101, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x107, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x107, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x10a, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x10a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x10d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x10d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x8c, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+ { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x8f, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+ { 0x8f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x92, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+ { 0x92, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x95, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+ { 0x95, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x14f, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+ { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x152, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+ { 0x152, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x155, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+ { 0x155, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x158, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+ { 0x158, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x110, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+ { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x113, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+ { 0x113, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x116, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+ { 0x116, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x119, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+ { 0x119, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x122, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x122, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x125, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x125, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x128, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x128, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x12b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x12b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x15b, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x15e, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x161, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x164, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x167, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x16a, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x16d, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+ { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x170, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x173, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x176, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x179, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x17c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x17f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0xFFFFFFFF }
+
+};
+
+static const struct si_cac_config_reg cac_override_tahiti[] =
+{
+ { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_tahiti =
+{
+ ((1 << 16) | 27027),
+ 6,
+ 0,
+ 4,
+ 95,
+ {
+ 0UL,
+ 0UL,
+ 4521550UL,
+ 309631529UL,
+ -1270850L,
+ 4513710L,
+ 40
+ },
+ 595000000UL,
+ 12,
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ },
+ true
+};
+
+static const struct si_dte_data dte_data_tahiti =
+{
+ { 1159409, 0, 0, 0, 0 },
+ { 777, 0, 0, 0, 0 },
+ 2,
+ 54000,
+ 127000,
+ 25,
+ 2,
+ 10,
+ 13,
+ { 27, 31, 35, 39, 43, 47, 54, 61, 67, 74, 81, 88, 95, 0, 0, 0 },
+ { 240888759, 221057860, 235370597, 162287531, 158510299, 131423027, 116673180, 103067515, 87941937, 76209048, 68209175, 64090048, 58301890, 0, 0, 0 },
+ { 12024, 11189, 11451, 8411, 7939, 6666, 5681, 4905, 4241, 3720, 3354, 3122, 2890, 0, 0, 0 },
+ 85,
+ false
+};
+
+static const struct si_dte_data dte_data_tahiti_pro =
+{
+ { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+ { 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 5,
+ 45000,
+ 100,
+ 0xA,
+ 1,
+ 0,
+ 0x10,
+ { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+ { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+ { 0x7D0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 90,
+ true
+};
+
+static const struct si_dte_data dte_data_new_zealand =
+{
+ { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0 },
+ { 0x29B, 0x3E9, 0x537, 0x7D2, 0 },
+ 0x5,
+ 0xAFC8,
+ 0x69,
+ 0x32,
+ 1,
+ 0,
+ 0x10,
+ { 0x82, 0xA0, 0xB4, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE },
+ { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+ { 0xDAC, 0x1388, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685 },
+ 85,
+ true
+};
+
+static const struct si_dte_data dte_data_aruba_pro =
+{
+ { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+ { 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 5,
+ 45000,
+ 100,
+ 0xA,
+ 1,
+ 0,
+ 0x10,
+ { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+ { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+ { 0x1000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 90,
+ true
+};
+
+static const struct si_dte_data dte_data_malta =
+{
+ { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+ { 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 5,
+ 45000,
+ 100,
+ 0xA,
+ 1,
+ 0,
+ 0x10,
+ { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+ { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+ { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 90,
+ true
+};
+
+static const struct si_cac_config_reg cac_weights_pitcairn[] =
+{
+ { 0x0, 0x0000ffff, 0, 0x8a, SISLANDS_CACCONFIG_CGIND },
+ { 0x0, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0xffff0000, 16, 0x24d, SISLANDS_CACCONFIG_CGIND },
+ { 0x2, 0x0000ffff, 0, 0x19, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0x0000ffff, 0, 0xc11, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0xffff0000, 16, 0x7f3, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0x0000ffff, 0, 0x403, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0xffff0000, 16, 0x367, SISLANDS_CACCONFIG_CGIND },
+ { 0x18f, 0x0000ffff, 0, 0x4c9, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0xffff0000, 16, 0x45d, SISLANDS_CACCONFIG_CGIND },
+ { 0x9, 0x0000ffff, 0, 0x36d, SISLANDS_CACCONFIG_CGIND },
+ { 0xa, 0x0000ffff, 0, 0x534, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0x0000ffff, 0, 0x5da, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0xffff0000, 16, 0x880, SISLANDS_CACCONFIG_CGIND },
+ { 0xc, 0x0000ffff, 0, 0x201, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0xe, 0x0000ffff, 0, 0x9f, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0x0000ffff, 0, 0x1f, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0x0000ffff, 0, 0x5de, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x12, 0x0000ffff, 0, 0x7b, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0xffff0000, 16, 0x13, SISLANDS_CACCONFIG_CGIND },
+ { 0x14, 0x0000ffff, 0, 0xf9, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0x0000ffff, 0, 0x66, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x4e, 0x0000ffff, 0, 0x13, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x20, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x6d, 0x0000ffff, 0, 0x186, SISLANDS_CACCONFIG_CGIND },
+ { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_pitcairn[] =
+{
+ { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x110, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x14f, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x8c, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x143, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x9b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x9b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x107, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x107, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x113, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x113, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x152, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x152, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x8f, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x8f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x146, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x146, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x9e, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x9e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x10a, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x10a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x116, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x116, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x155, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x155, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x92, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x92, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x149, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x149, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x101, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x101, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x10d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x10d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x119, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x119, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x158, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x158, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x95, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x95, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x14c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x14c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x122, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x122, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x125, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x125, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x128, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x128, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x12b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x12b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x164, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x167, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x16a, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x15e, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x161, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x15b, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x16d, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x170, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x173, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x176, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x179, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x17c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x17f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_override_pitcairn[] =
+{
+ { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_pitcairn =
+{
+ ((1 << 16) | 27027),
+ 5,
+ 0,
+ 6,
+ 100,
+ {
+ 51600000UL,
+ 1800000UL,
+ 7194395UL,
+ 309631529UL,
+ -1270850L,
+ 4513710L,
+ 100
+ },
+ 117830498UL,
+ 12,
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ },
+ true
+};
+
+static const struct si_dte_data dte_data_pitcairn =
+{
+ { 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0 },
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ 0,
+ false
+};
+
+static const struct si_dte_data dte_data_curacao_xt =
+{
+ { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+ { 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 5,
+ 45000,
+ 100,
+ 0xA,
+ 1,
+ 0,
+ 0x10,
+ { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+ { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+ { 0x1D17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 90,
+ true
+};
+
+static const struct si_dte_data dte_data_curacao_pro =
+{
+ { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+ { 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 5,
+ 45000,
+ 100,
+ 0xA,
+ 1,
+ 0,
+ 0x10,
+ { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+ { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+ { 0x1D17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 90,
+ true
+};
+
+static const struct si_dte_data dte_data_neptune_xt =
+{
+ { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+ { 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 5,
+ 45000,
+ 100,
+ 0xA,
+ 1,
+ 0,
+ 0x10,
+ { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+ { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+ { 0x3A2F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 90,
+ true
+};
+
+static const struct si_cac_config_reg cac_weights_chelsea_pro[] =
+{
+ { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+ { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+ { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+ { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+ { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+ { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+ { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+ { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+ { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x14, 0x0000ffff, 0, 0x2BD, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+ { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+ { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+ { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_chelsea_xt[] =
+{
+ { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+ { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+ { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+ { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+ { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+ { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+ { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+ { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+ { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x14, 0x0000ffff, 0, 0x30A, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+ { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+ { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+ { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_heathrow[] =
+{
+ { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+ { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+ { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+ { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+ { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+ { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+ { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+ { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+ { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x14, 0x0000ffff, 0, 0x362, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+ { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+ { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+ { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_cape_verde_pro[] =
+{
+ { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+ { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+ { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+ { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+ { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+ { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+ { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+ { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+ { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x14, 0x0000ffff, 0, 0x315, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+ { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+ { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+ { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_cape_verde[] =
+{
+ { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+ { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+ { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+ { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+ { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+ { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+ { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+ { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+ { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x14, 0x0000ffff, 0, 0x3BA, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+ { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+ { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+ { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_cape_verde[] =
+{
+ { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x110, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x14f, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x8c, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x143, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x9b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x9b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x107, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x107, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x113, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x113, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x152, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x152, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x8f, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x8f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x146, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x146, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x164, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x167, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x16a, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x15e, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x161, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x15b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x16d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x170, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x173, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x176, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x179, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x17c, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x17f, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_override_cape_verde[] =
+{
+ { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_cape_verde =
+{
+ ((1 << 16) | 0x6993),
+ 5,
+ 0,
+ 7,
+ 105,
+ {
+ 0UL,
+ 0UL,
+ 7194395UL,
+ 309631529UL,
+ -1270850L,
+ 4513710L,
+ 100
+ },
+ 117830498UL,
+ 12,
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ },
+ true
+};
+
+static const struct si_dte_data dte_data_cape_verde =
+{
+ { 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0 },
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ 0,
+ false
+};
+
+static const struct si_dte_data dte_data_venus_xtx =
+{
+ { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+ { 0x71C, 0xAAB, 0xE39, 0x11C7, 0x0 },
+ 5,
+ 55000,
+ 0x69,
+ 0xA,
+ 1,
+ 0,
+ 0x3,
+ { 0x96, 0xB4, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+ { 0x895440, 0x3D0900, 0x989680, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+ { 0xD6D8, 0x88B8, 0x1555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 90,
+ true
+};
+
+static const struct si_dte_data dte_data_venus_xt =
+{
+ { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+ { 0xBDA, 0x11C7, 0x17B4, 0x1DA1, 0x0 },
+ 5,
+ 55000,
+ 0x69,
+ 0xA,
+ 1,
+ 0,
+ 0x3,
+ { 0x96, 0xB4, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+ { 0x895440, 0x3D0900, 0x989680, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+ { 0xAFC8, 0x88B8, 0x238E, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 90,
+ true
+};
+
+static const struct si_dte_data dte_data_venus_pro =
+{
+ { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+ { 0x11C7, 0x1AAB, 0x238E, 0x2C72, 0x0 },
+ 5,
+ 55000,
+ 0x69,
+ 0xA,
+ 1,
+ 0,
+ 0x3,
+ { 0x96, 0xB4, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+ { 0x895440, 0x3D0900, 0x989680, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+ { 0x88B8, 0x88B8, 0x3555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 90,
+ true
+};
+
+static const struct si_cac_config_reg cac_weights_oland[] =
+{
+ { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+ { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+ { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+ { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+ { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+ { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+ { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+ { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+ { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x14, 0x0000ffff, 0, 0x3BA, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+ { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+ { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+ { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_mars_pro[] =
+{
+ { 0x0, 0x0000ffff, 0, 0x43, SISLANDS_CACCONFIG_CGIND },
+ { 0x0, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0x0000ffff, 0, 0xAF, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0xffff0000, 16, 0x2A, SISLANDS_CACCONFIG_CGIND },
+ { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0xffff0000, 16, 0x59, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0x0000ffff, 0, 0x1A5, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0xffff0000, 16, 0x1D6, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0x0000ffff, 0, 0x2A3, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0xffff0000, 16, 0x8FD, SISLANDS_CACCONFIG_CGIND },
+ { 0x18f, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0x0000ffff, 0, 0x8A, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0xffff0000, 16, 0xA3, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0x0000ffff, 0, 0x71, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0xffff0000, 16, 0x36, SISLANDS_CACCONFIG_CGIND },
+ { 0x9, 0x0000ffff, 0, 0xA6, SISLANDS_CACCONFIG_CGIND },
+ { 0xa, 0x0000ffff, 0, 0x81, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0x0000ffff, 0, 0x3D2, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0xffff0000, 16, 0x27C, SISLANDS_CACCONFIG_CGIND },
+ { 0xc, 0x0000ffff, 0, 0xA96, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0xffff0000, 16, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0xe, 0x0000ffff, 0, 0xB, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0x0000ffff, 0, 0x15, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+ { 0x12, 0x0000ffff, 0, 0x36, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0x0000ffff, 0, 0x10, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0xffff0000, 16, 0x10, SISLANDS_CACCONFIG_CGIND },
+ { 0x14, 0x0000ffff, 0, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+ { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0x0000ffff, 0, 0x32, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0xffff0000, 16, 0x7E, SISLANDS_CACCONFIG_CGIND },
+ { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0x0000ffff, 0, 0x280, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0x0000ffff, 0, 0x3C, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0xffff0000, 16, 0x203, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x6d, 0x0000ffff, 0, 0xB4, SISLANDS_CACCONFIG_CGIND },
+ { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_mars_xt[] =
+{
+ { 0x0, 0x0000ffff, 0, 0x43, SISLANDS_CACCONFIG_CGIND },
+ { 0x0, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0x0000ffff, 0, 0xAF, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0xffff0000, 16, 0x2A, SISLANDS_CACCONFIG_CGIND },
+ { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0xffff0000, 16, 0x59, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0x0000ffff, 0, 0x1A5, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0xffff0000, 16, 0x1D6, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0x0000ffff, 0, 0x2A3, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0xffff0000, 16, 0x8FD, SISLANDS_CACCONFIG_CGIND },
+ { 0x18f, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0x0000ffff, 0, 0x8A, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0xffff0000, 16, 0xA3, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0x0000ffff, 0, 0x71, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0xffff0000, 16, 0x36, SISLANDS_CACCONFIG_CGIND },
+ { 0x9, 0x0000ffff, 0, 0xA6, SISLANDS_CACCONFIG_CGIND },
+ { 0xa, 0x0000ffff, 0, 0x81, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0x0000ffff, 0, 0x3D2, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0xffff0000, 16, 0x27C, SISLANDS_CACCONFIG_CGIND },
+ { 0xc, 0x0000ffff, 0, 0xA96, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0xffff0000, 16, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0xe, 0x0000ffff, 0, 0xB, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0x0000ffff, 0, 0x15, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+ { 0x12, 0x0000ffff, 0, 0x36, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0x0000ffff, 0, 0x10, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0xffff0000, 16, 0x10, SISLANDS_CACCONFIG_CGIND },
+ { 0x14, 0x0000ffff, 0, 0x60, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+ { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0x0000ffff, 0, 0x32, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0xffff0000, 16, 0x7E, SISLANDS_CACCONFIG_CGIND },
+ { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0x0000ffff, 0, 0x280, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0x0000ffff, 0, 0x3C, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0xffff0000, 16, 0x203, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x6d, 0x0000ffff, 0, 0xB4, SISLANDS_CACCONFIG_CGIND },
+ { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_oland_pro[] =
+{
+ { 0x0, 0x0000ffff, 0, 0x43, SISLANDS_CACCONFIG_CGIND },
+ { 0x0, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0x0000ffff, 0, 0xAF, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0xffff0000, 16, 0x2A, SISLANDS_CACCONFIG_CGIND },
+ { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0xffff0000, 16, 0x59, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0x0000ffff, 0, 0x1A5, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0xffff0000, 16, 0x1D6, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0x0000ffff, 0, 0x2A3, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0xffff0000, 16, 0x8FD, SISLANDS_CACCONFIG_CGIND },
+ { 0x18f, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0x0000ffff, 0, 0x8A, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0xffff0000, 16, 0xA3, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0x0000ffff, 0, 0x71, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0xffff0000, 16, 0x36, SISLANDS_CACCONFIG_CGIND },
+ { 0x9, 0x0000ffff, 0, 0xA6, SISLANDS_CACCONFIG_CGIND },
+ { 0xa, 0x0000ffff, 0, 0x81, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0x0000ffff, 0, 0x3D2, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0xffff0000, 16, 0x27C, SISLANDS_CACCONFIG_CGIND },
+ { 0xc, 0x0000ffff, 0, 0xA96, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0xffff0000, 16, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0xe, 0x0000ffff, 0, 0xB, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0x0000ffff, 0, 0x15, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+ { 0x12, 0x0000ffff, 0, 0x36, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0x0000ffff, 0, 0x10, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0xffff0000, 16, 0x10, SISLANDS_CACCONFIG_CGIND },
+ { 0x14, 0x0000ffff, 0, 0x90, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+ { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0x0000ffff, 0, 0x32, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0xffff0000, 16, 0x7E, SISLANDS_CACCONFIG_CGIND },
+ { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0x0000ffff, 0, 0x280, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0x0000ffff, 0, 0x3C, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0xffff0000, 16, 0x203, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x6d, 0x0000ffff, 0, 0xB4, SISLANDS_CACCONFIG_CGIND },
+ { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_oland_xt[] =
+{
+ { 0x0, 0x0000ffff, 0, 0x43, SISLANDS_CACCONFIG_CGIND },
+ { 0x0, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0x0000ffff, 0, 0xAF, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0xffff0000, 16, 0x2A, SISLANDS_CACCONFIG_CGIND },
+ { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0xffff0000, 16, 0x59, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0x0000ffff, 0, 0x1A5, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0xffff0000, 16, 0x1D6, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0x0000ffff, 0, 0x2A3, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0xffff0000, 16, 0x8FD, SISLANDS_CACCONFIG_CGIND },
+ { 0x18f, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0x0000ffff, 0, 0x8A, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0xffff0000, 16, 0xA3, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0x0000ffff, 0, 0x71, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0xffff0000, 16, 0x36, SISLANDS_CACCONFIG_CGIND },
+ { 0x9, 0x0000ffff, 0, 0xA6, SISLANDS_CACCONFIG_CGIND },
+ { 0xa, 0x0000ffff, 0, 0x81, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0x0000ffff, 0, 0x3D2, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0xffff0000, 16, 0x27C, SISLANDS_CACCONFIG_CGIND },
+ { 0xc, 0x0000ffff, 0, 0xA96, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0xffff0000, 16, 0x5, SISLANDS_CACCONFIG_CGIND },
+ { 0xe, 0x0000ffff, 0, 0xB, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0x0000ffff, 0, 0x15, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+ { 0x12, 0x0000ffff, 0, 0x36, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0x0000ffff, 0, 0x10, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0xffff0000, 16, 0x10, SISLANDS_CACCONFIG_CGIND },
+ { 0x14, 0x0000ffff, 0, 0x120, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+ { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0x0000ffff, 0, 0x32, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0xffff0000, 16, 0x7E, SISLANDS_CACCONFIG_CGIND },
+ { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0x0000ffff, 0, 0x280, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0x0000ffff, 0, 0x3C, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0xffff0000, 16, 0x203, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x6d, 0x0000ffff, 0, 0xB4, SISLANDS_CACCONFIG_CGIND },
+ { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_oland[] =
+{
+ { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x110, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+ { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x14f, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+ { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x8c, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+ { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x143, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+ { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x164, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x167, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x16a, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x15e, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x161, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x15b, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x16d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x170, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x173, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x176, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x179, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x17c, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x17f, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_mars_pro[] =
+{
+ { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x110, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+ { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x14f, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+ { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x8c, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+ { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x143, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x164, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x167, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x16a, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x15e, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x161, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x15b, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x16d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+ { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x170, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x173, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x176, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x179, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x17c, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x17f, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+ { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_override_oland[] =
+{
+ { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_oland =
+{
+ ((1 << 16) | 0x6993),
+ 5,
+ 0,
+ 7,
+ 105,
+ {
+ 0UL,
+ 0UL,
+ 7194395UL,
+ 309631529UL,
+ -1270850L,
+ 4513710L,
+ 100
+ },
+ 117830498UL,
+ 12,
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ },
+ true
+};
+
+static const struct si_powertune_data powertune_data_mars_pro =
+{
+ ((1 << 16) | 0x6993),
+ 5,
+ 0,
+ 7,
+ 105,
+ {
+ 0UL,
+ 0UL,
+ 7194395UL,
+ 309631529UL,
+ -1270850L,
+ 4513710L,
+ 100
+ },
+ 117830498UL,
+ 12,
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ },
+ true
+};
+
+static const struct si_dte_data dte_data_oland =
+{
+ { 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0 },
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ 0,
+ false
+};
+
+static const struct si_dte_data dte_data_mars_pro =
+{
+ { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+ { 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 5,
+ 55000,
+ 105,
+ 0xA,
+ 1,
+ 0,
+ 0x10,
+ { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+ { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+ { 0xF627, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 90,
+ true
+};
+
+static const struct si_dte_data dte_data_sun_xt =
+{
+ { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+ { 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 5,
+ 55000,
+ 105,
+ 0xA,
+ 1,
+ 0,
+ 0x10,
+ { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+ { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+ { 0xD555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+ 90,
+ true
+};
+
+
+static const struct si_cac_config_reg cac_weights_hainan[] =
+{
+ { 0x0, 0x0000ffff, 0, 0x2d9, SISLANDS_CACCONFIG_CGIND },
+ { 0x0, 0xffff0000, 16, 0x22b, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0x0000ffff, 0, 0x21c, SISLANDS_CACCONFIG_CGIND },
+ { 0x1, 0xffff0000, 16, 0x1dc, SISLANDS_CACCONFIG_CGIND },
+ { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0x0000ffff, 0, 0x24e, SISLANDS_CACCONFIG_CGIND },
+ { 0x3, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x4, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0x0000ffff, 0, 0x35e, SISLANDS_CACCONFIG_CGIND },
+ { 0x5, 0xffff0000, 16, 0x1143, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0x0000ffff, 0, 0xe17, SISLANDS_CACCONFIG_CGIND },
+ { 0x6, 0xffff0000, 16, 0x441, SISLANDS_CACCONFIG_CGIND },
+ { 0x18f, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0x0000ffff, 0, 0x28b, SISLANDS_CACCONFIG_CGIND },
+ { 0x7, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x8, 0xffff0000, 16, 0xabe, SISLANDS_CACCONFIG_CGIND },
+ { 0x9, 0x0000ffff, 0, 0xf11, SISLANDS_CACCONFIG_CGIND },
+ { 0xa, 0x0000ffff, 0, 0x907, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0x0000ffff, 0, 0xb45, SISLANDS_CACCONFIG_CGIND },
+ { 0xb, 0xffff0000, 16, 0xd1e, SISLANDS_CACCONFIG_CGIND },
+ { 0xc, 0x0000ffff, 0, 0xa2c, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0x0000ffff, 0, 0x62, SISLANDS_CACCONFIG_CGIND },
+ { 0xd, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0xe, 0x0000ffff, 0, 0x1f3, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0x0000ffff, 0, 0x42, SISLANDS_CACCONFIG_CGIND },
+ { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x10, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0x0000ffff, 0, 0x709, SISLANDS_CACCONFIG_CGIND },
+ { 0x11, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x12, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x13, 0xffff0000, 16, 0x3a, SISLANDS_CACCONFIG_CGIND },
+ { 0x14, 0x0000ffff, 0, 0x357, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0x0000ffff, 0, 0x9f, SISLANDS_CACCONFIG_CGIND },
+ { 0x15, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0x0000ffff, 0, 0x314, SISLANDS_CACCONFIG_CGIND },
+ { 0x16, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x17, 0x0000ffff, 0, 0x6d, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+ { 0x6d, 0x0000ffff, 0, 0x1b9, SISLANDS_CACCONFIG_CGIND },
+ { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_hainan =
+{
+ ((1 << 16) | 0x6993),
+ 5,
+ 0,
+ 9,
+ 105,
+ {
+ 0UL,
+ 0UL,
+ 7194395UL,
+ 309631529UL,
+ -1270850L,
+ 4513710L,
+ 100
+ },
+ 117830498UL,
+ 12,
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ },
+ true
+};
+
+static struct rv7xx_power_info *rv770_get_pi(struct amdgpu_device *adev);
+static struct evergreen_power_info *evergreen_get_pi(struct amdgpu_device *adev);
+static struct ni_power_info *ni_get_pi(struct amdgpu_device *adev);
+static struct si_ps *si_get_ps(struct amdgpu_ps *rps);
+
+static int si_populate_voltage_value(struct amdgpu_device *adev,
+ const struct atom_voltage_table *table,
+ u16 value, SISLANDS_SMC_VOLTAGE_VALUE *voltage);
+static int si_get_std_voltage_value(struct amdgpu_device *adev,
+ SISLANDS_SMC_VOLTAGE_VALUE *voltage,
+ u16 *std_voltage);
+static int si_write_smc_soft_register(struct amdgpu_device *adev,
+ u16 reg_offset, u32 value);
+static int si_convert_power_level_to_smc(struct amdgpu_device *adev,
+ struct rv7xx_pl *pl,
+ SISLANDS_SMC_HW_PERFORMANCE_LEVEL *level);
+static int si_calculate_sclk_params(struct amdgpu_device *adev,
+ u32 engine_clock,
+ SISLANDS_SMC_SCLK_VALUE *sclk);
+
+static void si_thermal_start_smc_fan_control(struct amdgpu_device *adev);
+static void si_fan_ctrl_set_default_mode(struct amdgpu_device *adev);
+static void si_dpm_set_irq_funcs(struct amdgpu_device *adev);
+
+static struct si_power_info *si_get_pi(struct amdgpu_device *adev)
+{
+ struct si_power_info *pi = adev->pm.dpm.priv;
+ return pi;
+}
+
+static void si_calculate_leakage_for_v_and_t_formula(const struct ni_leakage_coeffients *coeff,
+ u16 v, s32 t, u32 ileakage, u32 *leakage)
+{
+ s64 kt, kv, leakage_w, i_leakage, vddc;
+ s64 temperature, t_slope, t_intercept, av, bv, t_ref;
+ s64 tmp;
+
+ i_leakage = div64_s64(drm_int2fixp(ileakage), 100);
+ vddc = div64_s64(drm_int2fixp(v), 1000);
+ temperature = div64_s64(drm_int2fixp(t), 1000);
+
+ t_slope = div64_s64(drm_int2fixp(coeff->t_slope), 100000000);
+ t_intercept = div64_s64(drm_int2fixp(coeff->t_intercept), 100000000);
+ av = div64_s64(drm_int2fixp(coeff->av), 100000000);
+ bv = div64_s64(drm_int2fixp(coeff->bv), 100000000);
+ t_ref = drm_int2fixp(coeff->t_ref);
+
+ tmp = drm_fixp_mul(t_slope, vddc) + t_intercept;
+ kt = drm_fixp_exp(drm_fixp_mul(tmp, temperature));
+ kt = drm_fixp_div(kt, drm_fixp_exp(drm_fixp_mul(tmp, t_ref)));
+ kv = drm_fixp_mul(av, drm_fixp_exp(drm_fixp_mul(bv, vddc)));
+
+ leakage_w = drm_fixp_mul(drm_fixp_mul(drm_fixp_mul(i_leakage, kt), kv), vddc);
+
+ *leakage = drm_fixp2int(leakage_w * 1000);
+}
+
+static void si_calculate_leakage_for_v_and_t(struct amdgpu_device *adev,
+ const struct ni_leakage_coeffients *coeff,
+ u16 v,
+ s32 t,
+ u32 i_leakage,
+ u32 *leakage)
+{
+ si_calculate_leakage_for_v_and_t_formula(coeff, v, t, i_leakage, leakage);
+}
+
+static void si_calculate_leakage_for_v_formula(const struct ni_leakage_coeffients *coeff,
+ const u32 fixed_kt, u16 v,
+ u32 ileakage, u32 *leakage)
+{
+ s64 kt, kv, leakage_w, i_leakage, vddc;
+
+ i_leakage = div64_s64(drm_int2fixp(ileakage), 100);
+ vddc = div64_s64(drm_int2fixp(v), 1000);
+
+ kt = div64_s64(drm_int2fixp(fixed_kt), 100000000);
+ kv = drm_fixp_mul(div64_s64(drm_int2fixp(coeff->av), 100000000),
+ drm_fixp_exp(drm_fixp_mul(div64_s64(drm_int2fixp(coeff->bv), 100000000), vddc)));
+
+ leakage_w = drm_fixp_mul(drm_fixp_mul(drm_fixp_mul(i_leakage, kt), kv), vddc);
+
+ *leakage = drm_fixp2int(leakage_w * 1000);
+}
+
+static void si_calculate_leakage_for_v(struct amdgpu_device *adev,
+ const struct ni_leakage_coeffients *coeff,
+ const u32 fixed_kt,
+ u16 v,
+ u32 i_leakage,
+ u32 *leakage)
+{
+ si_calculate_leakage_for_v_formula(coeff, fixed_kt, v, i_leakage, leakage);
+}
+
+
+static void si_update_dte_from_pl2(struct amdgpu_device *adev,
+ struct si_dte_data *dte_data)
+{
+ u32 p_limit1 = adev->pm.dpm.tdp_limit;
+ u32 p_limit2 = adev->pm.dpm.near_tdp_limit;
+ u32 k = dte_data->k;
+ u32 t_max = dte_data->max_t;
+ u32 t_split[5] = { 10, 15, 20, 25, 30 };
+ u32 t_0 = dte_data->t0;
+ u32 i;
+
+ if (p_limit2 != 0 && p_limit2 <= p_limit1) {
+ dte_data->tdep_count = 3;
+
+ for (i = 0; i < k; i++) {
+ dte_data->r[i] =
+ (t_split[i] * (t_max - t_0/(u32)1000) * (1 << 14)) /
+ (p_limit2 * (u32)100);
+ }
+
+ dte_data->tdep_r[1] = dte_data->r[4] * 2;
+
+ for (i = 2; i < SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE; i++) {
+ dte_data->tdep_r[i] = dte_data->r[4];
+ }
+ } else {
+ DRM_ERROR("Invalid PL2! DTE will not be updated.\n");
+ }
+}
+
+static struct rv7xx_power_info *rv770_get_pi(struct amdgpu_device *adev)
+{
+ struct rv7xx_power_info *pi = adev->pm.dpm.priv;
+
+ return pi;
+}
+
+static struct ni_power_info *ni_get_pi(struct amdgpu_device *adev)
+{
+ struct ni_power_info *pi = adev->pm.dpm.priv;
+
+ return pi;
+}
+
+static struct si_ps *si_get_ps(struct amdgpu_ps *aps)
+{
+ struct si_ps *ps = aps->ps_priv;
+
+ return ps;
+}
+
+static void si_initialize_powertune_defaults(struct amdgpu_device *adev)
+{
+ struct ni_power_info *ni_pi = ni_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+ bool update_dte_from_pl2 = false;
+
+ if (adev->asic_type == CHIP_TAHITI) {
+ si_pi->cac_weights = cac_weights_tahiti;
+ si_pi->lcac_config = lcac_tahiti;
+ si_pi->cac_override = cac_override_tahiti;
+ si_pi->powertune_data = &powertune_data_tahiti;
+ si_pi->dte_data = dte_data_tahiti;
+
+ switch (adev->pdev->device) {
+ case 0x6798:
+ si_pi->dte_data.enable_dte_by_default = true;
+ break;
+ case 0x6799:
+ si_pi->dte_data = dte_data_new_zealand;
+ break;
+ case 0x6790:
+ case 0x6791:
+ case 0x6792:
+ case 0x679E:
+ si_pi->dte_data = dte_data_aruba_pro;
+ update_dte_from_pl2 = true;
+ break;
+ case 0x679B:
+ si_pi->dte_data = dte_data_malta;
+ update_dte_from_pl2 = true;
+ break;
+ case 0x679A:
+ si_pi->dte_data = dte_data_tahiti_pro;
+ update_dte_from_pl2 = true;
+ break;
+ default:
+ if (si_pi->dte_data.enable_dte_by_default == true)
+ DRM_ERROR("DTE is not enabled!\n");
+ break;
+ }
+ } else if (adev->asic_type == CHIP_PITCAIRN) {
+ si_pi->cac_weights = cac_weights_pitcairn;
+ si_pi->lcac_config = lcac_pitcairn;
+ si_pi->cac_override = cac_override_pitcairn;
+ si_pi->powertune_data = &powertune_data_pitcairn;
+
+ switch (adev->pdev->device) {
+ case 0x6810:
+ case 0x6818:
+ si_pi->dte_data = dte_data_curacao_xt;
+ update_dte_from_pl2 = true;
+ break;
+ case 0x6819:
+ case 0x6811:
+ si_pi->dte_data = dte_data_curacao_pro;
+ update_dte_from_pl2 = true;
+ break;
+ case 0x6800:
+ case 0x6806:
+ si_pi->dte_data = dte_data_neptune_xt;
+ update_dte_from_pl2 = true;
+ break;
+ default:
+ si_pi->dte_data = dte_data_pitcairn;
+ break;
+ }
+ } else if (adev->asic_type == CHIP_VERDE) {
+ si_pi->lcac_config = lcac_cape_verde;
+ si_pi->cac_override = cac_override_cape_verde;
+ si_pi->powertune_data = &powertune_data_cape_verde;
+
+ switch (adev->pdev->device) {
+ case 0x683B:
+ case 0x683F:
+ case 0x6829:
+ case 0x6835:
+ si_pi->cac_weights = cac_weights_cape_verde_pro;
+ si_pi->dte_data = dte_data_cape_verde;
+ break;
+ case 0x682C:
+ si_pi->cac_weights = cac_weights_cape_verde_pro;
+ si_pi->dte_data = dte_data_sun_xt;
+ update_dte_from_pl2 = true;
+ break;
+ case 0x6825:
+ case 0x6827:
+ si_pi->cac_weights = cac_weights_heathrow;
+ si_pi->dte_data = dte_data_cape_verde;
+ break;
+ case 0x6824:
+ case 0x682D:
+ si_pi->cac_weights = cac_weights_chelsea_xt;
+ si_pi->dte_data = dte_data_cape_verde;
+ break;
+ case 0x682F:
+ si_pi->cac_weights = cac_weights_chelsea_pro;
+ si_pi->dte_data = dte_data_cape_verde;
+ break;
+ case 0x6820:
+ si_pi->cac_weights = cac_weights_heathrow;
+ si_pi->dte_data = dte_data_venus_xtx;
+ break;
+ case 0x6821:
+ si_pi->cac_weights = cac_weights_heathrow;
+ si_pi->dte_data = dte_data_venus_xt;
+ break;
+ case 0x6823:
+ case 0x682B:
+ case 0x6822:
+ case 0x682A:
+ si_pi->cac_weights = cac_weights_chelsea_pro;
+ si_pi->dte_data = dte_data_venus_pro;
+ break;
+ default:
+ si_pi->cac_weights = cac_weights_cape_verde;
+ si_pi->dte_data = dte_data_cape_verde;
+ break;
+ }
+ } else if (adev->asic_type == CHIP_OLAND) {
+ si_pi->lcac_config = lcac_mars_pro;
+ si_pi->cac_override = cac_override_oland;
+ si_pi->powertune_data = &powertune_data_mars_pro;
+ si_pi->dte_data = dte_data_mars_pro;
+
+ switch (adev->pdev->device) {
+ case 0x6601:
+ case 0x6621:
+ case 0x6603:
+ case 0x6605:
+ si_pi->cac_weights = cac_weights_mars_pro;
+ update_dte_from_pl2 = true;
+ break;
+ case 0x6600:
+ case 0x6606:
+ case 0x6620:
+ case 0x6604:
+ si_pi->cac_weights = cac_weights_mars_xt;
+ update_dte_from_pl2 = true;
+ break;
+ case 0x6611:
+ case 0x6613:
+ case 0x6608:
+ si_pi->cac_weights = cac_weights_oland_pro;
+ update_dte_from_pl2 = true;
+ break;
+ case 0x6610:
+ si_pi->cac_weights = cac_weights_oland_xt;
+ update_dte_from_pl2 = true;
+ break;
+ default:
+ si_pi->cac_weights = cac_weights_oland;
+ si_pi->lcac_config = lcac_oland;
+ si_pi->cac_override = cac_override_oland;
+ si_pi->powertune_data = &powertune_data_oland;
+ si_pi->dte_data = dte_data_oland;
+ break;
+ }
+ } else if (adev->asic_type == CHIP_HAINAN) {
+ si_pi->cac_weights = cac_weights_hainan;
+ si_pi->lcac_config = lcac_oland;
+ si_pi->cac_override = cac_override_oland;
+ si_pi->powertune_data = &powertune_data_hainan;
+ si_pi->dte_data = dte_data_sun_xt;
+ update_dte_from_pl2 = true;
+ } else {
+ DRM_ERROR("Unknown SI asic revision, failed to initialize PowerTune!\n");
+ return;
+ }
+
+ ni_pi->enable_power_containment = false;
+ ni_pi->enable_cac = false;
+ ni_pi->enable_sq_ramping = false;
+ si_pi->enable_dte = false;
+
+ if (si_pi->powertune_data->enable_powertune_by_default) {
+ ni_pi->enable_power_containment = true;
+ ni_pi->enable_cac = true;
+ if (si_pi->dte_data.enable_dte_by_default) {
+ si_pi->enable_dte = true;
+ if (update_dte_from_pl2)
+ si_update_dte_from_pl2(adev, &si_pi->dte_data);
+
+ }
+ ni_pi->enable_sq_ramping = true;
+ }
+
+ ni_pi->driver_calculate_cac_leakage = true;
+ ni_pi->cac_configuration_required = true;
+
+ if (ni_pi->cac_configuration_required) {
+ ni_pi->support_cac_long_term_average = true;
+ si_pi->dyn_powertune_data.l2_lta_window_size =
+ si_pi->powertune_data->l2_lta_window_size_default;
+ si_pi->dyn_powertune_data.lts_truncate =
+ si_pi->powertune_data->lts_truncate_default;
+ } else {
+ ni_pi->support_cac_long_term_average = false;
+ si_pi->dyn_powertune_data.l2_lta_window_size = 0;
+ si_pi->dyn_powertune_data.lts_truncate = 0;
+ }
+
+ si_pi->dyn_powertune_data.disable_uvd_powertune = false;
+}
+
+static u32 si_get_smc_power_scaling_factor(struct amdgpu_device *adev)
+{
+ return 1;
+}
+
+static u32 si_calculate_cac_wintime(struct amdgpu_device *adev)
+{
+ u32 xclk;
+ u32 wintime;
+ u32 cac_window;
+ u32 cac_window_size;
+
+ xclk = amdgpu_asic_get_xclk(adev);
+
+ if (xclk == 0)
+ return 0;
+
+ cac_window = RREG32(CG_CAC_CTRL) & CAC_WINDOW_MASK;
+ cac_window_size = ((cac_window & 0xFFFF0000) >> 16) * (cac_window & 0x0000FFFF);
+
+ wintime = (cac_window_size * 100) / xclk;
+
+ return wintime;
+}
+
+static u32 si_scale_power_for_smc(u32 power_in_watts, u32 scaling_factor)
+{
+ return power_in_watts;
+}
+
+static int si_calculate_adjusted_tdp_limits(struct amdgpu_device *adev,
+ bool adjust_polarity,
+ u32 tdp_adjustment,
+ u32 *tdp_limit,
+ u32 *near_tdp_limit)
+{
+ u32 adjustment_delta, max_tdp_limit;
+
+ if (tdp_adjustment > (u32)adev->pm.dpm.tdp_od_limit)
+ return -EINVAL;
+
+ max_tdp_limit = ((100 + 100) * adev->pm.dpm.tdp_limit) / 100;
+
+ if (adjust_polarity) {
+ *tdp_limit = ((100 + tdp_adjustment) * adev->pm.dpm.tdp_limit) / 100;
+ *near_tdp_limit = adev->pm.dpm.near_tdp_limit_adjusted + (*tdp_limit - adev->pm.dpm.tdp_limit);
+ } else {
+ *tdp_limit = ((100 - tdp_adjustment) * adev->pm.dpm.tdp_limit) / 100;
+ adjustment_delta = adev->pm.dpm.tdp_limit - *tdp_limit;
+ if (adjustment_delta < adev->pm.dpm.near_tdp_limit_adjusted)
+ *near_tdp_limit = adev->pm.dpm.near_tdp_limit_adjusted - adjustment_delta;
+ else
+ *near_tdp_limit = 0;
+ }
+
+ if ((*tdp_limit <= 0) || (*tdp_limit > max_tdp_limit))
+ return -EINVAL;
+ if ((*near_tdp_limit <= 0) || (*near_tdp_limit > *tdp_limit))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int si_populate_smc_tdp_limits(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_state)
+{
+ struct ni_power_info *ni_pi = ni_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+
+ if (ni_pi->enable_power_containment) {
+ SISLANDS_SMC_STATETABLE *smc_table = &si_pi->smc_statetable;
+ PP_SIslands_PAPMParameters *papm_parm;
+ struct amdgpu_ppm_table *ppm = adev->pm.dpm.dyn_state.ppm_table;
+ u32 scaling_factor = si_get_smc_power_scaling_factor(adev);
+ u32 tdp_limit;
+ u32 near_tdp_limit;
+ int ret;
+
+ if (scaling_factor == 0)
+ return -EINVAL;
+
+ memset(smc_table, 0, sizeof(SISLANDS_SMC_STATETABLE));
+
+ ret = si_calculate_adjusted_tdp_limits(adev,
+ false, /* ??? */
+ adev->pm.dpm.tdp_adjustment,
+ &tdp_limit,
+ &near_tdp_limit);
+ if (ret)
+ return ret;
+
+ smc_table->dpm2Params.TDPLimit =
+ cpu_to_be32(si_scale_power_for_smc(tdp_limit, scaling_factor) * 1000);
+ smc_table->dpm2Params.NearTDPLimit =
+ cpu_to_be32(si_scale_power_for_smc(near_tdp_limit, scaling_factor) * 1000);
+ smc_table->dpm2Params.SafePowerLimit =
+ cpu_to_be32(si_scale_power_for_smc((near_tdp_limit * SISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT) / 100, scaling_factor) * 1000);
+
+ ret = amdgpu_si_copy_bytes_to_smc(adev,
+ (si_pi->state_table_start + offsetof(SISLANDS_SMC_STATETABLE, dpm2Params) +
+ offsetof(PP_SIslands_DPM2Parameters, TDPLimit)),
+ (u8 *)(&(smc_table->dpm2Params.TDPLimit)),
+ sizeof(u32) * 3,
+ si_pi->sram_end);
+ if (ret)
+ return ret;
+
+ if (si_pi->enable_ppm) {
+ papm_parm = &si_pi->papm_parm;
+ memset(papm_parm, 0, sizeof(PP_SIslands_PAPMParameters));
+ papm_parm->NearTDPLimitTherm = cpu_to_be32(ppm->dgpu_tdp);
+ papm_parm->dGPU_T_Limit = cpu_to_be32(ppm->tj_max);
+ papm_parm->dGPU_T_Warning = cpu_to_be32(95);
+ papm_parm->dGPU_T_Hysteresis = cpu_to_be32(5);
+ papm_parm->PlatformPowerLimit = 0xffffffff;
+ papm_parm->NearTDPLimitPAPM = 0xffffffff;
+
+ ret = amdgpu_si_copy_bytes_to_smc(adev, si_pi->papm_cfg_table_start,
+ (u8 *)papm_parm,
+ sizeof(PP_SIslands_PAPMParameters),
+ si_pi->sram_end);
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int si_populate_smc_tdp_limits_2(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_state)
+{
+ struct ni_power_info *ni_pi = ni_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+
+ if (ni_pi->enable_power_containment) {
+ SISLANDS_SMC_STATETABLE *smc_table = &si_pi->smc_statetable;
+ u32 scaling_factor = si_get_smc_power_scaling_factor(adev);
+ int ret;
+
+ memset(smc_table, 0, sizeof(SISLANDS_SMC_STATETABLE));
+
+ smc_table->dpm2Params.NearTDPLimit =
+ cpu_to_be32(si_scale_power_for_smc(adev->pm.dpm.near_tdp_limit_adjusted, scaling_factor) * 1000);
+ smc_table->dpm2Params.SafePowerLimit =
+ cpu_to_be32(si_scale_power_for_smc((adev->pm.dpm.near_tdp_limit_adjusted * SISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT) / 100, scaling_factor) * 1000);
+
+ ret = amdgpu_si_copy_bytes_to_smc(adev,
+ (si_pi->state_table_start +
+ offsetof(SISLANDS_SMC_STATETABLE, dpm2Params) +
+ offsetof(PP_SIslands_DPM2Parameters, NearTDPLimit)),
+ (u8 *)(&(smc_table->dpm2Params.NearTDPLimit)),
+ sizeof(u32) * 2,
+ si_pi->sram_end);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static u16 si_calculate_power_efficiency_ratio(struct amdgpu_device *adev,
+ const u16 prev_std_vddc,
+ const u16 curr_std_vddc)
+{
+ u64 margin = (u64)SISLANDS_DPM2_PWREFFICIENCYRATIO_MARGIN;
+ u64 prev_vddc = (u64)prev_std_vddc;
+ u64 curr_vddc = (u64)curr_std_vddc;
+ u64 pwr_efficiency_ratio, n, d;
+
+ if ((prev_vddc == 0) || (curr_vddc == 0))
+ return 0;
+
+ n = div64_u64((u64)1024 * curr_vddc * curr_vddc * ((u64)1000 + margin), (u64)1000);
+ d = prev_vddc * prev_vddc;
+ pwr_efficiency_ratio = div64_u64(n, d);
+
+ if (pwr_efficiency_ratio > (u64)0xFFFF)
+ return 0;
+
+ return (u16)pwr_efficiency_ratio;
+}
+
+static bool si_should_disable_uvd_powertune(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_state)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+
+ if (si_pi->dyn_powertune_data.disable_uvd_powertune &&
+ amdgpu_state->vclk && amdgpu_state->dclk)
+ return true;
+
+ return false;
+}
+
+struct evergreen_power_info *evergreen_get_pi(struct amdgpu_device *adev)
+{
+ struct evergreen_power_info *pi = adev->pm.dpm.priv;
+
+ return pi;
+}
+
+static int si_populate_power_containment_values(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_state,
+ SISLANDS_SMC_SWSTATE *smc_state)
+{
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct ni_power_info *ni_pi = ni_get_pi(adev);
+ struct si_ps *state = si_get_ps(amdgpu_state);
+ SISLANDS_SMC_VOLTAGE_VALUE vddc;
+ u32 prev_sclk;
+ u32 max_sclk;
+ u32 min_sclk;
+ u16 prev_std_vddc;
+ u16 curr_std_vddc;
+ int i;
+ u16 pwr_efficiency_ratio;
+ u8 max_ps_percent;
+ bool disable_uvd_power_tune;
+ int ret;
+
+ if (ni_pi->enable_power_containment == false)
+ return 0;
+
+ if (state->performance_level_count == 0)
+ return -EINVAL;
+
+ if (smc_state->levelCount != state->performance_level_count)
+ return -EINVAL;
+
+ disable_uvd_power_tune = si_should_disable_uvd_powertune(adev, amdgpu_state);
+
+ smc_state->levels[0].dpm2.MaxPS = 0;
+ smc_state->levels[0].dpm2.NearTDPDec = 0;
+ smc_state->levels[0].dpm2.AboveSafeInc = 0;
+ smc_state->levels[0].dpm2.BelowSafeInc = 0;
+ smc_state->levels[0].dpm2.PwrEfficiencyRatio = 0;
+
+ for (i = 1; i < state->performance_level_count; i++) {
+ prev_sclk = state->performance_levels[i-1].sclk;
+ max_sclk = state->performance_levels[i].sclk;
+ if (i == 1)
+ max_ps_percent = SISLANDS_DPM2_MAXPS_PERCENT_M;
+ else
+ max_ps_percent = SISLANDS_DPM2_MAXPS_PERCENT_H;
+
+ if (prev_sclk > max_sclk)
+ return -EINVAL;
+
+ if ((max_ps_percent == 0) ||
+ (prev_sclk == max_sclk) ||
+ disable_uvd_power_tune)
+ min_sclk = max_sclk;
+ else if (i == 1)
+ min_sclk = prev_sclk;
+ else
+ min_sclk = (prev_sclk * (u32)max_ps_percent) / 100;
+
+ if (min_sclk < state->performance_levels[0].sclk)
+ min_sclk = state->performance_levels[0].sclk;
+
+ if (min_sclk == 0)
+ return -EINVAL;
+
+ ret = si_populate_voltage_value(adev, &eg_pi->vddc_voltage_table,
+ state->performance_levels[i-1].vddc, &vddc);
+ if (ret)
+ return ret;
+
+ ret = si_get_std_voltage_value(adev, &vddc, &prev_std_vddc);
+ if (ret)
+ return ret;
+
+ ret = si_populate_voltage_value(adev, &eg_pi->vddc_voltage_table,
+ state->performance_levels[i].vddc, &vddc);
+ if (ret)
+ return ret;
+
+ ret = si_get_std_voltage_value(adev, &vddc, &curr_std_vddc);
+ if (ret)
+ return ret;
+
+ pwr_efficiency_ratio = si_calculate_power_efficiency_ratio(adev,
+ prev_std_vddc, curr_std_vddc);
+
+ smc_state->levels[i].dpm2.MaxPS = (u8)((SISLANDS_DPM2_MAX_PULSE_SKIP * (max_sclk - min_sclk)) / max_sclk);
+ smc_state->levels[i].dpm2.NearTDPDec = SISLANDS_DPM2_NEAR_TDP_DEC;
+ smc_state->levels[i].dpm2.AboveSafeInc = SISLANDS_DPM2_ABOVE_SAFE_INC;
+ smc_state->levels[i].dpm2.BelowSafeInc = SISLANDS_DPM2_BELOW_SAFE_INC;
+ smc_state->levels[i].dpm2.PwrEfficiencyRatio = cpu_to_be16(pwr_efficiency_ratio);
+ }
+
+ return 0;
+}
+
+static int si_populate_sq_ramping_values(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_state,
+ SISLANDS_SMC_SWSTATE *smc_state)
+{
+ struct ni_power_info *ni_pi = ni_get_pi(adev);
+ struct si_ps *state = si_get_ps(amdgpu_state);
+ u32 sq_power_throttle, sq_power_throttle2;
+ bool enable_sq_ramping = ni_pi->enable_sq_ramping;
+ int i;
+
+ if (state->performance_level_count == 0)
+ return -EINVAL;
+
+ if (smc_state->levelCount != state->performance_level_count)
+ return -EINVAL;
+
+ if (adev->pm.dpm.sq_ramping_threshold == 0)
+ return -EINVAL;
+
+ if (SISLANDS_DPM2_SQ_RAMP_MAX_POWER > (MAX_POWER_MASK >> MAX_POWER_SHIFT))
+ enable_sq_ramping = false;
+
+ if (SISLANDS_DPM2_SQ_RAMP_MIN_POWER > (MIN_POWER_MASK >> MIN_POWER_SHIFT))
+ enable_sq_ramping = false;
+
+ if (SISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA > (MAX_POWER_DELTA_MASK >> MAX_POWER_DELTA_SHIFT))
+ enable_sq_ramping = false;
+
+ if (SISLANDS_DPM2_SQ_RAMP_STI_SIZE > (STI_SIZE_MASK >> STI_SIZE_SHIFT))
+ enable_sq_ramping = false;
+
+ if (SISLANDS_DPM2_SQ_RAMP_LTI_RATIO > (LTI_RATIO_MASK >> LTI_RATIO_SHIFT))
+ enable_sq_ramping = false;
+
+ for (i = 0; i < state->performance_level_count; i++) {
+ sq_power_throttle = 0;
+ sq_power_throttle2 = 0;
+
+ if ((state->performance_levels[i].sclk >= adev->pm.dpm.sq_ramping_threshold) &&
+ enable_sq_ramping) {
+ sq_power_throttle |= MAX_POWER(SISLANDS_DPM2_SQ_RAMP_MAX_POWER);
+ sq_power_throttle |= MIN_POWER(SISLANDS_DPM2_SQ_RAMP_MIN_POWER);
+ sq_power_throttle2 |= MAX_POWER_DELTA(SISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA);
+ sq_power_throttle2 |= STI_SIZE(SISLANDS_DPM2_SQ_RAMP_STI_SIZE);
+ sq_power_throttle2 |= LTI_RATIO(SISLANDS_DPM2_SQ_RAMP_LTI_RATIO);
+ } else {
+ sq_power_throttle |= MAX_POWER_MASK | MIN_POWER_MASK;
+ sq_power_throttle2 |= MAX_POWER_DELTA_MASK | STI_SIZE_MASK | LTI_RATIO_MASK;
+ }
+
+ smc_state->levels[i].SQPowerThrottle = cpu_to_be32(sq_power_throttle);
+ smc_state->levels[i].SQPowerThrottle_2 = cpu_to_be32(sq_power_throttle2);
+ }
+
+ return 0;
+}
+
+static int si_enable_power_containment(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_new_state,
+ bool enable)
+{
+ struct ni_power_info *ni_pi = ni_get_pi(adev);
+ PPSMC_Result smc_result;
+ int ret = 0;
+
+ if (ni_pi->enable_power_containment) {
+ if (enable) {
+ if (!si_should_disable_uvd_powertune(adev, amdgpu_new_state)) {
+ smc_result = amdgpu_si_send_msg_to_smc(adev, PPSMC_TDPClampingActive);
+ if (smc_result != PPSMC_Result_OK) {
+ ret = -EINVAL;
+ ni_pi->pc_enabled = false;
+ } else {
+ ni_pi->pc_enabled = true;
+ }
+ }
+ } else {
+ smc_result = amdgpu_si_send_msg_to_smc(adev, PPSMC_TDPClampingInactive);
+ if (smc_result != PPSMC_Result_OK)
+ ret = -EINVAL;
+ ni_pi->pc_enabled = false;
+ }
+ }
+
+ return ret;
+}
+
+static int si_initialize_smc_dte_tables(struct amdgpu_device *adev)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ int ret = 0;
+ struct si_dte_data *dte_data = &si_pi->dte_data;
+ Smc_SIslands_DTE_Configuration *dte_tables = NULL;
+ u32 table_size;
+ u8 tdep_count;
+ u32 i;
+
+ if (dte_data == NULL)
+ si_pi->enable_dte = false;
+
+ if (si_pi->enable_dte == false)
+ return 0;
+
+ if (dte_data->k <= 0)
+ return -EINVAL;
+
+ dte_tables = kzalloc(sizeof(Smc_SIslands_DTE_Configuration), GFP_KERNEL);
+ if (dte_tables == NULL) {
+ si_pi->enable_dte = false;
+ return -ENOMEM;
+ }
+
+ table_size = dte_data->k;
+
+ if (table_size > SMC_SISLANDS_DTE_MAX_FILTER_STAGES)
+ table_size = SMC_SISLANDS_DTE_MAX_FILTER_STAGES;
+
+ tdep_count = dte_data->tdep_count;
+ if (tdep_count > SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE)
+ tdep_count = SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE;
+
+ dte_tables->K = cpu_to_be32(table_size);
+ dte_tables->T0 = cpu_to_be32(dte_data->t0);
+ dte_tables->MaxT = cpu_to_be32(dte_data->max_t);
+ dte_tables->WindowSize = dte_data->window_size;
+ dte_tables->temp_select = dte_data->temp_select;
+ dte_tables->DTE_mode = dte_data->dte_mode;
+ dte_tables->Tthreshold = cpu_to_be32(dte_data->t_threshold);
+
+ if (tdep_count > 0)
+ table_size--;
+
+ for (i = 0; i < table_size; i++) {
+ dte_tables->tau[i] = cpu_to_be32(dte_data->tau[i]);
+ dte_tables->R[i] = cpu_to_be32(dte_data->r[i]);
+ }
+
+ dte_tables->Tdep_count = tdep_count;
+
+ for (i = 0; i < (u32)tdep_count; i++) {
+ dte_tables->T_limits[i] = dte_data->t_limits[i];
+ dte_tables->Tdep_tau[i] = cpu_to_be32(dte_data->tdep_tau[i]);
+ dte_tables->Tdep_R[i] = cpu_to_be32(dte_data->tdep_r[i]);
+ }
+
+ ret = amdgpu_si_copy_bytes_to_smc(adev, si_pi->dte_table_start,
+ (u8 *)dte_tables,
+ sizeof(Smc_SIslands_DTE_Configuration),
+ si_pi->sram_end);
+ kfree(dte_tables);
+
+ return ret;
+}
+
+static int si_get_cac_std_voltage_max_min(struct amdgpu_device *adev,
+ u16 *max, u16 *min)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ struct amdgpu_cac_leakage_table *table =
+ &adev->pm.dpm.dyn_state.cac_leakage_table;
+ u32 i;
+ u32 v0_loadline;
+
+ if (table == NULL)
+ return -EINVAL;
+
+ *max = 0;
+ *min = 0xFFFF;
+
+ for (i = 0; i < table->count; i++) {
+ if (table->entries[i].vddc > *max)
+ *max = table->entries[i].vddc;
+ if (table->entries[i].vddc < *min)
+ *min = table->entries[i].vddc;
+ }
+
+ if (si_pi->powertune_data->lkge_lut_v0_percent > 100)
+ return -EINVAL;
+
+ v0_loadline = (*min) * (100 - si_pi->powertune_data->lkge_lut_v0_percent) / 100;
+
+ if (v0_loadline > 0xFFFFUL)
+ return -EINVAL;
+
+ *min = (u16)v0_loadline;
+
+ if ((*min > *max) || (*max == 0) || (*min == 0))
+ return -EINVAL;
+
+ return 0;
+}
+
+static u16 si_get_cac_std_voltage_step(u16 max, u16 min)
+{
+ return ((max - min) + (SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES - 1)) /
+ SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES;
+}
+
+static int si_init_dte_leakage_table(struct amdgpu_device *adev,
+ PP_SIslands_CacConfig *cac_tables,
+ u16 vddc_max, u16 vddc_min, u16 vddc_step,
+ u16 t0, u16 t_step)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ u32 leakage;
+ unsigned int i, j;
+ s32 t;
+ u32 smc_leakage;
+ u32 scaling_factor;
+ u16 voltage;
+
+ scaling_factor = si_get_smc_power_scaling_factor(adev);
+
+ for (i = 0; i < SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES ; i++) {
+ t = (1000 * (i * t_step + t0));
+
+ for (j = 0; j < SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES; j++) {
+ voltage = vddc_max - (vddc_step * j);
+
+ si_calculate_leakage_for_v_and_t(adev,
+ &si_pi->powertune_data->leakage_coefficients,
+ voltage,
+ t,
+ si_pi->dyn_powertune_data.cac_leakage,
+ &leakage);
+
+ smc_leakage = si_scale_power_for_smc(leakage, scaling_factor) / 4;
+
+ if (smc_leakage > 0xFFFF)
+ smc_leakage = 0xFFFF;
+
+ cac_tables->cac_lkge_lut[i][SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES-1-j] =
+ cpu_to_be16((u16)smc_leakage);
+ }
+ }
+ return 0;
+}
+
+static int si_init_simplified_leakage_table(struct amdgpu_device *adev,
+ PP_SIslands_CacConfig *cac_tables,
+ u16 vddc_max, u16 vddc_min, u16 vddc_step)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ u32 leakage;
+ unsigned int i, j;
+ u32 smc_leakage;
+ u32 scaling_factor;
+ u16 voltage;
+
+ scaling_factor = si_get_smc_power_scaling_factor(adev);
+
+ for (j = 0; j < SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES; j++) {
+ voltage = vddc_max - (vddc_step * j);
+
+ si_calculate_leakage_for_v(adev,
+ &si_pi->powertune_data->leakage_coefficients,
+ si_pi->powertune_data->fixed_kt,
+ voltage,
+ si_pi->dyn_powertune_data.cac_leakage,
+ &leakage);
+
+ smc_leakage = si_scale_power_for_smc(leakage, scaling_factor) / 4;
+
+ if (smc_leakage > 0xFFFF)
+ smc_leakage = 0xFFFF;
+
+ for (i = 0; i < SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES ; i++)
+ cac_tables->cac_lkge_lut[i][SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES-1-j] =
+ cpu_to_be16((u16)smc_leakage);
+ }
+ return 0;
+}
+
+static int si_initialize_smc_cac_tables(struct amdgpu_device *adev)
+{
+ struct ni_power_info *ni_pi = ni_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+ PP_SIslands_CacConfig *cac_tables = NULL;
+ u16 vddc_max, vddc_min, vddc_step;
+ u16 t0, t_step;
+ u32 load_line_slope, reg;
+ int ret = 0;
+ u32 ticks_per_us = amdgpu_asic_get_xclk(adev) / 100;
+
+ if (ni_pi->enable_cac == false)
+ return 0;
+
+ cac_tables = kzalloc(sizeof(PP_SIslands_CacConfig), GFP_KERNEL);
+ if (!cac_tables)
+ return -ENOMEM;
+
+ reg = RREG32(CG_CAC_CTRL) & ~CAC_WINDOW_MASK;
+ reg |= CAC_WINDOW(si_pi->powertune_data->cac_window);
+ WREG32(CG_CAC_CTRL, reg);
+
+ si_pi->dyn_powertune_data.cac_leakage = adev->pm.dpm.cac_leakage;
+ si_pi->dyn_powertune_data.dc_pwr_value =
+ si_pi->powertune_data->dc_cac[NISLANDS_DCCAC_LEVEL_0];
+ si_pi->dyn_powertune_data.wintime = si_calculate_cac_wintime(adev);
+ si_pi->dyn_powertune_data.shift_n = si_pi->powertune_data->shift_n_default;
+
+ si_pi->dyn_powertune_data.leakage_minimum_temperature = 80 * 1000;
+
+ ret = si_get_cac_std_voltage_max_min(adev, &vddc_max, &vddc_min);
+ if (ret)
+ goto done_free;
+
+ vddc_step = si_get_cac_std_voltage_step(vddc_max, vddc_min);
+ vddc_min = vddc_max - (vddc_step * (SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES - 1));
+ t_step = 4;
+ t0 = 60;
+
+ if (si_pi->enable_dte || ni_pi->driver_calculate_cac_leakage)
+ ret = si_init_dte_leakage_table(adev, cac_tables,
+ vddc_max, vddc_min, vddc_step,
+ t0, t_step);
+ else
+ ret = si_init_simplified_leakage_table(adev, cac_tables,
+ vddc_max, vddc_min, vddc_step);
+ if (ret)
+ goto done_free;
+
+ load_line_slope = ((u32)adev->pm.dpm.load_line_slope << SMC_SISLANDS_SCALE_R) / 100;
+
+ cac_tables->l2numWin_TDP = cpu_to_be32(si_pi->dyn_powertune_data.l2_lta_window_size);
+ cac_tables->lts_truncate_n = si_pi->dyn_powertune_data.lts_truncate;
+ cac_tables->SHIFT_N = si_pi->dyn_powertune_data.shift_n;
+ cac_tables->lkge_lut_V0 = cpu_to_be32((u32)vddc_min);
+ cac_tables->lkge_lut_Vstep = cpu_to_be32((u32)vddc_step);
+ cac_tables->R_LL = cpu_to_be32(load_line_slope);
+ cac_tables->WinTime = cpu_to_be32(si_pi->dyn_powertune_data.wintime);
+ cac_tables->calculation_repeats = cpu_to_be32(2);
+ cac_tables->dc_cac = cpu_to_be32(0);
+ cac_tables->log2_PG_LKG_SCALE = 12;
+ cac_tables->cac_temp = si_pi->powertune_data->operating_temp;
+ cac_tables->lkge_lut_T0 = cpu_to_be32((u32)t0);
+ cac_tables->lkge_lut_Tstep = cpu_to_be32((u32)t_step);
+
+ ret = amdgpu_si_copy_bytes_to_smc(adev, si_pi->cac_table_start,
+ (u8 *)cac_tables,
+ sizeof(PP_SIslands_CacConfig),
+ si_pi->sram_end);
+
+ if (ret)
+ goto done_free;
+
+ ret = si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_ticks_per_us, ticks_per_us);
+
+done_free:
+ if (ret) {
+ ni_pi->enable_cac = false;
+ ni_pi->enable_power_containment = false;
+ }
+
+ kfree(cac_tables);
+
+ return ret;
+}
+
+static int si_program_cac_config_registers(struct amdgpu_device *adev,
+ const struct si_cac_config_reg *cac_config_regs)
+{
+ const struct si_cac_config_reg *config_regs = cac_config_regs;
+ u32 data = 0, offset;
+
+ if (!config_regs)
+ return -EINVAL;
+
+ while (config_regs->offset != 0xFFFFFFFF) {
+ switch (config_regs->type) {
+ case SISLANDS_CACCONFIG_CGIND:
+ offset = SMC_CG_IND_START + config_regs->offset;
+ if (offset < SMC_CG_IND_END)
+ data = RREG32_SMC(offset);
+ break;
+ default:
+ data = RREG32(config_regs->offset);
+ break;
+ }
+
+ data &= ~config_regs->mask;
+ data |= ((config_regs->value << config_regs->shift) & config_regs->mask);
+
+ switch (config_regs->type) {
+ case SISLANDS_CACCONFIG_CGIND:
+ offset = SMC_CG_IND_START + config_regs->offset;
+ if (offset < SMC_CG_IND_END)
+ WREG32_SMC(offset, data);
+ break;
+ default:
+ WREG32(config_regs->offset, data);
+ break;
+ }
+ config_regs++;
+ }
+ return 0;
+}
+
+static int si_initialize_hardware_cac_manager(struct amdgpu_device *adev)
+{
+ struct ni_power_info *ni_pi = ni_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+ int ret;
+
+ if ((ni_pi->enable_cac == false) ||
+ (ni_pi->cac_configuration_required == false))
+ return 0;
+
+ ret = si_program_cac_config_registers(adev, si_pi->lcac_config);
+ if (ret)
+ return ret;
+ ret = si_program_cac_config_registers(adev, si_pi->cac_override);
+ if (ret)
+ return ret;
+ ret = si_program_cac_config_registers(adev, si_pi->cac_weights);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int si_enable_smc_cac(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_new_state,
+ bool enable)
+{
+ struct ni_power_info *ni_pi = ni_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+ PPSMC_Result smc_result;
+ int ret = 0;
+
+ if (ni_pi->enable_cac) {
+ if (enable) {
+ if (!si_should_disable_uvd_powertune(adev, amdgpu_new_state)) {
+ if (ni_pi->support_cac_long_term_average) {
+ smc_result = amdgpu_si_send_msg_to_smc(adev, PPSMC_CACLongTermAvgEnable);
+ if (smc_result != PPSMC_Result_OK)
+ ni_pi->support_cac_long_term_average = false;
+ }
+
+ smc_result = amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_EnableCac);
+ if (smc_result != PPSMC_Result_OK) {
+ ret = -EINVAL;
+ ni_pi->cac_enabled = false;
+ } else {
+ ni_pi->cac_enabled = true;
+ }
+
+ if (si_pi->enable_dte) {
+ smc_result = amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_EnableDTE);
+ if (smc_result != PPSMC_Result_OK)
+ ret = -EINVAL;
+ }
+ }
+ } else if (ni_pi->cac_enabled) {
+ if (si_pi->enable_dte)
+ smc_result = amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_DisableDTE);
+
+ smc_result = amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_DisableCac);
+
+ ni_pi->cac_enabled = false;
+
+ if (ni_pi->support_cac_long_term_average)
+ smc_result = amdgpu_si_send_msg_to_smc(adev, PPSMC_CACLongTermAvgDisable);
+ }
+ }
+ return ret;
+}
+
+static int si_init_smc_spll_table(struct amdgpu_device *adev)
+{
+ struct ni_power_info *ni_pi = ni_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+ SMC_SISLANDS_SPLL_DIV_TABLE *spll_table;
+ SISLANDS_SMC_SCLK_VALUE sclk_params;
+ u32 fb_div, p_div;
+ u32 clk_s, clk_v;
+ u32 sclk = 0;
+ int ret = 0;
+ u32 tmp;
+ int i;
+
+ if (si_pi->spll_table_start == 0)
+ return -EINVAL;
+
+ spll_table = kzalloc(sizeof(SMC_SISLANDS_SPLL_DIV_TABLE), GFP_KERNEL);
+ if (spll_table == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < 256; i++) {
+ ret = si_calculate_sclk_params(adev, sclk, &sclk_params);
+ if (ret)
+ break;
+ p_div = (sclk_params.vCG_SPLL_FUNC_CNTL & SPLL_PDIV_A_MASK) >> SPLL_PDIV_A_SHIFT;
+ fb_div = (sclk_params.vCG_SPLL_FUNC_CNTL_3 & SPLL_FB_DIV_MASK) >> SPLL_FB_DIV_SHIFT;
+ clk_s = (sclk_params.vCG_SPLL_SPREAD_SPECTRUM & CLK_S_MASK) >> CLK_S_SHIFT;
+ clk_v = (sclk_params.vCG_SPLL_SPREAD_SPECTRUM_2 & CLK_V_MASK) >> CLK_V_SHIFT;
+
+ fb_div &= ~0x00001FFF;
+ fb_div >>= 1;
+ clk_v >>= 6;
+
+ if (p_div & ~(SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_MASK >> SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT))
+ ret = -EINVAL;
+ if (fb_div & ~(SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_MASK >> SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_SHIFT))
+ ret = -EINVAL;
+ if (clk_s & ~(SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_MASK >> SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT))
+ ret = -EINVAL;
+ if (clk_v & ~(SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_MASK >> SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT))
+ ret = -EINVAL;
+
+ if (ret)
+ break;
+
+ tmp = ((fb_div << SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_SHIFT) & SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_MASK) |
+ ((p_div << SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT) & SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_MASK);
+ spll_table->freq[i] = cpu_to_be32(tmp);
+
+ tmp = ((clk_v << SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT) & SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_MASK) |
+ ((clk_s << SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT) & SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_MASK);
+ spll_table->ss[i] = cpu_to_be32(tmp);
+
+ sclk += 512;
+ }
+
+
+ if (!ret)
+ ret = amdgpu_si_copy_bytes_to_smc(adev, si_pi->spll_table_start,
+ (u8 *)spll_table,
+ sizeof(SMC_SISLANDS_SPLL_DIV_TABLE),
+ si_pi->sram_end);
+
+ if (ret)
+ ni_pi->enable_power_containment = false;
+
+ kfree(spll_table);
+
+ return ret;
+}
+
+static u16 si_get_lower_of_leakage_and_vce_voltage(struct amdgpu_device *adev,
+ u16 vce_voltage)
+{
+ u16 highest_leakage = 0;
+ struct si_power_info *si_pi = si_get_pi(adev);
+ int i;
+
+ for (i = 0; i < si_pi->leakage_voltage.count; i++){
+ if (highest_leakage < si_pi->leakage_voltage.entries[i].voltage)
+ highest_leakage = si_pi->leakage_voltage.entries[i].voltage;
+ }
+
+ if (si_pi->leakage_voltage.count && (highest_leakage < vce_voltage))
+ return highest_leakage;
+
+ return vce_voltage;
+}
+
+static int si_get_vce_clock_voltage(struct amdgpu_device *adev,
+ u32 evclk, u32 ecclk, u16 *voltage)
+{
+ u32 i;
+ int ret = -EINVAL;
+ struct amdgpu_vce_clock_voltage_dependency_table *table =
+ &adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table;
+
+ if (((evclk == 0) && (ecclk == 0)) ||
+ (table && (table->count == 0))) {
+ *voltage = 0;
+ return 0;
+ }
+
+ for (i = 0; i < table->count; i++) {
+ if ((evclk <= table->entries[i].evclk) &&
+ (ecclk <= table->entries[i].ecclk)) {
+ *voltage = table->entries[i].v;
+ ret = 0;
+ break;
+ }
+ }
+
+ /* if no match return the highest voltage */
+ if (ret)
+ *voltage = table->entries[table->count - 1].v;
+
+ *voltage = si_get_lower_of_leakage_and_vce_voltage(adev, *voltage);
+
+ return ret;
+}
+
+static bool si_dpm_vblank_too_short(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ u32 vblank_time = amdgpu_dpm_get_vblank_time(adev);
+ /* we never hit the non-gddr5 limit so disable it */
+ u32 switch_limit = adev->gmc.vram_type == AMDGPU_VRAM_TYPE_GDDR5 ? 450 : 0;
+
+ if (vblank_time < switch_limit)
+ return true;
+ else
+ return false;
+
+}
+
+static int ni_copy_and_switch_arb_sets(struct amdgpu_device *adev,
+ u32 arb_freq_src, u32 arb_freq_dest)
+{
+ u32 mc_arb_dram_timing;
+ u32 mc_arb_dram_timing2;
+ u32 burst_time;
+ u32 mc_cg_config;
+
+ switch (arb_freq_src) {
+ case MC_CG_ARB_FREQ_F0:
+ mc_arb_dram_timing = RREG32(MC_ARB_DRAM_TIMING);
+ mc_arb_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2);
+ burst_time = (RREG32(MC_ARB_BURST_TIME) & STATE0_MASK) >> STATE0_SHIFT;
+ break;
+ case MC_CG_ARB_FREQ_F1:
+ mc_arb_dram_timing = RREG32(MC_ARB_DRAM_TIMING_1);
+ mc_arb_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2_1);
+ burst_time = (RREG32(MC_ARB_BURST_TIME) & STATE1_MASK) >> STATE1_SHIFT;
+ break;
+ case MC_CG_ARB_FREQ_F2:
+ mc_arb_dram_timing = RREG32(MC_ARB_DRAM_TIMING_2);
+ mc_arb_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2_2);
+ burst_time = (RREG32(MC_ARB_BURST_TIME) & STATE2_MASK) >> STATE2_SHIFT;
+ break;
+ case MC_CG_ARB_FREQ_F3:
+ mc_arb_dram_timing = RREG32(MC_ARB_DRAM_TIMING_3);
+ mc_arb_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2_3);
+ burst_time = (RREG32(MC_ARB_BURST_TIME) & STATE3_MASK) >> STATE3_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (arb_freq_dest) {
+ case MC_CG_ARB_FREQ_F0:
+ WREG32(MC_ARB_DRAM_TIMING, mc_arb_dram_timing);
+ WREG32(MC_ARB_DRAM_TIMING2, mc_arb_dram_timing2);
+ WREG32_P(MC_ARB_BURST_TIME, STATE0(burst_time), ~STATE0_MASK);
+ break;
+ case MC_CG_ARB_FREQ_F1:
+ WREG32(MC_ARB_DRAM_TIMING_1, mc_arb_dram_timing);
+ WREG32(MC_ARB_DRAM_TIMING2_1, mc_arb_dram_timing2);
+ WREG32_P(MC_ARB_BURST_TIME, STATE1(burst_time), ~STATE1_MASK);
+ break;
+ case MC_CG_ARB_FREQ_F2:
+ WREG32(MC_ARB_DRAM_TIMING_2, mc_arb_dram_timing);
+ WREG32(MC_ARB_DRAM_TIMING2_2, mc_arb_dram_timing2);
+ WREG32_P(MC_ARB_BURST_TIME, STATE2(burst_time), ~STATE2_MASK);
+ break;
+ case MC_CG_ARB_FREQ_F3:
+ WREG32(MC_ARB_DRAM_TIMING_3, mc_arb_dram_timing);
+ WREG32(MC_ARB_DRAM_TIMING2_3, mc_arb_dram_timing2);
+ WREG32_P(MC_ARB_BURST_TIME, STATE3(burst_time), ~STATE3_MASK);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mc_cg_config = RREG32(MC_CG_CONFIG) | 0x0000000F;
+ WREG32(MC_CG_CONFIG, mc_cg_config);
+ WREG32_P(MC_ARB_CG, CG_ARB_REQ(arb_freq_dest), ~CG_ARB_REQ_MASK);
+
+ return 0;
+}
+
+static void ni_update_current_ps(struct amdgpu_device *adev,
+ struct amdgpu_ps *rps)
+{
+ struct si_ps *new_ps = si_get_ps(rps);
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct ni_power_info *ni_pi = ni_get_pi(adev);
+
+ eg_pi->current_rps = *rps;
+ ni_pi->current_ps = *new_ps;
+ eg_pi->current_rps.ps_priv = &ni_pi->current_ps;
+ adev->pm.dpm.current_ps = &eg_pi->current_rps;
+}
+
+static void ni_update_requested_ps(struct amdgpu_device *adev,
+ struct amdgpu_ps *rps)
+{
+ struct si_ps *new_ps = si_get_ps(rps);
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct ni_power_info *ni_pi = ni_get_pi(adev);
+
+ eg_pi->requested_rps = *rps;
+ ni_pi->requested_ps = *new_ps;
+ eg_pi->requested_rps.ps_priv = &ni_pi->requested_ps;
+ adev->pm.dpm.requested_ps = &eg_pi->requested_rps;
+}
+
+static void ni_set_uvd_clock_before_set_eng_clock(struct amdgpu_device *adev,
+ struct amdgpu_ps *new_ps,
+ struct amdgpu_ps *old_ps)
+{
+ struct si_ps *new_state = si_get_ps(new_ps);
+ struct si_ps *current_state = si_get_ps(old_ps);
+
+ if ((new_ps->vclk == old_ps->vclk) &&
+ (new_ps->dclk == old_ps->dclk))
+ return;
+
+ if (new_state->performance_levels[new_state->performance_level_count - 1].sclk >=
+ current_state->performance_levels[current_state->performance_level_count - 1].sclk)
+ return;
+
+ amdgpu_asic_set_uvd_clocks(adev, new_ps->vclk, new_ps->dclk);
+}
+
+static void ni_set_uvd_clock_after_set_eng_clock(struct amdgpu_device *adev,
+ struct amdgpu_ps *new_ps,
+ struct amdgpu_ps *old_ps)
+{
+ struct si_ps *new_state = si_get_ps(new_ps);
+ struct si_ps *current_state = si_get_ps(old_ps);
+
+ if ((new_ps->vclk == old_ps->vclk) &&
+ (new_ps->dclk == old_ps->dclk))
+ return;
+
+ if (new_state->performance_levels[new_state->performance_level_count - 1].sclk <
+ current_state->performance_levels[current_state->performance_level_count - 1].sclk)
+ return;
+
+ amdgpu_asic_set_uvd_clocks(adev, new_ps->vclk, new_ps->dclk);
+}
+
+static u16 btc_find_voltage(struct atom_voltage_table *table, u16 voltage)
+{
+ unsigned int i;
+
+ for (i = 0; i < table->count; i++)
+ if (voltage <= table->entries[i].value)
+ return table->entries[i].value;
+
+ return table->entries[table->count - 1].value;
+}
+
+static u32 btc_find_valid_clock(struct amdgpu_clock_array *clocks,
+ u32 max_clock, u32 requested_clock)
+{
+ unsigned int i;
+
+ if ((clocks == NULL) || (clocks->count == 0))
+ return (requested_clock < max_clock) ? requested_clock : max_clock;
+
+ for (i = 0; i < clocks->count; i++) {
+ if (clocks->values[i] >= requested_clock)
+ return (clocks->values[i] < max_clock) ? clocks->values[i] : max_clock;
+ }
+
+ return (clocks->values[clocks->count - 1] < max_clock) ?
+ clocks->values[clocks->count - 1] : max_clock;
+}
+
+static u32 btc_get_valid_mclk(struct amdgpu_device *adev,
+ u32 max_mclk, u32 requested_mclk)
+{
+ return btc_find_valid_clock(&adev->pm.dpm.dyn_state.valid_mclk_values,
+ max_mclk, requested_mclk);
+}
+
+static u32 btc_get_valid_sclk(struct amdgpu_device *adev,
+ u32 max_sclk, u32 requested_sclk)
+{
+ return btc_find_valid_clock(&adev->pm.dpm.dyn_state.valid_sclk_values,
+ max_sclk, requested_sclk);
+}
+
+static void btc_get_max_clock_from_voltage_dependency_table(struct amdgpu_clock_voltage_dependency_table *table,
+ u32 *max_clock)
+{
+ u32 i, clock = 0;
+
+ if ((table == NULL) || (table->count == 0)) {
+ *max_clock = clock;
+ return;
+ }
+
+ for (i = 0; i < table->count; i++) {
+ if (clock < table->entries[i].clk)
+ clock = table->entries[i].clk;
+ }
+ *max_clock = clock;
+}
+
+static void btc_apply_voltage_dependency_rules(struct amdgpu_clock_voltage_dependency_table *table,
+ u32 clock, u16 max_voltage, u16 *voltage)
+{
+ u32 i;
+
+ if ((table == NULL) || (table->count == 0))
+ return;
+
+ for (i= 0; i < table->count; i++) {
+ if (clock <= table->entries[i].clk) {
+ if (*voltage < table->entries[i].v)
+ *voltage = (u16)((table->entries[i].v < max_voltage) ?
+ table->entries[i].v : max_voltage);
+ return;
+ }
+ }
+
+ *voltage = (*voltage > max_voltage) ? *voltage : max_voltage;
+}
+
+static void btc_adjust_clock_combinations(struct amdgpu_device *adev,
+ const struct amdgpu_clock_and_voltage_limits *max_limits,
+ struct rv7xx_pl *pl)
+{
+
+ if ((pl->mclk == 0) || (pl->sclk == 0))
+ return;
+
+ if (pl->mclk == pl->sclk)
+ return;
+
+ if (pl->mclk > pl->sclk) {
+ if (((pl->mclk + (pl->sclk - 1)) / pl->sclk) > adev->pm.dpm.dyn_state.mclk_sclk_ratio)
+ pl->sclk = btc_get_valid_sclk(adev,
+ max_limits->sclk,
+ (pl->mclk +
+ (adev->pm.dpm.dyn_state.mclk_sclk_ratio - 1)) /
+ adev->pm.dpm.dyn_state.mclk_sclk_ratio);
+ } else {
+ if ((pl->sclk - pl->mclk) > adev->pm.dpm.dyn_state.sclk_mclk_delta)
+ pl->mclk = btc_get_valid_mclk(adev,
+ max_limits->mclk,
+ pl->sclk -
+ adev->pm.dpm.dyn_state.sclk_mclk_delta);
+ }
+}
+
+static void btc_apply_voltage_delta_rules(struct amdgpu_device *adev,
+ u16 max_vddc, u16 max_vddci,
+ u16 *vddc, u16 *vddci)
+{
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ u16 new_voltage;
+
+ if ((0 == *vddc) || (0 == *vddci))
+ return;
+
+ if (*vddc > *vddci) {
+ if ((*vddc - *vddci) > adev->pm.dpm.dyn_state.vddc_vddci_delta) {
+ new_voltage = btc_find_voltage(&eg_pi->vddci_voltage_table,
+ (*vddc - adev->pm.dpm.dyn_state.vddc_vddci_delta));
+ *vddci = (new_voltage < max_vddci) ? new_voltage : max_vddci;
+ }
+ } else {
+ if ((*vddci - *vddc) > adev->pm.dpm.dyn_state.vddc_vddci_delta) {
+ new_voltage = btc_find_voltage(&eg_pi->vddc_voltage_table,
+ (*vddci - adev->pm.dpm.dyn_state.vddc_vddci_delta));
+ *vddc = (new_voltage < max_vddc) ? new_voltage : max_vddc;
+ }
+ }
+}
+
+static void r600_calculate_u_and_p(u32 i, u32 r_c, u32 p_b,
+ u32 *p, u32 *u)
+{
+ u32 b_c = 0;
+ u32 i_c;
+ u32 tmp;
+
+ i_c = (i * r_c) / 100;
+ tmp = i_c >> p_b;
+
+ while (tmp) {
+ b_c++;
+ tmp >>= 1;
+ }
+
+ *u = (b_c + 1) / 2;
+ *p = i_c / (1 << (2 * (*u)));
+}
+
+static int r600_calculate_at(u32 t, u32 h, u32 fh, u32 fl, u32 *tl, u32 *th)
+{
+ u32 k, a, ah, al;
+ u32 t1;
+
+ if ((fl == 0) || (fh == 0) || (fl > fh))
+ return -EINVAL;
+
+ k = (100 * fh) / fl;
+ t1 = (t * (k - 100));
+ a = (1000 * (100 * h + t1)) / (10000 + (t1 / 100));
+ a = (a + 5) / 10;
+ ah = ((a * t) + 5000) / 10000;
+ al = a - ah;
+
+ *th = t - ah;
+ *tl = t + al;
+
+ return 0;
+}
+
+static bool r600_is_uvd_state(u32 class, u32 class2)
+{
+ if (class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
+ return true;
+ if (class & ATOM_PPLIB_CLASSIFICATION_HD2STATE)
+ return true;
+ if (class & ATOM_PPLIB_CLASSIFICATION_HDSTATE)
+ return true;
+ if (class & ATOM_PPLIB_CLASSIFICATION_SDSTATE)
+ return true;
+ if (class2 & ATOM_PPLIB_CLASSIFICATION2_MVC)
+ return true;
+ return false;
+}
+
+static u8 rv770_get_memory_module_index(struct amdgpu_device *adev)
+{
+ return (u8) ((RREG32(BIOS_SCRATCH_4) >> 16) & 0xff);
+}
+
+static void rv770_get_max_vddc(struct amdgpu_device *adev)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ u16 vddc;
+
+ if (amdgpu_atombios_get_max_vddc(adev, 0, 0, &vddc))
+ pi->max_vddc = 0;
+ else
+ pi->max_vddc = vddc;
+}
+
+static void rv770_get_engine_memory_ss(struct amdgpu_device *adev)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ struct amdgpu_atom_ss ss;
+
+ pi->sclk_ss = amdgpu_atombios_get_asic_ss_info(adev, &ss,
+ ASIC_INTERNAL_ENGINE_SS, 0);
+ pi->mclk_ss = amdgpu_atombios_get_asic_ss_info(adev, &ss,
+ ASIC_INTERNAL_MEMORY_SS, 0);
+
+ if (pi->sclk_ss || pi->mclk_ss)
+ pi->dynamic_ss = true;
+ else
+ pi->dynamic_ss = false;
+}
+
+
+static void si_apply_state_adjust_rules(struct amdgpu_device *adev,
+ struct amdgpu_ps *rps)
+{
+ struct si_ps *ps = si_get_ps(rps);
+ struct amdgpu_clock_and_voltage_limits *max_limits;
+ bool disable_mclk_switching = false;
+ bool disable_sclk_switching = false;
+ u32 mclk, sclk;
+ u16 vddc, vddci, min_vce_voltage = 0;
+ u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc;
+ u32 max_sclk = 0, max_mclk = 0;
+ int i;
+
+ if (adev->asic_type == CHIP_HAINAN) {
+ if ((adev->pdev->revision == 0x81) ||
+ (adev->pdev->revision == 0xC3) ||
+ (adev->pdev->device == 0x6664) ||
+ (adev->pdev->device == 0x6665) ||
+ (adev->pdev->device == 0x6667)) {
+ max_sclk = 75000;
+ }
+ if ((adev->pdev->revision == 0xC3) ||
+ (adev->pdev->device == 0x6665)) {
+ max_sclk = 60000;
+ max_mclk = 80000;
+ }
+ } else if (adev->asic_type == CHIP_OLAND) {
+ if ((adev->pdev->revision == 0xC7) ||
+ (adev->pdev->revision == 0x80) ||
+ (adev->pdev->revision == 0x81) ||
+ (adev->pdev->revision == 0x83) ||
+ (adev->pdev->revision == 0x87) ||
+ (adev->pdev->device == 0x6604) ||
+ (adev->pdev->device == 0x6605)) {
+ max_sclk = 75000;
+ }
+ }
+
+ if (rps->vce_active) {
+ rps->evclk = adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].evclk;
+ rps->ecclk = adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].ecclk;
+ si_get_vce_clock_voltage(adev, rps->evclk, rps->ecclk,
+ &min_vce_voltage);
+ } else {
+ rps->evclk = 0;
+ rps->ecclk = 0;
+ }
+
+ if ((adev->pm.dpm.new_active_crtc_count > 1) ||
+ si_dpm_vblank_too_short(adev))
+ disable_mclk_switching = true;
+
+ if (rps->vclk || rps->dclk) {
+ disable_mclk_switching = true;
+ disable_sclk_switching = true;
+ }
+
+ if (adev->pm.ac_power)
+ max_limits = &adev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+ else
+ max_limits = &adev->pm.dpm.dyn_state.max_clock_voltage_on_dc;
+
+ for (i = ps->performance_level_count - 2; i >= 0; i--) {
+ if (ps->performance_levels[i].vddc > ps->performance_levels[i+1].vddc)
+ ps->performance_levels[i].vddc = ps->performance_levels[i+1].vddc;
+ }
+ if (adev->pm.ac_power == false) {
+ for (i = 0; i < ps->performance_level_count; i++) {
+ if (ps->performance_levels[i].mclk > max_limits->mclk)
+ ps->performance_levels[i].mclk = max_limits->mclk;
+ if (ps->performance_levels[i].sclk > max_limits->sclk)
+ ps->performance_levels[i].sclk = max_limits->sclk;
+ if (ps->performance_levels[i].vddc > max_limits->vddc)
+ ps->performance_levels[i].vddc = max_limits->vddc;
+ if (ps->performance_levels[i].vddci > max_limits->vddci)
+ ps->performance_levels[i].vddci = max_limits->vddci;
+ }
+ }
+
+ /* limit clocks to max supported clocks based on voltage dependency tables */
+ btc_get_max_clock_from_voltage_dependency_table(&adev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
+ &max_sclk_vddc);
+ btc_get_max_clock_from_voltage_dependency_table(&adev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+ &max_mclk_vddci);
+ btc_get_max_clock_from_voltage_dependency_table(&adev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+ &max_mclk_vddc);
+
+ for (i = 0; i < ps->performance_level_count; i++) {
+ if (max_sclk_vddc) {
+ if (ps->performance_levels[i].sclk > max_sclk_vddc)
+ ps->performance_levels[i].sclk = max_sclk_vddc;
+ }
+ if (max_mclk_vddci) {
+ if (ps->performance_levels[i].mclk > max_mclk_vddci)
+ ps->performance_levels[i].mclk = max_mclk_vddci;
+ }
+ if (max_mclk_vddc) {
+ if (ps->performance_levels[i].mclk > max_mclk_vddc)
+ ps->performance_levels[i].mclk = max_mclk_vddc;
+ }
+ if (max_mclk) {
+ if (ps->performance_levels[i].mclk > max_mclk)
+ ps->performance_levels[i].mclk = max_mclk;
+ }
+ if (max_sclk) {
+ if (ps->performance_levels[i].sclk > max_sclk)
+ ps->performance_levels[i].sclk = max_sclk;
+ }
+ }
+
+ /* XXX validate the min clocks required for display */
+
+ if (disable_mclk_switching) {
+ mclk = ps->performance_levels[ps->performance_level_count - 1].mclk;
+ vddci = ps->performance_levels[ps->performance_level_count - 1].vddci;
+ } else {
+ mclk = ps->performance_levels[0].mclk;
+ vddci = ps->performance_levels[0].vddci;
+ }
+
+ if (disable_sclk_switching) {
+ sclk = ps->performance_levels[ps->performance_level_count - 1].sclk;
+ vddc = ps->performance_levels[ps->performance_level_count - 1].vddc;
+ } else {
+ sclk = ps->performance_levels[0].sclk;
+ vddc = ps->performance_levels[0].vddc;
+ }
+
+ if (rps->vce_active) {
+ if (sclk < adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].sclk)
+ sclk = adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].sclk;
+ if (mclk < adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].mclk)
+ mclk = adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].mclk;
+ }
+
+ /* adjusted low state */
+ ps->performance_levels[0].sclk = sclk;
+ ps->performance_levels[0].mclk = mclk;
+ ps->performance_levels[0].vddc = vddc;
+ ps->performance_levels[0].vddci = vddci;
+
+ if (disable_sclk_switching) {
+ sclk = ps->performance_levels[0].sclk;
+ for (i = 1; i < ps->performance_level_count; i++) {
+ if (sclk < ps->performance_levels[i].sclk)
+ sclk = ps->performance_levels[i].sclk;
+ }
+ for (i = 0; i < ps->performance_level_count; i++) {
+ ps->performance_levels[i].sclk = sclk;
+ ps->performance_levels[i].vddc = vddc;
+ }
+ } else {
+ for (i = 1; i < ps->performance_level_count; i++) {
+ if (ps->performance_levels[i].sclk < ps->performance_levels[i - 1].sclk)
+ ps->performance_levels[i].sclk = ps->performance_levels[i - 1].sclk;
+ if (ps->performance_levels[i].vddc < ps->performance_levels[i - 1].vddc)
+ ps->performance_levels[i].vddc = ps->performance_levels[i - 1].vddc;
+ }
+ }
+
+ if (disable_mclk_switching) {
+ mclk = ps->performance_levels[0].mclk;
+ for (i = 1; i < ps->performance_level_count; i++) {
+ if (mclk < ps->performance_levels[i].mclk)
+ mclk = ps->performance_levels[i].mclk;
+ }
+ for (i = 0; i < ps->performance_level_count; i++) {
+ ps->performance_levels[i].mclk = mclk;
+ ps->performance_levels[i].vddci = vddci;
+ }
+ } else {
+ for (i = 1; i < ps->performance_level_count; i++) {
+ if (ps->performance_levels[i].mclk < ps->performance_levels[i - 1].mclk)
+ ps->performance_levels[i].mclk = ps->performance_levels[i - 1].mclk;
+ if (ps->performance_levels[i].vddci < ps->performance_levels[i - 1].vddci)
+ ps->performance_levels[i].vddci = ps->performance_levels[i - 1].vddci;
+ }
+ }
+
+ for (i = 0; i < ps->performance_level_count; i++)
+ btc_adjust_clock_combinations(adev, max_limits,
+ &ps->performance_levels[i]);
+
+ for (i = 0; i < ps->performance_level_count; i++) {
+ if (ps->performance_levels[i].vddc < min_vce_voltage)
+ ps->performance_levels[i].vddc = min_vce_voltage;
+ btc_apply_voltage_dependency_rules(&adev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
+ ps->performance_levels[i].sclk,
+ max_limits->vddc, &ps->performance_levels[i].vddc);
+ btc_apply_voltage_dependency_rules(&adev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+ ps->performance_levels[i].mclk,
+ max_limits->vddci, &ps->performance_levels[i].vddci);
+ btc_apply_voltage_dependency_rules(&adev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+ ps->performance_levels[i].mclk,
+ max_limits->vddc, &ps->performance_levels[i].vddc);
+ btc_apply_voltage_dependency_rules(&adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk,
+ adev->clock.current_dispclk,
+ max_limits->vddc, &ps->performance_levels[i].vddc);
+ }
+
+ for (i = 0; i < ps->performance_level_count; i++) {
+ btc_apply_voltage_delta_rules(adev,
+ max_limits->vddc, max_limits->vddci,
+ &ps->performance_levels[i].vddc,
+ &ps->performance_levels[i].vddci);
+ }
+
+ ps->dc_compatible = true;
+ for (i = 0; i < ps->performance_level_count; i++) {
+ if (ps->performance_levels[i].vddc > adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc)
+ ps->dc_compatible = false;
+ }
+}
+
+#if 0
+static int si_read_smc_soft_register(struct amdgpu_device *adev,
+ u16 reg_offset, u32 *value)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+
+ return amdgpu_si_read_smc_sram_dword(adev,
+ si_pi->soft_regs_start + reg_offset, value,
+ si_pi->sram_end);
+}
+#endif
+
+static int si_write_smc_soft_register(struct amdgpu_device *adev,
+ u16 reg_offset, u32 value)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+
+ return amdgpu_si_write_smc_sram_dword(adev,
+ si_pi->soft_regs_start + reg_offset,
+ value, si_pi->sram_end);
+}
+
+static bool si_is_special_1gb_platform(struct amdgpu_device *adev)
+{
+ bool ret = false;
+ u32 tmp, width, row, column, bank, density;
+ bool is_memory_gddr5, is_special;
+
+ tmp = RREG32(MC_SEQ_MISC0);
+ is_memory_gddr5 = (MC_SEQ_MISC0_GDDR5_VALUE == ((tmp & MC_SEQ_MISC0_GDDR5_MASK) >> MC_SEQ_MISC0_GDDR5_SHIFT));
+ is_special = (MC_SEQ_MISC0_REV_ID_VALUE == ((tmp & MC_SEQ_MISC0_REV_ID_MASK) >> MC_SEQ_MISC0_REV_ID_SHIFT))
+ & (MC_SEQ_MISC0_VEN_ID_VALUE == ((tmp & MC_SEQ_MISC0_VEN_ID_MASK) >> MC_SEQ_MISC0_VEN_ID_SHIFT));
+
+ WREG32(MC_SEQ_IO_DEBUG_INDEX, 0xb);
+ width = ((RREG32(MC_SEQ_IO_DEBUG_DATA) >> 1) & 1) ? 16 : 32;
+
+ tmp = RREG32(MC_ARB_RAMCFG);
+ row = ((tmp & NOOFROWS_MASK) >> NOOFROWS_SHIFT) + 10;
+ column = ((tmp & NOOFCOLS_MASK) >> NOOFCOLS_SHIFT) + 8;
+ bank = ((tmp & NOOFBANK_MASK) >> NOOFBANK_SHIFT) + 2;
+
+ density = (1 << (row + column - 20 + bank)) * width;
+
+ if ((adev->pdev->device == 0x6819) &&
+ is_memory_gddr5 && is_special && (density == 0x400))
+ ret = true;
+
+ return ret;
+}
+
+static void si_get_leakage_vddc(struct amdgpu_device *adev)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ u16 vddc, count = 0;
+ int i, ret;
+
+ for (i = 0; i < SISLANDS_MAX_LEAKAGE_COUNT; i++) {
+ ret = amdgpu_atombios_get_leakage_vddc_based_on_leakage_idx(adev, &vddc, SISLANDS_LEAKAGE_INDEX0 + i);
+
+ if (!ret && (vddc > 0) && (vddc != (SISLANDS_LEAKAGE_INDEX0 + i))) {
+ si_pi->leakage_voltage.entries[count].voltage = vddc;
+ si_pi->leakage_voltage.entries[count].leakage_index =
+ SISLANDS_LEAKAGE_INDEX0 + i;
+ count++;
+ }
+ }
+ si_pi->leakage_voltage.count = count;
+}
+
+static int si_get_leakage_voltage_from_leakage_index(struct amdgpu_device *adev,
+ u32 index, u16 *leakage_voltage)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ int i;
+
+ if (leakage_voltage == NULL)
+ return -EINVAL;
+
+ if ((index & 0xff00) != 0xff00)
+ return -EINVAL;
+
+ if ((index & 0xff) > SISLANDS_MAX_LEAKAGE_COUNT + 1)
+ return -EINVAL;
+
+ if (index < SISLANDS_LEAKAGE_INDEX0)
+ return -EINVAL;
+
+ for (i = 0; i < si_pi->leakage_voltage.count; i++) {
+ if (si_pi->leakage_voltage.entries[i].leakage_index == index) {
+ *leakage_voltage = si_pi->leakage_voltage.entries[i].voltage;
+ return 0;
+ }
+ }
+ return -EAGAIN;
+}
+
+static void si_set_dpm_event_sources(struct amdgpu_device *adev, u32 sources)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ bool want_thermal_protection;
+ enum amdgpu_dpm_event_src dpm_event_src;
+
+ switch (sources) {
+ case 0:
+ default:
+ want_thermal_protection = false;
+ break;
+ case (1 << AMDGPU_DPM_AUTO_THROTTLE_SRC_THERMAL):
+ want_thermal_protection = true;
+ dpm_event_src = AMDGPU_DPM_EVENT_SRC_DIGITAL;
+ break;
+ case (1 << AMDGPU_DPM_AUTO_THROTTLE_SRC_EXTERNAL):
+ want_thermal_protection = true;
+ dpm_event_src = AMDGPU_DPM_EVENT_SRC_EXTERNAL;
+ break;
+ case ((1 << AMDGPU_DPM_AUTO_THROTTLE_SRC_EXTERNAL) |
+ (1 << AMDGPU_DPM_AUTO_THROTTLE_SRC_THERMAL)):
+ want_thermal_protection = true;
+ dpm_event_src = AMDGPU_DPM_EVENT_SRC_DIGIAL_OR_EXTERNAL;
+ break;
+ }
+
+ if (want_thermal_protection) {
+ WREG32_P(CG_THERMAL_CTRL, DPM_EVENT_SRC(dpm_event_src), ~DPM_EVENT_SRC_MASK);
+ if (pi->thermal_protection)
+ WREG32_P(GENERAL_PWRMGT, 0, ~THERMAL_PROTECTION_DIS);
+ } else {
+ WREG32_P(GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, ~THERMAL_PROTECTION_DIS);
+ }
+}
+
+static void si_enable_auto_throttle_source(struct amdgpu_device *adev,
+ enum amdgpu_dpm_auto_throttle_src source,
+ bool enable)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+
+ if (enable) {
+ if (!(pi->active_auto_throttle_sources & (1 << source))) {
+ pi->active_auto_throttle_sources |= 1 << source;
+ si_set_dpm_event_sources(adev, pi->active_auto_throttle_sources);
+ }
+ } else {
+ if (pi->active_auto_throttle_sources & (1 << source)) {
+ pi->active_auto_throttle_sources &= ~(1 << source);
+ si_set_dpm_event_sources(adev, pi->active_auto_throttle_sources);
+ }
+ }
+}
+
+static void si_start_dpm(struct amdgpu_device *adev)
+{
+ WREG32_P(GENERAL_PWRMGT, GLOBAL_PWRMGT_EN, ~GLOBAL_PWRMGT_EN);
+}
+
+static void si_stop_dpm(struct amdgpu_device *adev)
+{
+ WREG32_P(GENERAL_PWRMGT, 0, ~GLOBAL_PWRMGT_EN);
+}
+
+static void si_enable_sclk_control(struct amdgpu_device *adev, bool enable)
+{
+ if (enable)
+ WREG32_P(SCLK_PWRMGT_CNTL, 0, ~SCLK_PWRMGT_OFF);
+ else
+ WREG32_P(SCLK_PWRMGT_CNTL, SCLK_PWRMGT_OFF, ~SCLK_PWRMGT_OFF);
+
+}
+
+#if 0
+static int si_notify_hardware_of_thermal_state(struct amdgpu_device *adev,
+ u32 thermal_level)
+{
+ PPSMC_Result ret;
+
+ if (thermal_level == 0) {
+ ret = amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_EnableThermalInterrupt);
+ if (ret == PPSMC_Result_OK)
+ return 0;
+ else
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void si_notify_hardware_vpu_recovery_event(struct amdgpu_device *adev)
+{
+ si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_tdr_is_about_to_happen, true);
+}
+#endif
+
+#if 0
+static int si_notify_hw_of_powersource(struct amdgpu_device *adev, bool ac_power)
+{
+ if (ac_power)
+ return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_RunningOnAC) == PPSMC_Result_OK) ?
+ 0 : -EINVAL;
+
+ return 0;
+}
+#endif
+
+static PPSMC_Result si_send_msg_to_smc_with_parameter(struct amdgpu_device *adev,
+ PPSMC_Msg msg, u32 parameter)
+{
+ WREG32(SMC_SCRATCH0, parameter);
+ return amdgpu_si_send_msg_to_smc(adev, msg);
+}
+
+static int si_restrict_performance_levels_before_switch(struct amdgpu_device *adev)
+{
+ if (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_NoForcedLevel) != PPSMC_Result_OK)
+ return -EINVAL;
+
+ return (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetEnabledLevels, 1) == PPSMC_Result_OK) ?
+ 0 : -EINVAL;
+}
+
+static int si_dpm_force_performance_level(void *handle,
+ enum amd_dpm_forced_level level)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct amdgpu_ps *rps = adev->pm.dpm.current_ps;
+ struct si_ps *ps = si_get_ps(rps);
+ u32 levels = ps->performance_level_count;
+
+ if (level == AMD_DPM_FORCED_LEVEL_HIGH) {
+ if (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetEnabledLevels, levels) != PPSMC_Result_OK)
+ return -EINVAL;
+
+ if (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetForcedLevels, 1) != PPSMC_Result_OK)
+ return -EINVAL;
+ } else if (level == AMD_DPM_FORCED_LEVEL_LOW) {
+ if (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK)
+ return -EINVAL;
+
+ if (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetEnabledLevels, 1) != PPSMC_Result_OK)
+ return -EINVAL;
+ } else if (level == AMD_DPM_FORCED_LEVEL_AUTO) {
+ if (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK)
+ return -EINVAL;
+
+ if (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetEnabledLevels, levels) != PPSMC_Result_OK)
+ return -EINVAL;
+ }
+
+ adev->pm.dpm.forced_level = level;
+
+ return 0;
+}
+
+#if 0
+static int si_set_boot_state(struct amdgpu_device *adev)
+{
+ return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_SwitchToInitialState) == PPSMC_Result_OK) ?
+ 0 : -EINVAL;
+}
+#endif
+
+static int si_set_sw_state(struct amdgpu_device *adev)
+{
+ return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_SwitchToSwState) == PPSMC_Result_OK) ?
+ 0 : -EINVAL;
+}
+
+static int si_halt_smc(struct amdgpu_device *adev)
+{
+ if (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_Halt) != PPSMC_Result_OK)
+ return -EINVAL;
+
+ return (amdgpu_si_wait_for_smc_inactive(adev) == PPSMC_Result_OK) ?
+ 0 : -EINVAL;
+}
+
+static int si_resume_smc(struct amdgpu_device *adev)
+{
+ if (amdgpu_si_send_msg_to_smc(adev, PPSMC_FlushDataCache) != PPSMC_Result_OK)
+ return -EINVAL;
+
+ return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_Resume) == PPSMC_Result_OK) ?
+ 0 : -EINVAL;
+}
+
+static void si_dpm_start_smc(struct amdgpu_device *adev)
+{
+ amdgpu_si_program_jump_on_start(adev);
+ amdgpu_si_start_smc(adev);
+ amdgpu_si_smc_clock(adev, true);
+}
+
+static void si_dpm_stop_smc(struct amdgpu_device *adev)
+{
+ amdgpu_si_reset_smc(adev);
+ amdgpu_si_smc_clock(adev, false);
+}
+
+static int si_process_firmware_header(struct amdgpu_device *adev)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ u32 tmp;
+ int ret;
+
+ ret = amdgpu_si_read_smc_sram_dword(adev,
+ SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+ SISLANDS_SMC_FIRMWARE_HEADER_stateTable,
+ &tmp, si_pi->sram_end);
+ if (ret)
+ return ret;
+
+ si_pi->state_table_start = tmp;
+
+ ret = amdgpu_si_read_smc_sram_dword(adev,
+ SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+ SISLANDS_SMC_FIRMWARE_HEADER_softRegisters,
+ &tmp, si_pi->sram_end);
+ if (ret)
+ return ret;
+
+ si_pi->soft_regs_start = tmp;
+
+ ret = amdgpu_si_read_smc_sram_dword(adev,
+ SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+ SISLANDS_SMC_FIRMWARE_HEADER_mcRegisterTable,
+ &tmp, si_pi->sram_end);
+ if (ret)
+ return ret;
+
+ si_pi->mc_reg_table_start = tmp;
+
+ ret = amdgpu_si_read_smc_sram_dword(adev,
+ SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+ SISLANDS_SMC_FIRMWARE_HEADER_fanTable,
+ &tmp, si_pi->sram_end);
+ if (ret)
+ return ret;
+
+ si_pi->fan_table_start = tmp;
+
+ ret = amdgpu_si_read_smc_sram_dword(adev,
+ SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+ SISLANDS_SMC_FIRMWARE_HEADER_mcArbDramAutoRefreshTable,
+ &tmp, si_pi->sram_end);
+ if (ret)
+ return ret;
+
+ si_pi->arb_table_start = tmp;
+
+ ret = amdgpu_si_read_smc_sram_dword(adev,
+ SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+ SISLANDS_SMC_FIRMWARE_HEADER_CacConfigTable,
+ &tmp, si_pi->sram_end);
+ if (ret)
+ return ret;
+
+ si_pi->cac_table_start = tmp;
+
+ ret = amdgpu_si_read_smc_sram_dword(adev,
+ SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+ SISLANDS_SMC_FIRMWARE_HEADER_DteConfiguration,
+ &tmp, si_pi->sram_end);
+ if (ret)
+ return ret;
+
+ si_pi->dte_table_start = tmp;
+
+ ret = amdgpu_si_read_smc_sram_dword(adev,
+ SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+ SISLANDS_SMC_FIRMWARE_HEADER_spllTable,
+ &tmp, si_pi->sram_end);
+ if (ret)
+ return ret;
+
+ si_pi->spll_table_start = tmp;
+
+ ret = amdgpu_si_read_smc_sram_dword(adev,
+ SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+ SISLANDS_SMC_FIRMWARE_HEADER_PAPMParameters,
+ &tmp, si_pi->sram_end);
+ if (ret)
+ return ret;
+
+ si_pi->papm_cfg_table_start = tmp;
+
+ return ret;
+}
+
+static void si_read_clock_registers(struct amdgpu_device *adev)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+
+ si_pi->clock_registers.cg_spll_func_cntl = RREG32(CG_SPLL_FUNC_CNTL);
+ si_pi->clock_registers.cg_spll_func_cntl_2 = RREG32(CG_SPLL_FUNC_CNTL_2);
+ si_pi->clock_registers.cg_spll_func_cntl_3 = RREG32(CG_SPLL_FUNC_CNTL_3);
+ si_pi->clock_registers.cg_spll_func_cntl_4 = RREG32(CG_SPLL_FUNC_CNTL_4);
+ si_pi->clock_registers.cg_spll_spread_spectrum = RREG32(CG_SPLL_SPREAD_SPECTRUM);
+ si_pi->clock_registers.cg_spll_spread_spectrum_2 = RREG32(CG_SPLL_SPREAD_SPECTRUM_2);
+ si_pi->clock_registers.dll_cntl = RREG32(DLL_CNTL);
+ si_pi->clock_registers.mclk_pwrmgt_cntl = RREG32(MCLK_PWRMGT_CNTL);
+ si_pi->clock_registers.mpll_ad_func_cntl = RREG32(MPLL_AD_FUNC_CNTL);
+ si_pi->clock_registers.mpll_dq_func_cntl = RREG32(MPLL_DQ_FUNC_CNTL);
+ si_pi->clock_registers.mpll_func_cntl = RREG32(MPLL_FUNC_CNTL);
+ si_pi->clock_registers.mpll_func_cntl_1 = RREG32(MPLL_FUNC_CNTL_1);
+ si_pi->clock_registers.mpll_func_cntl_2 = RREG32(MPLL_FUNC_CNTL_2);
+ si_pi->clock_registers.mpll_ss1 = RREG32(MPLL_SS1);
+ si_pi->clock_registers.mpll_ss2 = RREG32(MPLL_SS2);
+}
+
+static void si_enable_thermal_protection(struct amdgpu_device *adev,
+ bool enable)
+{
+ if (enable)
+ WREG32_P(GENERAL_PWRMGT, 0, ~THERMAL_PROTECTION_DIS);
+ else
+ WREG32_P(GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, ~THERMAL_PROTECTION_DIS);
+}
+
+static void si_enable_acpi_power_management(struct amdgpu_device *adev)
+{
+ WREG32_P(GENERAL_PWRMGT, STATIC_PM_EN, ~STATIC_PM_EN);
+}
+
+#if 0
+static int si_enter_ulp_state(struct amdgpu_device *adev)
+{
+ WREG32(SMC_MESSAGE_0, PPSMC_MSG_SwitchToMinimumPower);
+
+ udelay(25000);
+
+ return 0;
+}
+
+static int si_exit_ulp_state(struct amdgpu_device *adev)
+{
+ int i;
+
+ WREG32(SMC_MESSAGE_0, PPSMC_MSG_ResumeFromMinimumPower);
+
+ udelay(7000);
+
+ for (i = 0; i < adev->usec_timeout; i++) {
+ if (RREG32(SMC_RESP_0) == 1)
+ break;
+ udelay(1000);
+ }
+
+ return 0;
+}
+#endif
+
+static int si_notify_smc_display_change(struct amdgpu_device *adev,
+ bool has_display)
+{
+ PPSMC_Msg msg = has_display ?
+ PPSMC_MSG_HasDisplay : PPSMC_MSG_NoDisplay;
+
+ return (amdgpu_si_send_msg_to_smc(adev, msg) == PPSMC_Result_OK) ?
+ 0 : -EINVAL;
+}
+
+static void si_program_response_times(struct amdgpu_device *adev)
+{
+ u32 voltage_response_time, acpi_delay_time, vbi_time_out;
+ u32 vddc_dly, acpi_dly, vbi_dly;
+ u32 reference_clock;
+
+ si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_mvdd_chg_time, 1);
+
+ voltage_response_time = (u32)adev->pm.dpm.voltage_response_time;
+
+ if (voltage_response_time == 0)
+ voltage_response_time = 1000;
+
+ acpi_delay_time = 15000;
+ vbi_time_out = 100000;
+
+ reference_clock = amdgpu_asic_get_xclk(adev);
+
+ vddc_dly = (voltage_response_time * reference_clock) / 100;
+ acpi_dly = (acpi_delay_time * reference_clock) / 100;
+ vbi_dly = (vbi_time_out * reference_clock) / 100;
+
+ si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_delay_vreg, vddc_dly);
+ si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_delay_acpi, acpi_dly);
+ si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_mclk_chg_timeout, vbi_dly);
+ si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_mc_block_delay, 0xAA);
+}
+
+static void si_program_ds_registers(struct amdgpu_device *adev)
+{
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ u32 tmp;
+
+ /* DEEP_SLEEP_CLK_SEL field should be 0x10 on tahiti A0 */
+ if (adev->asic_type == CHIP_TAHITI && adev->rev_id == 0x0)
+ tmp = 0x10;
+ else
+ tmp = 0x1;
+
+ if (eg_pi->sclk_deep_sleep) {
+ WREG32_P(MISC_CLK_CNTL, DEEP_SLEEP_CLK_SEL(tmp), ~DEEP_SLEEP_CLK_SEL_MASK);
+ WREG32_P(CG_SPLL_AUTOSCALE_CNTL, AUTOSCALE_ON_SS_CLEAR,
+ ~AUTOSCALE_ON_SS_CLEAR);
+ }
+}
+
+static void si_program_display_gap(struct amdgpu_device *adev)
+{
+ u32 tmp, pipe;
+ int i;
+
+ tmp = RREG32(CG_DISPLAY_GAP_CNTL) & ~(DISP1_GAP_MASK | DISP2_GAP_MASK);
+ if (adev->pm.dpm.new_active_crtc_count > 0)
+ tmp |= DISP1_GAP(R600_PM_DISPLAY_GAP_VBLANK_OR_WM);
+ else
+ tmp |= DISP1_GAP(R600_PM_DISPLAY_GAP_IGNORE);
+
+ if (adev->pm.dpm.new_active_crtc_count > 1)
+ tmp |= DISP2_GAP(R600_PM_DISPLAY_GAP_VBLANK_OR_WM);
+ else
+ tmp |= DISP2_GAP(R600_PM_DISPLAY_GAP_IGNORE);
+
+ WREG32(CG_DISPLAY_GAP_CNTL, tmp);
+
+ tmp = RREG32(DCCG_DISP_SLOW_SELECT_REG);
+ pipe = (tmp & DCCG_DISP1_SLOW_SELECT_MASK) >> DCCG_DISP1_SLOW_SELECT_SHIFT;
+
+ if ((adev->pm.dpm.new_active_crtc_count > 0) &&
+ (!(adev->pm.dpm.new_active_crtcs & (1 << pipe)))) {
+ /* find the first active crtc */
+ for (i = 0; i < adev->mode_info.num_crtc; i++) {
+ if (adev->pm.dpm.new_active_crtcs & (1 << i))
+ break;
+ }
+ if (i == adev->mode_info.num_crtc)
+ pipe = 0;
+ else
+ pipe = i;
+
+ tmp &= ~DCCG_DISP1_SLOW_SELECT_MASK;
+ tmp |= DCCG_DISP1_SLOW_SELECT(pipe);
+ WREG32(DCCG_DISP_SLOW_SELECT_REG, tmp);
+ }
+
+ /* Setting this to false forces the performance state to low if the crtcs are disabled.
+ * This can be a problem on PowerXpress systems or if you want to use the card
+ * for offscreen rendering or compute if there are no crtcs enabled.
+ */
+ si_notify_smc_display_change(adev, adev->pm.dpm.new_active_crtc_count > 0);
+}
+
+static void si_enable_spread_spectrum(struct amdgpu_device *adev, bool enable)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+
+ if (enable) {
+ if (pi->sclk_ss)
+ WREG32_P(GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, ~DYN_SPREAD_SPECTRUM_EN);
+ } else {
+ WREG32_P(CG_SPLL_SPREAD_SPECTRUM, 0, ~SSEN);
+ WREG32_P(GENERAL_PWRMGT, 0, ~DYN_SPREAD_SPECTRUM_EN);
+ }
+}
+
+static void si_setup_bsp(struct amdgpu_device *adev)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ u32 xclk = amdgpu_asic_get_xclk(adev);
+
+ r600_calculate_u_and_p(pi->asi,
+ xclk,
+ 16,
+ &pi->bsp,
+ &pi->bsu);
+
+ r600_calculate_u_and_p(pi->pasi,
+ xclk,
+ 16,
+ &pi->pbsp,
+ &pi->pbsu);
+
+
+ pi->dsp = BSP(pi->bsp) | BSU(pi->bsu);
+ pi->psp = BSP(pi->pbsp) | BSU(pi->pbsu);
+
+ WREG32(CG_BSP, pi->dsp);
+}
+
+static void si_program_git(struct amdgpu_device *adev)
+{
+ WREG32_P(CG_GIT, CG_GICST(R600_GICST_DFLT), ~CG_GICST_MASK);
+}
+
+static void si_program_tp(struct amdgpu_device *adev)
+{
+ int i;
+ enum r600_td td = R600_TD_DFLT;
+
+ for (i = 0; i < R600_PM_NUMBER_OF_TC; i++)
+ WREG32(CG_FFCT_0 + i, (UTC_0(r600_utc[i]) | DTC_0(r600_dtc[i])));
+
+ if (td == R600_TD_AUTO)
+ WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_FORCE_TREND_SEL);
+ else
+ WREG32_P(SCLK_PWRMGT_CNTL, FIR_FORCE_TREND_SEL, ~FIR_FORCE_TREND_SEL);
+
+ if (td == R600_TD_UP)
+ WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_TREND_MODE);
+
+ if (td == R600_TD_DOWN)
+ WREG32_P(SCLK_PWRMGT_CNTL, FIR_TREND_MODE, ~FIR_TREND_MODE);
+}
+
+static void si_program_tpp(struct amdgpu_device *adev)
+{
+ WREG32(CG_TPC, R600_TPC_DFLT);
+}
+
+static void si_program_sstp(struct amdgpu_device *adev)
+{
+ WREG32(CG_SSP, (SSTU(R600_SSTU_DFLT) | SST(R600_SST_DFLT)));
+}
+
+static void si_enable_display_gap(struct amdgpu_device *adev)
+{
+ u32 tmp = RREG32(CG_DISPLAY_GAP_CNTL);
+
+ tmp &= ~(DISP1_GAP_MASK | DISP2_GAP_MASK);
+ tmp |= (DISP1_GAP(R600_PM_DISPLAY_GAP_IGNORE) |
+ DISP2_GAP(R600_PM_DISPLAY_GAP_IGNORE));
+
+ tmp &= ~(DISP1_GAP_MCHG_MASK | DISP2_GAP_MCHG_MASK);
+ tmp |= (DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK) |
+ DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE));
+ WREG32(CG_DISPLAY_GAP_CNTL, tmp);
+}
+
+static void si_program_vc(struct amdgpu_device *adev)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+
+ WREG32(CG_FTV, pi->vrc);
+}
+
+static void si_clear_vc(struct amdgpu_device *adev)
+{
+ WREG32(CG_FTV, 0);
+}
+
+static u8 si_get_ddr3_mclk_frequency_ratio(u32 memory_clock)
+{
+ u8 mc_para_index;
+
+ if (memory_clock < 10000)
+ mc_para_index = 0;
+ else if (memory_clock >= 80000)
+ mc_para_index = 0x0f;
+ else
+ mc_para_index = (u8)((memory_clock - 10000) / 5000 + 1);
+ return mc_para_index;
+}
+
+static u8 si_get_mclk_frequency_ratio(u32 memory_clock, bool strobe_mode)
+{
+ u8 mc_para_index;
+
+ if (strobe_mode) {
+ if (memory_clock < 12500)
+ mc_para_index = 0x00;
+ else if (memory_clock > 47500)
+ mc_para_index = 0x0f;
+ else
+ mc_para_index = (u8)((memory_clock - 10000) / 2500);
+ } else {
+ if (memory_clock < 65000)
+ mc_para_index = 0x00;
+ else if (memory_clock > 135000)
+ mc_para_index = 0x0f;
+ else
+ mc_para_index = (u8)((memory_clock - 60000) / 5000);
+ }
+ return mc_para_index;
+}
+
+static u8 si_get_strobe_mode_settings(struct amdgpu_device *adev, u32 mclk)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ bool strobe_mode = false;
+ u8 result = 0;
+
+ if (mclk <= pi->mclk_strobe_mode_threshold)
+ strobe_mode = true;
+
+ if (adev->gmc.vram_type == AMDGPU_VRAM_TYPE_GDDR5)
+ result = si_get_mclk_frequency_ratio(mclk, strobe_mode);
+ else
+ result = si_get_ddr3_mclk_frequency_ratio(mclk);
+
+ if (strobe_mode)
+ result |= SISLANDS_SMC_STROBE_ENABLE;
+
+ return result;
+}
+
+static int si_upload_firmware(struct amdgpu_device *adev)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+
+ amdgpu_si_reset_smc(adev);
+ amdgpu_si_smc_clock(adev, false);
+
+ return amdgpu_si_load_smc_ucode(adev, si_pi->sram_end);
+}
+
+static bool si_validate_phase_shedding_tables(struct amdgpu_device *adev,
+ const struct atom_voltage_table *table,
+ const struct amdgpu_phase_shedding_limits_table *limits)
+{
+ u32 data, num_bits, num_levels;
+
+ if ((table == NULL) || (limits == NULL))
+ return false;
+
+ data = table->mask_low;
+
+ num_bits = hweight32(data);
+
+ if (num_bits == 0)
+ return false;
+
+ num_levels = (1 << num_bits);
+
+ if (table->count != num_levels)
+ return false;
+
+ if (limits->count != (num_levels - 1))
+ return false;
+
+ return true;
+}
+
+static void si_trim_voltage_table_to_fit_state_table(struct amdgpu_device *adev,
+ u32 max_voltage_steps,
+ struct atom_voltage_table *voltage_table)
+{
+ unsigned int i, diff;
+
+ if (voltage_table->count <= max_voltage_steps)
+ return;
+
+ diff = voltage_table->count - max_voltage_steps;
+
+ for (i= 0; i < max_voltage_steps; i++)
+ voltage_table->entries[i] = voltage_table->entries[i + diff];
+
+ voltage_table->count = max_voltage_steps;
+}
+
+static int si_get_svi2_voltage_table(struct amdgpu_device *adev,
+ struct amdgpu_clock_voltage_dependency_table *voltage_dependency_table,
+ struct atom_voltage_table *voltage_table)
+{
+ u32 i;
+
+ if (voltage_dependency_table == NULL)
+ return -EINVAL;
+
+ voltage_table->mask_low = 0;
+ voltage_table->phase_delay = 0;
+
+ voltage_table->count = voltage_dependency_table->count;
+ for (i = 0; i < voltage_table->count; i++) {
+ voltage_table->entries[i].value = voltage_dependency_table->entries[i].v;
+ voltage_table->entries[i].smio_low = 0;
+ }
+
+ return 0;
+}
+
+static int si_construct_voltage_tables(struct amdgpu_device *adev)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+ int ret;
+
+ if (pi->voltage_control) {
+ ret = amdgpu_atombios_get_voltage_table(adev, VOLTAGE_TYPE_VDDC,
+ VOLTAGE_OBJ_GPIO_LUT, &eg_pi->vddc_voltage_table);
+ if (ret)
+ return ret;
+
+ if (eg_pi->vddc_voltage_table.count > SISLANDS_MAX_NO_VREG_STEPS)
+ si_trim_voltage_table_to_fit_state_table(adev,
+ SISLANDS_MAX_NO_VREG_STEPS,
+ &eg_pi->vddc_voltage_table);
+ } else if (si_pi->voltage_control_svi2) {
+ ret = si_get_svi2_voltage_table(adev,
+ &adev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+ &eg_pi->vddc_voltage_table);
+ if (ret)
+ return ret;
+ } else {
+ return -EINVAL;
+ }
+
+ if (eg_pi->vddci_control) {
+ ret = amdgpu_atombios_get_voltage_table(adev, VOLTAGE_TYPE_VDDCI,
+ VOLTAGE_OBJ_GPIO_LUT, &eg_pi->vddci_voltage_table);
+ if (ret)
+ return ret;
+
+ if (eg_pi->vddci_voltage_table.count > SISLANDS_MAX_NO_VREG_STEPS)
+ si_trim_voltage_table_to_fit_state_table(adev,
+ SISLANDS_MAX_NO_VREG_STEPS,
+ &eg_pi->vddci_voltage_table);
+ }
+ if (si_pi->vddci_control_svi2) {
+ ret = si_get_svi2_voltage_table(adev,
+ &adev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+ &eg_pi->vddci_voltage_table);
+ if (ret)
+ return ret;
+ }
+
+ if (pi->mvdd_control) {
+ ret = amdgpu_atombios_get_voltage_table(adev, VOLTAGE_TYPE_MVDDC,
+ VOLTAGE_OBJ_GPIO_LUT, &si_pi->mvdd_voltage_table);
+
+ if (ret) {
+ pi->mvdd_control = false;
+ return ret;
+ }
+
+ if (si_pi->mvdd_voltage_table.count == 0) {
+ pi->mvdd_control = false;
+ return -EINVAL;
+ }
+
+ if (si_pi->mvdd_voltage_table.count > SISLANDS_MAX_NO_VREG_STEPS)
+ si_trim_voltage_table_to_fit_state_table(adev,
+ SISLANDS_MAX_NO_VREG_STEPS,
+ &si_pi->mvdd_voltage_table);
+ }
+
+ if (si_pi->vddc_phase_shed_control) {
+ ret = amdgpu_atombios_get_voltage_table(adev, VOLTAGE_TYPE_VDDC,
+ VOLTAGE_OBJ_PHASE_LUT, &si_pi->vddc_phase_shed_table);
+ if (ret)
+ si_pi->vddc_phase_shed_control = false;
+
+ if ((si_pi->vddc_phase_shed_table.count == 0) ||
+ (si_pi->vddc_phase_shed_table.count > SISLANDS_MAX_NO_VREG_STEPS))
+ si_pi->vddc_phase_shed_control = false;
+ }
+
+ return 0;
+}
+
+static void si_populate_smc_voltage_table(struct amdgpu_device *adev,
+ const struct atom_voltage_table *voltage_table,
+ SISLANDS_SMC_STATETABLE *table)
+{
+ unsigned int i;
+
+ for (i = 0; i < voltage_table->count; i++)
+ table->lowSMIO[i] |= cpu_to_be32(voltage_table->entries[i].smio_low);
+}
+
+static int si_populate_smc_voltage_tables(struct amdgpu_device *adev,
+ SISLANDS_SMC_STATETABLE *table)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+ u8 i;
+
+ if (si_pi->voltage_control_svi2) {
+ si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_svi_rework_gpio_id_svc,
+ si_pi->svc_gpio_id);
+ si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_svi_rework_gpio_id_svd,
+ si_pi->svd_gpio_id);
+ si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_svi_rework_plat_type,
+ 2);
+ } else {
+ if (eg_pi->vddc_voltage_table.count) {
+ si_populate_smc_voltage_table(adev, &eg_pi->vddc_voltage_table, table);
+ table->voltageMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC] =
+ cpu_to_be32(eg_pi->vddc_voltage_table.mask_low);
+
+ for (i = 0; i < eg_pi->vddc_voltage_table.count; i++) {
+ if (pi->max_vddc_in_table <= eg_pi->vddc_voltage_table.entries[i].value) {
+ table->maxVDDCIndexInPPTable = i;
+ break;
+ }
+ }
+ }
+
+ if (eg_pi->vddci_voltage_table.count) {
+ si_populate_smc_voltage_table(adev, &eg_pi->vddci_voltage_table, table);
+
+ table->voltageMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDCI] =
+ cpu_to_be32(eg_pi->vddci_voltage_table.mask_low);
+ }
+
+
+ if (si_pi->mvdd_voltage_table.count) {
+ si_populate_smc_voltage_table(adev, &si_pi->mvdd_voltage_table, table);
+
+ table->voltageMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_MVDD] =
+ cpu_to_be32(si_pi->mvdd_voltage_table.mask_low);
+ }
+
+ if (si_pi->vddc_phase_shed_control) {
+ if (si_validate_phase_shedding_tables(adev, &si_pi->vddc_phase_shed_table,
+ &adev->pm.dpm.dyn_state.phase_shedding_limits_table)) {
+ si_populate_smc_voltage_table(adev, &si_pi->vddc_phase_shed_table, table);
+
+ table->phaseMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC_PHASE_SHEDDING] =
+ cpu_to_be32(si_pi->vddc_phase_shed_table.mask_low);
+
+ si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_phase_shedding_delay,
+ (u32)si_pi->vddc_phase_shed_table.phase_delay);
+ } else {
+ si_pi->vddc_phase_shed_control = false;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int si_populate_voltage_value(struct amdgpu_device *adev,
+ const struct atom_voltage_table *table,
+ u16 value, SISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+ unsigned int i;
+
+ for (i = 0; i < table->count; i++) {
+ if (value <= table->entries[i].value) {
+ voltage->index = (u8)i;
+ voltage->value = cpu_to_be16(table->entries[i].value);
+ break;
+ }
+ }
+
+ if (i >= table->count)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int si_populate_mvdd_value(struct amdgpu_device *adev, u32 mclk,
+ SISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+
+ if (pi->mvdd_control) {
+ if (mclk <= pi->mvdd_split_frequency)
+ voltage->index = 0;
+ else
+ voltage->index = (u8)(si_pi->mvdd_voltage_table.count) - 1;
+
+ voltage->value = cpu_to_be16(si_pi->mvdd_voltage_table.entries[voltage->index].value);
+ }
+ return 0;
+}
+
+static int si_get_std_voltage_value(struct amdgpu_device *adev,
+ SISLANDS_SMC_VOLTAGE_VALUE *voltage,
+ u16 *std_voltage)
+{
+ u16 v_index;
+ bool voltage_found = false;
+ *std_voltage = be16_to_cpu(voltage->value);
+
+ if (adev->pm.dpm.dyn_state.cac_leakage_table.entries) {
+ if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_NEW_CAC_VOLTAGE) {
+ if (adev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries == NULL)
+ return -EINVAL;
+
+ for (v_index = 0; (u32)v_index < adev->pm.dpm.dyn_state.vddc_dependency_on_sclk.count; v_index++) {
+ if (be16_to_cpu(voltage->value) ==
+ (u16)adev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[v_index].v) {
+ voltage_found = true;
+ if ((u32)v_index < adev->pm.dpm.dyn_state.cac_leakage_table.count)
+ *std_voltage =
+ adev->pm.dpm.dyn_state.cac_leakage_table.entries[v_index].vddc;
+ else
+ *std_voltage =
+ adev->pm.dpm.dyn_state.cac_leakage_table.entries[adev->pm.dpm.dyn_state.cac_leakage_table.count-1].vddc;
+ break;
+ }
+ }
+
+ if (!voltage_found) {
+ for (v_index = 0; (u32)v_index < adev->pm.dpm.dyn_state.vddc_dependency_on_sclk.count; v_index++) {
+ if (be16_to_cpu(voltage->value) <=
+ (u16)adev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[v_index].v) {
+ voltage_found = true;
+ if ((u32)v_index < adev->pm.dpm.dyn_state.cac_leakage_table.count)
+ *std_voltage =
+ adev->pm.dpm.dyn_state.cac_leakage_table.entries[v_index].vddc;
+ else
+ *std_voltage =
+ adev->pm.dpm.dyn_state.cac_leakage_table.entries[adev->pm.dpm.dyn_state.cac_leakage_table.count-1].vddc;
+ break;
+ }
+ }
+ }
+ } else {
+ if ((u32)voltage->index < adev->pm.dpm.dyn_state.cac_leakage_table.count)
+ *std_voltage = adev->pm.dpm.dyn_state.cac_leakage_table.entries[voltage->index].vddc;
+ }
+ }
+
+ return 0;
+}
+
+static int si_populate_std_voltage_value(struct amdgpu_device *adev,
+ u16 value, u8 index,
+ SISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+ voltage->index = index;
+ voltage->value = cpu_to_be16(value);
+
+ return 0;
+}
+
+static int si_populate_phase_shedding_value(struct amdgpu_device *adev,
+ const struct amdgpu_phase_shedding_limits_table *limits,
+ u16 voltage, u32 sclk, u32 mclk,
+ SISLANDS_SMC_VOLTAGE_VALUE *smc_voltage)
+{
+ unsigned int i;
+
+ for (i = 0; i < limits->count; i++) {
+ if ((voltage <= limits->entries[i].voltage) &&
+ (sclk <= limits->entries[i].sclk) &&
+ (mclk <= limits->entries[i].mclk))
+ break;
+ }
+
+ smc_voltage->phase_settings = (u8)i;
+
+ return 0;
+}
+
+static int si_init_arb_table_index(struct amdgpu_device *adev)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ u32 tmp;
+ int ret;
+
+ ret = amdgpu_si_read_smc_sram_dword(adev, si_pi->arb_table_start,
+ &tmp, si_pi->sram_end);
+ if (ret)
+ return ret;
+
+ tmp &= 0x00FFFFFF;
+ tmp |= MC_CG_ARB_FREQ_F1 << 24;
+
+ return amdgpu_si_write_smc_sram_dword(adev, si_pi->arb_table_start,
+ tmp, si_pi->sram_end);
+}
+
+static int si_initial_switch_from_arb_f0_to_f1(struct amdgpu_device *adev)
+{
+ return ni_copy_and_switch_arb_sets(adev, MC_CG_ARB_FREQ_F0, MC_CG_ARB_FREQ_F1);
+}
+
+static int si_reset_to_default(struct amdgpu_device *adev)
+{
+ return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_ResetToDefaults) == PPSMC_Result_OK) ?
+ 0 : -EINVAL;
+}
+
+static int si_force_switch_to_arb_f0(struct amdgpu_device *adev)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ u32 tmp;
+ int ret;
+
+ ret = amdgpu_si_read_smc_sram_dword(adev, si_pi->arb_table_start,
+ &tmp, si_pi->sram_end);
+ if (ret)
+ return ret;
+
+ tmp = (tmp >> 24) & 0xff;
+
+ if (tmp == MC_CG_ARB_FREQ_F0)
+ return 0;
+
+ return ni_copy_and_switch_arb_sets(adev, tmp, MC_CG_ARB_FREQ_F0);
+}
+
+static u32 si_calculate_memory_refresh_rate(struct amdgpu_device *adev,
+ u32 engine_clock)
+{
+ u32 dram_rows;
+ u32 dram_refresh_rate;
+ u32 mc_arb_rfsh_rate;
+ u32 tmp = (RREG32(MC_ARB_RAMCFG) & NOOFROWS_MASK) >> NOOFROWS_SHIFT;
+
+ if (tmp >= 4)
+ dram_rows = 16384;
+ else
+ dram_rows = 1 << (tmp + 10);
+
+ dram_refresh_rate = 1 << ((RREG32(MC_SEQ_MISC0) & 0x3) + 3);
+ mc_arb_rfsh_rate = ((engine_clock * 10) * dram_refresh_rate / dram_rows - 32) / 64;
+
+ return mc_arb_rfsh_rate;
+}
+
+static int si_populate_memory_timing_parameters(struct amdgpu_device *adev,
+ struct rv7xx_pl *pl,
+ SMC_SIslands_MCArbDramTimingRegisterSet *arb_regs)
+{
+ u32 dram_timing;
+ u32 dram_timing2;
+ u32 burst_time;
+
+ arb_regs->mc_arb_rfsh_rate =
+ (u8)si_calculate_memory_refresh_rate(adev, pl->sclk);
+
+ amdgpu_atombios_set_engine_dram_timings(adev,
+ pl->sclk,
+ pl->mclk);
+
+ dram_timing = RREG32(MC_ARB_DRAM_TIMING);
+ dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2);
+ burst_time = RREG32(MC_ARB_BURST_TIME) & STATE0_MASK;
+
+ arb_regs->mc_arb_dram_timing = cpu_to_be32(dram_timing);
+ arb_regs->mc_arb_dram_timing2 = cpu_to_be32(dram_timing2);
+ arb_regs->mc_arb_burst_time = (u8)burst_time;
+
+ return 0;
+}
+
+static int si_do_program_memory_timing_parameters(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_state,
+ unsigned int first_arb_set)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ struct si_ps *state = si_get_ps(amdgpu_state);
+ SMC_SIslands_MCArbDramTimingRegisterSet arb_regs = { 0 };
+ int i, ret = 0;
+
+ for (i = 0; i < state->performance_level_count; i++) {
+ ret = si_populate_memory_timing_parameters(adev, &state->performance_levels[i], &arb_regs);
+ if (ret)
+ break;
+ ret = amdgpu_si_copy_bytes_to_smc(adev,
+ si_pi->arb_table_start +
+ offsetof(SMC_SIslands_MCArbDramTimingRegisters, data) +
+ sizeof(SMC_SIslands_MCArbDramTimingRegisterSet) * (first_arb_set + i),
+ (u8 *)&arb_regs,
+ sizeof(SMC_SIslands_MCArbDramTimingRegisterSet),
+ si_pi->sram_end);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int si_program_memory_timing_parameters(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_new_state)
+{
+ return si_do_program_memory_timing_parameters(adev, amdgpu_new_state,
+ SISLANDS_DRIVER_STATE_ARB_INDEX);
+}
+
+static int si_populate_initial_mvdd_value(struct amdgpu_device *adev,
+ struct SISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+
+ if (pi->mvdd_control)
+ return si_populate_voltage_value(adev, &si_pi->mvdd_voltage_table,
+ si_pi->mvdd_bootup_value, voltage);
+
+ return 0;
+}
+
+static int si_populate_smc_initial_state(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_initial_state,
+ SISLANDS_SMC_STATETABLE *table)
+{
+ struct si_ps *initial_state = si_get_ps(amdgpu_initial_state);
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+ u32 reg;
+ int ret;
+
+ table->initialState.levels[0].mclk.vDLL_CNTL =
+ cpu_to_be32(si_pi->clock_registers.dll_cntl);
+ table->initialState.levels[0].mclk.vMCLK_PWRMGT_CNTL =
+ cpu_to_be32(si_pi->clock_registers.mclk_pwrmgt_cntl);
+ table->initialState.levels[0].mclk.vMPLL_AD_FUNC_CNTL =
+ cpu_to_be32(si_pi->clock_registers.mpll_ad_func_cntl);
+ table->initialState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL =
+ cpu_to_be32(si_pi->clock_registers.mpll_dq_func_cntl);
+ table->initialState.levels[0].mclk.vMPLL_FUNC_CNTL =
+ cpu_to_be32(si_pi->clock_registers.mpll_func_cntl);
+ table->initialState.levels[0].mclk.vMPLL_FUNC_CNTL_1 =
+ cpu_to_be32(si_pi->clock_registers.mpll_func_cntl_1);
+ table->initialState.levels[0].mclk.vMPLL_FUNC_CNTL_2 =
+ cpu_to_be32(si_pi->clock_registers.mpll_func_cntl_2);
+ table->initialState.levels[0].mclk.vMPLL_SS =
+ cpu_to_be32(si_pi->clock_registers.mpll_ss1);
+ table->initialState.levels[0].mclk.vMPLL_SS2 =
+ cpu_to_be32(si_pi->clock_registers.mpll_ss2);
+
+ table->initialState.levels[0].mclk.mclk_value =
+ cpu_to_be32(initial_state->performance_levels[0].mclk);
+
+ table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL =
+ cpu_to_be32(si_pi->clock_registers.cg_spll_func_cntl);
+ table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 =
+ cpu_to_be32(si_pi->clock_registers.cg_spll_func_cntl_2);
+ table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 =
+ cpu_to_be32(si_pi->clock_registers.cg_spll_func_cntl_3);
+ table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_4 =
+ cpu_to_be32(si_pi->clock_registers.cg_spll_func_cntl_4);
+ table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM =
+ cpu_to_be32(si_pi->clock_registers.cg_spll_spread_spectrum);
+ table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM_2 =
+ cpu_to_be32(si_pi->clock_registers.cg_spll_spread_spectrum_2);
+
+ table->initialState.levels[0].sclk.sclk_value =
+ cpu_to_be32(initial_state->performance_levels[0].sclk);
+
+ table->initialState.levels[0].arbRefreshState =
+ SISLANDS_INITIAL_STATE_ARB_INDEX;
+
+ table->initialState.levels[0].ACIndex = 0;
+
+ ret = si_populate_voltage_value(adev, &eg_pi->vddc_voltage_table,
+ initial_state->performance_levels[0].vddc,
+ &table->initialState.levels[0].vddc);
+
+ if (!ret) {
+ u16 std_vddc;
+
+ ret = si_get_std_voltage_value(adev,
+ &table->initialState.levels[0].vddc,
+ &std_vddc);
+ if (!ret)
+ si_populate_std_voltage_value(adev, std_vddc,
+ table->initialState.levels[0].vddc.index,
+ &table->initialState.levels[0].std_vddc);
+ }
+
+ if (eg_pi->vddci_control)
+ si_populate_voltage_value(adev,
+ &eg_pi->vddci_voltage_table,
+ initial_state->performance_levels[0].vddci,
+ &table->initialState.levels[0].vddci);
+
+ if (si_pi->vddc_phase_shed_control)
+ si_populate_phase_shedding_value(adev,
+ &adev->pm.dpm.dyn_state.phase_shedding_limits_table,
+ initial_state->performance_levels[0].vddc,
+ initial_state->performance_levels[0].sclk,
+ initial_state->performance_levels[0].mclk,
+ &table->initialState.levels[0].vddc);
+
+ si_populate_initial_mvdd_value(adev, &table->initialState.levels[0].mvdd);
+
+ reg = CG_R(0xffff) | CG_L(0);
+ table->initialState.levels[0].aT = cpu_to_be32(reg);
+ table->initialState.levels[0].bSP = cpu_to_be32(pi->dsp);
+ table->initialState.levels[0].gen2PCIE = (u8)si_pi->boot_pcie_gen;
+
+ if (adev->gmc.vram_type == AMDGPU_VRAM_TYPE_GDDR5) {
+ table->initialState.levels[0].strobeMode =
+ si_get_strobe_mode_settings(adev,
+ initial_state->performance_levels[0].mclk);
+
+ if (initial_state->performance_levels[0].mclk > pi->mclk_edc_enable_threshold)
+ table->initialState.levels[0].mcFlags = SISLANDS_SMC_MC_EDC_RD_FLAG | SISLANDS_SMC_MC_EDC_WR_FLAG;
+ else
+ table->initialState.levels[0].mcFlags = 0;
+ }
+
+ table->initialState.levelCount = 1;
+
+ table->initialState.flags |= PPSMC_SWSTATE_FLAG_DC;
+
+ table->initialState.levels[0].dpm2.MaxPS = 0;
+ table->initialState.levels[0].dpm2.NearTDPDec = 0;
+ table->initialState.levels[0].dpm2.AboveSafeInc = 0;
+ table->initialState.levels[0].dpm2.BelowSafeInc = 0;
+ table->initialState.levels[0].dpm2.PwrEfficiencyRatio = 0;
+
+ reg = MIN_POWER_MASK | MAX_POWER_MASK;
+ table->initialState.levels[0].SQPowerThrottle = cpu_to_be32(reg);
+
+ reg = MAX_POWER_DELTA_MASK | STI_SIZE_MASK | LTI_RATIO_MASK;
+ table->initialState.levels[0].SQPowerThrottle_2 = cpu_to_be32(reg);
+
+ return 0;
+}
+
+static int si_populate_smc_acpi_state(struct amdgpu_device *adev,
+ SISLANDS_SMC_STATETABLE *table)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+ u32 spll_func_cntl = si_pi->clock_registers.cg_spll_func_cntl;
+ u32 spll_func_cntl_2 = si_pi->clock_registers.cg_spll_func_cntl_2;
+ u32 spll_func_cntl_3 = si_pi->clock_registers.cg_spll_func_cntl_3;
+ u32 spll_func_cntl_4 = si_pi->clock_registers.cg_spll_func_cntl_4;
+ u32 dll_cntl = si_pi->clock_registers.dll_cntl;
+ u32 mclk_pwrmgt_cntl = si_pi->clock_registers.mclk_pwrmgt_cntl;
+ u32 mpll_ad_func_cntl = si_pi->clock_registers.mpll_ad_func_cntl;
+ u32 mpll_dq_func_cntl = si_pi->clock_registers.mpll_dq_func_cntl;
+ u32 mpll_func_cntl = si_pi->clock_registers.mpll_func_cntl;
+ u32 mpll_func_cntl_1 = si_pi->clock_registers.mpll_func_cntl_1;
+ u32 mpll_func_cntl_2 = si_pi->clock_registers.mpll_func_cntl_2;
+ u32 reg;
+ int ret;
+
+ table->ACPIState = table->initialState;
+
+ table->ACPIState.flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+ if (pi->acpi_vddc) {
+ ret = si_populate_voltage_value(adev, &eg_pi->vddc_voltage_table,
+ pi->acpi_vddc, &table->ACPIState.levels[0].vddc);
+ if (!ret) {
+ u16 std_vddc;
+
+ ret = si_get_std_voltage_value(adev,
+ &table->ACPIState.levels[0].vddc, &std_vddc);
+ if (!ret)
+ si_populate_std_voltage_value(adev, std_vddc,
+ table->ACPIState.levels[0].vddc.index,
+ &table->ACPIState.levels[0].std_vddc);
+ }
+ table->ACPIState.levels[0].gen2PCIE = si_pi->acpi_pcie_gen;
+
+ if (si_pi->vddc_phase_shed_control) {
+ si_populate_phase_shedding_value(adev,
+ &adev->pm.dpm.dyn_state.phase_shedding_limits_table,
+ pi->acpi_vddc,
+ 0,
+ 0,
+ &table->ACPIState.levels[0].vddc);
+ }
+ } else {
+ ret = si_populate_voltage_value(adev, &eg_pi->vddc_voltage_table,
+ pi->min_vddc_in_table, &table->ACPIState.levels[0].vddc);
+ if (!ret) {
+ u16 std_vddc;
+
+ ret = si_get_std_voltage_value(adev,
+ &table->ACPIState.levels[0].vddc, &std_vddc);
+
+ if (!ret)
+ si_populate_std_voltage_value(adev, std_vddc,
+ table->ACPIState.levels[0].vddc.index,
+ &table->ACPIState.levels[0].std_vddc);
+ }
+ table->ACPIState.levels[0].gen2PCIE =
+ (u8)amdgpu_get_pcie_gen_support(adev,
+ si_pi->sys_pcie_mask,
+ si_pi->boot_pcie_gen,
+ AMDGPU_PCIE_GEN1);
+
+ if (si_pi->vddc_phase_shed_control)
+ si_populate_phase_shedding_value(adev,
+ &adev->pm.dpm.dyn_state.phase_shedding_limits_table,
+ pi->min_vddc_in_table,
+ 0,
+ 0,
+ &table->ACPIState.levels[0].vddc);
+ }
+
+ if (pi->acpi_vddc) {
+ if (eg_pi->acpi_vddci)
+ si_populate_voltage_value(adev, &eg_pi->vddci_voltage_table,
+ eg_pi->acpi_vddci,
+ &table->ACPIState.levels[0].vddci);
+ }
+
+ mclk_pwrmgt_cntl |= MRDCK0_RESET | MRDCK1_RESET;
+ mclk_pwrmgt_cntl &= ~(MRDCK0_PDNB | MRDCK1_PDNB);
+
+ dll_cntl &= ~(MRDCK0_BYPASS | MRDCK1_BYPASS);
+
+ spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
+ spll_func_cntl_2 |= SCLK_MUX_SEL(4);
+
+ table->ACPIState.levels[0].mclk.vDLL_CNTL =
+ cpu_to_be32(dll_cntl);
+ table->ACPIState.levels[0].mclk.vMCLK_PWRMGT_CNTL =
+ cpu_to_be32(mclk_pwrmgt_cntl);
+ table->ACPIState.levels[0].mclk.vMPLL_AD_FUNC_CNTL =
+ cpu_to_be32(mpll_ad_func_cntl);
+ table->ACPIState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL =
+ cpu_to_be32(mpll_dq_func_cntl);
+ table->ACPIState.levels[0].mclk.vMPLL_FUNC_CNTL =
+ cpu_to_be32(mpll_func_cntl);
+ table->ACPIState.levels[0].mclk.vMPLL_FUNC_CNTL_1 =
+ cpu_to_be32(mpll_func_cntl_1);
+ table->ACPIState.levels[0].mclk.vMPLL_FUNC_CNTL_2 =
+ cpu_to_be32(mpll_func_cntl_2);
+ table->ACPIState.levels[0].mclk.vMPLL_SS =
+ cpu_to_be32(si_pi->clock_registers.mpll_ss1);
+ table->ACPIState.levels[0].mclk.vMPLL_SS2 =
+ cpu_to_be32(si_pi->clock_registers.mpll_ss2);
+
+ table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL =
+ cpu_to_be32(spll_func_cntl);
+ table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 =
+ cpu_to_be32(spll_func_cntl_2);
+ table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 =
+ cpu_to_be32(spll_func_cntl_3);
+ table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_4 =
+ cpu_to_be32(spll_func_cntl_4);
+
+ table->ACPIState.levels[0].mclk.mclk_value = 0;
+ table->ACPIState.levels[0].sclk.sclk_value = 0;
+
+ si_populate_mvdd_value(adev, 0, &table->ACPIState.levels[0].mvdd);
+
+ if (eg_pi->dynamic_ac_timing)
+ table->ACPIState.levels[0].ACIndex = 0;
+
+ table->ACPIState.levels[0].dpm2.MaxPS = 0;
+ table->ACPIState.levels[0].dpm2.NearTDPDec = 0;
+ table->ACPIState.levels[0].dpm2.AboveSafeInc = 0;
+ table->ACPIState.levels[0].dpm2.BelowSafeInc = 0;
+ table->ACPIState.levels[0].dpm2.PwrEfficiencyRatio = 0;
+
+ reg = MIN_POWER_MASK | MAX_POWER_MASK;
+ table->ACPIState.levels[0].SQPowerThrottle = cpu_to_be32(reg);
+
+ reg = MAX_POWER_DELTA_MASK | STI_SIZE_MASK | LTI_RATIO_MASK;
+ table->ACPIState.levels[0].SQPowerThrottle_2 = cpu_to_be32(reg);
+
+ return 0;
+}
+
+static int si_populate_ulv_state(struct amdgpu_device *adev,
+ SISLANDS_SMC_SWSTATE *state)
+{
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+ struct si_ulv_param *ulv = &si_pi->ulv;
+ u32 sclk_in_sr = 1350; /* ??? */
+ int ret;
+
+ ret = si_convert_power_level_to_smc(adev, &ulv->pl,
+ &state->levels[0]);
+ if (!ret) {
+ if (eg_pi->sclk_deep_sleep) {
+ if (sclk_in_sr <= SCLK_MIN_DEEPSLEEP_FREQ)
+ state->levels[0].stateFlags |= PPSMC_STATEFLAG_DEEPSLEEP_BYPASS;
+ else
+ state->levels[0].stateFlags |= PPSMC_STATEFLAG_DEEPSLEEP_THROTTLE;
+ }
+ if (ulv->one_pcie_lane_in_ulv)
+ state->flags |= PPSMC_SWSTATE_FLAG_PCIE_X1;
+ state->levels[0].arbRefreshState = (u8)(SISLANDS_ULV_STATE_ARB_INDEX);
+ state->levels[0].ACIndex = 1;
+ state->levels[0].std_vddc = state->levels[0].vddc;
+ state->levelCount = 1;
+
+ state->flags |= PPSMC_SWSTATE_FLAG_DC;
+ }
+
+ return ret;
+}
+
+static int si_program_ulv_memory_timing_parameters(struct amdgpu_device *adev)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ struct si_ulv_param *ulv = &si_pi->ulv;
+ SMC_SIslands_MCArbDramTimingRegisterSet arb_regs = { 0 };
+ int ret;
+
+ ret = si_populate_memory_timing_parameters(adev, &ulv->pl,
+ &arb_regs);
+ if (ret)
+ return ret;
+
+ si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_ulv_volt_change_delay,
+ ulv->volt_change_delay);
+
+ ret = amdgpu_si_copy_bytes_to_smc(adev,
+ si_pi->arb_table_start +
+ offsetof(SMC_SIslands_MCArbDramTimingRegisters, data) +
+ sizeof(SMC_SIslands_MCArbDramTimingRegisterSet) * SISLANDS_ULV_STATE_ARB_INDEX,
+ (u8 *)&arb_regs,
+ sizeof(SMC_SIslands_MCArbDramTimingRegisterSet),
+ si_pi->sram_end);
+
+ return ret;
+}
+
+static void si_get_mvdd_configuration(struct amdgpu_device *adev)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+
+ pi->mvdd_split_frequency = 30000;
+}
+
+static int si_init_smc_table(struct amdgpu_device *adev)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ struct amdgpu_ps *amdgpu_boot_state = adev->pm.dpm.boot_ps;
+ const struct si_ulv_param *ulv = &si_pi->ulv;
+ SISLANDS_SMC_STATETABLE *table = &si_pi->smc_statetable;
+ int ret;
+ u32 lane_width;
+ u32 vr_hot_gpio;
+
+ si_populate_smc_voltage_tables(adev, table);
+
+ switch (adev->pm.int_thermal_type) {
+ case THERMAL_TYPE_SI:
+ case THERMAL_TYPE_EMC2103_WITH_INTERNAL:
+ table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_INTERNAL;
+ break;
+ case THERMAL_TYPE_NONE:
+ table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_NONE;
+ break;
+ default:
+ table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_EXTERNAL;
+ break;
+ }
+
+ if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC)
+ table->systemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+ if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REGULATOR_HOT) {
+ if ((adev->pdev->device != 0x6818) && (adev->pdev->device != 0x6819))
+ table->systemFlags |= PPSMC_SYSTEMFLAG_REGULATOR_HOT;
+ }
+
+ if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC)
+ table->systemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+ if (adev->gmc.vram_type == AMDGPU_VRAM_TYPE_GDDR5)
+ table->systemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
+
+ if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REVERT_GPIO5_POLARITY)
+ table->extraFlags |= PPSMC_EXTRAFLAGS_AC2DC_GPIO5_POLARITY_HIGH;
+
+ if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_VRHOT_GPIO_CONFIGURABLE) {
+ table->systemFlags |= PPSMC_SYSTEMFLAG_REGULATOR_HOT_PROG_GPIO;
+ vr_hot_gpio = adev->pm.dpm.backbias_response_time;
+ si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_vr_hot_gpio,
+ vr_hot_gpio);
+ }
+
+ ret = si_populate_smc_initial_state(adev, amdgpu_boot_state, table);
+ if (ret)
+ return ret;
+
+ ret = si_populate_smc_acpi_state(adev, table);
+ if (ret)
+ return ret;
+
+ table->driverState = table->initialState;
+
+ ret = si_do_program_memory_timing_parameters(adev, amdgpu_boot_state,
+ SISLANDS_INITIAL_STATE_ARB_INDEX);
+ if (ret)
+ return ret;
+
+ if (ulv->supported && ulv->pl.vddc) {
+ ret = si_populate_ulv_state(adev, &table->ULVState);
+ if (ret)
+ return ret;
+
+ ret = si_program_ulv_memory_timing_parameters(adev);
+ if (ret)
+ return ret;
+
+ WREG32(CG_ULV_CONTROL, ulv->cg_ulv_control);
+ WREG32(CG_ULV_PARAMETER, ulv->cg_ulv_parameter);
+
+ lane_width = amdgpu_get_pcie_lanes(adev);
+ si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_non_ulv_pcie_link_width, lane_width);
+ } else {
+ table->ULVState = table->initialState;
+ }
+
+ return amdgpu_si_copy_bytes_to_smc(adev, si_pi->state_table_start,
+ (u8 *)table, sizeof(SISLANDS_SMC_STATETABLE),
+ si_pi->sram_end);
+}
+
+static int si_calculate_sclk_params(struct amdgpu_device *adev,
+ u32 engine_clock,
+ SISLANDS_SMC_SCLK_VALUE *sclk)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+ struct atom_clock_dividers dividers;
+ u32 spll_func_cntl = si_pi->clock_registers.cg_spll_func_cntl;
+ u32 spll_func_cntl_2 = si_pi->clock_registers.cg_spll_func_cntl_2;
+ u32 spll_func_cntl_3 = si_pi->clock_registers.cg_spll_func_cntl_3;
+ u32 spll_func_cntl_4 = si_pi->clock_registers.cg_spll_func_cntl_4;
+ u32 cg_spll_spread_spectrum = si_pi->clock_registers.cg_spll_spread_spectrum;
+ u32 cg_spll_spread_spectrum_2 = si_pi->clock_registers.cg_spll_spread_spectrum_2;
+ u64 tmp;
+ u32 reference_clock = adev->clock.spll.reference_freq;
+ u32 reference_divider;
+ u32 fbdiv;
+ int ret;
+
+ ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_ENGINE_PLL_PARAM,
+ engine_clock, false, &dividers);
+ if (ret)
+ return ret;
+
+ reference_divider = 1 + dividers.ref_div;
+
+ tmp = (u64) engine_clock * reference_divider * dividers.post_div * 16384;
+ do_div(tmp, reference_clock);
+ fbdiv = (u32) tmp;
+
+ spll_func_cntl &= ~(SPLL_PDIV_A_MASK | SPLL_REF_DIV_MASK);
+ spll_func_cntl |= SPLL_REF_DIV(dividers.ref_div);
+ spll_func_cntl |= SPLL_PDIV_A(dividers.post_div);
+
+ spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
+ spll_func_cntl_2 |= SCLK_MUX_SEL(2);
+
+ spll_func_cntl_3 &= ~SPLL_FB_DIV_MASK;
+ spll_func_cntl_3 |= SPLL_FB_DIV(fbdiv);
+ spll_func_cntl_3 |= SPLL_DITHEN;
+
+ if (pi->sclk_ss) {
+ struct amdgpu_atom_ss ss;
+ u32 vco_freq = engine_clock * dividers.post_div;
+
+ if (amdgpu_atombios_get_asic_ss_info(adev, &ss,
+ ASIC_INTERNAL_ENGINE_SS, vco_freq)) {
+ u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate);
+ u32 clk_v = 4 * ss.percentage * fbdiv / (clk_s * 10000);
+
+ cg_spll_spread_spectrum &= ~CLK_S_MASK;
+ cg_spll_spread_spectrum |= CLK_S(clk_s);
+ cg_spll_spread_spectrum |= SSEN;
+
+ cg_spll_spread_spectrum_2 &= ~CLK_V_MASK;
+ cg_spll_spread_spectrum_2 |= CLK_V(clk_v);
+ }
+ }
+
+ sclk->sclk_value = engine_clock;
+ sclk->vCG_SPLL_FUNC_CNTL = spll_func_cntl;
+ sclk->vCG_SPLL_FUNC_CNTL_2 = spll_func_cntl_2;
+ sclk->vCG_SPLL_FUNC_CNTL_3 = spll_func_cntl_3;
+ sclk->vCG_SPLL_FUNC_CNTL_4 = spll_func_cntl_4;
+ sclk->vCG_SPLL_SPREAD_SPECTRUM = cg_spll_spread_spectrum;
+ sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cg_spll_spread_spectrum_2;
+
+ return 0;
+}
+
+static int si_populate_sclk_value(struct amdgpu_device *adev,
+ u32 engine_clock,
+ SISLANDS_SMC_SCLK_VALUE *sclk)
+{
+ SISLANDS_SMC_SCLK_VALUE sclk_tmp;
+ int ret;
+
+ ret = si_calculate_sclk_params(adev, engine_clock, &sclk_tmp);
+ if (!ret) {
+ sclk->sclk_value = cpu_to_be32(sclk_tmp.sclk_value);
+ sclk->vCG_SPLL_FUNC_CNTL = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL);
+ sclk->vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL_2);
+ sclk->vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL_3);
+ sclk->vCG_SPLL_FUNC_CNTL_4 = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL_4);
+ sclk->vCG_SPLL_SPREAD_SPECTRUM = cpu_to_be32(sclk_tmp.vCG_SPLL_SPREAD_SPECTRUM);
+ sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cpu_to_be32(sclk_tmp.vCG_SPLL_SPREAD_SPECTRUM_2);
+ }
+
+ return ret;
+}
+
+static int si_populate_mclk_value(struct amdgpu_device *adev,
+ u32 engine_clock,
+ u32 memory_clock,
+ SISLANDS_SMC_MCLK_VALUE *mclk,
+ bool strobe_mode,
+ bool dll_state_on)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+ u32 dll_cntl = si_pi->clock_registers.dll_cntl;
+ u32 mclk_pwrmgt_cntl = si_pi->clock_registers.mclk_pwrmgt_cntl;
+ u32 mpll_ad_func_cntl = si_pi->clock_registers.mpll_ad_func_cntl;
+ u32 mpll_dq_func_cntl = si_pi->clock_registers.mpll_dq_func_cntl;
+ u32 mpll_func_cntl = si_pi->clock_registers.mpll_func_cntl;
+ u32 mpll_func_cntl_1 = si_pi->clock_registers.mpll_func_cntl_1;
+ u32 mpll_func_cntl_2 = si_pi->clock_registers.mpll_func_cntl_2;
+ u32 mpll_ss1 = si_pi->clock_registers.mpll_ss1;
+ u32 mpll_ss2 = si_pi->clock_registers.mpll_ss2;
+ struct atom_mpll_param mpll_param;
+ int ret;
+
+ ret = amdgpu_atombios_get_memory_pll_dividers(adev, memory_clock, strobe_mode, &mpll_param);
+ if (ret)
+ return ret;
+
+ mpll_func_cntl &= ~BWCTRL_MASK;
+ mpll_func_cntl |= BWCTRL(mpll_param.bwcntl);
+
+ mpll_func_cntl_1 &= ~(CLKF_MASK | CLKFRAC_MASK | VCO_MODE_MASK);
+ mpll_func_cntl_1 |= CLKF(mpll_param.clkf) |
+ CLKFRAC(mpll_param.clkfrac) | VCO_MODE(mpll_param.vco_mode);
+
+ mpll_ad_func_cntl &= ~YCLK_POST_DIV_MASK;
+ mpll_ad_func_cntl |= YCLK_POST_DIV(mpll_param.post_div);
+
+ if (adev->gmc.vram_type == AMDGPU_VRAM_TYPE_GDDR5) {
+ mpll_dq_func_cntl &= ~(YCLK_SEL_MASK | YCLK_POST_DIV_MASK);
+ mpll_dq_func_cntl |= YCLK_SEL(mpll_param.yclk_sel) |
+ YCLK_POST_DIV(mpll_param.post_div);
+ }
+
+ if (pi->mclk_ss) {
+ struct amdgpu_atom_ss ss;
+ u32 freq_nom;
+ u32 tmp;
+ u32 reference_clock = adev->clock.mpll.reference_freq;
+
+ if (adev->gmc.vram_type == AMDGPU_VRAM_TYPE_GDDR5)
+ freq_nom = memory_clock * 4;
+ else
+ freq_nom = memory_clock * 2;
+
+ tmp = freq_nom / reference_clock;
+ tmp = tmp * tmp;
+ if (amdgpu_atombios_get_asic_ss_info(adev, &ss,
+ ASIC_INTERNAL_MEMORY_SS, freq_nom)) {
+ u32 clks = reference_clock * 5 / ss.rate;
+ u32 clkv = (u32)((((131 * ss.percentage * ss.rate) / 100) * tmp) / freq_nom);
+
+ mpll_ss1 &= ~CLKV_MASK;
+ mpll_ss1 |= CLKV(clkv);
+
+ mpll_ss2 &= ~CLKS_MASK;
+ mpll_ss2 |= CLKS(clks);
+ }
+ }
+
+ mclk_pwrmgt_cntl &= ~DLL_SPEED_MASK;
+ mclk_pwrmgt_cntl |= DLL_SPEED(mpll_param.dll_speed);
+
+ if (dll_state_on)
+ mclk_pwrmgt_cntl |= MRDCK0_PDNB | MRDCK1_PDNB;
+ else
+ mclk_pwrmgt_cntl &= ~(MRDCK0_PDNB | MRDCK1_PDNB);
+
+ mclk->mclk_value = cpu_to_be32(memory_clock);
+ mclk->vMPLL_FUNC_CNTL = cpu_to_be32(mpll_func_cntl);
+ mclk->vMPLL_FUNC_CNTL_1 = cpu_to_be32(mpll_func_cntl_1);
+ mclk->vMPLL_FUNC_CNTL_2 = cpu_to_be32(mpll_func_cntl_2);
+ mclk->vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl);
+ mclk->vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl);
+ mclk->vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl);
+ mclk->vDLL_CNTL = cpu_to_be32(dll_cntl);
+ mclk->vMPLL_SS = cpu_to_be32(mpll_ss1);
+ mclk->vMPLL_SS2 = cpu_to_be32(mpll_ss2);
+
+ return 0;
+}
+
+static void si_populate_smc_sp(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_state,
+ SISLANDS_SMC_SWSTATE *smc_state)
+{
+ struct si_ps *ps = si_get_ps(amdgpu_state);
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ int i;
+
+ for (i = 0; i < ps->performance_level_count - 1; i++)
+ smc_state->levels[i].bSP = cpu_to_be32(pi->dsp);
+
+ smc_state->levels[ps->performance_level_count - 1].bSP =
+ cpu_to_be32(pi->psp);
+}
+
+static int si_convert_power_level_to_smc(struct amdgpu_device *adev,
+ struct rv7xx_pl *pl,
+ SISLANDS_SMC_HW_PERFORMANCE_LEVEL *level)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+ int ret;
+ bool dll_state_on;
+ u16 std_vddc;
+ bool gmc_pg = false;
+
+ if (eg_pi->pcie_performance_request &&
+ (si_pi->force_pcie_gen != AMDGPU_PCIE_GEN_INVALID))
+ level->gen2PCIE = (u8)si_pi->force_pcie_gen;
+ else
+ level->gen2PCIE = (u8)pl->pcie_gen;
+
+ ret = si_populate_sclk_value(adev, pl->sclk, &level->sclk);
+ if (ret)
+ return ret;
+
+ level->mcFlags = 0;
+
+ if (pi->mclk_stutter_mode_threshold &&
+ (pl->mclk <= pi->mclk_stutter_mode_threshold) &&
+ !eg_pi->uvd_enabled &&
+ (RREG32(DPG_PIPE_STUTTER_CONTROL) & STUTTER_ENABLE) &&
+ (adev->pm.dpm.new_active_crtc_count <= 2)) {
+ level->mcFlags |= SISLANDS_SMC_MC_STUTTER_EN;
+
+ if (gmc_pg)
+ level->mcFlags |= SISLANDS_SMC_MC_PG_EN;
+ }
+
+ if (adev->gmc.vram_type == AMDGPU_VRAM_TYPE_GDDR5) {
+ if (pl->mclk > pi->mclk_edc_enable_threshold)
+ level->mcFlags |= SISLANDS_SMC_MC_EDC_RD_FLAG;
+
+ if (pl->mclk > eg_pi->mclk_edc_wr_enable_threshold)
+ level->mcFlags |= SISLANDS_SMC_MC_EDC_WR_FLAG;
+
+ level->strobeMode = si_get_strobe_mode_settings(adev, pl->mclk);
+
+ if (level->strobeMode & SISLANDS_SMC_STROBE_ENABLE) {
+ if (si_get_mclk_frequency_ratio(pl->mclk, true) >=
+ ((RREG32(MC_SEQ_MISC7) >> 16) & 0xf))
+ dll_state_on = ((RREG32(MC_SEQ_MISC5) >> 1) & 0x1) ? true : false;
+ else
+ dll_state_on = ((RREG32(MC_SEQ_MISC6) >> 1) & 0x1) ? true : false;
+ } else {
+ dll_state_on = false;
+ }
+ } else {
+ level->strobeMode = si_get_strobe_mode_settings(adev,
+ pl->mclk);
+
+ dll_state_on = ((RREG32(MC_SEQ_MISC5) >> 1) & 0x1) ? true : false;
+ }
+
+ ret = si_populate_mclk_value(adev,
+ pl->sclk,
+ pl->mclk,
+ &level->mclk,
+ (level->strobeMode & SISLANDS_SMC_STROBE_ENABLE) != 0, dll_state_on);
+ if (ret)
+ return ret;
+
+ ret = si_populate_voltage_value(adev,
+ &eg_pi->vddc_voltage_table,
+ pl->vddc, &level->vddc);
+ if (ret)
+ return ret;
+
+
+ ret = si_get_std_voltage_value(adev, &level->vddc, &std_vddc);
+ if (ret)
+ return ret;
+
+ ret = si_populate_std_voltage_value(adev, std_vddc,
+ level->vddc.index, &level->std_vddc);
+ if (ret)
+ return ret;
+
+ if (eg_pi->vddci_control) {
+ ret = si_populate_voltage_value(adev, &eg_pi->vddci_voltage_table,
+ pl->vddci, &level->vddci);
+ if (ret)
+ return ret;
+ }
+
+ if (si_pi->vddc_phase_shed_control) {
+ ret = si_populate_phase_shedding_value(adev,
+ &adev->pm.dpm.dyn_state.phase_shedding_limits_table,
+ pl->vddc,
+ pl->sclk,
+ pl->mclk,
+ &level->vddc);
+ if (ret)
+ return ret;
+ }
+
+ level->MaxPoweredUpCU = si_pi->max_cu;
+
+ ret = si_populate_mvdd_value(adev, pl->mclk, &level->mvdd);
+
+ return ret;
+}
+
+static int si_populate_smc_t(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_state,
+ SISLANDS_SMC_SWSTATE *smc_state)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ struct si_ps *state = si_get_ps(amdgpu_state);
+ u32 a_t;
+ u32 t_l, t_h;
+ u32 high_bsp;
+ int i, ret;
+
+ if (state->performance_level_count >= 9)
+ return -EINVAL;
+
+ if (state->performance_level_count < 2) {
+ a_t = CG_R(0xffff) | CG_L(0);
+ smc_state->levels[0].aT = cpu_to_be32(a_t);
+ return 0;
+ }
+
+ smc_state->levels[0].aT = cpu_to_be32(0);
+
+ for (i = 0; i <= state->performance_level_count - 2; i++) {
+ ret = r600_calculate_at(
+ (50 / SISLANDS_MAX_HARDWARE_POWERLEVELS) * 100 * (i + 1),
+ 100 * R600_AH_DFLT,
+ state->performance_levels[i + 1].sclk,
+ state->performance_levels[i].sclk,
+ &t_l,
+ &t_h);
+
+ if (ret) {
+ t_h = (i + 1) * 1000 - 50 * R600_AH_DFLT;
+ t_l = (i + 1) * 1000 + 50 * R600_AH_DFLT;
+ }
+
+ a_t = be32_to_cpu(smc_state->levels[i].aT) & ~CG_R_MASK;
+ a_t |= CG_R(t_l * pi->bsp / 20000);
+ smc_state->levels[i].aT = cpu_to_be32(a_t);
+
+ high_bsp = (i == state->performance_level_count - 2) ?
+ pi->pbsp : pi->bsp;
+ a_t = CG_R(0xffff) | CG_L(t_h * high_bsp / 20000);
+ smc_state->levels[i + 1].aT = cpu_to_be32(a_t);
+ }
+
+ return 0;
+}
+
+static int si_disable_ulv(struct amdgpu_device *adev)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ struct si_ulv_param *ulv = &si_pi->ulv;
+
+ if (ulv->supported)
+ return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_DisableULV) == PPSMC_Result_OK) ?
+ 0 : -EINVAL;
+
+ return 0;
+}
+
+static bool si_is_state_ulv_compatible(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_state)
+{
+ const struct si_power_info *si_pi = si_get_pi(adev);
+ const struct si_ulv_param *ulv = &si_pi->ulv;
+ const struct si_ps *state = si_get_ps(amdgpu_state);
+ int i;
+
+ if (state->performance_levels[0].mclk != ulv->pl.mclk)
+ return false;
+
+ /* XXX validate against display requirements! */
+
+ for (i = 0; i < adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.count; i++) {
+ if (adev->clock.current_dispclk <=
+ adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[i].clk) {
+ if (ulv->pl.vddc <
+ adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[i].v)
+ return false;
+ }
+ }
+
+ if ((amdgpu_state->vclk != 0) || (amdgpu_state->dclk != 0))
+ return false;
+
+ return true;
+}
+
+static int si_set_power_state_conditionally_enable_ulv(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_new_state)
+{
+ const struct si_power_info *si_pi = si_get_pi(adev);
+ const struct si_ulv_param *ulv = &si_pi->ulv;
+
+ if (ulv->supported) {
+ if (si_is_state_ulv_compatible(adev, amdgpu_new_state))
+ return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_EnableULV) == PPSMC_Result_OK) ?
+ 0 : -EINVAL;
+ }
+ return 0;
+}
+
+static int si_convert_power_state_to_smc(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_state,
+ SISLANDS_SMC_SWSTATE *smc_state)
+{
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct ni_power_info *ni_pi = ni_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+ struct si_ps *state = si_get_ps(amdgpu_state);
+ int i, ret;
+ u32 threshold;
+ u32 sclk_in_sr = 1350; /* ??? */
+
+ if (state->performance_level_count > SISLANDS_MAX_HARDWARE_POWERLEVELS)
+ return -EINVAL;
+
+ threshold = state->performance_levels[state->performance_level_count-1].sclk * 100 / 100;
+
+ if (amdgpu_state->vclk && amdgpu_state->dclk) {
+ eg_pi->uvd_enabled = true;
+ if (eg_pi->smu_uvd_hs)
+ smc_state->flags |= PPSMC_SWSTATE_FLAG_UVD;
+ } else {
+ eg_pi->uvd_enabled = false;
+ }
+
+ if (state->dc_compatible)
+ smc_state->flags |= PPSMC_SWSTATE_FLAG_DC;
+
+ smc_state->levelCount = 0;
+ for (i = 0; i < state->performance_level_count; i++) {
+ if (eg_pi->sclk_deep_sleep) {
+ if ((i == 0) || si_pi->sclk_deep_sleep_above_low) {
+ if (sclk_in_sr <= SCLK_MIN_DEEPSLEEP_FREQ)
+ smc_state->levels[i].stateFlags |= PPSMC_STATEFLAG_DEEPSLEEP_BYPASS;
+ else
+ smc_state->levels[i].stateFlags |= PPSMC_STATEFLAG_DEEPSLEEP_THROTTLE;
+ }
+ }
+
+ ret = si_convert_power_level_to_smc(adev, &state->performance_levels[i],
+ &smc_state->levels[i]);
+ smc_state->levels[i].arbRefreshState =
+ (u8)(SISLANDS_DRIVER_STATE_ARB_INDEX + i);
+
+ if (ret)
+ return ret;
+
+ if (ni_pi->enable_power_containment)
+ smc_state->levels[i].displayWatermark =
+ (state->performance_levels[i].sclk < threshold) ?
+ PPSMC_DISPLAY_WATERMARK_LOW : PPSMC_DISPLAY_WATERMARK_HIGH;
+ else
+ smc_state->levels[i].displayWatermark = (i < 2) ?
+ PPSMC_DISPLAY_WATERMARK_LOW : PPSMC_DISPLAY_WATERMARK_HIGH;
+
+ if (eg_pi->dynamic_ac_timing)
+ smc_state->levels[i].ACIndex = SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT + i;
+ else
+ smc_state->levels[i].ACIndex = 0;
+
+ smc_state->levelCount++;
+ }
+
+ si_write_smc_soft_register(adev,
+ SI_SMC_SOFT_REGISTER_watermark_threshold,
+ threshold / 512);
+
+ si_populate_smc_sp(adev, amdgpu_state, smc_state);
+
+ ret = si_populate_power_containment_values(adev, amdgpu_state, smc_state);
+ if (ret)
+ ni_pi->enable_power_containment = false;
+
+ ret = si_populate_sq_ramping_values(adev, amdgpu_state, smc_state);
+ if (ret)
+ ni_pi->enable_sq_ramping = false;
+
+ return si_populate_smc_t(adev, amdgpu_state, smc_state);
+}
+
+static int si_upload_sw_state(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_new_state)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ struct si_ps *new_state = si_get_ps(amdgpu_new_state);
+ int ret;
+ u32 address = si_pi->state_table_start +
+ offsetof(SISLANDS_SMC_STATETABLE, driverState);
+ u32 state_size = sizeof(SISLANDS_SMC_SWSTATE) +
+ ((new_state->performance_level_count - 1) *
+ sizeof(SISLANDS_SMC_HW_PERFORMANCE_LEVEL));
+ SISLANDS_SMC_SWSTATE *smc_state = &si_pi->smc_statetable.driverState;
+
+ memset(smc_state, 0, state_size);
+
+ ret = si_convert_power_state_to_smc(adev, amdgpu_new_state, smc_state);
+ if (ret)
+ return ret;
+
+ return amdgpu_si_copy_bytes_to_smc(adev, address, (u8 *)smc_state,
+ state_size, si_pi->sram_end);
+}
+
+static int si_upload_ulv_state(struct amdgpu_device *adev)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ struct si_ulv_param *ulv = &si_pi->ulv;
+ int ret = 0;
+
+ if (ulv->supported && ulv->pl.vddc) {
+ u32 address = si_pi->state_table_start +
+ offsetof(SISLANDS_SMC_STATETABLE, ULVState);
+ SISLANDS_SMC_SWSTATE *smc_state = &si_pi->smc_statetable.ULVState;
+ u32 state_size = sizeof(SISLANDS_SMC_SWSTATE);
+
+ memset(smc_state, 0, state_size);
+
+ ret = si_populate_ulv_state(adev, smc_state);
+ if (!ret)
+ ret = amdgpu_si_copy_bytes_to_smc(adev, address, (u8 *)smc_state,
+ state_size, si_pi->sram_end);
+ }
+
+ return ret;
+}
+
+static int si_upload_smc_data(struct amdgpu_device *adev)
+{
+ struct amdgpu_crtc *amdgpu_crtc = NULL;
+ int i;
+
+ if (adev->pm.dpm.new_active_crtc_count == 0)
+ return 0;
+
+ for (i = 0; i < adev->mode_info.num_crtc; i++) {
+ if (adev->pm.dpm.new_active_crtcs & (1 << i)) {
+ amdgpu_crtc = adev->mode_info.crtcs[i];
+ break;
+ }
+ }
+
+ if (amdgpu_crtc == NULL)
+ return 0;
+
+ if (amdgpu_crtc->line_time <= 0)
+ return 0;
+
+ if (si_write_smc_soft_register(adev,
+ SI_SMC_SOFT_REGISTER_crtc_index,
+ amdgpu_crtc->crtc_id) != PPSMC_Result_OK)
+ return 0;
+
+ if (si_write_smc_soft_register(adev,
+ SI_SMC_SOFT_REGISTER_mclk_change_block_cp_min,
+ amdgpu_crtc->wm_high / amdgpu_crtc->line_time) != PPSMC_Result_OK)
+ return 0;
+
+ if (si_write_smc_soft_register(adev,
+ SI_SMC_SOFT_REGISTER_mclk_change_block_cp_max,
+ amdgpu_crtc->wm_low / amdgpu_crtc->line_time) != PPSMC_Result_OK)
+ return 0;
+
+ return 0;
+}
+
+static int si_set_mc_special_registers(struct amdgpu_device *adev,
+ struct si_mc_reg_table *table)
+{
+ u8 i, j, k;
+ u32 temp_reg;
+
+ for (i = 0, j = table->last; i < table->last; i++) {
+ if (j >= SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+ return -EINVAL;
+ switch (table->mc_reg_address[i].s1) {
+ case MC_SEQ_MISC1:
+ temp_reg = RREG32(MC_PMG_CMD_EMRS);
+ table->mc_reg_address[j].s1 = MC_PMG_CMD_EMRS;
+ table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_EMRS_LP;
+ for (k = 0; k < table->num_entries; k++)
+ table->mc_reg_table_entry[k].mc_data[j] =
+ ((temp_reg & 0xffff0000)) |
+ ((table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16);
+ j++;
+
+ if (j >= SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+ return -EINVAL;
+ temp_reg = RREG32(MC_PMG_CMD_MRS);
+ table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS;
+ table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS_LP;
+ for (k = 0; k < table->num_entries; k++) {
+ table->mc_reg_table_entry[k].mc_data[j] =
+ (temp_reg & 0xffff0000) |
+ (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
+ if (adev->gmc.vram_type != AMDGPU_VRAM_TYPE_GDDR5)
+ table->mc_reg_table_entry[k].mc_data[j] |= 0x100;
+ }
+ j++;
+
+ if (adev->gmc.vram_type != AMDGPU_VRAM_TYPE_GDDR5) {
+ if (j >= SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+ return -EINVAL;
+ table->mc_reg_address[j].s1 = MC_PMG_AUTO_CMD;
+ table->mc_reg_address[j].s0 = MC_PMG_AUTO_CMD;
+ for (k = 0; k < table->num_entries; k++)
+ table->mc_reg_table_entry[k].mc_data[j] =
+ (table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16;
+ j++;
+ }
+ break;
+ case MC_SEQ_RESERVE_M:
+ temp_reg = RREG32(MC_PMG_CMD_MRS1);
+ table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS1;
+ table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS1_LP;
+ for(k = 0; k < table->num_entries; k++)
+ table->mc_reg_table_entry[k].mc_data[j] =
+ (temp_reg & 0xffff0000) |
+ (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
+ j++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ table->last = j;
+
+ return 0;
+}
+
+static bool si_check_s0_mc_reg_index(u16 in_reg, u16 *out_reg)
+{
+ bool result = true;
+ switch (in_reg) {
+ case MC_SEQ_RAS_TIMING:
+ *out_reg = MC_SEQ_RAS_TIMING_LP;
+ break;
+ case MC_SEQ_CAS_TIMING:
+ *out_reg = MC_SEQ_CAS_TIMING_LP;
+ break;
+ case MC_SEQ_MISC_TIMING:
+ *out_reg = MC_SEQ_MISC_TIMING_LP;
+ break;
+ case MC_SEQ_MISC_TIMING2:
+ *out_reg = MC_SEQ_MISC_TIMING2_LP;
+ break;
+ case MC_SEQ_RD_CTL_D0:
+ *out_reg = MC_SEQ_RD_CTL_D0_LP;
+ break;
+ case MC_SEQ_RD_CTL_D1:
+ *out_reg = MC_SEQ_RD_CTL_D1_LP;
+ break;
+ case MC_SEQ_WR_CTL_D0:
+ *out_reg = MC_SEQ_WR_CTL_D0_LP;
+ break;
+ case MC_SEQ_WR_CTL_D1:
+ *out_reg = MC_SEQ_WR_CTL_D1_LP;
+ break;
+ case MC_PMG_CMD_EMRS:
+ *out_reg = MC_SEQ_PMG_CMD_EMRS_LP;
+ break;
+ case MC_PMG_CMD_MRS:
+ *out_reg = MC_SEQ_PMG_CMD_MRS_LP;
+ break;
+ case MC_PMG_CMD_MRS1:
+ *out_reg = MC_SEQ_PMG_CMD_MRS1_LP;
+ break;
+ case MC_SEQ_PMG_TIMING:
+ *out_reg = MC_SEQ_PMG_TIMING_LP;
+ break;
+ case MC_PMG_CMD_MRS2:
+ *out_reg = MC_SEQ_PMG_CMD_MRS2_LP;
+ break;
+ case MC_SEQ_WR_CTL_2:
+ *out_reg = MC_SEQ_WR_CTL_2_LP;
+ break;
+ default:
+ result = false;
+ break;
+ }
+
+ return result;
+}
+
+static void si_set_valid_flag(struct si_mc_reg_table *table)
+{
+ u8 i, j;
+
+ for (i = 0; i < table->last; i++) {
+ for (j = 1; j < table->num_entries; j++) {
+ if (table->mc_reg_table_entry[j-1].mc_data[i] != table->mc_reg_table_entry[j].mc_data[i]) {
+ table->valid_flag |= 1 << i;
+ break;
+ }
+ }
+ }
+}
+
+static void si_set_s0_mc_reg_index(struct si_mc_reg_table *table)
+{
+ u32 i;
+ u16 address;
+
+ for (i = 0; i < table->last; i++)
+ table->mc_reg_address[i].s0 = si_check_s0_mc_reg_index(table->mc_reg_address[i].s1, &address) ?
+ address : table->mc_reg_address[i].s1;
+
+}
+
+static int si_copy_vbios_mc_reg_table(struct atom_mc_reg_table *table,
+ struct si_mc_reg_table *si_table)
+{
+ u8 i, j;
+
+ if (table->last > SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+ return -EINVAL;
+ if (table->num_entries > MAX_AC_TIMING_ENTRIES)
+ return -EINVAL;
+
+ for (i = 0; i < table->last; i++)
+ si_table->mc_reg_address[i].s1 = table->mc_reg_address[i].s1;
+ si_table->last = table->last;
+
+ for (i = 0; i < table->num_entries; i++) {
+ si_table->mc_reg_table_entry[i].mclk_max =
+ table->mc_reg_table_entry[i].mclk_max;
+ for (j = 0; j < table->last; j++) {
+ si_table->mc_reg_table_entry[i].mc_data[j] =
+ table->mc_reg_table_entry[i].mc_data[j];
+ }
+ }
+ si_table->num_entries = table->num_entries;
+
+ return 0;
+}
+
+static int si_initialize_mc_reg_table(struct amdgpu_device *adev)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ struct atom_mc_reg_table *table;
+ struct si_mc_reg_table *si_table = &si_pi->mc_reg_table;
+ u8 module_index = rv770_get_memory_module_index(adev);
+ int ret;
+
+ table = kzalloc(sizeof(struct atom_mc_reg_table), GFP_KERNEL);
+ if (!table)
+ return -ENOMEM;
+
+ WREG32(MC_SEQ_RAS_TIMING_LP, RREG32(MC_SEQ_RAS_TIMING));
+ WREG32(MC_SEQ_CAS_TIMING_LP, RREG32(MC_SEQ_CAS_TIMING));
+ WREG32(MC_SEQ_MISC_TIMING_LP, RREG32(MC_SEQ_MISC_TIMING));
+ WREG32(MC_SEQ_MISC_TIMING2_LP, RREG32(MC_SEQ_MISC_TIMING2));
+ WREG32(MC_SEQ_PMG_CMD_EMRS_LP, RREG32(MC_PMG_CMD_EMRS));
+ WREG32(MC_SEQ_PMG_CMD_MRS_LP, RREG32(MC_PMG_CMD_MRS));
+ WREG32(MC_SEQ_PMG_CMD_MRS1_LP, RREG32(MC_PMG_CMD_MRS1));
+ WREG32(MC_SEQ_WR_CTL_D0_LP, RREG32(MC_SEQ_WR_CTL_D0));
+ WREG32(MC_SEQ_WR_CTL_D1_LP, RREG32(MC_SEQ_WR_CTL_D1));
+ WREG32(MC_SEQ_RD_CTL_D0_LP, RREG32(MC_SEQ_RD_CTL_D0));
+ WREG32(MC_SEQ_RD_CTL_D1_LP, RREG32(MC_SEQ_RD_CTL_D1));
+ WREG32(MC_SEQ_PMG_TIMING_LP, RREG32(MC_SEQ_PMG_TIMING));
+ WREG32(MC_SEQ_PMG_CMD_MRS2_LP, RREG32(MC_PMG_CMD_MRS2));
+ WREG32(MC_SEQ_WR_CTL_2_LP, RREG32(MC_SEQ_WR_CTL_2));
+
+ ret = amdgpu_atombios_init_mc_reg_table(adev, module_index, table);
+ if (ret)
+ goto init_mc_done;
+
+ ret = si_copy_vbios_mc_reg_table(table, si_table);
+ if (ret)
+ goto init_mc_done;
+
+ si_set_s0_mc_reg_index(si_table);
+
+ ret = si_set_mc_special_registers(adev, si_table);
+ if (ret)
+ goto init_mc_done;
+
+ si_set_valid_flag(si_table);
+
+init_mc_done:
+ kfree(table);
+
+ return ret;
+
+}
+
+static void si_populate_mc_reg_addresses(struct amdgpu_device *adev,
+ SMC_SIslands_MCRegisters *mc_reg_table)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ u32 i, j;
+
+ for (i = 0, j = 0; j < si_pi->mc_reg_table.last; j++) {
+ if (si_pi->mc_reg_table.valid_flag & (1 << j)) {
+ if (i >= SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+ break;
+ mc_reg_table->address[i].s0 =
+ cpu_to_be16(si_pi->mc_reg_table.mc_reg_address[j].s0);
+ mc_reg_table->address[i].s1 =
+ cpu_to_be16(si_pi->mc_reg_table.mc_reg_address[j].s1);
+ i++;
+ }
+ }
+ mc_reg_table->last = (u8)i;
+}
+
+static void si_convert_mc_registers(const struct si_mc_reg_entry *entry,
+ SMC_SIslands_MCRegisterSet *data,
+ u32 num_entries, u32 valid_flag)
+{
+ u32 i, j;
+
+ for(i = 0, j = 0; j < num_entries; j++) {
+ if (valid_flag & (1 << j)) {
+ data->value[i] = cpu_to_be32(entry->mc_data[j]);
+ i++;
+ }
+ }
+}
+
+static void si_convert_mc_reg_table_entry_to_smc(struct amdgpu_device *adev,
+ struct rv7xx_pl *pl,
+ SMC_SIslands_MCRegisterSet *mc_reg_table_data)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ u32 i = 0;
+
+ for (i = 0; i < si_pi->mc_reg_table.num_entries; i++) {
+ if (pl->mclk <= si_pi->mc_reg_table.mc_reg_table_entry[i].mclk_max)
+ break;
+ }
+
+ if ((i == si_pi->mc_reg_table.num_entries) && (i > 0))
+ --i;
+
+ si_convert_mc_registers(&si_pi->mc_reg_table.mc_reg_table_entry[i],
+ mc_reg_table_data, si_pi->mc_reg_table.last,
+ si_pi->mc_reg_table.valid_flag);
+}
+
+static void si_convert_mc_reg_table_to_smc(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_state,
+ SMC_SIslands_MCRegisters *mc_reg_table)
+{
+ struct si_ps *state = si_get_ps(amdgpu_state);
+ int i;
+
+ for (i = 0; i < state->performance_level_count; i++) {
+ si_convert_mc_reg_table_entry_to_smc(adev,
+ &state->performance_levels[i],
+ &mc_reg_table->data[SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT + i]);
+ }
+}
+
+static int si_populate_mc_reg_table(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_boot_state)
+{
+ struct si_ps *boot_state = si_get_ps(amdgpu_boot_state);
+ struct si_power_info *si_pi = si_get_pi(adev);
+ struct si_ulv_param *ulv = &si_pi->ulv;
+ SMC_SIslands_MCRegisters *smc_mc_reg_table = &si_pi->smc_mc_reg_table;
+
+ memset(smc_mc_reg_table, 0, sizeof(SMC_SIslands_MCRegisters));
+
+ si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_seq_index, 1);
+
+ si_populate_mc_reg_addresses(adev, smc_mc_reg_table);
+
+ si_convert_mc_reg_table_entry_to_smc(adev, &boot_state->performance_levels[0],
+ &smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_INITIAL_SLOT]);
+
+ si_convert_mc_registers(&si_pi->mc_reg_table.mc_reg_table_entry[0],
+ &smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_ACPI_SLOT],
+ si_pi->mc_reg_table.last,
+ si_pi->mc_reg_table.valid_flag);
+
+ if (ulv->supported && ulv->pl.vddc != 0)
+ si_convert_mc_reg_table_entry_to_smc(adev, &ulv->pl,
+ &smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_ULV_SLOT]);
+ else
+ si_convert_mc_registers(&si_pi->mc_reg_table.mc_reg_table_entry[0],
+ &smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_ULV_SLOT],
+ si_pi->mc_reg_table.last,
+ si_pi->mc_reg_table.valid_flag);
+
+ si_convert_mc_reg_table_to_smc(adev, amdgpu_boot_state, smc_mc_reg_table);
+
+ return amdgpu_si_copy_bytes_to_smc(adev, si_pi->mc_reg_table_start,
+ (u8 *)smc_mc_reg_table,
+ sizeof(SMC_SIslands_MCRegisters), si_pi->sram_end);
+}
+
+static int si_upload_mc_reg_table(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_new_state)
+{
+ struct si_ps *new_state = si_get_ps(amdgpu_new_state);
+ struct si_power_info *si_pi = si_get_pi(adev);
+ u32 address = si_pi->mc_reg_table_start +
+ offsetof(SMC_SIslands_MCRegisters,
+ data[SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT]);
+ SMC_SIslands_MCRegisters *smc_mc_reg_table = &si_pi->smc_mc_reg_table;
+
+ memset(smc_mc_reg_table, 0, sizeof(SMC_SIslands_MCRegisters));
+
+ si_convert_mc_reg_table_to_smc(adev, amdgpu_new_state, smc_mc_reg_table);
+
+ return amdgpu_si_copy_bytes_to_smc(adev, address,
+ (u8 *)&smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT],
+ sizeof(SMC_SIslands_MCRegisterSet) * new_state->performance_level_count,
+ si_pi->sram_end);
+}
+
+static void si_enable_voltage_control(struct amdgpu_device *adev, bool enable)
+{
+ if (enable)
+ WREG32_P(GENERAL_PWRMGT, VOLT_PWRMGT_EN, ~VOLT_PWRMGT_EN);
+ else
+ WREG32_P(GENERAL_PWRMGT, 0, ~VOLT_PWRMGT_EN);
+}
+
+static enum amdgpu_pcie_gen si_get_maximum_link_speed(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_state)
+{
+ struct si_ps *state = si_get_ps(amdgpu_state);
+ int i;
+ u16 pcie_speed, max_speed = 0;
+
+ for (i = 0; i < state->performance_level_count; i++) {
+ pcie_speed = state->performance_levels[i].pcie_gen;
+ if (max_speed < pcie_speed)
+ max_speed = pcie_speed;
+ }
+ return max_speed;
+}
+
+static u16 si_get_current_pcie_speed(struct amdgpu_device *adev)
+{
+ u32 speed_cntl;
+
+ speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL) & LC_CURRENT_DATA_RATE_MASK;
+ speed_cntl >>= LC_CURRENT_DATA_RATE_SHIFT;
+
+ return (u16)speed_cntl;
+}
+
+static void si_request_link_speed_change_before_state_change(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_new_state,
+ struct amdgpu_ps *amdgpu_current_state)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ enum amdgpu_pcie_gen target_link_speed = si_get_maximum_link_speed(adev, amdgpu_new_state);
+ enum amdgpu_pcie_gen current_link_speed;
+
+ if (si_pi->force_pcie_gen == AMDGPU_PCIE_GEN_INVALID)
+ current_link_speed = si_get_maximum_link_speed(adev, amdgpu_current_state);
+ else
+ current_link_speed = si_pi->force_pcie_gen;
+
+ si_pi->force_pcie_gen = AMDGPU_PCIE_GEN_INVALID;
+ si_pi->pspp_notify_required = false;
+ if (target_link_speed > current_link_speed) {
+ switch (target_link_speed) {
+#if defined(CONFIG_ACPI)
+ case AMDGPU_PCIE_GEN3:
+ if (amdgpu_acpi_pcie_performance_request(adev, PCIE_PERF_REQ_PECI_GEN3, false) == 0)
+ break;
+ si_pi->force_pcie_gen = AMDGPU_PCIE_GEN2;
+ if (current_link_speed == AMDGPU_PCIE_GEN2)
+ break;
+ fallthrough;
+ case AMDGPU_PCIE_GEN2:
+ if (amdgpu_acpi_pcie_performance_request(adev, PCIE_PERF_REQ_PECI_GEN2, false) == 0)
+ break;
+#endif
+ /* fall through */
+ default:
+ si_pi->force_pcie_gen = si_get_current_pcie_speed(adev);
+ break;
+ }
+ } else {
+ if (target_link_speed < current_link_speed)
+ si_pi->pspp_notify_required = true;
+ }
+}
+
+static void si_notify_link_speed_change_after_state_change(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_new_state,
+ struct amdgpu_ps *amdgpu_current_state)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ enum amdgpu_pcie_gen target_link_speed = si_get_maximum_link_speed(adev, amdgpu_new_state);
+ u8 request;
+
+ if (si_pi->pspp_notify_required) {
+ if (target_link_speed == AMDGPU_PCIE_GEN3)
+ request = PCIE_PERF_REQ_PECI_GEN3;
+ else if (target_link_speed == AMDGPU_PCIE_GEN2)
+ request = PCIE_PERF_REQ_PECI_GEN2;
+ else
+ request = PCIE_PERF_REQ_PECI_GEN1;
+
+ if ((request == PCIE_PERF_REQ_PECI_GEN1) &&
+ (si_get_current_pcie_speed(adev) > 0))
+ return;
+
+#if defined(CONFIG_ACPI)
+ amdgpu_acpi_pcie_performance_request(adev, request, false);
+#endif
+ }
+}
+
+#if 0
+static int si_ds_request(struct amdgpu_device *adev,
+ bool ds_status_on, u32 count_write)
+{
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+
+ if (eg_pi->sclk_deep_sleep) {
+ if (ds_status_on)
+ return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_CancelThrottleOVRDSCLKDS) ==
+ PPSMC_Result_OK) ?
+ 0 : -EINVAL;
+ else
+ return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_ThrottleOVRDSCLKDS) ==
+ PPSMC_Result_OK) ? 0 : -EINVAL;
+ }
+ return 0;
+}
+#endif
+
+static void si_set_max_cu_value(struct amdgpu_device *adev)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+
+ if (adev->asic_type == CHIP_VERDE) {
+ switch (adev->pdev->device) {
+ case 0x6820:
+ case 0x6825:
+ case 0x6821:
+ case 0x6823:
+ case 0x6827:
+ si_pi->max_cu = 10;
+ break;
+ case 0x682D:
+ case 0x6824:
+ case 0x682F:
+ case 0x6826:
+ si_pi->max_cu = 8;
+ break;
+ case 0x6828:
+ case 0x6830:
+ case 0x6831:
+ case 0x6838:
+ case 0x6839:
+ case 0x683D:
+ si_pi->max_cu = 10;
+ break;
+ case 0x683B:
+ case 0x683F:
+ case 0x6829:
+ si_pi->max_cu = 8;
+ break;
+ default:
+ si_pi->max_cu = 0;
+ break;
+ }
+ } else {
+ si_pi->max_cu = 0;
+ }
+}
+
+static int si_patch_single_dependency_table_based_on_leakage(struct amdgpu_device *adev,
+ struct amdgpu_clock_voltage_dependency_table *table)
+{
+ u32 i;
+ int j;
+ u16 leakage_voltage;
+
+ if (table) {
+ for (i = 0; i < table->count; i++) {
+ switch (si_get_leakage_voltage_from_leakage_index(adev,
+ table->entries[i].v,
+ &leakage_voltage)) {
+ case 0:
+ table->entries[i].v = leakage_voltage;
+ break;
+ case -EAGAIN:
+ return -EINVAL;
+ case -EINVAL:
+ default:
+ break;
+ }
+ }
+
+ for (j = (table->count - 2); j >= 0; j--) {
+ table->entries[j].v = (table->entries[j].v <= table->entries[j + 1].v) ?
+ table->entries[j].v : table->entries[j + 1].v;
+ }
+ }
+ return 0;
+}
+
+static int si_patch_dependency_tables_based_on_leakage(struct amdgpu_device *adev)
+{
+ int ret = 0;
+
+ ret = si_patch_single_dependency_table_based_on_leakage(adev,
+ &adev->pm.dpm.dyn_state.vddc_dependency_on_sclk);
+ if (ret)
+ DRM_ERROR("Could not patch vddc_on_sclk leakage table\n");
+ ret = si_patch_single_dependency_table_based_on_leakage(adev,
+ &adev->pm.dpm.dyn_state.vddc_dependency_on_mclk);
+ if (ret)
+ DRM_ERROR("Could not patch vddc_on_mclk leakage table\n");
+ ret = si_patch_single_dependency_table_based_on_leakage(adev,
+ &adev->pm.dpm.dyn_state.vddci_dependency_on_mclk);
+ if (ret)
+ DRM_ERROR("Could not patch vddci_on_mclk leakage table\n");
+ return ret;
+}
+
+static void si_set_pcie_lane_width_in_smc(struct amdgpu_device *adev,
+ struct amdgpu_ps *amdgpu_new_state,
+ struct amdgpu_ps *amdgpu_current_state)
+{
+ u32 lane_width;
+ u32 new_lane_width =
+ ((amdgpu_new_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT) + 1;
+ u32 current_lane_width =
+ ((amdgpu_current_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT) + 1;
+
+ if (new_lane_width != current_lane_width) {
+ amdgpu_set_pcie_lanes(adev, new_lane_width);
+ lane_width = amdgpu_get_pcie_lanes(adev);
+ si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_non_ulv_pcie_link_width, lane_width);
+ }
+}
+
+static void si_dpm_setup_asic(struct amdgpu_device *adev)
+{
+ si_read_clock_registers(adev);
+ si_enable_acpi_power_management(adev);
+}
+
+static int si_thermal_enable_alert(struct amdgpu_device *adev,
+ bool enable)
+{
+ u32 thermal_int = RREG32(CG_THERMAL_INT);
+
+ if (enable) {
+ PPSMC_Result result;
+
+ thermal_int &= ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW);
+ WREG32(CG_THERMAL_INT, thermal_int);
+ result = amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_EnableThermalInterrupt);
+ if (result != PPSMC_Result_OK) {
+ DRM_DEBUG_KMS("Could not enable thermal interrupts.\n");
+ return -EINVAL;
+ }
+ } else {
+ thermal_int |= THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW;
+ WREG32(CG_THERMAL_INT, thermal_int);
+ }
+
+ return 0;
+}
+
+static int si_thermal_set_temperature_range(struct amdgpu_device *adev,
+ int min_temp, int max_temp)
+{
+ int low_temp = 0 * 1000;
+ int high_temp = 255 * 1000;
+
+ if (low_temp < min_temp)
+ low_temp = min_temp;
+ if (high_temp > max_temp)
+ high_temp = max_temp;
+ if (high_temp < low_temp) {
+ DRM_ERROR("invalid thermal range: %d - %d\n", low_temp, high_temp);
+ return -EINVAL;
+ }
+
+ WREG32_P(CG_THERMAL_INT, DIG_THERM_INTH(high_temp / 1000), ~DIG_THERM_INTH_MASK);
+ WREG32_P(CG_THERMAL_INT, DIG_THERM_INTL(low_temp / 1000), ~DIG_THERM_INTL_MASK);
+ WREG32_P(CG_THERMAL_CTRL, DIG_THERM_DPM(high_temp / 1000), ~DIG_THERM_DPM_MASK);
+
+ adev->pm.dpm.thermal.min_temp = low_temp;
+ adev->pm.dpm.thermal.max_temp = high_temp;
+
+ return 0;
+}
+
+static void si_fan_ctrl_set_static_mode(struct amdgpu_device *adev, u32 mode)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ u32 tmp;
+
+ if (si_pi->fan_ctrl_is_in_default_mode) {
+ tmp = (RREG32(CG_FDO_CTRL2) & FDO_PWM_MODE_MASK) >> FDO_PWM_MODE_SHIFT;
+ si_pi->fan_ctrl_default_mode = tmp;
+ tmp = (RREG32(CG_FDO_CTRL2) & TMIN_MASK) >> TMIN_SHIFT;
+ si_pi->t_min = tmp;
+ si_pi->fan_ctrl_is_in_default_mode = false;
+ }
+
+ tmp = RREG32(CG_FDO_CTRL2) & ~TMIN_MASK;
+ tmp |= TMIN(0);
+ WREG32(CG_FDO_CTRL2, tmp);
+
+ tmp = RREG32(CG_FDO_CTRL2) & ~FDO_PWM_MODE_MASK;
+ tmp |= FDO_PWM_MODE(mode);
+ WREG32(CG_FDO_CTRL2, tmp);
+}
+
+static int si_thermal_setup_fan_table(struct amdgpu_device *adev)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ PP_SIslands_FanTable fan_table = { FDO_MODE_HARDWARE };
+ u32 duty100;
+ u32 t_diff1, t_diff2, pwm_diff1, pwm_diff2;
+ u16 fdo_min, slope1, slope2;
+ u32 reference_clock, tmp;
+ int ret;
+ u64 tmp64;
+
+ if (!si_pi->fan_table_start) {
+ adev->pm.dpm.fan.ucode_fan_control = false;
+ return 0;
+ }
+
+ duty100 = (RREG32(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT;
+
+ if (duty100 == 0) {
+ adev->pm.dpm.fan.ucode_fan_control = false;
+ return 0;
+ }
+
+ tmp64 = (u64)adev->pm.dpm.fan.pwm_min * duty100;
+ do_div(tmp64, 10000);
+ fdo_min = (u16)tmp64;
+
+ t_diff1 = adev->pm.dpm.fan.t_med - adev->pm.dpm.fan.t_min;
+ t_diff2 = adev->pm.dpm.fan.t_high - adev->pm.dpm.fan.t_med;
+
+ pwm_diff1 = adev->pm.dpm.fan.pwm_med - adev->pm.dpm.fan.pwm_min;
+ pwm_diff2 = adev->pm.dpm.fan.pwm_high - adev->pm.dpm.fan.pwm_med;
+
+ slope1 = (u16)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100);
+ slope2 = (u16)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100);
+
+ fan_table.temp_min = cpu_to_be16((50 + adev->pm.dpm.fan.t_min) / 100);
+ fan_table.temp_med = cpu_to_be16((50 + adev->pm.dpm.fan.t_med) / 100);
+ fan_table.temp_max = cpu_to_be16((50 + adev->pm.dpm.fan.t_max) / 100);
+ fan_table.slope1 = cpu_to_be16(slope1);
+ fan_table.slope2 = cpu_to_be16(slope2);
+ fan_table.fdo_min = cpu_to_be16(fdo_min);
+ fan_table.hys_down = cpu_to_be16(adev->pm.dpm.fan.t_hyst);
+ fan_table.hys_up = cpu_to_be16(1);
+ fan_table.hys_slope = cpu_to_be16(1);
+ fan_table.temp_resp_lim = cpu_to_be16(5);
+ reference_clock = amdgpu_asic_get_xclk(adev);
+
+ fan_table.refresh_period = cpu_to_be32((adev->pm.dpm.fan.cycle_delay *
+ reference_clock) / 1600);
+ fan_table.fdo_max = cpu_to_be16((u16)duty100);
+
+ tmp = (RREG32(CG_MULT_THERMAL_CTRL) & TEMP_SEL_MASK) >> TEMP_SEL_SHIFT;
+ fan_table.temp_src = (uint8_t)tmp;
+
+ ret = amdgpu_si_copy_bytes_to_smc(adev,
+ si_pi->fan_table_start,
+ (u8 *)(&fan_table),
+ sizeof(fan_table),
+ si_pi->sram_end);
+
+ if (ret) {
+ DRM_ERROR("Failed to load fan table to the SMC.");
+ adev->pm.dpm.fan.ucode_fan_control = false;
+ }
+
+ return ret;
+}
+
+static int si_fan_ctrl_start_smc_fan_control(struct amdgpu_device *adev)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ PPSMC_Result ret;
+
+ ret = amdgpu_si_send_msg_to_smc(adev, PPSMC_StartFanControl);
+ if (ret == PPSMC_Result_OK) {
+ si_pi->fan_is_controlled_by_smc = true;
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+}
+
+static int si_fan_ctrl_stop_smc_fan_control(struct amdgpu_device *adev)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ PPSMC_Result ret;
+
+ ret = amdgpu_si_send_msg_to_smc(adev, PPSMC_StopFanControl);
+
+ if (ret == PPSMC_Result_OK) {
+ si_pi->fan_is_controlled_by_smc = false;
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+}
+
+static int si_dpm_get_fan_speed_percent(void *handle,
+ u32 *speed)
+{
+ u32 duty, duty100;
+ u64 tmp64;
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ if (adev->pm.no_fan)
+ return -ENOENT;
+
+ duty100 = (RREG32(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT;
+ duty = (RREG32(CG_THERMAL_STATUS) & FDO_PWM_DUTY_MASK) >> FDO_PWM_DUTY_SHIFT;
+
+ if (duty100 == 0)
+ return -EINVAL;
+
+ tmp64 = (u64)duty * 100;
+ do_div(tmp64, duty100);
+ *speed = (u32)tmp64;
+
+ if (*speed > 100)
+ *speed = 100;
+
+ return 0;
+}
+
+static int si_dpm_set_fan_speed_percent(void *handle,
+ u32 speed)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct si_power_info *si_pi = si_get_pi(adev);
+ u32 tmp;
+ u32 duty, duty100;
+ u64 tmp64;
+
+ if (adev->pm.no_fan)
+ return -ENOENT;
+
+ if (si_pi->fan_is_controlled_by_smc)
+ return -EINVAL;
+
+ if (speed > 100)
+ return -EINVAL;
+
+ duty100 = (RREG32(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT;
+
+ if (duty100 == 0)
+ return -EINVAL;
+
+ tmp64 = (u64)speed * duty100;
+ do_div(tmp64, 100);
+ duty = (u32)tmp64;
+
+ tmp = RREG32(CG_FDO_CTRL0) & ~FDO_STATIC_DUTY_MASK;
+ tmp |= FDO_STATIC_DUTY(duty);
+ WREG32(CG_FDO_CTRL0, tmp);
+
+ return 0;
+}
+
+static void si_dpm_set_fan_control_mode(void *handle, u32 mode)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ if (mode) {
+ /* stop auto-manage */
+ if (adev->pm.dpm.fan.ucode_fan_control)
+ si_fan_ctrl_stop_smc_fan_control(adev);
+ si_fan_ctrl_set_static_mode(adev, mode);
+ } else {
+ /* restart auto-manage */
+ if (adev->pm.dpm.fan.ucode_fan_control)
+ si_thermal_start_smc_fan_control(adev);
+ else
+ si_fan_ctrl_set_default_mode(adev);
+ }
+}
+
+static u32 si_dpm_get_fan_control_mode(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct si_power_info *si_pi = si_get_pi(adev);
+ u32 tmp;
+
+ if (si_pi->fan_is_controlled_by_smc)
+ return 0;
+
+ tmp = RREG32(CG_FDO_CTRL2) & FDO_PWM_MODE_MASK;
+ return (tmp >> FDO_PWM_MODE_SHIFT);
+}
+
+#if 0
+static int si_fan_ctrl_get_fan_speed_rpm(struct amdgpu_device *adev,
+ u32 *speed)
+{
+ u32 tach_period;
+ u32 xclk = amdgpu_asic_get_xclk(adev);
+
+ if (adev->pm.no_fan)
+ return -ENOENT;
+
+ if (adev->pm.fan_pulses_per_revolution == 0)
+ return -ENOENT;
+
+ tach_period = (RREG32(CG_TACH_STATUS) & TACH_PERIOD_MASK) >> TACH_PERIOD_SHIFT;
+ if (tach_period == 0)
+ return -ENOENT;
+
+ *speed = 60 * xclk * 10000 / tach_period;
+
+ return 0;
+}
+
+static int si_fan_ctrl_set_fan_speed_rpm(struct amdgpu_device *adev,
+ u32 speed)
+{
+ u32 tach_period, tmp;
+ u32 xclk = amdgpu_asic_get_xclk(adev);
+
+ if (adev->pm.no_fan)
+ return -ENOENT;
+
+ if (adev->pm.fan_pulses_per_revolution == 0)
+ return -ENOENT;
+
+ if ((speed < adev->pm.fan_min_rpm) ||
+ (speed > adev->pm.fan_max_rpm))
+ return -EINVAL;
+
+ if (adev->pm.dpm.fan.ucode_fan_control)
+ si_fan_ctrl_stop_smc_fan_control(adev);
+
+ tach_period = 60 * xclk * 10000 / (8 * speed);
+ tmp = RREG32(CG_TACH_CTRL) & ~TARGET_PERIOD_MASK;
+ tmp |= TARGET_PERIOD(tach_period);
+ WREG32(CG_TACH_CTRL, tmp);
+
+ si_fan_ctrl_set_static_mode(adev, FDO_PWM_MODE_STATIC_RPM);
+
+ return 0;
+}
+#endif
+
+static void si_fan_ctrl_set_default_mode(struct amdgpu_device *adev)
+{
+ struct si_power_info *si_pi = si_get_pi(adev);
+ u32 tmp;
+
+ if (!si_pi->fan_ctrl_is_in_default_mode) {
+ tmp = RREG32(CG_FDO_CTRL2) & ~FDO_PWM_MODE_MASK;
+ tmp |= FDO_PWM_MODE(si_pi->fan_ctrl_default_mode);
+ WREG32(CG_FDO_CTRL2, tmp);
+
+ tmp = RREG32(CG_FDO_CTRL2) & ~TMIN_MASK;
+ tmp |= TMIN(si_pi->t_min);
+ WREG32(CG_FDO_CTRL2, tmp);
+ si_pi->fan_ctrl_is_in_default_mode = true;
+ }
+}
+
+static void si_thermal_start_smc_fan_control(struct amdgpu_device *adev)
+{
+ if (adev->pm.dpm.fan.ucode_fan_control) {
+ si_fan_ctrl_start_smc_fan_control(adev);
+ si_fan_ctrl_set_static_mode(adev, FDO_PWM_MODE_STATIC);
+ }
+}
+
+static void si_thermal_initialize(struct amdgpu_device *adev)
+{
+ u32 tmp;
+
+ if (adev->pm.fan_pulses_per_revolution) {
+ tmp = RREG32(CG_TACH_CTRL) & ~EDGE_PER_REV_MASK;
+ tmp |= EDGE_PER_REV(adev->pm.fan_pulses_per_revolution -1);
+ WREG32(CG_TACH_CTRL, tmp);
+ }
+
+ tmp = RREG32(CG_FDO_CTRL2) & ~TACH_PWM_RESP_RATE_MASK;
+ tmp |= TACH_PWM_RESP_RATE(0x28);
+ WREG32(CG_FDO_CTRL2, tmp);
+}
+
+static int si_thermal_start_thermal_controller(struct amdgpu_device *adev)
+{
+ int ret;
+
+ si_thermal_initialize(adev);
+ ret = si_thermal_set_temperature_range(adev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
+ if (ret)
+ return ret;
+ ret = si_thermal_enable_alert(adev, true);
+ if (ret)
+ return ret;
+ if (adev->pm.dpm.fan.ucode_fan_control) {
+ ret = si_halt_smc(adev);
+ if (ret)
+ return ret;
+ ret = si_thermal_setup_fan_table(adev);
+ if (ret)
+ return ret;
+ ret = si_resume_smc(adev);
+ if (ret)
+ return ret;
+ si_thermal_start_smc_fan_control(adev);
+ }
+
+ return 0;
+}
+
+static void si_thermal_stop_thermal_controller(struct amdgpu_device *adev)
+{
+ if (!adev->pm.no_fan) {
+ si_fan_ctrl_set_default_mode(adev);
+ si_fan_ctrl_stop_smc_fan_control(adev);
+ }
+}
+
+static int si_dpm_enable(struct amdgpu_device *adev)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+ struct amdgpu_ps *boot_ps = adev->pm.dpm.boot_ps;
+ int ret;
+
+ if (amdgpu_si_is_smc_running(adev))
+ return -EINVAL;
+ if (pi->voltage_control || si_pi->voltage_control_svi2)
+ si_enable_voltage_control(adev, true);
+ if (pi->mvdd_control)
+ si_get_mvdd_configuration(adev);
+ if (pi->voltage_control || si_pi->voltage_control_svi2) {
+ ret = si_construct_voltage_tables(adev);
+ if (ret) {
+ DRM_ERROR("si_construct_voltage_tables failed\n");
+ return ret;
+ }
+ }
+ if (eg_pi->dynamic_ac_timing) {
+ ret = si_initialize_mc_reg_table(adev);
+ if (ret)
+ eg_pi->dynamic_ac_timing = false;
+ }
+ if (pi->dynamic_ss)
+ si_enable_spread_spectrum(adev, true);
+ if (pi->thermal_protection)
+ si_enable_thermal_protection(adev, true);
+ si_setup_bsp(adev);
+ si_program_git(adev);
+ si_program_tp(adev);
+ si_program_tpp(adev);
+ si_program_sstp(adev);
+ si_enable_display_gap(adev);
+ si_program_vc(adev);
+ ret = si_upload_firmware(adev);
+ if (ret) {
+ DRM_ERROR("si_upload_firmware failed\n");
+ return ret;
+ }
+ ret = si_process_firmware_header(adev);
+ if (ret) {
+ DRM_ERROR("si_process_firmware_header failed\n");
+ return ret;
+ }
+ ret = si_initial_switch_from_arb_f0_to_f1(adev);
+ if (ret) {
+ DRM_ERROR("si_initial_switch_from_arb_f0_to_f1 failed\n");
+ return ret;
+ }
+ ret = si_init_smc_table(adev);
+ if (ret) {
+ DRM_ERROR("si_init_smc_table failed\n");
+ return ret;
+ }
+ ret = si_init_smc_spll_table(adev);
+ if (ret) {
+ DRM_ERROR("si_init_smc_spll_table failed\n");
+ return ret;
+ }
+ ret = si_init_arb_table_index(adev);
+ if (ret) {
+ DRM_ERROR("si_init_arb_table_index failed\n");
+ return ret;
+ }
+ if (eg_pi->dynamic_ac_timing) {
+ ret = si_populate_mc_reg_table(adev, boot_ps);
+ if (ret) {
+ DRM_ERROR("si_populate_mc_reg_table failed\n");
+ return ret;
+ }
+ }
+ ret = si_initialize_smc_cac_tables(adev);
+ if (ret) {
+ DRM_ERROR("si_initialize_smc_cac_tables failed\n");
+ return ret;
+ }
+ ret = si_initialize_hardware_cac_manager(adev);
+ if (ret) {
+ DRM_ERROR("si_initialize_hardware_cac_manager failed\n");
+ return ret;
+ }
+ ret = si_initialize_smc_dte_tables(adev);
+ if (ret) {
+ DRM_ERROR("si_initialize_smc_dte_tables failed\n");
+ return ret;
+ }
+ ret = si_populate_smc_tdp_limits(adev, boot_ps);
+ if (ret) {
+ DRM_ERROR("si_populate_smc_tdp_limits failed\n");
+ return ret;
+ }
+ ret = si_populate_smc_tdp_limits_2(adev, boot_ps);
+ if (ret) {
+ DRM_ERROR("si_populate_smc_tdp_limits_2 failed\n");
+ return ret;
+ }
+ si_program_response_times(adev);
+ si_program_ds_registers(adev);
+ si_dpm_start_smc(adev);
+ ret = si_notify_smc_display_change(adev, false);
+ if (ret) {
+ DRM_ERROR("si_notify_smc_display_change failed\n");
+ return ret;
+ }
+ si_enable_sclk_control(adev, true);
+ si_start_dpm(adev);
+
+ si_enable_auto_throttle_source(adev, AMDGPU_DPM_AUTO_THROTTLE_SRC_THERMAL, true);
+ si_thermal_start_thermal_controller(adev);
+
+ return 0;
+}
+
+static int si_set_temperature_range(struct amdgpu_device *adev)
+{
+ int ret;
+
+ ret = si_thermal_enable_alert(adev, false);
+ if (ret)
+ return ret;
+ ret = si_thermal_set_temperature_range(adev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
+ if (ret)
+ return ret;
+ ret = si_thermal_enable_alert(adev, true);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static void si_dpm_disable(struct amdgpu_device *adev)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ struct amdgpu_ps *boot_ps = adev->pm.dpm.boot_ps;
+
+ if (!amdgpu_si_is_smc_running(adev))
+ return;
+ si_thermal_stop_thermal_controller(adev);
+ si_disable_ulv(adev);
+ si_clear_vc(adev);
+ if (pi->thermal_protection)
+ si_enable_thermal_protection(adev, false);
+ si_enable_power_containment(adev, boot_ps, false);
+ si_enable_smc_cac(adev, boot_ps, false);
+ si_enable_spread_spectrum(adev, false);
+ si_enable_auto_throttle_source(adev, AMDGPU_DPM_AUTO_THROTTLE_SRC_THERMAL, false);
+ si_stop_dpm(adev);
+ si_reset_to_default(adev);
+ si_dpm_stop_smc(adev);
+ si_force_switch_to_arb_f0(adev);
+
+ ni_update_current_ps(adev, boot_ps);
+}
+
+static int si_dpm_pre_set_power_state(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct amdgpu_ps requested_ps = *adev->pm.dpm.requested_ps;
+ struct amdgpu_ps *new_ps = &requested_ps;
+
+ ni_update_requested_ps(adev, new_ps);
+ si_apply_state_adjust_rules(adev, &eg_pi->requested_rps);
+
+ return 0;
+}
+
+static int si_power_control_set_level(struct amdgpu_device *adev)
+{
+ struct amdgpu_ps *new_ps = adev->pm.dpm.requested_ps;
+ int ret;
+
+ ret = si_restrict_performance_levels_before_switch(adev);
+ if (ret)
+ return ret;
+ ret = si_halt_smc(adev);
+ if (ret)
+ return ret;
+ ret = si_populate_smc_tdp_limits(adev, new_ps);
+ if (ret)
+ return ret;
+ ret = si_populate_smc_tdp_limits_2(adev, new_ps);
+ if (ret)
+ return ret;
+ ret = si_resume_smc(adev);
+ if (ret)
+ return ret;
+ ret = si_set_sw_state(adev);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+static void si_set_vce_clock(struct amdgpu_device *adev,
+ struct amdgpu_ps *new_rps,
+ struct amdgpu_ps *old_rps)
+{
+ if ((old_rps->evclk != new_rps->evclk) ||
+ (old_rps->ecclk != new_rps->ecclk)) {
+ /* Turn the clocks on when encoding, off otherwise */
+ if (new_rps->evclk || new_rps->ecclk) {
+ /* Place holder for future VCE1.0 porting to amdgpu
+ vce_v1_0_enable_mgcg(adev, false, false);*/
+ } else {
+ /* Place holder for future VCE1.0 porting to amdgpu
+ vce_v1_0_enable_mgcg(adev, true, false);
+ amdgpu_asic_set_vce_clocks(adev, new_rps->evclk, new_rps->ecclk);*/
+ }
+ }
+}
+
+static int si_dpm_set_power_state(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct amdgpu_ps *new_ps = &eg_pi->requested_rps;
+ struct amdgpu_ps *old_ps = &eg_pi->current_rps;
+ int ret;
+
+ ret = si_disable_ulv(adev);
+ if (ret) {
+ DRM_ERROR("si_disable_ulv failed\n");
+ return ret;
+ }
+ ret = si_restrict_performance_levels_before_switch(adev);
+ if (ret) {
+ DRM_ERROR("si_restrict_performance_levels_before_switch failed\n");
+ return ret;
+ }
+ if (eg_pi->pcie_performance_request)
+ si_request_link_speed_change_before_state_change(adev, new_ps, old_ps);
+ ni_set_uvd_clock_before_set_eng_clock(adev, new_ps, old_ps);
+ ret = si_enable_power_containment(adev, new_ps, false);
+ if (ret) {
+ DRM_ERROR("si_enable_power_containment failed\n");
+ return ret;
+ }
+ ret = si_enable_smc_cac(adev, new_ps, false);
+ if (ret) {
+ DRM_ERROR("si_enable_smc_cac failed\n");
+ return ret;
+ }
+ ret = si_halt_smc(adev);
+ if (ret) {
+ DRM_ERROR("si_halt_smc failed\n");
+ return ret;
+ }
+ ret = si_upload_sw_state(adev, new_ps);
+ if (ret) {
+ DRM_ERROR("si_upload_sw_state failed\n");
+ return ret;
+ }
+ ret = si_upload_smc_data(adev);
+ if (ret) {
+ DRM_ERROR("si_upload_smc_data failed\n");
+ return ret;
+ }
+ ret = si_upload_ulv_state(adev);
+ if (ret) {
+ DRM_ERROR("si_upload_ulv_state failed\n");
+ return ret;
+ }
+ if (eg_pi->dynamic_ac_timing) {
+ ret = si_upload_mc_reg_table(adev, new_ps);
+ if (ret) {
+ DRM_ERROR("si_upload_mc_reg_table failed\n");
+ return ret;
+ }
+ }
+ ret = si_program_memory_timing_parameters(adev, new_ps);
+ if (ret) {
+ DRM_ERROR("si_program_memory_timing_parameters failed\n");
+ return ret;
+ }
+ si_set_pcie_lane_width_in_smc(adev, new_ps, old_ps);
+
+ ret = si_resume_smc(adev);
+ if (ret) {
+ DRM_ERROR("si_resume_smc failed\n");
+ return ret;
+ }
+ ret = si_set_sw_state(adev);
+ if (ret) {
+ DRM_ERROR("si_set_sw_state failed\n");
+ return ret;
+ }
+ ni_set_uvd_clock_after_set_eng_clock(adev, new_ps, old_ps);
+ si_set_vce_clock(adev, new_ps, old_ps);
+ if (eg_pi->pcie_performance_request)
+ si_notify_link_speed_change_after_state_change(adev, new_ps, old_ps);
+ ret = si_set_power_state_conditionally_enable_ulv(adev, new_ps);
+ if (ret) {
+ DRM_ERROR("si_set_power_state_conditionally_enable_ulv failed\n");
+ return ret;
+ }
+ ret = si_enable_smc_cac(adev, new_ps, true);
+ if (ret) {
+ DRM_ERROR("si_enable_smc_cac failed\n");
+ return ret;
+ }
+ ret = si_enable_power_containment(adev, new_ps, true);
+ if (ret) {
+ DRM_ERROR("si_enable_power_containment failed\n");
+ return ret;
+ }
+
+ ret = si_power_control_set_level(adev);
+ if (ret) {
+ DRM_ERROR("si_power_control_set_level failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void si_dpm_post_set_power_state(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct amdgpu_ps *new_ps = &eg_pi->requested_rps;
+
+ ni_update_current_ps(adev, new_ps);
+}
+
+#if 0
+void si_dpm_reset_asic(struct amdgpu_device *adev)
+{
+ si_restrict_performance_levels_before_switch(adev);
+ si_disable_ulv(adev);
+ si_set_boot_state(adev);
+}
+#endif
+
+static void si_dpm_display_configuration_changed(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ si_program_display_gap(adev);
+}
+
+
+static void si_parse_pplib_non_clock_info(struct amdgpu_device *adev,
+ struct amdgpu_ps *rps,
+ struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info,
+ u8 table_rev)
+{
+ rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
+ rps->class = le16_to_cpu(non_clock_info->usClassification);
+ rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
+
+ if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) {
+ rps->vclk = le32_to_cpu(non_clock_info->ulVCLK);
+ rps->dclk = le32_to_cpu(non_clock_info->ulDCLK);
+ } else if (r600_is_uvd_state(rps->class, rps->class2)) {
+ rps->vclk = RV770_DEFAULT_VCLK_FREQ;
+ rps->dclk = RV770_DEFAULT_DCLK_FREQ;
+ } else {
+ rps->vclk = 0;
+ rps->dclk = 0;
+ }
+
+ if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT)
+ adev->pm.dpm.boot_ps = rps;
+ if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
+ adev->pm.dpm.uvd_ps = rps;
+}
+
+static void si_parse_pplib_clock_info(struct amdgpu_device *adev,
+ struct amdgpu_ps *rps, int index,
+ union pplib_clock_info *clock_info)
+{
+ struct rv7xx_power_info *pi = rv770_get_pi(adev);
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct si_power_info *si_pi = si_get_pi(adev);
+ struct si_ps *ps = si_get_ps(rps);
+ u16 leakage_voltage;
+ struct rv7xx_pl *pl = &ps->performance_levels[index];
+ int ret;
+
+ ps->performance_level_count = index + 1;
+
+ pl->sclk = le16_to_cpu(clock_info->si.usEngineClockLow);
+ pl->sclk |= clock_info->si.ucEngineClockHigh << 16;
+ pl->mclk = le16_to_cpu(clock_info->si.usMemoryClockLow);
+ pl->mclk |= clock_info->si.ucMemoryClockHigh << 16;
+
+ pl->vddc = le16_to_cpu(clock_info->si.usVDDC);
+ pl->vddci = le16_to_cpu(clock_info->si.usVDDCI);
+ pl->flags = le32_to_cpu(clock_info->si.ulFlags);
+ pl->pcie_gen = amdgpu_get_pcie_gen_support(adev,
+ si_pi->sys_pcie_mask,
+ si_pi->boot_pcie_gen,
+ clock_info->si.ucPCIEGen);
+
+ /* patch up vddc if necessary */
+ ret = si_get_leakage_voltage_from_leakage_index(adev, pl->vddc,
+ &leakage_voltage);
+ if (ret == 0)
+ pl->vddc = leakage_voltage;
+
+ if (rps->class & ATOM_PPLIB_CLASSIFICATION_ACPI) {
+ pi->acpi_vddc = pl->vddc;
+ eg_pi->acpi_vddci = pl->vddci;
+ si_pi->acpi_pcie_gen = pl->pcie_gen;
+ }
+
+ if ((rps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) &&
+ index == 0) {
+ /* XXX disable for A0 tahiti */
+ si_pi->ulv.supported = false;
+ si_pi->ulv.pl = *pl;
+ si_pi->ulv.one_pcie_lane_in_ulv = false;
+ si_pi->ulv.volt_change_delay = SISLANDS_ULVVOLTAGECHANGEDELAY_DFLT;
+ si_pi->ulv.cg_ulv_parameter = SISLANDS_CGULVPARAMETER_DFLT;
+ si_pi->ulv.cg_ulv_control = SISLANDS_CGULVCONTROL_DFLT;
+ }
+
+ if (pi->min_vddc_in_table > pl->vddc)
+ pi->min_vddc_in_table = pl->vddc;
+
+ if (pi->max_vddc_in_table < pl->vddc)
+ pi->max_vddc_in_table = pl->vddc;
+
+ /* patch up boot state */
+ if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
+ u16 vddc, vddci, mvdd;
+ amdgpu_atombios_get_default_voltages(adev, &vddc, &vddci, &mvdd);
+ pl->mclk = adev->clock.default_mclk;
+ pl->sclk = adev->clock.default_sclk;
+ pl->vddc = vddc;
+ pl->vddci = vddci;
+ si_pi->mvdd_bootup_value = mvdd;
+ }
+
+ if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) ==
+ ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) {
+ adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.sclk = pl->sclk;
+ adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.mclk = pl->mclk;
+ adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddc = pl->vddc;
+ adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddci = pl->vddci;
+ }
+}
+
+union pplib_power_state {
+ struct _ATOM_PPLIB_STATE v1;
+ struct _ATOM_PPLIB_STATE_V2 v2;
+};
+
+static int si_parse_power_table(struct amdgpu_device *adev)
+{
+ struct amdgpu_mode_info *mode_info = &adev->mode_info;
+ struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
+ union pplib_power_state *power_state;
+ int i, j, k, non_clock_array_index, clock_array_index;
+ union pplib_clock_info *clock_info;
+ struct _StateArray *state_array;
+ struct _ClockInfoArray *clock_info_array;
+ struct _NonClockInfoArray *non_clock_info_array;
+ union power_info *power_info;
+ int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
+ u16 data_offset;
+ u8 frev, crev;
+ u8 *power_state_offset;
+ struct si_ps *ps;
+
+ if (!amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL,
+ &frev, &crev, &data_offset))
+ return -EINVAL;
+ power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
+
+ amdgpu_add_thermal_controller(adev);
+
+ state_array = (struct _StateArray *)
+ (mode_info->atom_context->bios + data_offset +
+ le16_to_cpu(power_info->pplib.usStateArrayOffset));
+ clock_info_array = (struct _ClockInfoArray *)
+ (mode_info->atom_context->bios + data_offset +
+ le16_to_cpu(power_info->pplib.usClockInfoArrayOffset));
+ non_clock_info_array = (struct _NonClockInfoArray *)
+ (mode_info->atom_context->bios + data_offset +
+ le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset));
+
+ adev->pm.dpm.ps = kcalloc(state_array->ucNumEntries,
+ sizeof(struct amdgpu_ps),
+ GFP_KERNEL);
+ if (!adev->pm.dpm.ps)
+ return -ENOMEM;
+ power_state_offset = (u8 *)state_array->states;
+ for (i = 0; i < state_array->ucNumEntries; i++) {
+ u8 *idx;
+ power_state = (union pplib_power_state *)power_state_offset;
+ non_clock_array_index = power_state->v2.nonClockInfoIndex;
+ non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
+ &non_clock_info_array->nonClockInfo[non_clock_array_index];
+ ps = kzalloc(sizeof(struct si_ps), GFP_KERNEL);
+ if (ps == NULL) {
+ kfree(adev->pm.dpm.ps);
+ return -ENOMEM;
+ }
+ adev->pm.dpm.ps[i].ps_priv = ps;
+ si_parse_pplib_non_clock_info(adev, &adev->pm.dpm.ps[i],
+ non_clock_info,
+ non_clock_info_array->ucEntrySize);
+ k = 0;
+ idx = (u8 *)&power_state->v2.clockInfoIndex[0];
+ for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) {
+ clock_array_index = idx[j];
+ if (clock_array_index >= clock_info_array->ucNumEntries)
+ continue;
+ if (k >= SISLANDS_MAX_HARDWARE_POWERLEVELS)
+ break;
+ clock_info = (union pplib_clock_info *)
+ ((u8 *)&clock_info_array->clockInfo[0] +
+ (clock_array_index * clock_info_array->ucEntrySize));
+ si_parse_pplib_clock_info(adev,
+ &adev->pm.dpm.ps[i], k,
+ clock_info);
+ k++;
+ }
+ power_state_offset += 2 + power_state->v2.ucNumDPMLevels;
+ }
+ adev->pm.dpm.num_ps = state_array->ucNumEntries;
+
+ /* fill in the vce power states */
+ for (i = 0; i < adev->pm.dpm.num_of_vce_states; i++) {
+ u32 sclk, mclk;
+ clock_array_index = adev->pm.dpm.vce_states[i].clk_idx;
+ clock_info = (union pplib_clock_info *)
+ &clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize];
+ sclk = le16_to_cpu(clock_info->si.usEngineClockLow);
+ sclk |= clock_info->si.ucEngineClockHigh << 16;
+ mclk = le16_to_cpu(clock_info->si.usMemoryClockLow);
+ mclk |= clock_info->si.ucMemoryClockHigh << 16;
+ adev->pm.dpm.vce_states[i].sclk = sclk;
+ adev->pm.dpm.vce_states[i].mclk = mclk;
+ }
+
+ return 0;
+}
+
+static int si_dpm_init(struct amdgpu_device *adev)
+{
+ struct rv7xx_power_info *pi;
+ struct evergreen_power_info *eg_pi;
+ struct ni_power_info *ni_pi;
+ struct si_power_info *si_pi;
+ struct atom_clock_dividers dividers;
+ int ret;
+
+ si_pi = kzalloc(sizeof(struct si_power_info), GFP_KERNEL);
+ if (si_pi == NULL)
+ return -ENOMEM;
+ adev->pm.dpm.priv = si_pi;
+ ni_pi = &si_pi->ni;
+ eg_pi = &ni_pi->eg;
+ pi = &eg_pi->rv7xx;
+
+ si_pi->sys_pcie_mask =
+ adev->pm.pcie_gen_mask & CAIL_PCIE_LINK_SPEED_SUPPORT_MASK;
+ si_pi->force_pcie_gen = AMDGPU_PCIE_GEN_INVALID;
+ si_pi->boot_pcie_gen = si_get_current_pcie_speed(adev);
+
+ si_set_max_cu_value(adev);
+
+ rv770_get_max_vddc(adev);
+ si_get_leakage_vddc(adev);
+ si_patch_dependency_tables_based_on_leakage(adev);
+
+ pi->acpi_vddc = 0;
+ eg_pi->acpi_vddci = 0;
+ pi->min_vddc_in_table = 0;
+ pi->max_vddc_in_table = 0;
+
+ ret = amdgpu_get_platform_caps(adev);
+ if (ret)
+ return ret;
+
+ ret = amdgpu_parse_extended_power_table(adev);
+ if (ret)
+ return ret;
+
+ ret = si_parse_power_table(adev);
+ if (ret)
+ return ret;
+
+ adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries =
+ kcalloc(4,
+ sizeof(struct amdgpu_clock_voltage_dependency_entry),
+ GFP_KERNEL);
+ if (!adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries) {
+ amdgpu_free_extended_power_table(adev);
+ return -ENOMEM;
+ }
+ adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.count = 4;
+ adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[0].clk = 0;
+ adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[0].v = 0;
+ adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[1].clk = 36000;
+ adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[1].v = 720;
+ adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[2].clk = 54000;
+ adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[2].v = 810;
+ adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[3].clk = 72000;
+ adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[3].v = 900;
+
+ if (adev->pm.dpm.voltage_response_time == 0)
+ adev->pm.dpm.voltage_response_time = R600_VOLTAGERESPONSETIME_DFLT;
+ if (adev->pm.dpm.backbias_response_time == 0)
+ adev->pm.dpm.backbias_response_time = R600_BACKBIASRESPONSETIME_DFLT;
+
+ ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_ENGINE_PLL_PARAM,
+ 0, false, &dividers);
+ if (ret)
+ pi->ref_div = dividers.ref_div + 1;
+ else
+ pi->ref_div = R600_REFERENCEDIVIDER_DFLT;
+
+ eg_pi->smu_uvd_hs = false;
+
+ pi->mclk_strobe_mode_threshold = 40000;
+ if (si_is_special_1gb_platform(adev))
+ pi->mclk_stutter_mode_threshold = 0;
+ else
+ pi->mclk_stutter_mode_threshold = pi->mclk_strobe_mode_threshold;
+ pi->mclk_edc_enable_threshold = 40000;
+ eg_pi->mclk_edc_wr_enable_threshold = 40000;
+
+ ni_pi->mclk_rtt_mode_threshold = eg_pi->mclk_edc_wr_enable_threshold;
+
+ pi->voltage_control =
+ amdgpu_atombios_is_voltage_gpio(adev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+ VOLTAGE_OBJ_GPIO_LUT);
+ if (!pi->voltage_control) {
+ si_pi->voltage_control_svi2 =
+ amdgpu_atombios_is_voltage_gpio(adev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+ VOLTAGE_OBJ_SVID2);
+ if (si_pi->voltage_control_svi2)
+ amdgpu_atombios_get_svi2_info(adev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+ &si_pi->svd_gpio_id, &si_pi->svc_gpio_id);
+ }
+
+ pi->mvdd_control =
+ amdgpu_atombios_is_voltage_gpio(adev, SET_VOLTAGE_TYPE_ASIC_MVDDC,
+ VOLTAGE_OBJ_GPIO_LUT);
+
+ eg_pi->vddci_control =
+ amdgpu_atombios_is_voltage_gpio(adev, SET_VOLTAGE_TYPE_ASIC_VDDCI,
+ VOLTAGE_OBJ_GPIO_LUT);
+ if (!eg_pi->vddci_control)
+ si_pi->vddci_control_svi2 =
+ amdgpu_atombios_is_voltage_gpio(adev, SET_VOLTAGE_TYPE_ASIC_VDDCI,
+ VOLTAGE_OBJ_SVID2);
+
+ si_pi->vddc_phase_shed_control =
+ amdgpu_atombios_is_voltage_gpio(adev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+ VOLTAGE_OBJ_PHASE_LUT);
+
+ rv770_get_engine_memory_ss(adev);
+
+ pi->asi = RV770_ASI_DFLT;
+ pi->pasi = CYPRESS_HASI_DFLT;
+ pi->vrc = SISLANDS_VRC_DFLT;
+
+ pi->gfx_clock_gating = true;
+
+ eg_pi->sclk_deep_sleep = true;
+ si_pi->sclk_deep_sleep_above_low = false;
+
+ if (adev->pm.int_thermal_type != THERMAL_TYPE_NONE)
+ pi->thermal_protection = true;
+ else
+ pi->thermal_protection = false;
+
+ eg_pi->dynamic_ac_timing = true;
+
+ eg_pi->light_sleep = true;
+#if defined(CONFIG_ACPI)
+ eg_pi->pcie_performance_request =
+ amdgpu_acpi_is_pcie_performance_request_supported(adev);
+#else
+ eg_pi->pcie_performance_request = false;
+#endif
+
+ si_pi->sram_end = SMC_RAM_END;
+
+ adev->pm.dpm.dyn_state.mclk_sclk_ratio = 4;
+ adev->pm.dpm.dyn_state.sclk_mclk_delta = 15000;
+ adev->pm.dpm.dyn_state.vddc_vddci_delta = 200;
+ adev->pm.dpm.dyn_state.valid_sclk_values.count = 0;
+ adev->pm.dpm.dyn_state.valid_sclk_values.values = NULL;
+ adev->pm.dpm.dyn_state.valid_mclk_values.count = 0;
+ adev->pm.dpm.dyn_state.valid_mclk_values.values = NULL;
+
+ si_initialize_powertune_defaults(adev);
+
+ /* make sure dc limits are valid */
+ if ((adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.sclk == 0) ||
+ (adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.mclk == 0))
+ adev->pm.dpm.dyn_state.max_clock_voltage_on_dc =
+ adev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+
+ si_pi->fan_ctrl_is_in_default_mode = true;
+
+ return 0;
+}
+
+static void si_dpm_fini(struct amdgpu_device *adev)
+{
+ int i;
+
+ if (adev->pm.dpm.ps)
+ for (i = 0; i < adev->pm.dpm.num_ps; i++)
+ kfree(adev->pm.dpm.ps[i].ps_priv);
+ kfree(adev->pm.dpm.ps);
+ kfree(adev->pm.dpm.priv);
+ kfree(adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries);
+ amdgpu_free_extended_power_table(adev);
+}
+
+static void si_dpm_debugfs_print_current_performance_level(void *handle,
+ struct seq_file *m)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct amdgpu_ps *rps = &eg_pi->current_rps;
+ struct si_ps *ps = si_get_ps(rps);
+ struct rv7xx_pl *pl;
+ u32 current_index =
+ (RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_STATE_INDEX_MASK) >>
+ CURRENT_STATE_INDEX_SHIFT;
+
+ if (current_index >= ps->performance_level_count) {
+ seq_printf(m, "invalid dpm profile %d\n", current_index);
+ } else {
+ pl = &ps->performance_levels[current_index];
+ seq_printf(m, "uvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+ seq_printf(m, "power level %d sclk: %u mclk: %u vddc: %u vddci: %u pcie gen: %u\n",
+ current_index, pl->sclk, pl->mclk, pl->vddc, pl->vddci, pl->pcie_gen + 1);
+ }
+}
+
+static int si_dpm_set_interrupt_state(struct amdgpu_device *adev,
+ struct amdgpu_irq_src *source,
+ unsigned type,
+ enum amdgpu_interrupt_state state)
+{
+ u32 cg_thermal_int;
+
+ switch (type) {
+ case AMDGPU_THERMAL_IRQ_LOW_TO_HIGH:
+ switch (state) {
+ case AMDGPU_IRQ_STATE_DISABLE:
+ cg_thermal_int = RREG32_SMC(CG_THERMAL_INT);
+ cg_thermal_int |= THERM_INT_MASK_HIGH;
+ WREG32_SMC(CG_THERMAL_INT, cg_thermal_int);
+ break;
+ case AMDGPU_IRQ_STATE_ENABLE:
+ cg_thermal_int = RREG32_SMC(CG_THERMAL_INT);
+ cg_thermal_int &= ~THERM_INT_MASK_HIGH;
+ WREG32_SMC(CG_THERMAL_INT, cg_thermal_int);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case AMDGPU_THERMAL_IRQ_HIGH_TO_LOW:
+ switch (state) {
+ case AMDGPU_IRQ_STATE_DISABLE:
+ cg_thermal_int = RREG32_SMC(CG_THERMAL_INT);
+ cg_thermal_int |= THERM_INT_MASK_LOW;
+ WREG32_SMC(CG_THERMAL_INT, cg_thermal_int);
+ break;
+ case AMDGPU_IRQ_STATE_ENABLE:
+ cg_thermal_int = RREG32_SMC(CG_THERMAL_INT);
+ cg_thermal_int &= ~THERM_INT_MASK_LOW;
+ WREG32_SMC(CG_THERMAL_INT, cg_thermal_int);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int si_dpm_process_interrupt(struct amdgpu_device *adev,
+ struct amdgpu_irq_src *source,
+ struct amdgpu_iv_entry *entry)
+{
+ bool queue_thermal = false;
+
+ if (entry == NULL)
+ return -EINVAL;
+
+ switch (entry->src_id) {
+ case 230: /* thermal low to high */
+ DRM_DEBUG("IH: thermal low to high\n");
+ adev->pm.dpm.thermal.high_to_low = false;
+ queue_thermal = true;
+ break;
+ case 231: /* thermal high to low */
+ DRM_DEBUG("IH: thermal high to low\n");
+ adev->pm.dpm.thermal.high_to_low = true;
+ queue_thermal = true;
+ break;
+ default:
+ break;
+ }
+
+ if (queue_thermal)
+ schedule_work(&adev->pm.dpm.thermal.work);
+
+ return 0;
+}
+
+static int si_dpm_late_init(void *handle)
+{
+ int ret;
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ if (!adev->pm.dpm_enabled)
+ return 0;
+
+ ret = si_set_temperature_range(adev);
+ if (ret)
+ return ret;
+#if 0 //TODO ?
+ si_dpm_powergate_uvd(adev, true);
+#endif
+ return 0;
+}
+
+/**
+ * si_dpm_init_microcode - load ucode images from disk
+ *
+ * @adev: amdgpu_device pointer
+ *
+ * Use the firmware interface to load the ucode images into
+ * the driver (not loaded into hw).
+ * Returns 0 on success, error on failure.
+ */
+static int si_dpm_init_microcode(struct amdgpu_device *adev)
+{
+ const char *chip_name;
+ char fw_name[30];
+ int err;
+
+ DRM_DEBUG("\n");
+ switch (adev->asic_type) {
+ case CHIP_TAHITI:
+ chip_name = "tahiti";
+ break;
+ case CHIP_PITCAIRN:
+ if ((adev->pdev->revision == 0x81) &&
+ ((adev->pdev->device == 0x6810) ||
+ (adev->pdev->device == 0x6811)))
+ chip_name = "pitcairn_k";
+ else
+ chip_name = "pitcairn";
+ break;
+ case CHIP_VERDE:
+ if (((adev->pdev->device == 0x6820) &&
+ ((adev->pdev->revision == 0x81) ||
+ (adev->pdev->revision == 0x83))) ||
+ ((adev->pdev->device == 0x6821) &&
+ ((adev->pdev->revision == 0x83) ||
+ (adev->pdev->revision == 0x87))) ||
+ ((adev->pdev->revision == 0x87) &&
+ ((adev->pdev->device == 0x6823) ||
+ (adev->pdev->device == 0x682b))))
+ chip_name = "verde_k";
+ else
+ chip_name = "verde";
+ break;
+ case CHIP_OLAND:
+ if (((adev->pdev->revision == 0x81) &&
+ ((adev->pdev->device == 0x6600) ||
+ (adev->pdev->device == 0x6604) ||
+ (adev->pdev->device == 0x6605) ||
+ (adev->pdev->device == 0x6610))) ||
+ ((adev->pdev->revision == 0x83) &&
+ (adev->pdev->device == 0x6610)))
+ chip_name = "oland_k";
+ else
+ chip_name = "oland";
+ break;
+ case CHIP_HAINAN:
+ if (((adev->pdev->revision == 0x81) &&
+ (adev->pdev->device == 0x6660)) ||
+ ((adev->pdev->revision == 0x83) &&
+ ((adev->pdev->device == 0x6660) ||
+ (adev->pdev->device == 0x6663) ||
+ (adev->pdev->device == 0x6665) ||
+ (adev->pdev->device == 0x6667))))
+ chip_name = "hainan_k";
+ else if ((adev->pdev->revision == 0xc3) &&
+ (adev->pdev->device == 0x6665))
+ chip_name = "banks_k_2";
+ else
+ chip_name = "hainan";
+ break;
+ default: BUG();
+ }
+
+ snprintf(fw_name, sizeof(fw_name), "amdgpu/%s_smc.bin", chip_name);
+ err = request_firmware(&adev->pm.fw, fw_name, adev->dev);
+ if (err)
+ goto out;
+ err = amdgpu_ucode_validate(adev->pm.fw);
+
+out:
+ if (err) {
+ DRM_ERROR("si_smc: Failed to load firmware. err = %d\"%s\"\n",
+ err, fw_name);
+ release_firmware(adev->pm.fw);
+ adev->pm.fw = NULL;
+ }
+ return err;
+
+}
+
+static int si_dpm_sw_init(void *handle)
+{
+ int ret;
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ ret = amdgpu_irq_add_id(adev, AMDGPU_IRQ_CLIENTID_LEGACY, 230, &adev->pm.dpm.thermal.irq);
+ if (ret)
+ return ret;
+
+ ret = amdgpu_irq_add_id(adev, AMDGPU_IRQ_CLIENTID_LEGACY, 231, &adev->pm.dpm.thermal.irq);
+ if (ret)
+ return ret;
+
+ /* default to balanced state */
+ adev->pm.dpm.state = POWER_STATE_TYPE_BALANCED;
+ adev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED;
+ adev->pm.dpm.forced_level = AMD_DPM_FORCED_LEVEL_AUTO;
+ adev->pm.default_sclk = adev->clock.default_sclk;
+ adev->pm.default_mclk = adev->clock.default_mclk;
+ adev->pm.current_sclk = adev->clock.default_sclk;
+ adev->pm.current_mclk = adev->clock.default_mclk;
+ adev->pm.int_thermal_type = THERMAL_TYPE_NONE;
+
+ if (amdgpu_dpm == 0)
+ return 0;
+
+ ret = si_dpm_init_microcode(adev);
+ if (ret)
+ return ret;
+
+ INIT_WORK(&adev->pm.dpm.thermal.work, amdgpu_dpm_thermal_work_handler);
+ mutex_lock(&adev->pm.mutex);
+ ret = si_dpm_init(adev);
+ if (ret)
+ goto dpm_failed;
+ adev->pm.dpm.current_ps = adev->pm.dpm.requested_ps = adev->pm.dpm.boot_ps;
+ if (amdgpu_dpm == 1)
+ amdgpu_pm_print_power_states(adev);
+ mutex_unlock(&adev->pm.mutex);
+ DRM_INFO("amdgpu: dpm initialized\n");
+
+ return 0;
+
+dpm_failed:
+ si_dpm_fini(adev);
+ mutex_unlock(&adev->pm.mutex);
+ DRM_ERROR("amdgpu: dpm initialization failed\n");
+ return ret;
+}
+
+static int si_dpm_sw_fini(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ flush_work(&adev->pm.dpm.thermal.work);
+
+ mutex_lock(&adev->pm.mutex);
+ si_dpm_fini(adev);
+ mutex_unlock(&adev->pm.mutex);
+
+ return 0;
+}
+
+static int si_dpm_hw_init(void *handle)
+{
+ int ret;
+
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ if (!amdgpu_dpm)
+ return 0;
+
+ mutex_lock(&adev->pm.mutex);
+ si_dpm_setup_asic(adev);
+ ret = si_dpm_enable(adev);
+ if (ret)
+ adev->pm.dpm_enabled = false;
+ else
+ adev->pm.dpm_enabled = true;
+ mutex_unlock(&adev->pm.mutex);
+ amdgpu_pm_compute_clocks(adev);
+ return ret;
+}
+
+static int si_dpm_hw_fini(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ if (adev->pm.dpm_enabled) {
+ mutex_lock(&adev->pm.mutex);
+ si_dpm_disable(adev);
+ mutex_unlock(&adev->pm.mutex);
+ }
+
+ return 0;
+}
+
+static int si_dpm_suspend(void *handle)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ if (adev->pm.dpm_enabled) {
+ mutex_lock(&adev->pm.mutex);
+ /* disable dpm */
+ si_dpm_disable(adev);
+ /* reset the power state */
+ adev->pm.dpm.current_ps = adev->pm.dpm.requested_ps = adev->pm.dpm.boot_ps;
+ mutex_unlock(&adev->pm.mutex);
+ }
+ return 0;
+}
+
+static int si_dpm_resume(void *handle)
+{
+ int ret;
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ if (adev->pm.dpm_enabled) {
+ /* asic init will reset to the boot state */
+ mutex_lock(&adev->pm.mutex);
+ si_dpm_setup_asic(adev);
+ ret = si_dpm_enable(adev);
+ if (ret)
+ adev->pm.dpm_enabled = false;
+ else
+ adev->pm.dpm_enabled = true;
+ mutex_unlock(&adev->pm.mutex);
+ if (adev->pm.dpm_enabled)
+ amdgpu_pm_compute_clocks(adev);
+ }
+ return 0;
+}
+
+static bool si_dpm_is_idle(void *handle)
+{
+ /* XXX */
+ return true;
+}
+
+static int si_dpm_wait_for_idle(void *handle)
+{
+ /* XXX */
+ return 0;
+}
+
+static int si_dpm_soft_reset(void *handle)
+{
+ return 0;
+}
+
+static int si_dpm_set_clockgating_state(void *handle,
+ enum amd_clockgating_state state)
+{
+ return 0;
+}
+
+static int si_dpm_set_powergating_state(void *handle,
+ enum amd_powergating_state state)
+{
+ return 0;
+}
+
+/* get temperature in millidegrees */
+static int si_dpm_get_temp(void *handle)
+{
+ u32 temp;
+ int actual_temp = 0;
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ temp = (RREG32(CG_MULT_THERMAL_STATUS) & CTF_TEMP_MASK) >>
+ CTF_TEMP_SHIFT;
+
+ if (temp & 0x200)
+ actual_temp = 255;
+ else
+ actual_temp = temp & 0x1ff;
+
+ actual_temp = (actual_temp * 1000);
+
+ return actual_temp;
+}
+
+static u32 si_dpm_get_sclk(void *handle, bool low)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct si_ps *requested_state = si_get_ps(&eg_pi->requested_rps);
+
+ if (low)
+ return requested_state->performance_levels[0].sclk;
+ else
+ return requested_state->performance_levels[requested_state->performance_level_count - 1].sclk;
+}
+
+static u32 si_dpm_get_mclk(void *handle, bool low)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct si_ps *requested_state = si_get_ps(&eg_pi->requested_rps);
+
+ if (low)
+ return requested_state->performance_levels[0].mclk;
+ else
+ return requested_state->performance_levels[requested_state->performance_level_count - 1].mclk;
+}
+
+static void si_dpm_print_power_state(void *handle,
+ void *current_ps)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct amdgpu_ps *rps = (struct amdgpu_ps *)current_ps;
+ struct si_ps *ps = si_get_ps(rps);
+ struct rv7xx_pl *pl;
+ int i;
+
+ amdgpu_dpm_print_class_info(rps->class, rps->class2);
+ amdgpu_dpm_print_cap_info(rps->caps);
+ DRM_INFO("\tuvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+ for (i = 0; i < ps->performance_level_count; i++) {
+ pl = &ps->performance_levels[i];
+ if (adev->asic_type >= CHIP_TAHITI)
+ DRM_INFO("\t\tpower level %d sclk: %u mclk: %u vddc: %u vddci: %u pcie gen: %u\n",
+ i, pl->sclk, pl->mclk, pl->vddc, pl->vddci, pl->pcie_gen + 1);
+ else
+ DRM_INFO("\t\tpower level %d sclk: %u mclk: %u vddc: %u vddci: %u\n",
+ i, pl->sclk, pl->mclk, pl->vddc, pl->vddci);
+ }
+ amdgpu_dpm_print_ps_status(adev, rps);
+}
+
+static int si_dpm_early_init(void *handle)
+{
+
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ adev->powerplay.pp_funcs = &si_dpm_funcs;
+ adev->powerplay.pp_handle = adev;
+ si_dpm_set_irq_funcs(adev);
+ return 0;
+}
+
+static inline bool si_are_power_levels_equal(const struct rv7xx_pl *si_cpl1,
+ const struct rv7xx_pl *si_cpl2)
+{
+ return ((si_cpl1->mclk == si_cpl2->mclk) &&
+ (si_cpl1->sclk == si_cpl2->sclk) &&
+ (si_cpl1->pcie_gen == si_cpl2->pcie_gen) &&
+ (si_cpl1->vddc == si_cpl2->vddc) &&
+ (si_cpl1->vddci == si_cpl2->vddci));
+}
+
+static int si_check_state_equal(void *handle,
+ void *current_ps,
+ void *request_ps,
+ bool *equal)
+{
+ struct si_ps *si_cps;
+ struct si_ps *si_rps;
+ int i;
+ struct amdgpu_ps *cps = (struct amdgpu_ps *)current_ps;
+ struct amdgpu_ps *rps = (struct amdgpu_ps *)request_ps;
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+ if (adev == NULL || cps == NULL || rps == NULL || equal == NULL)
+ return -EINVAL;
+
+ si_cps = si_get_ps((struct amdgpu_ps *)cps);
+ si_rps = si_get_ps((struct amdgpu_ps *)rps);
+
+ if (si_cps == NULL) {
+ printk("si_cps is NULL\n");
+ *equal = false;
+ return 0;
+ }
+
+ if (si_cps->performance_level_count != si_rps->performance_level_count) {
+ *equal = false;
+ return 0;
+ }
+
+ for (i = 0; i < si_cps->performance_level_count; i++) {
+ if (!si_are_power_levels_equal(&(si_cps->performance_levels[i]),
+ &(si_rps->performance_levels[i]))) {
+ *equal = false;
+ return 0;
+ }
+ }
+
+ /* If all performance levels are the same try to use the UVD clocks to break the tie.*/
+ *equal = ((cps->vclk == rps->vclk) && (cps->dclk == rps->dclk));
+ *equal &= ((cps->evclk == rps->evclk) && (cps->ecclk == rps->ecclk));
+
+ return 0;
+}
+
+static int si_dpm_read_sensor(void *handle, int idx,
+ void *value, int *size)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+ struct amdgpu_ps *rps = &eg_pi->current_rps;
+ struct si_ps *ps = si_get_ps(rps);
+ uint32_t sclk, mclk;
+ u32 pl_index =
+ (RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_STATE_INDEX_MASK) >>
+ CURRENT_STATE_INDEX_SHIFT;
+
+ /* size must be at least 4 bytes for all sensors */
+ if (*size < 4)
+ return -EINVAL;
+
+ switch (idx) {
+ case AMDGPU_PP_SENSOR_GFX_SCLK:
+ if (pl_index < ps->performance_level_count) {
+ sclk = ps->performance_levels[pl_index].sclk;
+ *((uint32_t *)value) = sclk;
+ *size = 4;
+ return 0;
+ }
+ return -EINVAL;
+ case AMDGPU_PP_SENSOR_GFX_MCLK:
+ if (pl_index < ps->performance_level_count) {
+ mclk = ps->performance_levels[pl_index].mclk;
+ *((uint32_t *)value) = mclk;
+ *size = 4;
+ return 0;
+ }
+ return -EINVAL;
+ case AMDGPU_PP_SENSOR_GPU_TEMP:
+ *((uint32_t *)value) = si_dpm_get_temp(adev);
+ *size = 4;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct amd_ip_funcs si_dpm_ip_funcs = {
+ .name = "si_dpm",
+ .early_init = si_dpm_early_init,
+ .late_init = si_dpm_late_init,
+ .sw_init = si_dpm_sw_init,
+ .sw_fini = si_dpm_sw_fini,
+ .hw_init = si_dpm_hw_init,
+ .hw_fini = si_dpm_hw_fini,
+ .suspend = si_dpm_suspend,
+ .resume = si_dpm_resume,
+ .is_idle = si_dpm_is_idle,
+ .wait_for_idle = si_dpm_wait_for_idle,
+ .soft_reset = si_dpm_soft_reset,
+ .set_clockgating_state = si_dpm_set_clockgating_state,
+ .set_powergating_state = si_dpm_set_powergating_state,
+};
+
+const struct amdgpu_ip_block_version si_smu_ip_block =
+{
+ .type = AMD_IP_BLOCK_TYPE_SMC,
+ .major = 6,
+ .minor = 0,
+ .rev = 0,
+ .funcs = &si_dpm_ip_funcs,
+};
+
+static const struct amd_pm_funcs si_dpm_funcs = {
+ .pre_set_power_state = &si_dpm_pre_set_power_state,
+ .set_power_state = &si_dpm_set_power_state,
+ .post_set_power_state = &si_dpm_post_set_power_state,
+ .display_configuration_changed = &si_dpm_display_configuration_changed,
+ .get_sclk = &si_dpm_get_sclk,
+ .get_mclk = &si_dpm_get_mclk,
+ .print_power_state = &si_dpm_print_power_state,
+ .debugfs_print_current_performance_level = &si_dpm_debugfs_print_current_performance_level,
+ .force_performance_level = &si_dpm_force_performance_level,
+ .vblank_too_short = &si_dpm_vblank_too_short,
+ .set_fan_control_mode = &si_dpm_set_fan_control_mode,
+ .get_fan_control_mode = &si_dpm_get_fan_control_mode,
+ .set_fan_speed_percent = &si_dpm_set_fan_speed_percent,
+ .get_fan_speed_percent = &si_dpm_get_fan_speed_percent,
+ .check_state_equal = &si_check_state_equal,
+ .get_vce_clock_state = amdgpu_get_vce_clock_state,
+ .read_sensor = &si_dpm_read_sensor,
+};
+
+static const struct amdgpu_irq_src_funcs si_dpm_irq_funcs = {
+ .set = si_dpm_set_interrupt_state,
+ .process = si_dpm_process_interrupt,
+};
+
+static void si_dpm_set_irq_funcs(struct amdgpu_device *adev)
+{
+ adev->pm.dpm.thermal.irq.num_types = AMDGPU_THERMAL_IRQ_LAST;
+ adev->pm.dpm.thermal.irq.funcs = &si_dpm_irq_funcs;
+}
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/si_dpm.h b/drivers/gpu/drm/amd/pm/powerplay/si_dpm.h
new file mode 100644
index 000000000000..bc0be6818e21
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/si_dpm.h
@@ -0,0 +1,1015 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 __SI_DPM_H__
+#define __SI_DPM_H__
+
+#include "amdgpu_atombios.h"
+#include "sislands_smc.h"
+
+#define MC_CG_CONFIG 0x96f
+#define MC_ARB_CG 0x9fa
+#define CG_ARB_REQ(x) ((x) << 0)
+#define CG_ARB_REQ_MASK (0xff << 0)
+
+#define MC_ARB_DRAM_TIMING_1 0x9fc
+#define MC_ARB_DRAM_TIMING_2 0x9fd
+#define MC_ARB_DRAM_TIMING_3 0x9fe
+#define MC_ARB_DRAM_TIMING2_1 0x9ff
+#define MC_ARB_DRAM_TIMING2_2 0xa00
+#define MC_ARB_DRAM_TIMING2_3 0xa01
+
+#define MAX_NO_OF_MVDD_VALUES 2
+#define MAX_NO_VREG_STEPS 32
+#define NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE 16
+#define SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE 32
+#define SMC_NISLANDS_MC_REGISTER_ARRAY_SET_COUNT 20
+#define RV770_ASI_DFLT 1000
+#define CYPRESS_HASI_DFLT 400000
+#define PCIE_PERF_REQ_PECI_GEN1 2
+#define PCIE_PERF_REQ_PECI_GEN2 3
+#define PCIE_PERF_REQ_PECI_GEN3 4
+#define RV770_DEFAULT_VCLK_FREQ 53300 /* 10 khz */
+#define RV770_DEFAULT_DCLK_FREQ 40000 /* 10 khz */
+
+#define SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE 16
+
+#define RV770_SMC_TABLE_ADDRESS 0xB000
+#define RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE 3
+
+#define SMC_STROBE_RATIO 0x0F
+#define SMC_STROBE_ENABLE 0x10
+
+#define SMC_MC_EDC_RD_FLAG 0x01
+#define SMC_MC_EDC_WR_FLAG 0x02
+#define SMC_MC_RTT_ENABLE 0x04
+#define SMC_MC_STUTTER_EN 0x08
+
+#define RV770_SMC_VOLTAGEMASK_VDDC 0
+#define RV770_SMC_VOLTAGEMASK_MVDD 1
+#define RV770_SMC_VOLTAGEMASK_VDDCI 2
+#define RV770_SMC_VOLTAGEMASK_MAX 4
+
+#define NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE 16
+#define NISLANDS_SMC_STROBE_RATIO 0x0F
+#define NISLANDS_SMC_STROBE_ENABLE 0x10
+
+#define NISLANDS_SMC_MC_EDC_RD_FLAG 0x01
+#define NISLANDS_SMC_MC_EDC_WR_FLAG 0x02
+#define NISLANDS_SMC_MC_RTT_ENABLE 0x04
+#define NISLANDS_SMC_MC_STUTTER_EN 0x08
+
+#define MAX_NO_VREG_STEPS 32
+
+#define NISLANDS_SMC_VOLTAGEMASK_VDDC 0
+#define NISLANDS_SMC_VOLTAGEMASK_MVDD 1
+#define NISLANDS_SMC_VOLTAGEMASK_VDDCI 2
+#define NISLANDS_SMC_VOLTAGEMASK_MAX 4
+
+#define SISLANDS_MCREGISTERTABLE_INITIAL_SLOT 0
+#define SISLANDS_MCREGISTERTABLE_ACPI_SLOT 1
+#define SISLANDS_MCREGISTERTABLE_ULV_SLOT 2
+#define SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT 3
+
+#define SISLANDS_LEAKAGE_INDEX0 0xff01
+#define SISLANDS_MAX_LEAKAGE_COUNT 4
+
+#define SISLANDS_MAX_HARDWARE_POWERLEVELS 5
+#define SISLANDS_INITIAL_STATE_ARB_INDEX 0
+#define SISLANDS_ACPI_STATE_ARB_INDEX 1
+#define SISLANDS_ULV_STATE_ARB_INDEX 2
+#define SISLANDS_DRIVER_STATE_ARB_INDEX 3
+
+#define SISLANDS_DPM2_MAX_PULSE_SKIP 256
+
+#define SISLANDS_DPM2_NEAR_TDP_DEC 10
+#define SISLANDS_DPM2_ABOVE_SAFE_INC 5
+#define SISLANDS_DPM2_BELOW_SAFE_INC 20
+
+#define SISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT 80
+
+#define SISLANDS_DPM2_MAXPS_PERCENT_H 99
+#define SISLANDS_DPM2_MAXPS_PERCENT_M 99
+
+#define SISLANDS_DPM2_SQ_RAMP_MAX_POWER 0x3FFF
+#define SISLANDS_DPM2_SQ_RAMP_MIN_POWER 0x12
+#define SISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA 0x15
+#define SISLANDS_DPM2_SQ_RAMP_STI_SIZE 0x1E
+#define SISLANDS_DPM2_SQ_RAMP_LTI_RATIO 0xF
+
+#define SISLANDS_DPM2_PWREFFICIENCYRATIO_MARGIN 10
+
+#define SISLANDS_VRC_DFLT 0xC000B3
+#define SISLANDS_ULVVOLTAGECHANGEDELAY_DFLT 1687
+#define SISLANDS_CGULVPARAMETER_DFLT 0x00040035
+#define SISLANDS_CGULVCONTROL_DFLT 0x1f007550
+
+#define SI_ASI_DFLT 10000
+#define SI_BSP_DFLT 0x41EB
+#define SI_BSU_DFLT 0x2
+#define SI_AH_DFLT 5
+#define SI_RLP_DFLT 25
+#define SI_RMP_DFLT 65
+#define SI_LHP_DFLT 40
+#define SI_LMP_DFLT 15
+#define SI_TD_DFLT 0
+#define SI_UTC_DFLT_00 0x24
+#define SI_UTC_DFLT_01 0x22
+#define SI_UTC_DFLT_02 0x22
+#define SI_UTC_DFLT_03 0x22
+#define SI_UTC_DFLT_04 0x22
+#define SI_UTC_DFLT_05 0x22
+#define SI_UTC_DFLT_06 0x22
+#define SI_UTC_DFLT_07 0x22
+#define SI_UTC_DFLT_08 0x22
+#define SI_UTC_DFLT_09 0x22
+#define SI_UTC_DFLT_10 0x22
+#define SI_UTC_DFLT_11 0x22
+#define SI_UTC_DFLT_12 0x22
+#define SI_UTC_DFLT_13 0x22
+#define SI_UTC_DFLT_14 0x22
+#define SI_DTC_DFLT_00 0x24
+#define SI_DTC_DFLT_01 0x22
+#define SI_DTC_DFLT_02 0x22
+#define SI_DTC_DFLT_03 0x22
+#define SI_DTC_DFLT_04 0x22
+#define SI_DTC_DFLT_05 0x22
+#define SI_DTC_DFLT_06 0x22
+#define SI_DTC_DFLT_07 0x22
+#define SI_DTC_DFLT_08 0x22
+#define SI_DTC_DFLT_09 0x22
+#define SI_DTC_DFLT_10 0x22
+#define SI_DTC_DFLT_11 0x22
+#define SI_DTC_DFLT_12 0x22
+#define SI_DTC_DFLT_13 0x22
+#define SI_DTC_DFLT_14 0x22
+#define SI_VRC_DFLT 0x0000C003
+#define SI_VOLTAGERESPONSETIME_DFLT 1000
+#define SI_BACKBIASRESPONSETIME_DFLT 1000
+#define SI_VRU_DFLT 0x3
+#define SI_SPLLSTEPTIME_DFLT 0x1000
+#define SI_SPLLSTEPUNIT_DFLT 0x3
+#define SI_TPU_DFLT 0
+#define SI_TPC_DFLT 0x200
+#define SI_SSTU_DFLT 0
+#define SI_SST_DFLT 0x00C8
+#define SI_GICST_DFLT 0x200
+#define SI_FCT_DFLT 0x0400
+#define SI_FCTU_DFLT 0
+#define SI_CTXCGTT3DRPHC_DFLT 0x20
+#define SI_CTXCGTT3DRSDC_DFLT 0x40
+#define SI_VDDC3DOORPHC_DFLT 0x100
+#define SI_VDDC3DOORSDC_DFLT 0x7
+#define SI_VDDC3DOORSU_DFLT 0
+#define SI_MPLLLOCKTIME_DFLT 100
+#define SI_MPLLRESETTIME_DFLT 150
+#define SI_VCOSTEPPCT_DFLT 20
+#define SI_ENDINGVCOSTEPPCT_DFLT 5
+#define SI_REFERENCEDIVIDER_DFLT 4
+
+#define SI_PM_NUMBER_OF_TC 15
+#define SI_PM_NUMBER_OF_SCLKS 20
+#define SI_PM_NUMBER_OF_MCLKS 4
+#define SI_PM_NUMBER_OF_VOLTAGE_LEVELS 4
+#define SI_PM_NUMBER_OF_ACTIVITY_LEVELS 3
+
+/* XXX are these ok? */
+#define SI_TEMP_RANGE_MIN (90 * 1000)
+#define SI_TEMP_RANGE_MAX (120 * 1000)
+
+#define FDO_PWM_MODE_STATIC 1
+#define FDO_PWM_MODE_STATIC_RPM 5
+
+enum ni_dc_cac_level
+{
+ NISLANDS_DCCAC_LEVEL_0 = 0,
+ NISLANDS_DCCAC_LEVEL_1,
+ NISLANDS_DCCAC_LEVEL_2,
+ NISLANDS_DCCAC_LEVEL_3,
+ NISLANDS_DCCAC_LEVEL_4,
+ NISLANDS_DCCAC_LEVEL_5,
+ NISLANDS_DCCAC_LEVEL_6,
+ NISLANDS_DCCAC_LEVEL_7,
+ NISLANDS_DCCAC_MAX_LEVELS
+};
+
+enum si_cac_config_reg_type
+{
+ SISLANDS_CACCONFIG_MMR = 0,
+ SISLANDS_CACCONFIG_CGIND,
+ SISLANDS_CACCONFIG_MAX
+};
+
+enum si_power_level {
+ SI_POWER_LEVEL_LOW = 0,
+ SI_POWER_LEVEL_MEDIUM = 1,
+ SI_POWER_LEVEL_HIGH = 2,
+ SI_POWER_LEVEL_CTXSW = 3,
+};
+
+enum si_td {
+ SI_TD_AUTO,
+ SI_TD_UP,
+ SI_TD_DOWN,
+};
+
+enum si_display_watermark {
+ SI_DISPLAY_WATERMARK_LOW = 0,
+ SI_DISPLAY_WATERMARK_HIGH = 1,
+};
+
+enum si_display_gap
+{
+ SI_PM_DISPLAY_GAP_VBLANK_OR_WM = 0,
+ SI_PM_DISPLAY_GAP_VBLANK = 1,
+ SI_PM_DISPLAY_GAP_WATERMARK = 2,
+ SI_PM_DISPLAY_GAP_IGNORE = 3,
+};
+
+extern const struct amdgpu_ip_block_version si_smu_ip_block;
+
+struct ni_leakage_coeffients
+{
+ u32 at;
+ u32 bt;
+ u32 av;
+ u32 bv;
+ s32 t_slope;
+ s32 t_intercept;
+ u32 t_ref;
+};
+
+struct SMC_Evergreen_MCRegisterAddress
+{
+ uint16_t s0;
+ uint16_t s1;
+};
+
+typedef struct SMC_Evergreen_MCRegisterAddress SMC_Evergreen_MCRegisterAddress;
+
+struct evergreen_mc_reg_entry {
+ u32 mclk_max;
+ u32 mc_data[SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct evergreen_mc_reg_table {
+ u8 last;
+ u8 num_entries;
+ u16 valid_flag;
+ struct evergreen_mc_reg_entry mc_reg_table_entry[MAX_AC_TIMING_ENTRIES];
+ SMC_Evergreen_MCRegisterAddress mc_reg_address[SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct SMC_Evergreen_MCRegisterSet
+{
+ uint32_t value[SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE];
+};
+
+typedef struct SMC_Evergreen_MCRegisterSet SMC_Evergreen_MCRegisterSet;
+
+struct SMC_Evergreen_MCRegisters
+{
+ uint8_t last;
+ uint8_t reserved[3];
+ SMC_Evergreen_MCRegisterAddress address[SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE];
+ SMC_Evergreen_MCRegisterSet data[5];
+};
+
+typedef struct SMC_Evergreen_MCRegisters SMC_Evergreen_MCRegisters;
+
+struct SMC_NIslands_MCRegisterSet
+{
+ uint32_t value[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+typedef struct SMC_NIslands_MCRegisterSet SMC_NIslands_MCRegisterSet;
+
+struct ni_mc_reg_entry {
+ u32 mclk_max;
+ u32 mc_data[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct SMC_NIslands_MCRegisterAddress
+{
+ uint16_t s0;
+ uint16_t s1;
+};
+
+typedef struct SMC_NIslands_MCRegisterAddress SMC_NIslands_MCRegisterAddress;
+
+struct SMC_NIslands_MCRegisters
+{
+ uint8_t last;
+ uint8_t reserved[3];
+ SMC_NIslands_MCRegisterAddress address[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE];
+ SMC_NIslands_MCRegisterSet data[SMC_NISLANDS_MC_REGISTER_ARRAY_SET_COUNT];
+};
+
+typedef struct SMC_NIslands_MCRegisters SMC_NIslands_MCRegisters;
+
+struct evergreen_ulv_param {
+ bool supported;
+ struct rv7xx_pl *pl;
+};
+
+struct evergreen_arb_registers {
+ u32 mc_arb_dram_timing;
+ u32 mc_arb_dram_timing2;
+ u32 mc_arb_rfsh_rate;
+ u32 mc_arb_burst_time;
+};
+
+struct at {
+ u32 rlp;
+ u32 rmp;
+ u32 lhp;
+ u32 lmp;
+};
+
+struct ni_clock_registers {
+ u32 cg_spll_func_cntl;
+ u32 cg_spll_func_cntl_2;
+ u32 cg_spll_func_cntl_3;
+ u32 cg_spll_func_cntl_4;
+ u32 cg_spll_spread_spectrum;
+ u32 cg_spll_spread_spectrum_2;
+ u32 mclk_pwrmgt_cntl;
+ u32 dll_cntl;
+ u32 mpll_ad_func_cntl;
+ u32 mpll_ad_func_cntl_2;
+ u32 mpll_dq_func_cntl;
+ u32 mpll_dq_func_cntl_2;
+ u32 mpll_ss1;
+ u32 mpll_ss2;
+};
+
+struct RV770_SMC_SCLK_VALUE
+{
+ uint32_t vCG_SPLL_FUNC_CNTL;
+ uint32_t vCG_SPLL_FUNC_CNTL_2;
+ uint32_t vCG_SPLL_FUNC_CNTL_3;
+ uint32_t vCG_SPLL_SPREAD_SPECTRUM;
+ uint32_t vCG_SPLL_SPREAD_SPECTRUM_2;
+ uint32_t sclk_value;
+};
+
+typedef struct RV770_SMC_SCLK_VALUE RV770_SMC_SCLK_VALUE;
+
+struct RV770_SMC_MCLK_VALUE
+{
+ uint32_t vMPLL_AD_FUNC_CNTL;
+ uint32_t vMPLL_AD_FUNC_CNTL_2;
+ uint32_t vMPLL_DQ_FUNC_CNTL;
+ uint32_t vMPLL_DQ_FUNC_CNTL_2;
+ uint32_t vMCLK_PWRMGT_CNTL;
+ uint32_t vDLL_CNTL;
+ uint32_t vMPLL_SS;
+ uint32_t vMPLL_SS2;
+ uint32_t mclk_value;
+};
+
+typedef struct RV770_SMC_MCLK_VALUE RV770_SMC_MCLK_VALUE;
+
+
+struct RV730_SMC_MCLK_VALUE
+{
+ uint32_t vMCLK_PWRMGT_CNTL;
+ uint32_t vDLL_CNTL;
+ uint32_t vMPLL_FUNC_CNTL;
+ uint32_t vMPLL_FUNC_CNTL2;
+ uint32_t vMPLL_FUNC_CNTL3;
+ uint32_t vMPLL_SS;
+ uint32_t vMPLL_SS2;
+ uint32_t mclk_value;
+};
+
+typedef struct RV730_SMC_MCLK_VALUE RV730_SMC_MCLK_VALUE;
+
+struct RV770_SMC_VOLTAGE_VALUE
+{
+ uint16_t value;
+ uint8_t index;
+ uint8_t padding;
+};
+
+typedef struct RV770_SMC_VOLTAGE_VALUE RV770_SMC_VOLTAGE_VALUE;
+
+union RV7XX_SMC_MCLK_VALUE
+{
+ RV770_SMC_MCLK_VALUE mclk770;
+ RV730_SMC_MCLK_VALUE mclk730;
+};
+
+typedef union RV7XX_SMC_MCLK_VALUE RV7XX_SMC_MCLK_VALUE, *LPRV7XX_SMC_MCLK_VALUE;
+
+struct RV770_SMC_HW_PERFORMANCE_LEVEL
+{
+ uint8_t arbValue;
+ union{
+ uint8_t seqValue;
+ uint8_t ACIndex;
+ };
+ uint8_t displayWatermark;
+ uint8_t gen2PCIE;
+ uint8_t gen2XSP;
+ uint8_t backbias;
+ uint8_t strobeMode;
+ uint8_t mcFlags;
+ uint32_t aT;
+ uint32_t bSP;
+ RV770_SMC_SCLK_VALUE sclk;
+ RV7XX_SMC_MCLK_VALUE mclk;
+ RV770_SMC_VOLTAGE_VALUE vddc;
+ RV770_SMC_VOLTAGE_VALUE mvdd;
+ RV770_SMC_VOLTAGE_VALUE vddci;
+ uint8_t reserved1;
+ uint8_t reserved2;
+ uint8_t stateFlags;
+ uint8_t padding;
+};
+
+typedef struct RV770_SMC_HW_PERFORMANCE_LEVEL RV770_SMC_HW_PERFORMANCE_LEVEL;
+
+struct RV770_SMC_SWSTATE
+{
+ uint8_t flags;
+ uint8_t padding1;
+ uint8_t padding2;
+ uint8_t padding3;
+ RV770_SMC_HW_PERFORMANCE_LEVEL levels[RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE];
+};
+
+typedef struct RV770_SMC_SWSTATE RV770_SMC_SWSTATE;
+
+struct RV770_SMC_VOLTAGEMASKTABLE
+{
+ uint8_t highMask[RV770_SMC_VOLTAGEMASK_MAX];
+ uint32_t lowMask[RV770_SMC_VOLTAGEMASK_MAX];
+};
+
+typedef struct RV770_SMC_VOLTAGEMASKTABLE RV770_SMC_VOLTAGEMASKTABLE;
+
+struct RV770_SMC_STATETABLE
+{
+ uint8_t thermalProtectType;
+ uint8_t systemFlags;
+ uint8_t maxVDDCIndexInPPTable;
+ uint8_t extraFlags;
+ uint8_t highSMIO[MAX_NO_VREG_STEPS];
+ uint32_t lowSMIO[MAX_NO_VREG_STEPS];
+ RV770_SMC_VOLTAGEMASKTABLE voltageMaskTable;
+ RV770_SMC_SWSTATE initialState;
+ RV770_SMC_SWSTATE ACPIState;
+ RV770_SMC_SWSTATE driverState;
+ RV770_SMC_SWSTATE ULVState;
+};
+
+typedef struct RV770_SMC_STATETABLE RV770_SMC_STATETABLE;
+
+struct vddc_table_entry {
+ u16 vddc;
+ u8 vddc_index;
+ u8 high_smio;
+ u32 low_smio;
+};
+
+struct rv770_clock_registers {
+ u32 cg_spll_func_cntl;
+ u32 cg_spll_func_cntl_2;
+ u32 cg_spll_func_cntl_3;
+ u32 cg_spll_spread_spectrum;
+ u32 cg_spll_spread_spectrum_2;
+ u32 mpll_ad_func_cntl;
+ u32 mpll_ad_func_cntl_2;
+ u32 mpll_dq_func_cntl;
+ u32 mpll_dq_func_cntl_2;
+ u32 mclk_pwrmgt_cntl;
+ u32 dll_cntl;
+ u32 mpll_ss1;
+ u32 mpll_ss2;
+};
+
+struct rv730_clock_registers {
+ u32 cg_spll_func_cntl;
+ u32 cg_spll_func_cntl_2;
+ u32 cg_spll_func_cntl_3;
+ u32 cg_spll_spread_spectrum;
+ u32 cg_spll_spread_spectrum_2;
+ u32 mclk_pwrmgt_cntl;
+ u32 dll_cntl;
+ u32 mpll_func_cntl;
+ u32 mpll_func_cntl2;
+ u32 mpll_func_cntl3;
+ u32 mpll_ss;
+ u32 mpll_ss2;
+};
+
+union r7xx_clock_registers {
+ struct rv770_clock_registers rv770;
+ struct rv730_clock_registers rv730;
+};
+
+struct rv7xx_power_info {
+ /* flags */
+ bool mem_gddr5;
+ bool pcie_gen2;
+ bool dynamic_pcie_gen2;
+ bool acpi_pcie_gen2;
+ bool boot_in_gen2;
+ bool voltage_control; /* vddc */
+ bool mvdd_control;
+ bool sclk_ss;
+ bool mclk_ss;
+ bool dynamic_ss;
+ bool gfx_clock_gating;
+ bool mg_clock_gating;
+ bool mgcgtssm;
+ bool power_gating;
+ bool thermal_protection;
+ bool display_gap;
+ bool dcodt;
+ bool ulps;
+ /* registers */
+ union r7xx_clock_registers clk_regs;
+ u32 s0_vid_lower_smio_cntl;
+ /* voltage */
+ u32 vddc_mask_low;
+ u32 mvdd_mask_low;
+ u32 mvdd_split_frequency;
+ u32 mvdd_low_smio[MAX_NO_OF_MVDD_VALUES];
+ u16 max_vddc;
+ u16 max_vddc_in_table;
+ u16 min_vddc_in_table;
+ struct vddc_table_entry vddc_table[MAX_NO_VREG_STEPS];
+ u8 valid_vddc_entries;
+ /* dc odt */
+ u32 mclk_odt_threshold;
+ u8 odt_value_0[2];
+ u8 odt_value_1[2];
+ /* stored values */
+ u32 boot_sclk;
+ u16 acpi_vddc;
+ u32 ref_div;
+ u32 active_auto_throttle_sources;
+ u32 mclk_stutter_mode_threshold;
+ u32 mclk_strobe_mode_threshold;
+ u32 mclk_edc_enable_threshold;
+ u32 bsp;
+ u32 bsu;
+ u32 pbsp;
+ u32 pbsu;
+ u32 dsp;
+ u32 psp;
+ u32 asi;
+ u32 pasi;
+ u32 vrc;
+ u32 restricted_levels;
+ u32 rlp;
+ u32 rmp;
+ u32 lhp;
+ u32 lmp;
+ /* smc offsets */
+ u16 state_table_start;
+ u16 soft_regs_start;
+ u16 sram_end;
+ /* scratch structs */
+ RV770_SMC_STATETABLE smc_statetable;
+};
+
+struct rv7xx_pl {
+ u32 sclk;
+ u32 mclk;
+ u16 vddc;
+ u16 vddci; /* eg+ only */
+ u32 flags;
+ enum amdgpu_pcie_gen pcie_gen; /* si+ only */
+};
+
+struct rv7xx_ps {
+ struct rv7xx_pl high;
+ struct rv7xx_pl medium;
+ struct rv7xx_pl low;
+ bool dc_compatible;
+};
+
+struct si_ps {
+ u16 performance_level_count;
+ bool dc_compatible;
+ struct rv7xx_pl performance_levels[NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE];
+};
+
+struct ni_mc_reg_table {
+ u8 last;
+ u8 num_entries;
+ u16 valid_flag;
+ struct ni_mc_reg_entry mc_reg_table_entry[MAX_AC_TIMING_ENTRIES];
+ SMC_NIslands_MCRegisterAddress mc_reg_address[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct ni_cac_data
+{
+ struct ni_leakage_coeffients leakage_coefficients;
+ u32 i_leakage;
+ s32 leakage_minimum_temperature;
+ u32 pwr_const;
+ u32 dc_cac_value;
+ u32 bif_cac_value;
+ u32 lkge_pwr;
+ u8 mc_wr_weight;
+ u8 mc_rd_weight;
+ u8 allow_ovrflw;
+ u8 num_win_tdp;
+ u8 l2num_win_tdp;
+ u8 lts_truncate_n;
+};
+
+struct evergreen_power_info {
+ /* must be first! */
+ struct rv7xx_power_info rv7xx;
+ /* flags */
+ bool vddci_control;
+ bool dynamic_ac_timing;
+ bool abm;
+ bool mcls;
+ bool light_sleep;
+ bool memory_transition;
+ bool pcie_performance_request;
+ bool pcie_performance_request_registered;
+ bool sclk_deep_sleep;
+ bool dll_default_on;
+ bool ls_clock_gating;
+ bool smu_uvd_hs;
+ bool uvd_enabled;
+ /* stored values */
+ u16 acpi_vddci;
+ u8 mvdd_high_index;
+ u8 mvdd_low_index;
+ u32 mclk_edc_wr_enable_threshold;
+ struct evergreen_mc_reg_table mc_reg_table;
+ struct atom_voltage_table vddc_voltage_table;
+ struct atom_voltage_table vddci_voltage_table;
+ struct evergreen_arb_registers bootup_arb_registers;
+ struct evergreen_ulv_param ulv;
+ struct at ats[2];
+ /* smc offsets */
+ u16 mc_reg_table_start;
+ struct amdgpu_ps current_rps;
+ struct rv7xx_ps current_ps;
+ struct amdgpu_ps requested_rps;
+ struct rv7xx_ps requested_ps;
+};
+
+struct PP_NIslands_Dpm2PerfLevel
+{
+ uint8_t MaxPS;
+ uint8_t TgtAct;
+ uint8_t MaxPS_StepInc;
+ uint8_t MaxPS_StepDec;
+ uint8_t PSST;
+ uint8_t NearTDPDec;
+ uint8_t AboveSafeInc;
+ uint8_t BelowSafeInc;
+ uint8_t PSDeltaLimit;
+ uint8_t PSDeltaWin;
+ uint8_t Reserved[6];
+};
+
+typedef struct PP_NIslands_Dpm2PerfLevel PP_NIslands_Dpm2PerfLevel;
+
+struct PP_NIslands_DPM2Parameters
+{
+ uint32_t TDPLimit;
+ uint32_t NearTDPLimit;
+ uint32_t SafePowerLimit;
+ uint32_t PowerBoostLimit;
+};
+typedef struct PP_NIslands_DPM2Parameters PP_NIslands_DPM2Parameters;
+
+struct NISLANDS_SMC_SCLK_VALUE
+{
+ uint32_t vCG_SPLL_FUNC_CNTL;
+ uint32_t vCG_SPLL_FUNC_CNTL_2;
+ uint32_t vCG_SPLL_FUNC_CNTL_3;
+ uint32_t vCG_SPLL_FUNC_CNTL_4;
+ uint32_t vCG_SPLL_SPREAD_SPECTRUM;
+ uint32_t vCG_SPLL_SPREAD_SPECTRUM_2;
+ uint32_t sclk_value;
+};
+
+typedef struct NISLANDS_SMC_SCLK_VALUE NISLANDS_SMC_SCLK_VALUE;
+
+struct NISLANDS_SMC_MCLK_VALUE
+{
+ uint32_t vMPLL_FUNC_CNTL;
+ uint32_t vMPLL_FUNC_CNTL_1;
+ uint32_t vMPLL_FUNC_CNTL_2;
+ uint32_t vMPLL_AD_FUNC_CNTL;
+ uint32_t vMPLL_AD_FUNC_CNTL_2;
+ uint32_t vMPLL_DQ_FUNC_CNTL;
+ uint32_t vMPLL_DQ_FUNC_CNTL_2;
+ uint32_t vMCLK_PWRMGT_CNTL;
+ uint32_t vDLL_CNTL;
+ uint32_t vMPLL_SS;
+ uint32_t vMPLL_SS2;
+ uint32_t mclk_value;
+};
+
+typedef struct NISLANDS_SMC_MCLK_VALUE NISLANDS_SMC_MCLK_VALUE;
+
+struct NISLANDS_SMC_VOLTAGE_VALUE
+{
+ uint16_t value;
+ uint8_t index;
+ uint8_t padding;
+};
+
+typedef struct NISLANDS_SMC_VOLTAGE_VALUE NISLANDS_SMC_VOLTAGE_VALUE;
+
+struct NISLANDS_SMC_HW_PERFORMANCE_LEVEL
+{
+ uint8_t arbValue;
+ uint8_t ACIndex;
+ uint8_t displayWatermark;
+ uint8_t gen2PCIE;
+ uint8_t reserved1;
+ uint8_t reserved2;
+ uint8_t strobeMode;
+ uint8_t mcFlags;
+ uint32_t aT;
+ uint32_t bSP;
+ NISLANDS_SMC_SCLK_VALUE sclk;
+ NISLANDS_SMC_MCLK_VALUE mclk;
+ NISLANDS_SMC_VOLTAGE_VALUE vddc;
+ NISLANDS_SMC_VOLTAGE_VALUE mvdd;
+ NISLANDS_SMC_VOLTAGE_VALUE vddci;
+ NISLANDS_SMC_VOLTAGE_VALUE std_vddc;
+ uint32_t powergate_en;
+ uint8_t hUp;
+ uint8_t hDown;
+ uint8_t stateFlags;
+ uint8_t arbRefreshState;
+ uint32_t SQPowerThrottle;
+ uint32_t SQPowerThrottle_2;
+ uint32_t reserved[2];
+ PP_NIslands_Dpm2PerfLevel dpm2;
+};
+
+typedef struct NISLANDS_SMC_HW_PERFORMANCE_LEVEL NISLANDS_SMC_HW_PERFORMANCE_LEVEL;
+
+struct NISLANDS_SMC_SWSTATE
+{
+ uint8_t flags;
+ uint8_t levelCount;
+ uint8_t padding2;
+ uint8_t padding3;
+ NISLANDS_SMC_HW_PERFORMANCE_LEVEL levels[];
+};
+
+typedef struct NISLANDS_SMC_SWSTATE NISLANDS_SMC_SWSTATE;
+
+struct NISLANDS_SMC_VOLTAGEMASKTABLE
+{
+ uint8_t highMask[NISLANDS_SMC_VOLTAGEMASK_MAX];
+ uint32_t lowMask[NISLANDS_SMC_VOLTAGEMASK_MAX];
+};
+
+typedef struct NISLANDS_SMC_VOLTAGEMASKTABLE NISLANDS_SMC_VOLTAGEMASKTABLE;
+
+#define NISLANDS_MAX_NO_VREG_STEPS 32
+
+struct NISLANDS_SMC_STATETABLE
+{
+ uint8_t thermalProtectType;
+ uint8_t systemFlags;
+ uint8_t maxVDDCIndexInPPTable;
+ uint8_t extraFlags;
+ uint8_t highSMIO[NISLANDS_MAX_NO_VREG_STEPS];
+ uint32_t lowSMIO[NISLANDS_MAX_NO_VREG_STEPS];
+ NISLANDS_SMC_VOLTAGEMASKTABLE voltageMaskTable;
+ PP_NIslands_DPM2Parameters dpm2Params;
+ NISLANDS_SMC_SWSTATE initialState;
+ NISLANDS_SMC_SWSTATE ACPIState;
+ NISLANDS_SMC_SWSTATE ULVState;
+ NISLANDS_SMC_SWSTATE driverState;
+ NISLANDS_SMC_HW_PERFORMANCE_LEVEL dpmLevels[NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1];
+};
+
+typedef struct NISLANDS_SMC_STATETABLE NISLANDS_SMC_STATETABLE;
+
+struct ni_power_info {
+ /* must be first! */
+ struct evergreen_power_info eg;
+ struct ni_clock_registers clock_registers;
+ struct ni_mc_reg_table mc_reg_table;
+ u32 mclk_rtt_mode_threshold;
+ /* flags */
+ bool use_power_boost_limit;
+ bool support_cac_long_term_average;
+ bool cac_enabled;
+ bool cac_configuration_required;
+ bool driver_calculate_cac_leakage;
+ bool pc_enabled;
+ bool enable_power_containment;
+ bool enable_cac;
+ bool enable_sq_ramping;
+ /* smc offsets */
+ u16 arb_table_start;
+ u16 fan_table_start;
+ u16 cac_table_start;
+ u16 spll_table_start;
+ /* CAC stuff */
+ struct ni_cac_data cac_data;
+ u32 dc_cac_table[NISLANDS_DCCAC_MAX_LEVELS];
+ const struct ni_cac_weights *cac_weights;
+ u8 lta_window_size;
+ u8 lts_truncate;
+ struct si_ps current_ps;
+ struct si_ps requested_ps;
+ /* scratch structs */
+ SMC_NIslands_MCRegisters smc_mc_reg_table;
+ NISLANDS_SMC_STATETABLE smc_statetable;
+};
+
+struct si_cac_config_reg
+{
+ u32 offset;
+ u32 mask;
+ u32 shift;
+ u32 value;
+ enum si_cac_config_reg_type type;
+};
+
+struct si_powertune_data
+{
+ u32 cac_window;
+ u32 l2_lta_window_size_default;
+ u8 lts_truncate_default;
+ u8 shift_n_default;
+ u8 operating_temp;
+ struct ni_leakage_coeffients leakage_coefficients;
+ u32 fixed_kt;
+ u32 lkge_lut_v0_percent;
+ u8 dc_cac[NISLANDS_DCCAC_MAX_LEVELS];
+ bool enable_powertune_by_default;
+};
+
+struct si_dyn_powertune_data
+{
+ u32 cac_leakage;
+ s32 leakage_minimum_temperature;
+ u32 wintime;
+ u32 l2_lta_window_size;
+ u8 lts_truncate;
+ u8 shift_n;
+ u8 dc_pwr_value;
+ bool disable_uvd_powertune;
+};
+
+struct si_dte_data
+{
+ u32 tau[SMC_SISLANDS_DTE_MAX_FILTER_STAGES];
+ u32 r[SMC_SISLANDS_DTE_MAX_FILTER_STAGES];
+ u32 k;
+ u32 t0;
+ u32 max_t;
+ u8 window_size;
+ u8 temp_select;
+ u8 dte_mode;
+ u8 tdep_count;
+ u8 t_limits[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+ u32 tdep_tau[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+ u32 tdep_r[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+ u32 t_threshold;
+ bool enable_dte_by_default;
+};
+
+struct si_clock_registers {
+ u32 cg_spll_func_cntl;
+ u32 cg_spll_func_cntl_2;
+ u32 cg_spll_func_cntl_3;
+ u32 cg_spll_func_cntl_4;
+ u32 cg_spll_spread_spectrum;
+ u32 cg_spll_spread_spectrum_2;
+ u32 dll_cntl;
+ u32 mclk_pwrmgt_cntl;
+ u32 mpll_ad_func_cntl;
+ u32 mpll_dq_func_cntl;
+ u32 mpll_func_cntl;
+ u32 mpll_func_cntl_1;
+ u32 mpll_func_cntl_2;
+ u32 mpll_ss1;
+ u32 mpll_ss2;
+};
+
+struct si_mc_reg_entry {
+ u32 mclk_max;
+ u32 mc_data[SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct si_mc_reg_table {
+ u8 last;
+ u8 num_entries;
+ u16 valid_flag;
+ struct si_mc_reg_entry mc_reg_table_entry[MAX_AC_TIMING_ENTRIES];
+ SMC_NIslands_MCRegisterAddress mc_reg_address[SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct si_leakage_voltage_entry
+{
+ u16 voltage;
+ u16 leakage_index;
+};
+
+struct si_leakage_voltage
+{
+ u16 count;
+ struct si_leakage_voltage_entry entries[SISLANDS_MAX_LEAKAGE_COUNT];
+};
+
+
+struct si_ulv_param {
+ bool supported;
+ u32 cg_ulv_control;
+ u32 cg_ulv_parameter;
+ u32 volt_change_delay;
+ struct rv7xx_pl pl;
+ bool one_pcie_lane_in_ulv;
+};
+
+struct si_power_info {
+ /* must be first! */
+ struct ni_power_info ni;
+ struct si_clock_registers clock_registers;
+ struct si_mc_reg_table mc_reg_table;
+ struct atom_voltage_table mvdd_voltage_table;
+ struct atom_voltage_table vddc_phase_shed_table;
+ struct si_leakage_voltage leakage_voltage;
+ u16 mvdd_bootup_value;
+ struct si_ulv_param ulv;
+ u32 max_cu;
+ /* pcie gen */
+ enum amdgpu_pcie_gen force_pcie_gen;
+ enum amdgpu_pcie_gen boot_pcie_gen;
+ enum amdgpu_pcie_gen acpi_pcie_gen;
+ u32 sys_pcie_mask;
+ /* flags */
+ bool enable_dte;
+ bool enable_ppm;
+ bool vddc_phase_shed_control;
+ bool pspp_notify_required;
+ bool sclk_deep_sleep_above_low;
+ bool voltage_control_svi2;
+ bool vddci_control_svi2;
+ /* smc offsets */
+ u32 sram_end;
+ u32 state_table_start;
+ u32 soft_regs_start;
+ u32 mc_reg_table_start;
+ u32 arb_table_start;
+ u32 cac_table_start;
+ u32 dte_table_start;
+ u32 spll_table_start;
+ u32 papm_cfg_table_start;
+ u32 fan_table_start;
+ /* CAC stuff */
+ const struct si_cac_config_reg *cac_weights;
+ const struct si_cac_config_reg *lcac_config;
+ const struct si_cac_config_reg *cac_override;
+ const struct si_powertune_data *powertune_data;
+ struct si_dyn_powertune_data dyn_powertune_data;
+ /* DTE stuff */
+ struct si_dte_data dte_data;
+ /* scratch structs */
+ SMC_SIslands_MCRegisters smc_mc_reg_table;
+ SISLANDS_SMC_STATETABLE smc_statetable;
+ PP_SIslands_PAPMParameters papm_parm;
+ /* SVI2 */
+ u8 svd_gpio_id;
+ u8 svc_gpio_id;
+ /* fan control */
+ bool fan_ctrl_is_in_default_mode;
+ u32 t_min;
+ u32 fan_ctrl_default_mode;
+ bool fan_is_controlled_by_smc;
+};
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/si_smc.c b/drivers/gpu/drm/amd/pm/powerplay/si_smc.c
new file mode 100644
index 000000000000..8f994ffa9cd1
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/si_smc.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Alex Deucher
+ */
+
+#include <linux/firmware.h>
+
+#include "amdgpu.h"
+#include "sid.h"
+#include "ppsmc.h"
+#include "amdgpu_ucode.h"
+#include "sislands_smc.h"
+
+static int si_set_smc_sram_address(struct amdgpu_device *adev,
+ u32 smc_address, u32 limit)
+{
+ if (smc_address & 3)
+ return -EINVAL;
+ if ((smc_address + 3) > limit)
+ return -EINVAL;
+
+ WREG32(SMC_IND_INDEX_0, smc_address);
+ WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
+
+ return 0;
+}
+
+int amdgpu_si_copy_bytes_to_smc(struct amdgpu_device *adev,
+ u32 smc_start_address,
+ const u8 *src, u32 byte_count, u32 limit)
+{
+ unsigned long flags;
+ int ret = 0;
+ u32 data, original_data, addr, extra_shift;
+
+ if (smc_start_address & 3)
+ return -EINVAL;
+ if ((smc_start_address + byte_count) > limit)
+ return -EINVAL;
+
+ addr = smc_start_address;
+
+ spin_lock_irqsave(&adev->smc_idx_lock, flags);
+ while (byte_count >= 4) {
+ /* SMC address space is BE */
+ data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
+
+ ret = si_set_smc_sram_address(adev, addr, limit);
+ if (ret)
+ goto done;
+
+ WREG32(SMC_IND_DATA_0, data);
+
+ src += 4;
+ byte_count -= 4;
+ addr += 4;
+ }
+
+ /* RMW for the final bytes */
+ if (byte_count > 0) {
+ data = 0;
+
+ ret = si_set_smc_sram_address(adev, addr, limit);
+ if (ret)
+ goto done;
+
+ original_data = RREG32(SMC_IND_DATA_0);
+ extra_shift = 8 * (4 - byte_count);
+
+ while (byte_count > 0) {
+ /* SMC address space is BE */
+ data = (data << 8) + *src++;
+ byte_count--;
+ }
+
+ data <<= extra_shift;
+ data |= (original_data & ~((~0UL) << extra_shift));
+
+ ret = si_set_smc_sram_address(adev, addr, limit);
+ if (ret)
+ goto done;
+
+ WREG32(SMC_IND_DATA_0, data);
+ }
+
+done:
+ spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
+
+ return ret;
+}
+
+void amdgpu_si_start_smc(struct amdgpu_device *adev)
+{
+ u32 tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
+
+ tmp &= ~RST_REG;
+
+ WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
+}
+
+void amdgpu_si_reset_smc(struct amdgpu_device *adev)
+{
+ u32 tmp;
+
+ RREG32(CB_CGTT_SCLK_CTRL);
+ RREG32(CB_CGTT_SCLK_CTRL);
+ RREG32(CB_CGTT_SCLK_CTRL);
+ RREG32(CB_CGTT_SCLK_CTRL);
+
+ tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL) |
+ RST_REG;
+ WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
+}
+
+int amdgpu_si_program_jump_on_start(struct amdgpu_device *adev)
+{
+ static const u8 data[] = { 0x0E, 0x00, 0x40, 0x40 };
+
+ return amdgpu_si_copy_bytes_to_smc(adev, 0x0, data, 4, sizeof(data)+1);
+}
+
+void amdgpu_si_smc_clock(struct amdgpu_device *adev, bool enable)
+{
+ u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
+
+ if (enable)
+ tmp &= ~CK_DISABLE;
+ else
+ tmp |= CK_DISABLE;
+
+ WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp);
+}
+
+bool amdgpu_si_is_smc_running(struct amdgpu_device *adev)
+{
+ u32 rst = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
+ u32 clk = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
+
+ if (!(rst & RST_REG) && !(clk & CK_DISABLE))
+ return true;
+
+ return false;
+}
+
+PPSMC_Result amdgpu_si_send_msg_to_smc(struct amdgpu_device *adev,
+ PPSMC_Msg msg)
+{
+ u32 tmp;
+ int i;
+
+ if (!amdgpu_si_is_smc_running(adev))
+ return PPSMC_Result_Failed;
+
+ WREG32(SMC_MESSAGE_0, msg);
+
+ for (i = 0; i < adev->usec_timeout; i++) {
+ tmp = RREG32(SMC_RESP_0);
+ if (tmp != 0)
+ break;
+ udelay(1);
+ }
+
+ return (PPSMC_Result)RREG32(SMC_RESP_0);
+}
+
+PPSMC_Result amdgpu_si_wait_for_smc_inactive(struct amdgpu_device *adev)
+{
+ u32 tmp;
+ int i;
+
+ if (!amdgpu_si_is_smc_running(adev))
+ return PPSMC_Result_OK;
+
+ for (i = 0; i < adev->usec_timeout; i++) {
+ tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
+ if ((tmp & CKEN) == 0)
+ break;
+ udelay(1);
+ }
+
+ return PPSMC_Result_OK;
+}
+
+int amdgpu_si_load_smc_ucode(struct amdgpu_device *adev, u32 limit)
+{
+ const struct smc_firmware_header_v1_0 *hdr;
+ unsigned long flags;
+ u32 ucode_start_address;
+ u32 ucode_size;
+ const u8 *src;
+ u32 data;
+
+ if (!adev->pm.fw)
+ return -EINVAL;
+
+ hdr = (const struct smc_firmware_header_v1_0 *)adev->pm.fw->data;
+
+ amdgpu_ucode_print_smc_hdr(&hdr->header);
+
+ adev->pm.fw_version = le32_to_cpu(hdr->header.ucode_version);
+ ucode_start_address = le32_to_cpu(hdr->ucode_start_addr);
+ ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes);
+ src = (const u8 *)
+ (adev->pm.fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+ if (ucode_size & 3)
+ return -EINVAL;
+
+ spin_lock_irqsave(&adev->smc_idx_lock, flags);
+ WREG32(SMC_IND_INDEX_0, ucode_start_address);
+ WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0);
+ while (ucode_size >= 4) {
+ /* SMC address space is BE */
+ data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
+
+ WREG32(SMC_IND_DATA_0, data);
+
+ src += 4;
+ ucode_size -= 4;
+ }
+ WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
+ spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
+
+ return 0;
+}
+
+int amdgpu_si_read_smc_sram_dword(struct amdgpu_device *adev, u32 smc_address,
+ u32 *value, u32 limit)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&adev->smc_idx_lock, flags);
+ ret = si_set_smc_sram_address(adev, smc_address, limit);
+ if (ret == 0)
+ *value = RREG32(SMC_IND_DATA_0);
+ spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
+
+ return ret;
+}
+
+int amdgpu_si_write_smc_sram_dword(struct amdgpu_device *adev, u32 smc_address,
+ u32 value, u32 limit)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&adev->smc_idx_lock, flags);
+ ret = si_set_smc_sram_address(adev, smc_address, limit);
+ if (ret == 0)
+ WREG32(SMC_IND_DATA_0, value);
+ spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/sislands_smc.h b/drivers/gpu/drm/amd/pm/powerplay/sislands_smc.h
new file mode 100644
index 000000000000..d2930eceaf3c
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/sislands_smc.h
@@ -0,0 +1,423 @@
+/*
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 PP_SISLANDS_SMC_H
+#define PP_SISLANDS_SMC_H
+
+#include "ppsmc.h"
+
+#pragma pack(push, 1)
+
+#define SISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE 16
+
+struct PP_SIslands_Dpm2PerfLevel
+{
+ uint8_t MaxPS;
+ uint8_t TgtAct;
+ uint8_t MaxPS_StepInc;
+ uint8_t MaxPS_StepDec;
+ uint8_t PSSamplingTime;
+ uint8_t NearTDPDec;
+ uint8_t AboveSafeInc;
+ uint8_t BelowSafeInc;
+ uint8_t PSDeltaLimit;
+ uint8_t PSDeltaWin;
+ uint16_t PwrEfficiencyRatio;
+ uint8_t Reserved[4];
+};
+
+typedef struct PP_SIslands_Dpm2PerfLevel PP_SIslands_Dpm2PerfLevel;
+
+struct PP_SIslands_DPM2Status
+{
+ uint32_t dpm2Flags;
+ uint8_t CurrPSkip;
+ uint8_t CurrPSkipPowerShift;
+ uint8_t CurrPSkipTDP;
+ uint8_t CurrPSkipOCP;
+ uint8_t MaxSPLLIndex;
+ uint8_t MinSPLLIndex;
+ uint8_t CurrSPLLIndex;
+ uint8_t InfSweepMode;
+ uint8_t InfSweepDir;
+ uint8_t TDPexceeded;
+ uint8_t reserved;
+ uint8_t SwitchDownThreshold;
+ uint32_t SwitchDownCounter;
+ uint32_t SysScalingFactor;
+};
+
+typedef struct PP_SIslands_DPM2Status PP_SIslands_DPM2Status;
+
+struct PP_SIslands_DPM2Parameters
+{
+ uint32_t TDPLimit;
+ uint32_t NearTDPLimit;
+ uint32_t SafePowerLimit;
+ uint32_t PowerBoostLimit;
+ uint32_t MinLimitDelta;
+};
+typedef struct PP_SIslands_DPM2Parameters PP_SIslands_DPM2Parameters;
+
+struct PP_SIslands_PAPMStatus
+{
+ uint32_t EstimatedDGPU_T;
+ uint32_t EstimatedDGPU_P;
+ uint32_t EstimatedAPU_T;
+ uint32_t EstimatedAPU_P;
+ uint8_t dGPU_T_Limit_Exceeded;
+ uint8_t reserved[3];
+};
+typedef struct PP_SIslands_PAPMStatus PP_SIslands_PAPMStatus;
+
+struct PP_SIslands_PAPMParameters
+{
+ uint32_t NearTDPLimitTherm;
+ uint32_t NearTDPLimitPAPM;
+ uint32_t PlatformPowerLimit;
+ uint32_t dGPU_T_Limit;
+ uint32_t dGPU_T_Warning;
+ uint32_t dGPU_T_Hysteresis;
+};
+typedef struct PP_SIslands_PAPMParameters PP_SIslands_PAPMParameters;
+
+struct SISLANDS_SMC_SCLK_VALUE
+{
+ uint32_t vCG_SPLL_FUNC_CNTL;
+ uint32_t vCG_SPLL_FUNC_CNTL_2;
+ uint32_t vCG_SPLL_FUNC_CNTL_3;
+ uint32_t vCG_SPLL_FUNC_CNTL_4;
+ uint32_t vCG_SPLL_SPREAD_SPECTRUM;
+ uint32_t vCG_SPLL_SPREAD_SPECTRUM_2;
+ uint32_t sclk_value;
+};
+
+typedef struct SISLANDS_SMC_SCLK_VALUE SISLANDS_SMC_SCLK_VALUE;
+
+struct SISLANDS_SMC_MCLK_VALUE
+{
+ uint32_t vMPLL_FUNC_CNTL;
+ uint32_t vMPLL_FUNC_CNTL_1;
+ uint32_t vMPLL_FUNC_CNTL_2;
+ uint32_t vMPLL_AD_FUNC_CNTL;
+ uint32_t vMPLL_DQ_FUNC_CNTL;
+ uint32_t vMCLK_PWRMGT_CNTL;
+ uint32_t vDLL_CNTL;
+ uint32_t vMPLL_SS;
+ uint32_t vMPLL_SS2;
+ uint32_t mclk_value;
+};
+
+typedef struct SISLANDS_SMC_MCLK_VALUE SISLANDS_SMC_MCLK_VALUE;
+
+struct SISLANDS_SMC_VOLTAGE_VALUE
+{
+ uint16_t value;
+ uint8_t index;
+ uint8_t phase_settings;
+};
+
+typedef struct SISLANDS_SMC_VOLTAGE_VALUE SISLANDS_SMC_VOLTAGE_VALUE;
+
+struct SISLANDS_SMC_HW_PERFORMANCE_LEVEL
+{
+ uint8_t ACIndex;
+ uint8_t displayWatermark;
+ uint8_t gen2PCIE;
+ uint8_t UVDWatermark;
+ uint8_t VCEWatermark;
+ uint8_t strobeMode;
+ uint8_t mcFlags;
+ uint8_t padding;
+ uint32_t aT;
+ uint32_t bSP;
+ SISLANDS_SMC_SCLK_VALUE sclk;
+ SISLANDS_SMC_MCLK_VALUE mclk;
+ SISLANDS_SMC_VOLTAGE_VALUE vddc;
+ SISLANDS_SMC_VOLTAGE_VALUE mvdd;
+ SISLANDS_SMC_VOLTAGE_VALUE vddci;
+ SISLANDS_SMC_VOLTAGE_VALUE std_vddc;
+ uint8_t hysteresisUp;
+ uint8_t hysteresisDown;
+ uint8_t stateFlags;
+ uint8_t arbRefreshState;
+ uint32_t SQPowerThrottle;
+ uint32_t SQPowerThrottle_2;
+ uint32_t MaxPoweredUpCU;
+ SISLANDS_SMC_VOLTAGE_VALUE high_temp_vddc;
+ SISLANDS_SMC_VOLTAGE_VALUE low_temp_vddc;
+ uint32_t reserved[2];
+ PP_SIslands_Dpm2PerfLevel dpm2;
+};
+
+#define SISLANDS_SMC_STROBE_RATIO 0x0F
+#define SISLANDS_SMC_STROBE_ENABLE 0x10
+
+#define SISLANDS_SMC_MC_EDC_RD_FLAG 0x01
+#define SISLANDS_SMC_MC_EDC_WR_FLAG 0x02
+#define SISLANDS_SMC_MC_RTT_ENABLE 0x04
+#define SISLANDS_SMC_MC_STUTTER_EN 0x08
+#define SISLANDS_SMC_MC_PG_EN 0x10
+
+typedef struct SISLANDS_SMC_HW_PERFORMANCE_LEVEL SISLANDS_SMC_HW_PERFORMANCE_LEVEL;
+
+struct SISLANDS_SMC_SWSTATE
+{
+ uint8_t flags;
+ uint8_t levelCount;
+ uint8_t padding2;
+ uint8_t padding3;
+ SISLANDS_SMC_HW_PERFORMANCE_LEVEL levels[1];
+};
+
+typedef struct SISLANDS_SMC_SWSTATE SISLANDS_SMC_SWSTATE;
+
+#define SISLANDS_SMC_VOLTAGEMASK_VDDC 0
+#define SISLANDS_SMC_VOLTAGEMASK_MVDD 1
+#define SISLANDS_SMC_VOLTAGEMASK_VDDCI 2
+#define SISLANDS_SMC_VOLTAGEMASK_VDDC_PHASE_SHEDDING 3
+#define SISLANDS_SMC_VOLTAGEMASK_MAX 4
+
+struct SISLANDS_SMC_VOLTAGEMASKTABLE
+{
+ uint32_t lowMask[SISLANDS_SMC_VOLTAGEMASK_MAX];
+};
+
+typedef struct SISLANDS_SMC_VOLTAGEMASKTABLE SISLANDS_SMC_VOLTAGEMASKTABLE;
+
+#define SISLANDS_MAX_NO_VREG_STEPS 32
+
+struct SISLANDS_SMC_STATETABLE
+{
+ uint8_t thermalProtectType;
+ uint8_t systemFlags;
+ uint8_t maxVDDCIndexInPPTable;
+ uint8_t extraFlags;
+ uint32_t lowSMIO[SISLANDS_MAX_NO_VREG_STEPS];
+ SISLANDS_SMC_VOLTAGEMASKTABLE voltageMaskTable;
+ SISLANDS_SMC_VOLTAGEMASKTABLE phaseMaskTable;
+ PP_SIslands_DPM2Parameters dpm2Params;
+ SISLANDS_SMC_SWSTATE initialState;
+ SISLANDS_SMC_SWSTATE ACPIState;
+ SISLANDS_SMC_SWSTATE ULVState;
+ SISLANDS_SMC_SWSTATE driverState;
+ SISLANDS_SMC_HW_PERFORMANCE_LEVEL dpmLevels[SISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1];
+};
+
+typedef struct SISLANDS_SMC_STATETABLE SISLANDS_SMC_STATETABLE;
+
+#define SI_SMC_SOFT_REGISTER_mclk_chg_timeout 0x0
+#define SI_SMC_SOFT_REGISTER_delay_vreg 0xC
+#define SI_SMC_SOFT_REGISTER_delay_acpi 0x28
+#define SI_SMC_SOFT_REGISTER_seq_index 0x5C
+#define SI_SMC_SOFT_REGISTER_mvdd_chg_time 0x60
+#define SI_SMC_SOFT_REGISTER_mclk_switch_lim 0x70
+#define SI_SMC_SOFT_REGISTER_watermark_threshold 0x78
+#define SI_SMC_SOFT_REGISTER_phase_shedding_delay 0x88
+#define SI_SMC_SOFT_REGISTER_ulv_volt_change_delay 0x8C
+#define SI_SMC_SOFT_REGISTER_mc_block_delay 0x98
+#define SI_SMC_SOFT_REGISTER_ticks_per_us 0xA8
+#define SI_SMC_SOFT_REGISTER_crtc_index 0xC4
+#define SI_SMC_SOFT_REGISTER_mclk_change_block_cp_min 0xC8
+#define SI_SMC_SOFT_REGISTER_mclk_change_block_cp_max 0xCC
+#define SI_SMC_SOFT_REGISTER_non_ulv_pcie_link_width 0xF4
+#define SI_SMC_SOFT_REGISTER_tdr_is_about_to_happen 0xFC
+#define SI_SMC_SOFT_REGISTER_vr_hot_gpio 0x100
+#define SI_SMC_SOFT_REGISTER_svi_rework_plat_type 0x118
+#define SI_SMC_SOFT_REGISTER_svi_rework_gpio_id_svd 0x11c
+#define SI_SMC_SOFT_REGISTER_svi_rework_gpio_id_svc 0x120
+
+struct PP_SIslands_FanTable
+{
+ uint8_t fdo_mode;
+ uint8_t padding;
+ int16_t temp_min;
+ int16_t temp_med;
+ int16_t temp_max;
+ int16_t slope1;
+ int16_t slope2;
+ int16_t fdo_min;
+ int16_t hys_up;
+ int16_t hys_down;
+ int16_t hys_slope;
+ int16_t temp_resp_lim;
+ int16_t temp_curr;
+ int16_t slope_curr;
+ int16_t pwm_curr;
+ uint32_t refresh_period;
+ int16_t fdo_max;
+ uint8_t temp_src;
+ int8_t padding2;
+};
+
+typedef struct PP_SIslands_FanTable PP_SIslands_FanTable;
+
+#define SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES 16
+#define SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES 32
+
+#define SMC_SISLANDS_SCALE_I 7
+#define SMC_SISLANDS_SCALE_R 12
+
+struct PP_SIslands_CacConfig
+{
+ uint16_t cac_lkge_lut[SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES][SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES];
+ uint32_t lkge_lut_V0;
+ uint32_t lkge_lut_Vstep;
+ uint32_t WinTime;
+ uint32_t R_LL;
+ uint32_t calculation_repeats;
+ uint32_t l2numWin_TDP;
+ uint32_t dc_cac;
+ uint8_t lts_truncate_n;
+ uint8_t SHIFT_N;
+ uint8_t log2_PG_LKG_SCALE;
+ uint8_t cac_temp;
+ uint32_t lkge_lut_T0;
+ uint32_t lkge_lut_Tstep;
+};
+
+typedef struct PP_SIslands_CacConfig PP_SIslands_CacConfig;
+
+#define SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE 16
+#define SMC_SISLANDS_MC_REGISTER_ARRAY_SET_COUNT 20
+
+struct SMC_SIslands_MCRegisterAddress
+{
+ uint16_t s0;
+ uint16_t s1;
+};
+
+typedef struct SMC_SIslands_MCRegisterAddress SMC_SIslands_MCRegisterAddress;
+
+struct SMC_SIslands_MCRegisterSet
+{
+ uint32_t value[SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+typedef struct SMC_SIslands_MCRegisterSet SMC_SIslands_MCRegisterSet;
+
+struct SMC_SIslands_MCRegisters
+{
+ uint8_t last;
+ uint8_t reserved[3];
+ SMC_SIslands_MCRegisterAddress address[SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE];
+ SMC_SIslands_MCRegisterSet data[SMC_SISLANDS_MC_REGISTER_ARRAY_SET_COUNT];
+};
+
+typedef struct SMC_SIslands_MCRegisters SMC_SIslands_MCRegisters;
+
+struct SMC_SIslands_MCArbDramTimingRegisterSet
+{
+ uint32_t mc_arb_dram_timing;
+ uint32_t mc_arb_dram_timing2;
+ uint8_t mc_arb_rfsh_rate;
+ uint8_t mc_arb_burst_time;
+ uint8_t padding[2];
+};
+
+typedef struct SMC_SIslands_MCArbDramTimingRegisterSet SMC_SIslands_MCArbDramTimingRegisterSet;
+
+struct SMC_SIslands_MCArbDramTimingRegisters
+{
+ uint8_t arb_current;
+ uint8_t reserved[3];
+ SMC_SIslands_MCArbDramTimingRegisterSet data[16];
+};
+
+typedef struct SMC_SIslands_MCArbDramTimingRegisters SMC_SIslands_MCArbDramTimingRegisters;
+
+struct SMC_SISLANDS_SPLL_DIV_TABLE
+{
+ uint32_t freq[256];
+ uint32_t ss[256];
+};
+
+#define SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_MASK 0x01ffffff
+#define SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_SHIFT 0
+#define SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_MASK 0xfe000000
+#define SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT 25
+#define SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_MASK 0x000fffff
+#define SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT 0
+#define SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_MASK 0xfff00000
+#define SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT 20
+
+typedef struct SMC_SISLANDS_SPLL_DIV_TABLE SMC_SISLANDS_SPLL_DIV_TABLE;
+
+#define SMC_SISLANDS_DTE_MAX_FILTER_STAGES 5
+
+#define SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE 16
+
+struct Smc_SIslands_DTE_Configuration
+{
+ uint32_t tau[SMC_SISLANDS_DTE_MAX_FILTER_STAGES];
+ uint32_t R[SMC_SISLANDS_DTE_MAX_FILTER_STAGES];
+ uint32_t K;
+ uint32_t T0;
+ uint32_t MaxT;
+ uint8_t WindowSize;
+ uint8_t Tdep_count;
+ uint8_t temp_select;
+ uint8_t DTE_mode;
+ uint8_t T_limits[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+ uint32_t Tdep_tau[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+ uint32_t Tdep_R[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+ uint32_t Tthreshold;
+};
+
+typedef struct Smc_SIslands_DTE_Configuration Smc_SIslands_DTE_Configuration;
+
+#define SMC_SISLANDS_DTE_STATUS_FLAG_DTE_ON 1
+
+#define SISLANDS_SMC_FIRMWARE_HEADER_LOCATION 0x10000
+
+#define SISLANDS_SMC_FIRMWARE_HEADER_version 0x0
+#define SISLANDS_SMC_FIRMWARE_HEADER_flags 0x4
+#define SISLANDS_SMC_FIRMWARE_HEADER_softRegisters 0xC
+#define SISLANDS_SMC_FIRMWARE_HEADER_stateTable 0x10
+#define SISLANDS_SMC_FIRMWARE_HEADER_fanTable 0x14
+#define SISLANDS_SMC_FIRMWARE_HEADER_CacConfigTable 0x18
+#define SISLANDS_SMC_FIRMWARE_HEADER_mcRegisterTable 0x24
+#define SISLANDS_SMC_FIRMWARE_HEADER_mcArbDramAutoRefreshTable 0x30
+#define SISLANDS_SMC_FIRMWARE_HEADER_spllTable 0x38
+#define SISLANDS_SMC_FIRMWARE_HEADER_DteConfiguration 0x40
+#define SISLANDS_SMC_FIRMWARE_HEADER_PAPMParameters 0x48
+
+#pragma pack(pop)
+
+int amdgpu_si_copy_bytes_to_smc(struct amdgpu_device *adev,
+ u32 smc_start_address,
+ const u8 *src, u32 byte_count, u32 limit);
+void amdgpu_si_start_smc(struct amdgpu_device *adev);
+void amdgpu_si_reset_smc(struct amdgpu_device *adev);
+int amdgpu_si_program_jump_on_start(struct amdgpu_device *adev);
+void amdgpu_si_smc_clock(struct amdgpu_device *adev, bool enable);
+bool amdgpu_si_is_smc_running(struct amdgpu_device *adev);
+PPSMC_Result amdgpu_si_send_msg_to_smc(struct amdgpu_device *adev, PPSMC_Msg msg);
+PPSMC_Result amdgpu_si_wait_for_smc_inactive(struct amdgpu_device *adev);
+int amdgpu_si_load_smc_ucode(struct amdgpu_device *adev, u32 limit);
+int amdgpu_si_read_smc_sram_dword(struct amdgpu_device *adev, u32 smc_address,
+ u32 *value, u32 limit);
+int amdgpu_si_write_smc_sram_dword(struct amdgpu_device *adev, u32 smc_address,
+ u32 value, u32 limit);
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/Makefile b/drivers/gpu/drm/amd/pm/powerplay/smumgr/Makefile
new file mode 100644
index 000000000000..6c59c61a0d81
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/Makefile
@@ -0,0 +1,33 @@
+#
+# Copyright 2017 Advanced Micro Devices, Inc.
+#
+# 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+#
+#
+# Makefile for the 'smu manager' sub-component of powerplay.
+# It provides the smu management services for the driver.
+
+SMU_MGR = smumgr.o smu8_smumgr.o tonga_smumgr.o fiji_smumgr.o \
+ polaris10_smumgr.o iceland_smumgr.o \
+ smu7_smumgr.o vega10_smumgr.o smu10_smumgr.o ci_smumgr.o \
+ vega12_smumgr.o vegam_smumgr.o smu9_smumgr.o vega20_smumgr.o
+
+AMD_PP_SMUMGR = $(addprefix $(AMD_PP_PATH)/smumgr/,$(SMU_MGR))
+
+AMD_POWERPLAY_FILES += $(AMD_PP_SMUMGR)
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c
new file mode 100644
index 000000000000..329bf4d44bbc
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c
@@ -0,0 +1,2988 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <linux/module.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include "linux/delay.h"
+#include <linux/types.h>
+#include <linux/pci.h>
+
+#include "smumgr.h"
+#include "pp_debug.h"
+#include "ci_smumgr.h"
+#include "ppsmc.h"
+#include "smu7_hwmgr.h"
+#include "hardwaremanager.h"
+#include "ppatomctrl.h"
+#include "cgs_common.h"
+#include "atombios.h"
+#include "pppcielanes.h"
+#include "smu7_smumgr.h"
+
+#include "smu/smu_7_0_1_d.h"
+#include "smu/smu_7_0_1_sh_mask.h"
+
+#include "dce/dce_8_0_d.h"
+#include "dce/dce_8_0_sh_mask.h"
+
+#include "bif/bif_4_1_d.h"
+#include "bif/bif_4_1_sh_mask.h"
+
+#include "gca/gfx_7_2_d.h"
+#include "gca/gfx_7_2_sh_mask.h"
+
+#include "gmc/gmc_7_1_d.h"
+#include "gmc/gmc_7_1_sh_mask.h"
+
+#include "processpptables.h"
+
+#define MC_CG_ARB_FREQ_F0 0x0a
+#define MC_CG_ARB_FREQ_F1 0x0b
+#define MC_CG_ARB_FREQ_F2 0x0c
+#define MC_CG_ARB_FREQ_F3 0x0d
+
+#define SMC_RAM_END 0x40000
+
+#define CISLAND_MINIMUM_ENGINE_CLOCK 800
+#define CISLAND_MAX_DEEPSLEEP_DIVIDER_ID 5
+
+static const struct ci_pt_defaults defaults_hawaii_xt = {
+ 1, 0xF, 0xFD, 0x19, 5, 0x14, 0, 0xB0000,
+ { 0x2E, 0x00, 0x00, 0x88, 0x00, 0x00, 0x72, 0x60, 0x51, 0xA7, 0x79, 0x6B, 0x90, 0xBD, 0x79 },
+ { 0x217, 0x217, 0x217, 0x242, 0x242, 0x242, 0x269, 0x269, 0x269, 0x2A1, 0x2A1, 0x2A1, 0x2C9, 0x2C9, 0x2C9 }
+};
+
+static const struct ci_pt_defaults defaults_hawaii_pro = {
+ 1, 0xF, 0xFD, 0x19, 5, 0x14, 0, 0x65062,
+ { 0x2E, 0x00, 0x00, 0x88, 0x00, 0x00, 0x72, 0x60, 0x51, 0xA7, 0x79, 0x6B, 0x90, 0xBD, 0x79 },
+ { 0x217, 0x217, 0x217, 0x242, 0x242, 0x242, 0x269, 0x269, 0x269, 0x2A1, 0x2A1, 0x2A1, 0x2C9, 0x2C9, 0x2C9 }
+};
+
+static const struct ci_pt_defaults defaults_bonaire_xt = {
+ 1, 0xF, 0xFD, 0x19, 5, 45, 0, 0xB0000,
+ { 0x79, 0x253, 0x25D, 0xAE, 0x72, 0x80, 0x83, 0x86, 0x6F, 0xC8, 0xC9, 0xC9, 0x2F, 0x4D, 0x61 },
+ { 0x17C, 0x172, 0x180, 0x1BC, 0x1B3, 0x1BD, 0x206, 0x200, 0x203, 0x25D, 0x25A, 0x255, 0x2C3, 0x2C5, 0x2B4 }
+};
+
+
+static const struct ci_pt_defaults defaults_saturn_xt = {
+ 1, 0xF, 0xFD, 0x19, 5, 55, 0, 0x70000,
+ { 0x8C, 0x247, 0x249, 0xA6, 0x80, 0x81, 0x8B, 0x89, 0x86, 0xC9, 0xCA, 0xC9, 0x4D, 0x4D, 0x4D },
+ { 0x187, 0x187, 0x187, 0x1C7, 0x1C7, 0x1C7, 0x210, 0x210, 0x210, 0x266, 0x266, 0x266, 0x2C9, 0x2C9, 0x2C9 }
+};
+
+
+static int ci_set_smc_sram_address(struct pp_hwmgr *hwmgr,
+ uint32_t smc_addr, uint32_t limit)
+{
+ if ((0 != (3 & smc_addr))
+ || ((smc_addr + 3) >= limit)) {
+ pr_err("smc_addr invalid \n");
+ return -EINVAL;
+ }
+
+ cgs_write_register(hwmgr->device, mmSMC_IND_INDEX_0, smc_addr);
+ PHM_WRITE_FIELD(hwmgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
+ return 0;
+}
+
+static int ci_copy_bytes_to_smc(struct pp_hwmgr *hwmgr, uint32_t smc_start_address,
+ const uint8_t *src, uint32_t byte_count, uint32_t limit)
+{
+ int result;
+ uint32_t data = 0;
+ uint32_t original_data;
+ uint32_t addr = 0;
+ uint32_t extra_shift;
+
+ if ((3 & smc_start_address)
+ || ((smc_start_address + byte_count) >= limit)) {
+ pr_err("smc_start_address invalid \n");
+ return -EINVAL;
+ }
+
+ addr = smc_start_address;
+
+ while (byte_count >= 4) {
+ /* Bytes are written into the SMC address space with the MSB first. */
+ data = src[0] * 0x1000000 + src[1] * 0x10000 + src[2] * 0x100 + src[3];
+
+ result = ci_set_smc_sram_address(hwmgr, addr, limit);
+
+ if (0 != result)
+ return result;
+
+ cgs_write_register(hwmgr->device, mmSMC_IND_DATA_0, data);
+
+ src += 4;
+ byte_count -= 4;
+ addr += 4;
+ }
+
+ if (0 != byte_count) {
+
+ data = 0;
+
+ result = ci_set_smc_sram_address(hwmgr, addr, limit);
+
+ if (0 != result)
+ return result;
+
+
+ original_data = cgs_read_register(hwmgr->device, mmSMC_IND_DATA_0);
+
+ extra_shift = 8 * (4 - byte_count);
+
+ while (byte_count > 0) {
+ /* Bytes are written into the SMC addres space with the MSB first. */
+ data = (0x100 * data) + *src++;
+ byte_count--;
+ }
+
+ data <<= extra_shift;
+
+ data |= (original_data & ~((~0UL) << extra_shift));
+
+ result = ci_set_smc_sram_address(hwmgr, addr, limit);
+
+ if (0 != result)
+ return result;
+
+ cgs_write_register(hwmgr->device, mmSMC_IND_DATA_0, data);
+ }
+
+ return 0;
+}
+
+
+static int ci_program_jump_on_start(struct pp_hwmgr *hwmgr)
+{
+ static const unsigned char data[4] = { 0xE0, 0x00, 0x80, 0x40 };
+
+ ci_copy_bytes_to_smc(hwmgr, 0x0, data, 4, sizeof(data)+1);
+
+ return 0;
+}
+
+static bool ci_is_smc_ram_running(struct pp_hwmgr *hwmgr)
+{
+ return ((0 == PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device,
+ CGS_IND_REG__SMC, SMC_SYSCON_CLOCK_CNTL_0, ck_disable))
+ && (0x20100 <= cgs_read_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, ixSMC_PC_C)));
+}
+
+static int ci_read_smc_sram_dword(struct pp_hwmgr *hwmgr, uint32_t smc_addr,
+ uint32_t *value, uint32_t limit)
+{
+ int result;
+
+ result = ci_set_smc_sram_address(hwmgr, smc_addr, limit);
+
+ if (result)
+ return result;
+
+ *value = cgs_read_register(hwmgr->device, mmSMC_IND_DATA_0);
+ return 0;
+}
+
+static int ci_send_msg_to_smc(struct pp_hwmgr *hwmgr, uint16_t msg)
+{
+ int ret;
+
+ cgs_write_register(hwmgr->device, mmSMC_RESP_0, 0);
+ cgs_write_register(hwmgr->device, mmSMC_MESSAGE_0, msg);
+
+ PHM_WAIT_FIELD_UNEQUAL(hwmgr, SMC_RESP_0, SMC_RESP, 0);
+
+ ret = PHM_READ_FIELD(hwmgr->device, SMC_RESP_0, SMC_RESP);
+
+ if (ret != 1)
+ pr_info("\n failed to send message %x ret is %d\n", msg, ret);
+
+ return 0;
+}
+
+static int ci_send_msg_to_smc_with_parameter(struct pp_hwmgr *hwmgr,
+ uint16_t msg, uint32_t parameter)
+{
+ cgs_write_register(hwmgr->device, mmSMC_MSG_ARG_0, parameter);
+ return ci_send_msg_to_smc(hwmgr, msg);
+}
+
+static void ci_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr)
+{
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t dev_id;
+
+ dev_id = adev->pdev->device;
+
+ switch (dev_id) {
+ case 0x67BA:
+ case 0x67B1:
+ smu_data->power_tune_defaults = &defaults_hawaii_pro;
+ break;
+ case 0x67B8:
+ case 0x66B0:
+ smu_data->power_tune_defaults = &defaults_hawaii_xt;
+ break;
+ case 0x6640:
+ case 0x6641:
+ case 0x6646:
+ case 0x6647:
+ smu_data->power_tune_defaults = &defaults_saturn_xt;
+ break;
+ case 0x6649:
+ case 0x6650:
+ case 0x6651:
+ case 0x6658:
+ case 0x665C:
+ case 0x665D:
+ case 0x67A0:
+ case 0x67A1:
+ case 0x67A2:
+ case 0x67A8:
+ case 0x67A9:
+ case 0x67AA:
+ case 0x67B9:
+ case 0x67BE:
+ default:
+ smu_data->power_tune_defaults = &defaults_bonaire_xt;
+ break;
+ }
+}
+
+static int ci_get_dependency_volt_by_clk(struct pp_hwmgr *hwmgr,
+ struct phm_clock_voltage_dependency_table *allowed_clock_voltage_table,
+ uint32_t clock, uint32_t *vol)
+{
+ uint32_t i = 0;
+
+ if (allowed_clock_voltage_table->count == 0)
+ return -EINVAL;
+
+ for (i = 0; i < allowed_clock_voltage_table->count; i++) {
+ if (allowed_clock_voltage_table->entries[i].clk >= clock) {
+ *vol = allowed_clock_voltage_table->entries[i].v;
+ return 0;
+ }
+ }
+
+ *vol = allowed_clock_voltage_table->entries[i - 1].v;
+ return 0;
+}
+
+static int ci_calculate_sclk_params(struct pp_hwmgr *hwmgr,
+ uint32_t clock, struct SMU7_Discrete_GraphicsLevel *sclk)
+{
+ const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct pp_atomctrl_clock_dividers_vi dividers;
+ uint32_t spll_func_cntl = data->clock_registers.vCG_SPLL_FUNC_CNTL;
+ uint32_t spll_func_cntl_3 = data->clock_registers.vCG_SPLL_FUNC_CNTL_3;
+ uint32_t spll_func_cntl_4 = data->clock_registers.vCG_SPLL_FUNC_CNTL_4;
+ uint32_t cg_spll_spread_spectrum = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM;
+ uint32_t cg_spll_spread_spectrum_2 = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2;
+ uint32_t ref_clock;
+ uint32_t ref_divider;
+ uint32_t fbdiv;
+ int result;
+
+ /* get the engine clock dividers for this clock value */
+ result = atomctrl_get_engine_pll_dividers_vi(hwmgr, clock, &dividers);
+
+ PP_ASSERT_WITH_CODE(result == 0,
+ "Error retrieving Engine Clock dividers from VBIOS.",
+ return result);
+
+ /* To get FBDIV we need to multiply this by 16384 and divide it by Fref. */
+ ref_clock = atomctrl_get_reference_clock(hwmgr);
+ ref_divider = 1 + dividers.uc_pll_ref_div;
+
+ /* low 14 bits is fraction and high 12 bits is divider */
+ fbdiv = dividers.ul_fb_div.ul_fb_divider & 0x3FFFFFF;
+
+ /* SPLL_FUNC_CNTL setup */
+ spll_func_cntl = PHM_SET_FIELD(spll_func_cntl, CG_SPLL_FUNC_CNTL,
+ SPLL_REF_DIV, dividers.uc_pll_ref_div);
+ spll_func_cntl = PHM_SET_FIELD(spll_func_cntl, CG_SPLL_FUNC_CNTL,
+ SPLL_PDIV_A, dividers.uc_pll_post_div);
+
+ /* SPLL_FUNC_CNTL_3 setup*/
+ spll_func_cntl_3 = PHM_SET_FIELD(spll_func_cntl_3, CG_SPLL_FUNC_CNTL_3,
+ SPLL_FB_DIV, fbdiv);
+
+ /* set to use fractional accumulation*/
+ spll_func_cntl_3 = PHM_SET_FIELD(spll_func_cntl_3, CG_SPLL_FUNC_CNTL_3,
+ SPLL_DITHEN, 1);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EngineSpreadSpectrumSupport)) {
+ struct pp_atomctrl_internal_ss_info ss_info;
+ uint32_t vco_freq = clock * dividers.uc_pll_post_div;
+
+ if (!atomctrl_get_engine_clock_spread_spectrum(hwmgr,
+ vco_freq, &ss_info)) {
+ uint32_t clk_s = ref_clock * 5 /
+ (ref_divider * ss_info.speed_spectrum_rate);
+ uint32_t clk_v = 4 * ss_info.speed_spectrum_percentage *
+ fbdiv / (clk_s * 10000);
+
+ cg_spll_spread_spectrum = PHM_SET_FIELD(cg_spll_spread_spectrum,
+ CG_SPLL_SPREAD_SPECTRUM, CLKS, clk_s);
+ cg_spll_spread_spectrum = PHM_SET_FIELD(cg_spll_spread_spectrum,
+ CG_SPLL_SPREAD_SPECTRUM, SSEN, 1);
+ cg_spll_spread_spectrum_2 = PHM_SET_FIELD(cg_spll_spread_spectrum_2,
+ CG_SPLL_SPREAD_SPECTRUM_2, CLKV, clk_v);
+ }
+ }
+
+ sclk->SclkFrequency = clock;
+ sclk->CgSpllFuncCntl3 = spll_func_cntl_3;
+ sclk->CgSpllFuncCntl4 = spll_func_cntl_4;
+ sclk->SpllSpreadSpectrum = cg_spll_spread_spectrum;
+ sclk->SpllSpreadSpectrum2 = cg_spll_spread_spectrum_2;
+ sclk->SclkDid = (uint8_t)dividers.pll_post_divider;
+
+ return 0;
+}
+
+static void ci_populate_phase_value_based_on_sclk(struct pp_hwmgr *hwmgr,
+ const struct phm_phase_shedding_limits_table *pl,
+ uint32_t sclk, uint32_t *p_shed)
+{
+ unsigned int i;
+
+ /* use the minimum phase shedding */
+ *p_shed = 1;
+
+ for (i = 0; i < pl->count; i++) {
+ if (sclk < pl->entries[i].Sclk) {
+ *p_shed = i;
+ break;
+ }
+ }
+}
+
+static uint8_t ci_get_sleep_divider_id_from_clock(uint32_t clock,
+ uint32_t clock_insr)
+{
+ uint8_t i;
+ uint32_t temp;
+ uint32_t min = min_t(uint32_t, clock_insr, CISLAND_MINIMUM_ENGINE_CLOCK);
+
+ if (clock < min) {
+ pr_info("Engine clock can't satisfy stutter requirement!\n");
+ return 0;
+ }
+ for (i = CISLAND_MAX_DEEPSLEEP_DIVIDER_ID; ; i--) {
+ temp = clock >> i;
+
+ if (temp >= min || i == 0)
+ break;
+ }
+ return i;
+}
+
+static int ci_populate_single_graphic_level(struct pp_hwmgr *hwmgr,
+ uint32_t clock, struct SMU7_Discrete_GraphicsLevel *level)
+{
+ int result;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+
+ result = ci_calculate_sclk_params(hwmgr, clock, level);
+
+ /* populate graphics levels */
+ result = ci_get_dependency_volt_by_clk(hwmgr,
+ hwmgr->dyn_state.vddc_dependency_on_sclk, clock,
+ (uint32_t *)(&level->MinVddc));
+ if (result) {
+ pr_err("vdd_dep_on_sclk table is NULL\n");
+ return result;
+ }
+
+ level->SclkFrequency = clock;
+ level->MinVddcPhases = 1;
+
+ if (data->vddc_phase_shed_control)
+ ci_populate_phase_value_based_on_sclk(hwmgr,
+ hwmgr->dyn_state.vddc_phase_shed_limits_table,
+ clock,
+ &level->MinVddcPhases);
+
+ level->ActivityLevel = data->current_profile_setting.sclk_activity;
+ level->CcPwrDynRm = 0;
+ level->CcPwrDynRm1 = 0;
+ level->EnabledForActivity = 0;
+ /* this level can be used for throttling.*/
+ level->EnabledForThrottle = 1;
+ level->UpH = data->current_profile_setting.sclk_up_hyst;
+ level->DownH = data->current_profile_setting.sclk_down_hyst;
+ level->VoltageDownH = 0;
+ level->PowerThrottle = 0;
+
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkDeepSleep))
+ level->DeepSleepDivId =
+ ci_get_sleep_divider_id_from_clock(clock,
+ CISLAND_MINIMUM_ENGINE_CLOCK);
+
+ /* Default to slow, highest DPM level will be set to PPSMC_DISPLAY_WATERMARK_LOW later.*/
+ level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+ if (0 == result) {
+ level->MinVddc = PP_HOST_TO_SMC_UL(level->MinVddc * VOLTAGE_SCALE);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->MinVddcPhases);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->SclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_US(level->ActivityLevel);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->CgSpllFuncCntl3);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->CgSpllFuncCntl4);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->SpllSpreadSpectrum);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->SpllSpreadSpectrum2);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->CcPwrDynRm);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->CcPwrDynRm1);
+ }
+
+ return result;
+}
+
+static int ci_populate_all_graphic_levels(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ struct smu7_dpm_table *dpm_table = &data->dpm_table;
+ int result = 0;
+ uint32_t array = smu_data->dpm_table_start +
+ offsetof(SMU7_Discrete_DpmTable, GraphicsLevel);
+ uint32_t array_size = sizeof(struct SMU7_Discrete_GraphicsLevel) *
+ SMU7_MAX_LEVELS_GRAPHICS;
+ struct SMU7_Discrete_GraphicsLevel *levels =
+ smu_data->smc_state_table.GraphicsLevel;
+ uint32_t i;
+
+ for (i = 0; i < dpm_table->sclk_table.count; i++) {
+ result = ci_populate_single_graphic_level(hwmgr,
+ dpm_table->sclk_table.dpm_levels[i].value,
+ &levels[i]);
+ if (result)
+ return result;
+ if (i > 1)
+ smu_data->smc_state_table.GraphicsLevel[i].DeepSleepDivId = 0;
+ if (i == (dpm_table->sclk_table.count - 1))
+ smu_data->smc_state_table.GraphicsLevel[i].DisplayWatermark =
+ PPSMC_DISPLAY_WATERMARK_HIGH;
+ }
+
+ smu_data->smc_state_table.GraphicsLevel[0].EnabledForActivity = 1;
+
+ smu_data->smc_state_table.GraphicsDpmLevelCount = (u8)dpm_table->sclk_table.count;
+ data->dpm_level_enable_mask.sclk_dpm_enable_mask =
+ phm_get_dpm_level_enable_mask_value(&dpm_table->sclk_table);
+
+ result = ci_copy_bytes_to_smc(hwmgr, array,
+ (u8 *)levels, array_size,
+ SMC_RAM_END);
+
+ return result;
+
+}
+
+static int ci_populate_svi_load_line(struct pp_hwmgr *hwmgr)
+{
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ const struct ci_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+ smu_data->power_tune_table.SviLoadLineEn = defaults->svi_load_line_en;
+ smu_data->power_tune_table.SviLoadLineVddC = defaults->svi_load_line_vddc;
+ smu_data->power_tune_table.SviLoadLineTrimVddC = 3;
+ smu_data->power_tune_table.SviLoadLineOffsetVddC = 0;
+
+ return 0;
+}
+
+static int ci_populate_tdc_limit(struct pp_hwmgr *hwmgr)
+{
+ uint16_t tdc_limit;
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ const struct ci_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+ tdc_limit = (uint16_t)(hwmgr->dyn_state.cac_dtp_table->usTDC * 256);
+ smu_data->power_tune_table.TDC_VDDC_PkgLimit =
+ CONVERT_FROM_HOST_TO_SMC_US(tdc_limit);
+ smu_data->power_tune_table.TDC_VDDC_ThrottleReleaseLimitPerc =
+ defaults->tdc_vddc_throttle_release_limit_perc;
+ smu_data->power_tune_table.TDC_MAWt = defaults->tdc_mawt;
+
+ return 0;
+}
+
+static int ci_populate_dw8(struct pp_hwmgr *hwmgr, uint32_t fuse_table_offset)
+{
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ const struct ci_pt_defaults *defaults = smu_data->power_tune_defaults;
+ uint32_t temp;
+
+ if (ci_read_smc_sram_dword(hwmgr,
+ fuse_table_offset +
+ offsetof(SMU7_Discrete_PmFuses, TdcWaterfallCtl),
+ (uint32_t *)&temp, SMC_RAM_END))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to read PmFuses.DW6 (SviLoadLineEn) from SMC Failed!",
+ return -EINVAL);
+ else
+ smu_data->power_tune_table.TdcWaterfallCtl = defaults->tdc_waterfall_ctl;
+
+ return 0;
+}
+
+static int ci_populate_fuzzy_fan(struct pp_hwmgr *hwmgr, uint32_t fuse_table_offset)
+{
+ uint16_t tmp;
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+
+ if ((hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity & (1 << 15))
+ || 0 == hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity)
+ tmp = hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity;
+ else
+ tmp = hwmgr->thermal_controller.advanceFanControlParameters.usDefaultFanOutputSensitivity;
+
+ smu_data->power_tune_table.FuzzyFan_PwmSetDelta = CONVERT_FROM_HOST_TO_SMC_US(tmp);
+
+ return 0;
+}
+
+static int ci_populate_bapm_vddc_vid_sidd(struct pp_hwmgr *hwmgr)
+{
+ int i;
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ uint8_t *hi_vid = smu_data->power_tune_table.BapmVddCVidHiSidd;
+ uint8_t *lo_vid = smu_data->power_tune_table.BapmVddCVidLoSidd;
+ uint8_t *hi2_vid = smu_data->power_tune_table.BapmVddCVidHiSidd2;
+
+ PP_ASSERT_WITH_CODE(NULL != hwmgr->dyn_state.cac_leakage_table,
+ "The CAC Leakage table does not exist!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(hwmgr->dyn_state.cac_leakage_table->count <= 8,
+ "There should never be more than 8 entries for BapmVddcVid!!!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(hwmgr->dyn_state.cac_leakage_table->count == hwmgr->dyn_state.vddc_dependency_on_sclk->count,
+ "CACLeakageTable->count and VddcDependencyOnSCLk->count not equal", return -EINVAL);
+
+ for (i = 0; (uint32_t) i < hwmgr->dyn_state.cac_leakage_table->count; i++) {
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_EVV)) {
+ lo_vid[i] = convert_to_vid(hwmgr->dyn_state.cac_leakage_table->entries[i].Vddc1);
+ hi_vid[i] = convert_to_vid(hwmgr->dyn_state.cac_leakage_table->entries[i].Vddc2);
+ hi2_vid[i] = convert_to_vid(hwmgr->dyn_state.cac_leakage_table->entries[i].Vddc3);
+ } else {
+ lo_vid[i] = convert_to_vid(hwmgr->dyn_state.cac_leakage_table->entries[i].Vddc);
+ hi_vid[i] = convert_to_vid(hwmgr->dyn_state.cac_leakage_table->entries[i].Leakage);
+ }
+ }
+
+ return 0;
+}
+
+static int ci_populate_vddc_vid(struct pp_hwmgr *hwmgr)
+{
+ int i;
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ uint8_t *vid = smu_data->power_tune_table.VddCVid;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ PP_ASSERT_WITH_CODE(data->vddc_voltage_table.count <= 8,
+ "There should never be more than 8 entries for VddcVid!!!",
+ return -EINVAL);
+
+ for (i = 0; i < (int)data->vddc_voltage_table.count; i++)
+ vid[i] = convert_to_vid(data->vddc_voltage_table.entries[i].value);
+
+ return 0;
+}
+
+static int ci_min_max_v_gnbl_pm_lid_from_bapm_vddc(struct pp_hwmgr *hwmgr)
+{
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ u8 *hi_vid = smu_data->power_tune_table.BapmVddCVidHiSidd;
+ u8 *lo_vid = smu_data->power_tune_table.BapmVddCVidLoSidd;
+ int i, min, max;
+
+ min = max = hi_vid[0];
+ for (i = 0; i < 8; i++) {
+ if (0 != hi_vid[i]) {
+ if (min > hi_vid[i])
+ min = hi_vid[i];
+ if (max < hi_vid[i])
+ max = hi_vid[i];
+ }
+
+ if (0 != lo_vid[i]) {
+ if (min > lo_vid[i])
+ min = lo_vid[i];
+ if (max < lo_vid[i])
+ max = lo_vid[i];
+ }
+ }
+
+ if ((min == 0) || (max == 0))
+ return -EINVAL;
+ smu_data->power_tune_table.GnbLPMLMaxVid = (u8)max;
+ smu_data->power_tune_table.GnbLPMLMinVid = (u8)min;
+
+ return 0;
+}
+
+static int ci_populate_bapm_vddc_base_leakage_sidd(struct pp_hwmgr *hwmgr)
+{
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ uint16_t HiSidd;
+ uint16_t LoSidd;
+ struct phm_cac_tdp_table *cac_table = hwmgr->dyn_state.cac_dtp_table;
+
+ HiSidd = (uint16_t)(cac_table->usHighCACLeakage / 100 * 256);
+ LoSidd = (uint16_t)(cac_table->usLowCACLeakage / 100 * 256);
+
+ smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd =
+ CONVERT_FROM_HOST_TO_SMC_US(HiSidd);
+ smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd =
+ CONVERT_FROM_HOST_TO_SMC_US(LoSidd);
+
+ return 0;
+}
+
+static int ci_populate_pm_fuses(struct pp_hwmgr *hwmgr)
+{
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ uint32_t pm_fuse_table_offset;
+ int ret = 0;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PowerContainment)) {
+ if (ci_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU7_Firmware_Header, PmFuseTable),
+ &pm_fuse_table_offset, SMC_RAM_END)) {
+ pr_err("Attempt to get pm_fuse_table_offset Failed!\n");
+ return -EINVAL;
+ }
+
+ /* DW0 - DW3 */
+ ret = ci_populate_bapm_vddc_vid_sidd(hwmgr);
+ /* DW4 - DW5 */
+ ret |= ci_populate_vddc_vid(hwmgr);
+ /* DW6 */
+ ret |= ci_populate_svi_load_line(hwmgr);
+ /* DW7 */
+ ret |= ci_populate_tdc_limit(hwmgr);
+ /* DW8 */
+ ret |= ci_populate_dw8(hwmgr, pm_fuse_table_offset);
+
+ ret |= ci_populate_fuzzy_fan(hwmgr, pm_fuse_table_offset);
+
+ ret |= ci_min_max_v_gnbl_pm_lid_from_bapm_vddc(hwmgr);
+
+ ret |= ci_populate_bapm_vddc_base_leakage_sidd(hwmgr);
+ if (ret)
+ return ret;
+
+ ret = ci_copy_bytes_to_smc(hwmgr, pm_fuse_table_offset,
+ (uint8_t *)&smu_data->power_tune_table,
+ sizeof(struct SMU7_Discrete_PmFuses), SMC_RAM_END);
+ }
+ return ret;
+}
+
+static int ci_populate_bapm_parameters_in_dpm_table(struct pp_hwmgr *hwmgr)
+{
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ const struct ci_pt_defaults *defaults = smu_data->power_tune_defaults;
+ SMU7_Discrete_DpmTable *dpm_table = &(smu_data->smc_state_table);
+ struct phm_cac_tdp_table *cac_dtp_table = hwmgr->dyn_state.cac_dtp_table;
+ struct phm_ppm_table *ppm = hwmgr->dyn_state.ppm_parameter_table;
+ const uint16_t *def1, *def2;
+ int i, j, k;
+
+ dpm_table->DefaultTdp = PP_HOST_TO_SMC_US((uint16_t)(cac_dtp_table->usTDP * 256));
+ dpm_table->TargetTdp = PP_HOST_TO_SMC_US((uint16_t)(cac_dtp_table->usConfigurableTDP * 256));
+
+ dpm_table->DTETjOffset = 0;
+ dpm_table->GpuTjMax = (uint8_t)(data->thermal_temp_setting.temperature_high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES);
+ dpm_table->GpuTjHyst = 8;
+
+ dpm_table->DTEAmbientTempBase = defaults->dte_ambient_temp_base;
+
+ if (ppm) {
+ dpm_table->PPM_PkgPwrLimit = (uint16_t)ppm->dgpu_tdp * 256 / 1000;
+ dpm_table->PPM_TemperatureLimit = (uint16_t)ppm->tj_max * 256;
+ } else {
+ dpm_table->PPM_PkgPwrLimit = 0;
+ dpm_table->PPM_TemperatureLimit = 0;
+ }
+
+ CONVERT_FROM_HOST_TO_SMC_US(dpm_table->PPM_PkgPwrLimit);
+ CONVERT_FROM_HOST_TO_SMC_US(dpm_table->PPM_TemperatureLimit);
+
+ dpm_table->BAPM_TEMP_GRADIENT = PP_HOST_TO_SMC_UL(defaults->bapm_temp_gradient);
+ def1 = defaults->bapmti_r;
+ def2 = defaults->bapmti_rc;
+
+ for (i = 0; i < SMU7_DTE_ITERATIONS; i++) {
+ for (j = 0; j < SMU7_DTE_SOURCES; j++) {
+ for (k = 0; k < SMU7_DTE_SINKS; k++) {
+ dpm_table->BAPMTI_R[i][j][k] = PP_HOST_TO_SMC_US(*def1);
+ dpm_table->BAPMTI_RC[i][j][k] = PP_HOST_TO_SMC_US(*def2);
+ def1++;
+ def2++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int ci_get_std_voltage_value_sidd(struct pp_hwmgr *hwmgr,
+ pp_atomctrl_voltage_table_entry *tab, uint16_t *hi,
+ uint16_t *lo)
+{
+ uint16_t v_index;
+ bool vol_found = false;
+ *hi = tab->value * VOLTAGE_SCALE;
+ *lo = tab->value * VOLTAGE_SCALE;
+
+ PP_ASSERT_WITH_CODE(NULL != hwmgr->dyn_state.vddc_dependency_on_sclk,
+ "The SCLK/VDDC Dependency Table does not exist.\n",
+ return -EINVAL);
+
+ if (NULL == hwmgr->dyn_state.cac_leakage_table) {
+ pr_warn("CAC Leakage Table does not exist, using vddc.\n");
+ return 0;
+ }
+
+ for (v_index = 0; (uint32_t)v_index < hwmgr->dyn_state.vddc_dependency_on_sclk->count; v_index++) {
+ if (tab->value == hwmgr->dyn_state.vddc_dependency_on_sclk->entries[v_index].v) {
+ vol_found = true;
+ if ((uint32_t)v_index < hwmgr->dyn_state.cac_leakage_table->count) {
+ *lo = hwmgr->dyn_state.cac_leakage_table->entries[v_index].Vddc * VOLTAGE_SCALE;
+ *hi = (uint16_t)(hwmgr->dyn_state.cac_leakage_table->entries[v_index].Leakage * VOLTAGE_SCALE);
+ } else {
+ pr_warn("Index from SCLK/VDDC Dependency Table exceeds the CAC Leakage Table index, using maximum index from CAC table.\n");
+ *lo = hwmgr->dyn_state.cac_leakage_table->entries[hwmgr->dyn_state.cac_leakage_table->count - 1].Vddc * VOLTAGE_SCALE;
+ *hi = (uint16_t)(hwmgr->dyn_state.cac_leakage_table->entries[hwmgr->dyn_state.cac_leakage_table->count - 1].Leakage * VOLTAGE_SCALE);
+ }
+ break;
+ }
+ }
+
+ if (!vol_found) {
+ for (v_index = 0; (uint32_t)v_index < hwmgr->dyn_state.vddc_dependency_on_sclk->count; v_index++) {
+ if (tab->value <= hwmgr->dyn_state.vddc_dependency_on_sclk->entries[v_index].v) {
+ vol_found = true;
+ if ((uint32_t)v_index < hwmgr->dyn_state.cac_leakage_table->count) {
+ *lo = hwmgr->dyn_state.cac_leakage_table->entries[v_index].Vddc * VOLTAGE_SCALE;
+ *hi = (uint16_t)(hwmgr->dyn_state.cac_leakage_table->entries[v_index].Leakage) * VOLTAGE_SCALE;
+ } else {
+ pr_warn("Index from SCLK/VDDC Dependency Table exceeds the CAC Leakage Table index in second look up, using maximum index from CAC table.");
+ *lo = hwmgr->dyn_state.cac_leakage_table->entries[hwmgr->dyn_state.cac_leakage_table->count - 1].Vddc * VOLTAGE_SCALE;
+ *hi = (uint16_t)(hwmgr->dyn_state.cac_leakage_table->entries[hwmgr->dyn_state.cac_leakage_table->count - 1].Leakage * VOLTAGE_SCALE);
+ }
+ break;
+ }
+ }
+
+ if (!vol_found)
+ pr_warn("Unable to get std_vddc from SCLK/VDDC Dependency Table, using vddc.\n");
+ }
+
+ return 0;
+}
+
+static int ci_populate_smc_voltage_table(struct pp_hwmgr *hwmgr,
+ pp_atomctrl_voltage_table_entry *tab,
+ SMU7_Discrete_VoltageLevel *smc_voltage_tab)
+{
+ int result;
+
+ result = ci_get_std_voltage_value_sidd(hwmgr, tab,
+ &smc_voltage_tab->StdVoltageHiSidd,
+ &smc_voltage_tab->StdVoltageLoSidd);
+ if (result) {
+ smc_voltage_tab->StdVoltageHiSidd = tab->value * VOLTAGE_SCALE;
+ smc_voltage_tab->StdVoltageLoSidd = tab->value * VOLTAGE_SCALE;
+ }
+
+ smc_voltage_tab->Voltage = PP_HOST_TO_SMC_US(tab->value * VOLTAGE_SCALE);
+ CONVERT_FROM_HOST_TO_SMC_US(smc_voltage_tab->StdVoltageHiSidd);
+ CONVERT_FROM_HOST_TO_SMC_US(smc_voltage_tab->StdVoltageLoSidd);
+
+ return 0;
+}
+
+static int ci_populate_smc_vddc_table(struct pp_hwmgr *hwmgr,
+ SMU7_Discrete_DpmTable *table)
+{
+ unsigned int count;
+ int result;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ table->VddcLevelCount = data->vddc_voltage_table.count;
+ for (count = 0; count < table->VddcLevelCount; count++) {
+ result = ci_populate_smc_voltage_table(hwmgr,
+ &(data->vddc_voltage_table.entries[count]),
+ &(table->VddcLevel[count]));
+ PP_ASSERT_WITH_CODE(0 == result, "do not populate SMC VDDC voltage table", return -EINVAL);
+
+ /* GPIO voltage control */
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->voltage_control) {
+ table->VddcLevel[count].Smio = (uint8_t) count;
+ table->Smio[count] |= data->vddc_voltage_table.entries[count].smio_low;
+ table->SmioMaskVddcVid |= data->vddc_voltage_table.entries[count].smio_low;
+ } else {
+ table->VddcLevel[count].Smio = 0;
+ }
+ }
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VddcLevelCount);
+
+ return 0;
+}
+
+static int ci_populate_smc_vdd_ci_table(struct pp_hwmgr *hwmgr,
+ SMU7_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t count;
+ int result;
+
+ table->VddciLevelCount = data->vddci_voltage_table.count;
+
+ for (count = 0; count < table->VddciLevelCount; count++) {
+ result = ci_populate_smc_voltage_table(hwmgr,
+ &(data->vddci_voltage_table.entries[count]),
+ &(table->VddciLevel[count]));
+ PP_ASSERT_WITH_CODE(result == 0, "do not populate SMC VDDCI voltage table", return -EINVAL);
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
+ table->VddciLevel[count].Smio = (uint8_t) count;
+ table->Smio[count] |= data->vddci_voltage_table.entries[count].smio_low;
+ table->SmioMaskVddciVid |= data->vddci_voltage_table.entries[count].smio_low;
+ } else {
+ table->VddciLevel[count].Smio = 0;
+ }
+ }
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VddciLevelCount);
+
+ return 0;
+}
+
+static int ci_populate_smc_mvdd_table(struct pp_hwmgr *hwmgr,
+ SMU7_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t count;
+ int result;
+
+ table->MvddLevelCount = data->mvdd_voltage_table.count;
+
+ for (count = 0; count < table->MvddLevelCount; count++) {
+ result = ci_populate_smc_voltage_table(hwmgr,
+ &(data->mvdd_voltage_table.entries[count]),
+ &table->MvddLevel[count]);
+ PP_ASSERT_WITH_CODE(result == 0, "do not populate SMC mvdd voltage table", return -EINVAL);
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
+ table->MvddLevel[count].Smio = (uint8_t) count;
+ table->Smio[count] |= data->mvdd_voltage_table.entries[count].smio_low;
+ table->SmioMaskMvddVid |= data->mvdd_voltage_table.entries[count].smio_low;
+ } else {
+ table->MvddLevel[count].Smio = 0;
+ }
+ }
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->MvddLevelCount);
+
+ return 0;
+}
+
+
+static int ci_populate_smc_voltage_tables(struct pp_hwmgr *hwmgr,
+ SMU7_Discrete_DpmTable *table)
+{
+ int result;
+
+ result = ci_populate_smc_vddc_table(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "can not populate VDDC voltage table to SMC", return -EINVAL);
+
+ result = ci_populate_smc_vdd_ci_table(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "can not populate VDDCI voltage table to SMC", return -EINVAL);
+
+ result = ci_populate_smc_mvdd_table(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "can not populate MVDD voltage table to SMC", return -EINVAL);
+
+ return 0;
+}
+
+static int ci_populate_ulv_level(struct pp_hwmgr *hwmgr,
+ struct SMU7_Discrete_Ulv *state)
+{
+ uint32_t voltage_response_time, ulv_voltage;
+ int result;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ state->CcPwrDynRm = 0;
+ state->CcPwrDynRm1 = 0;
+
+ result = pp_tables_get_response_times(hwmgr, &voltage_response_time, &ulv_voltage);
+ PP_ASSERT_WITH_CODE((0 == result), "can not get ULV voltage value", return result;);
+
+ if (ulv_voltage == 0) {
+ data->ulv_supported = false;
+ return 0;
+ }
+
+ if (data->voltage_control != SMU7_VOLTAGE_CONTROL_BY_SVID2) {
+ /* use minimum voltage if ulv voltage in pptable is bigger than minimum voltage */
+ if (ulv_voltage > hwmgr->dyn_state.vddc_dependency_on_sclk->entries[0].v)
+ state->VddcOffset = 0;
+ else
+ /* used in SMIO Mode. not implemented for now. this is backup only for CI. */
+ state->VddcOffset = (uint16_t)(hwmgr->dyn_state.vddc_dependency_on_sclk->entries[0].v - ulv_voltage);
+ } else {
+ /* use minimum voltage if ulv voltage in pptable is bigger than minimum voltage */
+ if (ulv_voltage > hwmgr->dyn_state.vddc_dependency_on_sclk->entries[0].v)
+ state->VddcOffsetVid = 0;
+ else /* used in SVI2 Mode */
+ state->VddcOffsetVid = (uint8_t)(
+ (hwmgr->dyn_state.vddc_dependency_on_sclk->entries[0].v - ulv_voltage)
+ * VOLTAGE_VID_OFFSET_SCALE2
+ / VOLTAGE_VID_OFFSET_SCALE1);
+ }
+ state->VddcPhase = 1;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm);
+ CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm1);
+ CONVERT_FROM_HOST_TO_SMC_US(state->VddcOffset);
+
+ return 0;
+}
+
+static int ci_populate_ulv_state(struct pp_hwmgr *hwmgr,
+ SMU7_Discrete_Ulv *ulv_level)
+{
+ return ci_populate_ulv_level(hwmgr, ulv_level);
+}
+
+static int ci_populate_smc_link_level(struct pp_hwmgr *hwmgr, SMU7_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_dpm_table *dpm_table = &data->dpm_table;
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ uint32_t i;
+
+/* Index dpm_table->pcie_speed_table.count is reserved for PCIE boot level.*/
+ for (i = 0; i <= dpm_table->pcie_speed_table.count; i++) {
+ table->LinkLevel[i].PcieGenSpeed =
+ (uint8_t)dpm_table->pcie_speed_table.dpm_levels[i].value;
+ table->LinkLevel[i].PcieLaneCount =
+ (uint8_t)encode_pcie_lane_width(dpm_table->pcie_speed_table.dpm_levels[i].param1);
+ table->LinkLevel[i].EnabledForActivity = 1;
+ table->LinkLevel[i].DownT = PP_HOST_TO_SMC_UL(5);
+ table->LinkLevel[i].UpT = PP_HOST_TO_SMC_UL(30);
+ }
+
+ smu_data->smc_state_table.LinkLevelCount =
+ (uint8_t)dpm_table->pcie_speed_table.count;
+ data->dpm_level_enable_mask.pcie_dpm_enable_mask =
+ phm_get_dpm_level_enable_mask_value(&dpm_table->pcie_speed_table);
+
+ return 0;
+}
+
+static int ci_calculate_mclk_params(
+ struct pp_hwmgr *hwmgr,
+ uint32_t memory_clock,
+ SMU7_Discrete_MemoryLevel *mclk,
+ bool strobe_mode,
+ bool dllStateOn
+ )
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t dll_cntl = data->clock_registers.vDLL_CNTL;
+ uint32_t mclk_pwrmgt_cntl = data->clock_registers.vMCLK_PWRMGT_CNTL;
+ uint32_t mpll_ad_func_cntl = data->clock_registers.vMPLL_AD_FUNC_CNTL;
+ uint32_t mpll_dq_func_cntl = data->clock_registers.vMPLL_DQ_FUNC_CNTL;
+ uint32_t mpll_func_cntl = data->clock_registers.vMPLL_FUNC_CNTL;
+ uint32_t mpll_func_cntl_1 = data->clock_registers.vMPLL_FUNC_CNTL_1;
+ uint32_t mpll_func_cntl_2 = data->clock_registers.vMPLL_FUNC_CNTL_2;
+ uint32_t mpll_ss1 = data->clock_registers.vMPLL_SS1;
+ uint32_t mpll_ss2 = data->clock_registers.vMPLL_SS2;
+
+ pp_atomctrl_memory_clock_param mpll_param;
+ int result;
+
+ result = atomctrl_get_memory_pll_dividers_si(hwmgr,
+ memory_clock, &mpll_param, strobe_mode);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Error retrieving Memory Clock Parameters from VBIOS.", return result);
+
+ mpll_func_cntl = PHM_SET_FIELD(mpll_func_cntl, MPLL_FUNC_CNTL, BWCTRL, mpll_param.bw_ctrl);
+
+ mpll_func_cntl_1 = PHM_SET_FIELD(mpll_func_cntl_1,
+ MPLL_FUNC_CNTL_1, CLKF, mpll_param.mpll_fb_divider.cl_kf);
+ mpll_func_cntl_1 = PHM_SET_FIELD(mpll_func_cntl_1,
+ MPLL_FUNC_CNTL_1, CLKFRAC, mpll_param.mpll_fb_divider.clk_frac);
+ mpll_func_cntl_1 = PHM_SET_FIELD(mpll_func_cntl_1,
+ MPLL_FUNC_CNTL_1, VCO_MODE, mpll_param.vco_mode);
+
+ mpll_ad_func_cntl = PHM_SET_FIELD(mpll_ad_func_cntl,
+ MPLL_AD_FUNC_CNTL, YCLK_POST_DIV, mpll_param.mpll_post_divider);
+
+ if (data->is_memory_gddr5) {
+ mpll_dq_func_cntl = PHM_SET_FIELD(mpll_dq_func_cntl,
+ MPLL_DQ_FUNC_CNTL, YCLK_SEL, mpll_param.yclk_sel);
+ mpll_dq_func_cntl = PHM_SET_FIELD(mpll_dq_func_cntl,
+ MPLL_DQ_FUNC_CNTL, YCLK_POST_DIV, mpll_param.mpll_post_divider);
+ }
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MemorySpreadSpectrumSupport)) {
+ pp_atomctrl_internal_ss_info ss_info;
+ uint32_t freq_nom;
+ uint32_t tmp;
+ uint32_t reference_clock = atomctrl_get_mpll_reference_clock(hwmgr);
+
+ /* for GDDR5 for all modes and DDR3 */
+ if (1 == mpll_param.qdr)
+ freq_nom = memory_clock * 4 * (1 << mpll_param.mpll_post_divider);
+ else
+ freq_nom = memory_clock * 2 * (1 << mpll_param.mpll_post_divider);
+
+ /* tmp = (freq_nom / reference_clock * reference_divider) ^ 2 Note: S.I. reference_divider = 1*/
+ tmp = (freq_nom / reference_clock);
+ tmp = tmp * tmp;
+
+ if (0 == atomctrl_get_memory_clock_spread_spectrum(hwmgr, freq_nom, &ss_info)) {
+ uint32_t clks = reference_clock * 5 / ss_info.speed_spectrum_rate;
+ uint32_t clkv =
+ (uint32_t)((((131 * ss_info.speed_spectrum_percentage *
+ ss_info.speed_spectrum_rate) / 100) * tmp) / freq_nom);
+
+ mpll_ss1 = PHM_SET_FIELD(mpll_ss1, MPLL_SS1, CLKV, clkv);
+ mpll_ss2 = PHM_SET_FIELD(mpll_ss2, MPLL_SS2, CLKS, clks);
+ }
+ }
+
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, DLL_SPEED, mpll_param.dll_speed);
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, MRDCK0_PDNB, dllStateOn);
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, MRDCK1_PDNB, dllStateOn);
+
+
+ mclk->MclkFrequency = memory_clock;
+ mclk->MpllFuncCntl = mpll_func_cntl;
+ mclk->MpllFuncCntl_1 = mpll_func_cntl_1;
+ mclk->MpllFuncCntl_2 = mpll_func_cntl_2;
+ mclk->MpllAdFuncCntl = mpll_ad_func_cntl;
+ mclk->MpllDqFuncCntl = mpll_dq_func_cntl;
+ mclk->MclkPwrmgtCntl = mclk_pwrmgt_cntl;
+ mclk->DllCntl = dll_cntl;
+ mclk->MpllSs1 = mpll_ss1;
+ mclk->MpllSs2 = mpll_ss2;
+
+ return 0;
+}
+
+static uint8_t ci_get_mclk_frequency_ratio(uint32_t memory_clock,
+ bool strobe_mode)
+{
+ uint8_t mc_para_index;
+
+ if (strobe_mode) {
+ if (memory_clock < 12500)
+ mc_para_index = 0x00;
+ else if (memory_clock > 47500)
+ mc_para_index = 0x0f;
+ else
+ mc_para_index = (uint8_t)((memory_clock - 10000) / 2500);
+ } else {
+ if (memory_clock < 65000)
+ mc_para_index = 0x00;
+ else if (memory_clock > 135000)
+ mc_para_index = 0x0f;
+ else
+ mc_para_index = (uint8_t)((memory_clock - 60000) / 5000);
+ }
+
+ return mc_para_index;
+}
+
+static uint8_t ci_get_ddr3_mclk_frequency_ratio(uint32_t memory_clock)
+{
+ uint8_t mc_para_index;
+
+ if (memory_clock < 10000)
+ mc_para_index = 0;
+ else if (memory_clock >= 80000)
+ mc_para_index = 0x0f;
+ else
+ mc_para_index = (uint8_t)((memory_clock - 10000) / 5000 + 1);
+
+ return mc_para_index;
+}
+
+static int ci_populate_phase_value_based_on_mclk(struct pp_hwmgr *hwmgr, const struct phm_phase_shedding_limits_table *pl,
+ uint32_t memory_clock, uint32_t *p_shed)
+{
+ unsigned int i;
+
+ *p_shed = 1;
+
+ for (i = 0; i < pl->count; i++) {
+ if (memory_clock < pl->entries[i].Mclk) {
+ *p_shed = i;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int ci_populate_single_memory_level(
+ struct pp_hwmgr *hwmgr,
+ uint32_t memory_clock,
+ SMU7_Discrete_MemoryLevel *memory_level
+ )
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ int result = 0;
+ bool dll_state_on;
+ uint32_t mclk_edc_wr_enable_threshold = 40000;
+ uint32_t mclk_edc_enable_threshold = 40000;
+ uint32_t mclk_strobe_mode_threshold = 40000;
+
+ if (hwmgr->dyn_state.vddc_dependency_on_mclk != NULL) {
+ result = ci_get_dependency_volt_by_clk(hwmgr,
+ hwmgr->dyn_state.vddc_dependency_on_mclk, memory_clock, &memory_level->MinVddc);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find MinVddc voltage value from memory VDDC voltage dependency table", return result);
+ }
+
+ if (NULL != hwmgr->dyn_state.vddci_dependency_on_mclk) {
+ result = ci_get_dependency_volt_by_clk(hwmgr,
+ hwmgr->dyn_state.vddci_dependency_on_mclk,
+ memory_clock,
+ &memory_level->MinVddci);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find MinVddci voltage value from memory VDDCI voltage dependency table", return result);
+ }
+
+ if (NULL != hwmgr->dyn_state.mvdd_dependency_on_mclk) {
+ result = ci_get_dependency_volt_by_clk(hwmgr,
+ hwmgr->dyn_state.mvdd_dependency_on_mclk,
+ memory_clock,
+ &memory_level->MinMvdd);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find MinVddci voltage value from memory MVDD voltage dependency table", return result);
+ }
+
+ memory_level->MinVddcPhases = 1;
+
+ if (data->vddc_phase_shed_control) {
+ ci_populate_phase_value_based_on_mclk(hwmgr, hwmgr->dyn_state.vddc_phase_shed_limits_table,
+ memory_clock, &memory_level->MinVddcPhases);
+ }
+
+ memory_level->EnabledForThrottle = 1;
+ memory_level->EnabledForActivity = 1;
+ memory_level->UpH = data->current_profile_setting.mclk_up_hyst;
+ memory_level->DownH = data->current_profile_setting.mclk_down_hyst;
+ memory_level->VoltageDownH = 0;
+
+ /* Indicates maximum activity level for this performance level.*/
+ memory_level->ActivityLevel = data->current_profile_setting.mclk_activity;
+ memory_level->StutterEnable = 0;
+ memory_level->StrobeEnable = 0;
+ memory_level->EdcReadEnable = 0;
+ memory_level->EdcWriteEnable = 0;
+ memory_level->RttEnable = 0;
+
+ /* default set to low watermark. Highest level will be set to high later.*/
+ memory_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+ data->display_timing.num_existing_displays = hwmgr->display_config->num_display;
+ data->display_timing.vrefresh = hwmgr->display_config->vrefresh;
+
+ /* stutter mode not support on ci */
+
+ /* decide strobe mode*/
+ memory_level->StrobeEnable = (mclk_strobe_mode_threshold != 0) &&
+ (memory_clock <= mclk_strobe_mode_threshold);
+
+ /* decide EDC mode and memory clock ratio*/
+ if (data->is_memory_gddr5) {
+ memory_level->StrobeRatio = ci_get_mclk_frequency_ratio(memory_clock,
+ memory_level->StrobeEnable);
+
+ if ((mclk_edc_enable_threshold != 0) &&
+ (memory_clock > mclk_edc_enable_threshold)) {
+ memory_level->EdcReadEnable = 1;
+ }
+
+ if ((mclk_edc_wr_enable_threshold != 0) &&
+ (memory_clock > mclk_edc_wr_enable_threshold)) {
+ memory_level->EdcWriteEnable = 1;
+ }
+
+ if (memory_level->StrobeEnable) {
+ if (ci_get_mclk_frequency_ratio(memory_clock, 1) >=
+ ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC7) >> 16) & 0xf))
+ dll_state_on = ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC5) >> 1) & 0x1) ? 1 : 0;
+ else
+ dll_state_on = ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC6) >> 1) & 0x1) ? 1 : 0;
+ } else
+ dll_state_on = data->dll_default_on;
+ } else {
+ memory_level->StrobeRatio =
+ ci_get_ddr3_mclk_frequency_ratio(memory_clock);
+ dll_state_on = ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC5) >> 1) & 0x1) ? 1 : 0;
+ }
+
+ result = ci_calculate_mclk_params(hwmgr,
+ memory_clock, memory_level, memory_level->StrobeEnable, dll_state_on);
+
+ if (0 == result) {
+ memory_level->MinVddc = PP_HOST_TO_SMC_UL(memory_level->MinVddc * VOLTAGE_SCALE);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MinVddcPhases);
+ memory_level->MinVddci = PP_HOST_TO_SMC_UL(memory_level->MinVddci * VOLTAGE_SCALE);
+ memory_level->MinMvdd = PP_HOST_TO_SMC_UL(memory_level->MinMvdd * VOLTAGE_SCALE);
+ /* MCLK frequency in units of 10KHz*/
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MclkFrequency);
+ /* Indicates maximum activity level for this performance level.*/
+ CONVERT_FROM_HOST_TO_SMC_US(memory_level->ActivityLevel);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl_1);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl_2);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllAdFuncCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllDqFuncCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MclkPwrmgtCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->DllCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllSs1);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllSs2);
+ }
+
+ return result;
+}
+
+static int ci_populate_all_memory_levels(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ struct smu7_dpm_table *dpm_table = &data->dpm_table;
+ int result;
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t dev_id;
+
+ uint32_t level_array_address = smu_data->dpm_table_start + offsetof(SMU7_Discrete_DpmTable, MemoryLevel);
+ uint32_t level_array_size = sizeof(SMU7_Discrete_MemoryLevel) * SMU7_MAX_LEVELS_MEMORY;
+ SMU7_Discrete_MemoryLevel *levels = smu_data->smc_state_table.MemoryLevel;
+ uint32_t i;
+
+ memset(levels, 0x00, level_array_size);
+
+ for (i = 0; i < dpm_table->mclk_table.count; i++) {
+ PP_ASSERT_WITH_CODE((0 != dpm_table->mclk_table.dpm_levels[i].value),
+ "can not populate memory level as memory clock is zero", return -EINVAL);
+ result = ci_populate_single_memory_level(hwmgr, dpm_table->mclk_table.dpm_levels[i].value,
+ &(smu_data->smc_state_table.MemoryLevel[i]));
+ if (0 != result)
+ return result;
+ }
+
+ smu_data->smc_state_table.MemoryLevel[0].EnabledForActivity = 1;
+
+ dev_id = adev->pdev->device;
+
+ if ((dpm_table->mclk_table.count >= 2)
+ && ((dev_id == 0x67B0) || (dev_id == 0x67B1))) {
+ smu_data->smc_state_table.MemoryLevel[1].MinVddci =
+ smu_data->smc_state_table.MemoryLevel[0].MinVddci;
+ smu_data->smc_state_table.MemoryLevel[1].MinMvdd =
+ smu_data->smc_state_table.MemoryLevel[0].MinMvdd;
+ }
+ smu_data->smc_state_table.MemoryLevel[0].ActivityLevel = 0x1F;
+ CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.MemoryLevel[0].ActivityLevel);
+
+ smu_data->smc_state_table.MemoryDpmLevelCount = (uint8_t)dpm_table->mclk_table.count;
+ data->dpm_level_enable_mask.mclk_dpm_enable_mask = phm_get_dpm_level_enable_mask_value(&dpm_table->mclk_table);
+ smu_data->smc_state_table.MemoryLevel[dpm_table->mclk_table.count-1].DisplayWatermark = PPSMC_DISPLAY_WATERMARK_HIGH;
+
+ result = ci_copy_bytes_to_smc(hwmgr,
+ level_array_address, (uint8_t *)levels, (uint32_t)level_array_size,
+ SMC_RAM_END);
+
+ return result;
+}
+
+static int ci_populate_mvdd_value(struct pp_hwmgr *hwmgr, uint32_t mclk,
+ SMU7_Discrete_VoltageLevel *voltage)
+{
+ const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ uint32_t i = 0;
+
+ if (SMU7_VOLTAGE_CONTROL_NONE != data->mvdd_control) {
+ /* find mvdd value which clock is more than request */
+ for (i = 0; i < hwmgr->dyn_state.mvdd_dependency_on_mclk->count; i++) {
+ if (mclk <= hwmgr->dyn_state.mvdd_dependency_on_mclk->entries[i].clk) {
+ /* Always round to higher voltage. */
+ voltage->Voltage = data->mvdd_voltage_table.entries[i].value;
+ break;
+ }
+ }
+
+ PP_ASSERT_WITH_CODE(i < hwmgr->dyn_state.mvdd_dependency_on_mclk->count,
+ "MVDD Voltage is outside the supported range.", return -EINVAL);
+
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ci_populate_smc_acpi_level(struct pp_hwmgr *hwmgr,
+ SMU7_Discrete_DpmTable *table)
+{
+ int result = 0;
+ const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct pp_atomctrl_clock_dividers_vi dividers;
+
+ SMU7_Discrete_VoltageLevel voltage_level;
+ uint32_t spll_func_cntl = data->clock_registers.vCG_SPLL_FUNC_CNTL;
+ uint32_t spll_func_cntl_2 = data->clock_registers.vCG_SPLL_FUNC_CNTL_2;
+ uint32_t dll_cntl = data->clock_registers.vDLL_CNTL;
+ uint32_t mclk_pwrmgt_cntl = data->clock_registers.vMCLK_PWRMGT_CNTL;
+
+
+ /* The ACPI state should not do DPM on DC (or ever).*/
+ table->ACPILevel.Flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+ if (data->acpi_vddc)
+ table->ACPILevel.MinVddc = PP_HOST_TO_SMC_UL(data->acpi_vddc * VOLTAGE_SCALE);
+ else
+ table->ACPILevel.MinVddc = PP_HOST_TO_SMC_UL(data->min_vddc_in_pptable * VOLTAGE_SCALE);
+
+ table->ACPILevel.MinVddcPhases = data->vddc_phase_shed_control ? 0 : 1;
+ /* assign zero for now*/
+ table->ACPILevel.SclkFrequency = atomctrl_get_reference_clock(hwmgr);
+
+ /* get the engine clock dividers for this clock value*/
+ result = atomctrl_get_engine_pll_dividers_vi(hwmgr,
+ table->ACPILevel.SclkFrequency, &dividers);
+
+ PP_ASSERT_WITH_CODE(result == 0,
+ "Error retrieving Engine Clock dividers from VBIOS.", return result);
+
+ /* divider ID for required SCLK*/
+ table->ACPILevel.SclkDid = (uint8_t)dividers.pll_post_divider;
+ table->ACPILevel.DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+ table->ACPILevel.DeepSleepDivId = 0;
+
+ spll_func_cntl = PHM_SET_FIELD(spll_func_cntl,
+ CG_SPLL_FUNC_CNTL, SPLL_PWRON, 0);
+ spll_func_cntl = PHM_SET_FIELD(spll_func_cntl,
+ CG_SPLL_FUNC_CNTL, SPLL_RESET, 1);
+ spll_func_cntl_2 = PHM_SET_FIELD(spll_func_cntl_2,
+ CG_SPLL_FUNC_CNTL_2, SCLK_MUX_SEL, 4);
+
+ table->ACPILevel.CgSpllFuncCntl = spll_func_cntl;
+ table->ACPILevel.CgSpllFuncCntl2 = spll_func_cntl_2;
+ table->ACPILevel.CgSpllFuncCntl3 = data->clock_registers.vCG_SPLL_FUNC_CNTL_3;
+ table->ACPILevel.CgSpllFuncCntl4 = data->clock_registers.vCG_SPLL_FUNC_CNTL_4;
+ table->ACPILevel.SpllSpreadSpectrum = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM;
+ table->ACPILevel.SpllSpreadSpectrum2 = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2;
+ table->ACPILevel.CcPwrDynRm = 0;
+ table->ACPILevel.CcPwrDynRm1 = 0;
+
+ /* For various features to be enabled/disabled while this level is active.*/
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.Flags);
+ /* SCLK frequency in units of 10KHz*/
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl2);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl3);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl4);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum2);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm1);
+
+
+ /* table->MemoryACPILevel.MinVddcPhases = table->ACPILevel.MinVddcPhases;*/
+ table->MemoryACPILevel.MinVddc = table->ACPILevel.MinVddc;
+ table->MemoryACPILevel.MinVddcPhases = table->ACPILevel.MinVddcPhases;
+
+ if (SMU7_VOLTAGE_CONTROL_NONE == data->vddci_control)
+ table->MemoryACPILevel.MinVddci = table->MemoryACPILevel.MinVddc;
+ else {
+ if (data->acpi_vddci != 0)
+ table->MemoryACPILevel.MinVddci = PP_HOST_TO_SMC_UL(data->acpi_vddci * VOLTAGE_SCALE);
+ else
+ table->MemoryACPILevel.MinVddci = PP_HOST_TO_SMC_UL(data->min_vddci_in_pptable * VOLTAGE_SCALE);
+ }
+
+ if (0 == ci_populate_mvdd_value(hwmgr, 0, &voltage_level))
+ table->MemoryACPILevel.MinMvdd =
+ PP_HOST_TO_SMC_UL(voltage_level.Voltage * VOLTAGE_SCALE);
+ else
+ table->MemoryACPILevel.MinMvdd = 0;
+
+ /* Force reset on DLL*/
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, MRDCK0_RESET, 0x1);
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, MRDCK1_RESET, 0x1);
+
+ /* Disable DLL in ACPIState*/
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, MRDCK0_PDNB, 0);
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, MRDCK1_PDNB, 0);
+
+ /* Enable DLL bypass signal*/
+ dll_cntl = PHM_SET_FIELD(dll_cntl,
+ DLL_CNTL, MRDCK0_BYPASS, 0);
+ dll_cntl = PHM_SET_FIELD(dll_cntl,
+ DLL_CNTL, MRDCK1_BYPASS, 0);
+
+ table->MemoryACPILevel.DllCntl =
+ PP_HOST_TO_SMC_UL(dll_cntl);
+ table->MemoryACPILevel.MclkPwrmgtCntl =
+ PP_HOST_TO_SMC_UL(mclk_pwrmgt_cntl);
+ table->MemoryACPILevel.MpllAdFuncCntl =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_AD_FUNC_CNTL);
+ table->MemoryACPILevel.MpllDqFuncCntl =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_DQ_FUNC_CNTL);
+ table->MemoryACPILevel.MpllFuncCntl =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_FUNC_CNTL);
+ table->MemoryACPILevel.MpllFuncCntl_1 =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_FUNC_CNTL_1);
+ table->MemoryACPILevel.MpllFuncCntl_2 =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_FUNC_CNTL_2);
+ table->MemoryACPILevel.MpllSs1 =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_SS1);
+ table->MemoryACPILevel.MpllSs2 =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_SS2);
+
+ table->MemoryACPILevel.EnabledForThrottle = 0;
+ table->MemoryACPILevel.EnabledForActivity = 0;
+ table->MemoryACPILevel.UpH = 0;
+ table->MemoryACPILevel.DownH = 100;
+ table->MemoryACPILevel.VoltageDownH = 0;
+ /* Indicates maximum activity level for this performance level.*/
+ table->MemoryACPILevel.ActivityLevel = PP_HOST_TO_SMC_US(data->current_profile_setting.mclk_activity);
+
+ table->MemoryACPILevel.StutterEnable = 0;
+ table->MemoryACPILevel.StrobeEnable = 0;
+ table->MemoryACPILevel.EdcReadEnable = 0;
+ table->MemoryACPILevel.EdcWriteEnable = 0;
+ table->MemoryACPILevel.RttEnable = 0;
+
+ return result;
+}
+
+static int ci_populate_smc_uvd_level(struct pp_hwmgr *hwmgr,
+ SMU7_Discrete_DpmTable *table)
+{
+ int result = 0;
+ uint8_t count;
+ struct pp_atomctrl_clock_dividers_vi dividers;
+ struct phm_uvd_clock_voltage_dependency_table *uvd_table =
+ hwmgr->dyn_state.uvd_clock_voltage_dependency_table;
+
+ table->UvdLevelCount = (uint8_t)(uvd_table->count);
+
+ for (count = 0; count < table->UvdLevelCount; count++) {
+ table->UvdLevel[count].VclkFrequency =
+ uvd_table->entries[count].vclk;
+ table->UvdLevel[count].DclkFrequency =
+ uvd_table->entries[count].dclk;
+ table->UvdLevel[count].MinVddc =
+ uvd_table->entries[count].v * VOLTAGE_SCALE;
+ table->UvdLevel[count].MinVddcPhases = 1;
+
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+ table->UvdLevel[count].VclkFrequency, &dividers);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find divide id for Vclk clock", return result);
+
+ table->UvdLevel[count].VclkDivider = (uint8_t)dividers.pll_post_divider;
+
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+ table->UvdLevel[count].DclkFrequency, &dividers);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find divide id for Dclk clock", return result);
+
+ table->UvdLevel[count].DclkDivider = (uint8_t)dividers.pll_post_divider;
+ CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].VclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].DclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_US(table->UvdLevel[count].MinVddc);
+ }
+
+ return result;
+}
+
+static int ci_populate_smc_vce_level(struct pp_hwmgr *hwmgr,
+ SMU7_Discrete_DpmTable *table)
+{
+ int result = -EINVAL;
+ uint8_t count;
+ struct pp_atomctrl_clock_dividers_vi dividers;
+ struct phm_vce_clock_voltage_dependency_table *vce_table =
+ hwmgr->dyn_state.vce_clock_voltage_dependency_table;
+
+ table->VceLevelCount = (uint8_t)(vce_table->count);
+ table->VceBootLevel = 0;
+
+ for (count = 0; count < table->VceLevelCount; count++) {
+ table->VceLevel[count].Frequency = vce_table->entries[count].evclk;
+ table->VceLevel[count].MinVoltage =
+ vce_table->entries[count].v * VOLTAGE_SCALE;
+ table->VceLevel[count].MinPhases = 1;
+
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+ table->VceLevel[count].Frequency, &dividers);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find divide id for VCE engine clock",
+ return result);
+
+ table->VceLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VceLevel[count].Frequency);
+ CONVERT_FROM_HOST_TO_SMC_US(table->VceLevel[count].MinVoltage);
+ }
+ return result;
+}
+
+static int ci_populate_smc_acp_level(struct pp_hwmgr *hwmgr,
+ SMU7_Discrete_DpmTable *table)
+{
+ int result = -EINVAL;
+ uint8_t count;
+ struct pp_atomctrl_clock_dividers_vi dividers;
+ struct phm_acp_clock_voltage_dependency_table *acp_table =
+ hwmgr->dyn_state.acp_clock_voltage_dependency_table;
+
+ table->AcpLevelCount = (uint8_t)(acp_table->count);
+ table->AcpBootLevel = 0;
+
+ for (count = 0; count < table->AcpLevelCount; count++) {
+ table->AcpLevel[count].Frequency = acp_table->entries[count].acpclk;
+ table->AcpLevel[count].MinVoltage = acp_table->entries[count].v;
+ table->AcpLevel[count].MinPhases = 1;
+
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+ table->AcpLevel[count].Frequency, &dividers);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find divide id for engine clock", return result);
+
+ table->AcpLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->AcpLevel[count].Frequency);
+ CONVERT_FROM_HOST_TO_SMC_US(table->AcpLevel[count].MinVoltage);
+ }
+ return result;
+}
+
+static int ci_populate_memory_timing_parameters(
+ struct pp_hwmgr *hwmgr,
+ uint32_t engine_clock,
+ uint32_t memory_clock,
+ struct SMU7_Discrete_MCArbDramTimingTableEntry *arb_regs
+ )
+{
+ uint32_t dramTiming;
+ uint32_t dramTiming2;
+ uint32_t burstTime;
+ int result;
+
+ result = atomctrl_set_engine_dram_timings_rv770(hwmgr,
+ engine_clock, memory_clock);
+
+ PP_ASSERT_WITH_CODE(result == 0,
+ "Error calling VBIOS to set DRAM_TIMING.", return result);
+
+ dramTiming = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING);
+ dramTiming2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2);
+ burstTime = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0);
+
+ arb_regs->McArbDramTiming = PP_HOST_TO_SMC_UL(dramTiming);
+ arb_regs->McArbDramTiming2 = PP_HOST_TO_SMC_UL(dramTiming2);
+ arb_regs->McArbBurstTime = (uint8_t)burstTime;
+
+ return 0;
+}
+
+static int ci_program_memory_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ int result = 0;
+ SMU7_Discrete_MCArbDramTimingTable arb_regs;
+ uint32_t i, j;
+
+ memset(&arb_regs, 0x00, sizeof(SMU7_Discrete_MCArbDramTimingTable));
+
+ for (i = 0; i < data->dpm_table.sclk_table.count; i++) {
+ for (j = 0; j < data->dpm_table.mclk_table.count; j++) {
+ result = ci_populate_memory_timing_parameters
+ (hwmgr, data->dpm_table.sclk_table.dpm_levels[i].value,
+ data->dpm_table.mclk_table.dpm_levels[j].value,
+ &arb_regs.entries[i][j]);
+
+ if (0 != result)
+ break;
+ }
+ }
+
+ if (0 == result) {
+ result = ci_copy_bytes_to_smc(
+ hwmgr,
+ smu_data->arb_table_start,
+ (uint8_t *)&arb_regs,
+ sizeof(SMU7_Discrete_MCArbDramTimingTable),
+ SMC_RAM_END
+ );
+ }
+
+ return result;
+}
+
+static int ci_populate_smc_boot_level(struct pp_hwmgr *hwmgr,
+ SMU7_Discrete_DpmTable *table)
+{
+ int result = 0;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+
+ table->GraphicsBootLevel = 0;
+ table->MemoryBootLevel = 0;
+
+ /* find boot level from dpm table*/
+ result = phm_find_boot_level(&(data->dpm_table.sclk_table),
+ data->vbios_boot_state.sclk_bootup_value,
+ (uint32_t *)&(smu_data->smc_state_table.GraphicsBootLevel));
+
+ if (0 != result) {
+ smu_data->smc_state_table.GraphicsBootLevel = 0;
+ pr_err("VBIOS did not find boot engine clock value in dependency table. Using Graphics DPM level 0!\n");
+ result = 0;
+ }
+
+ result = phm_find_boot_level(&(data->dpm_table.mclk_table),
+ data->vbios_boot_state.mclk_bootup_value,
+ (uint32_t *)&(smu_data->smc_state_table.MemoryBootLevel));
+
+ if (0 != result) {
+ smu_data->smc_state_table.MemoryBootLevel = 0;
+ pr_err("VBIOS did not find boot engine clock value in dependency table. Using Memory DPM level 0!\n");
+ result = 0;
+ }
+
+ table->BootVddc = data->vbios_boot_state.vddc_bootup_value;
+ table->BootVddci = data->vbios_boot_state.vddci_bootup_value;
+ table->BootMVdd = data->vbios_boot_state.mvdd_bootup_value;
+
+ return result;
+}
+
+static int ci_populate_mc_reg_address(struct pp_hwmgr *hwmgr,
+ SMU7_Discrete_MCRegisters *mc_reg_table)
+{
+ const struct ci_smumgr *smu_data = (struct ci_smumgr *)hwmgr->smu_backend;
+
+ uint32_t i, j;
+
+ for (i = 0, j = 0; j < smu_data->mc_reg_table.last; j++) {
+ if (smu_data->mc_reg_table.validflag & 1<<j) {
+ PP_ASSERT_WITH_CODE(i < SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE,
+ "Index of mc_reg_table->address[] array out of boundary", return -EINVAL);
+ mc_reg_table->address[i].s0 =
+ PP_HOST_TO_SMC_US(smu_data->mc_reg_table.mc_reg_address[j].s0);
+ mc_reg_table->address[i].s1 =
+ PP_HOST_TO_SMC_US(smu_data->mc_reg_table.mc_reg_address[j].s1);
+ i++;
+ }
+ }
+
+ mc_reg_table->last = (uint8_t)i;
+
+ return 0;
+}
+
+static void ci_convert_mc_registers(
+ const struct ci_mc_reg_entry *entry,
+ SMU7_Discrete_MCRegisterSet *data,
+ uint32_t num_entries, uint32_t valid_flag)
+{
+ uint32_t i, j;
+
+ for (i = 0, j = 0; j < num_entries; j++) {
+ if (valid_flag & 1<<j) {
+ data->value[i] = PP_HOST_TO_SMC_UL(entry->mc_data[j]);
+ i++;
+ }
+ }
+}
+
+static int ci_convert_mc_reg_table_entry_to_smc(
+ struct pp_hwmgr *hwmgr,
+ const uint32_t memory_clock,
+ SMU7_Discrete_MCRegisterSet *mc_reg_table_data
+ )
+{
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ uint32_t i = 0;
+
+ for (i = 0; i < smu_data->mc_reg_table.num_entries; i++) {
+ if (memory_clock <=
+ smu_data->mc_reg_table.mc_reg_table_entry[i].mclk_max) {
+ break;
+ }
+ }
+
+ if ((i == smu_data->mc_reg_table.num_entries) && (i > 0))
+ --i;
+
+ ci_convert_mc_registers(&smu_data->mc_reg_table.mc_reg_table_entry[i],
+ mc_reg_table_data, smu_data->mc_reg_table.last,
+ smu_data->mc_reg_table.validflag);
+
+ return 0;
+}
+
+static int ci_convert_mc_reg_table_to_smc(struct pp_hwmgr *hwmgr,
+ SMU7_Discrete_MCRegisters *mc_regs)
+{
+ int result = 0;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ int res;
+ uint32_t i;
+
+ for (i = 0; i < data->dpm_table.mclk_table.count; i++) {
+ res = ci_convert_mc_reg_table_entry_to_smc(
+ hwmgr,
+ data->dpm_table.mclk_table.dpm_levels[i].value,
+ &mc_regs->data[i]
+ );
+
+ if (0 != res)
+ result = res;
+ }
+
+ return result;
+}
+
+static int ci_update_and_upload_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t address;
+ int32_t result;
+
+ if (0 == (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK))
+ return 0;
+
+
+ memset(&smu_data->mc_regs, 0, sizeof(SMU7_Discrete_MCRegisters));
+
+ result = ci_convert_mc_reg_table_to_smc(hwmgr, &(smu_data->mc_regs));
+
+ if (result != 0)
+ return result;
+
+ address = smu_data->mc_reg_table_start + (uint32_t)offsetof(SMU7_Discrete_MCRegisters, data[0]);
+
+ return ci_copy_bytes_to_smc(hwmgr, address,
+ (uint8_t *)&smu_data->mc_regs.data[0],
+ sizeof(SMU7_Discrete_MCRegisterSet) * data->dpm_table.mclk_table.count,
+ SMC_RAM_END);
+}
+
+static int ci_populate_initial_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+
+ memset(&smu_data->mc_regs, 0x00, sizeof(SMU7_Discrete_MCRegisters));
+ result = ci_populate_mc_reg_address(hwmgr, &(smu_data->mc_regs));
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize MCRegTable for the MC register addresses!", return result;);
+
+ result = ci_convert_mc_reg_table_to_smc(hwmgr, &smu_data->mc_regs);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize MCRegTable for driver state!", return result;);
+
+ return ci_copy_bytes_to_smc(hwmgr, smu_data->mc_reg_table_start,
+ (uint8_t *)&smu_data->mc_regs, sizeof(SMU7_Discrete_MCRegisters), SMC_RAM_END);
+}
+
+static int ci_populate_smc_initial_state(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ uint8_t count, level;
+
+ count = (uint8_t)(hwmgr->dyn_state.vddc_dependency_on_sclk->count);
+
+ for (level = 0; level < count; level++) {
+ if (hwmgr->dyn_state.vddc_dependency_on_sclk->entries[level].clk
+ >= data->vbios_boot_state.sclk_bootup_value) {
+ smu_data->smc_state_table.GraphicsBootLevel = level;
+ break;
+ }
+ }
+
+ count = (uint8_t)(hwmgr->dyn_state.vddc_dependency_on_mclk->count);
+
+ for (level = 0; level < count; level++) {
+ if (hwmgr->dyn_state.vddc_dependency_on_mclk->entries[level].clk
+ >= data->vbios_boot_state.mclk_bootup_value) {
+ smu_data->smc_state_table.MemoryBootLevel = level;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int ci_populate_smc_svi2_config(struct pp_hwmgr *hwmgr,
+ SMU7_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control)
+ table->SVI2Enable = 1;
+ else
+ table->SVI2Enable = 0;
+ return 0;
+}
+
+static int ci_start_smc(struct pp_hwmgr *hwmgr)
+{
+ /* set smc instruct start point at 0x0 */
+ ci_program_jump_on_start(hwmgr);
+
+ /* enable smc clock */
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
+
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SMC_SYSCON_RESET_CNTL, rst_reg, 0);
+
+ PHM_WAIT_INDIRECT_FIELD(hwmgr, SMC_IND, FIRMWARE_FLAGS,
+ INTERRUPTS_ENABLED, 1);
+
+ return 0;
+}
+
+static int ci_populate_vr_config(struct pp_hwmgr *hwmgr, SMU7_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint16_t config;
+
+ config = VR_SVI2_PLANE_1;
+ table->VRConfig |= (config<<VRCONF_VDDGFX_SHIFT);
+
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
+ config = VR_SVI2_PLANE_2;
+ table->VRConfig |= config;
+ } else {
+ pr_info("VDDCshould be on SVI2 controller!");
+ }
+
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) {
+ config = VR_SVI2_PLANE_2;
+ table->VRConfig |= (config<<VRCONF_VDDCI_SHIFT);
+ } else if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
+ config = VR_SMIO_PATTERN_1;
+ table->VRConfig |= (config<<VRCONF_VDDCI_SHIFT);
+ }
+
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
+ config = VR_SMIO_PATTERN_2;
+ table->VRConfig |= (config<<VRCONF_MVDD_SHIFT);
+ }
+
+ return 0;
+}
+
+static int ci_init_smc_table(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ SMU7_Discrete_DpmTable *table = &(smu_data->smc_state_table);
+ struct pp_atomctrl_gpio_pin_assignment gpio_pin;
+ u32 i;
+
+ ci_initialize_power_tune_defaults(hwmgr);
+ memset(&(smu_data->smc_state_table), 0x00, sizeof(smu_data->smc_state_table));
+
+ if (SMU7_VOLTAGE_CONTROL_NONE != data->voltage_control)
+ ci_populate_smc_voltage_tables(hwmgr, table);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition))
+ table->SystemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_StepVddc))
+ table->SystemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+ if (data->is_memory_gddr5)
+ table->SystemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
+
+ if (data->ulv_supported) {
+ result = ci_populate_ulv_state(hwmgr, &(table->Ulv));
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize ULV state!", return result);
+
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixCG_ULV_PARAMETER, 0x40035);
+ }
+
+ result = ci_populate_all_graphic_levels(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize Graphics Level!", return result);
+
+ result = ci_populate_all_memory_levels(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize Memory Level!", return result);
+
+ result = ci_populate_smc_link_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize Link Level!", return result);
+
+ result = ci_populate_smc_acpi_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize ACPI Level!", return result);
+
+ result = ci_populate_smc_vce_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize VCE Level!", return result);
+
+ result = ci_populate_smc_acp_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize ACP Level!", return result);
+
+ /* Since only the initial state is completely set up at this point (the other states are just copies of the boot state) we only */
+ /* need to populate the ARB settings for the initial state. */
+ result = ci_program_memory_timing_parameters(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to Write ARB settings for the initial state.", return result);
+
+ result = ci_populate_smc_uvd_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize UVD Level!", return result);
+
+ table->UvdBootLevel = 0;
+ table->VceBootLevel = 0;
+ table->AcpBootLevel = 0;
+ table->SamuBootLevel = 0;
+
+ table->GraphicsBootLevel = 0;
+ table->MemoryBootLevel = 0;
+
+ result = ci_populate_smc_boot_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize Boot Level!", return result);
+
+ result = ci_populate_smc_initial_state(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result, "Failed to initialize Boot State!", return result);
+
+ result = ci_populate_bapm_parameters_in_dpm_table(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result, "Failed to populate BAPM Parameters!", return result);
+
+ table->UVDInterval = 1;
+ table->VCEInterval = 1;
+ table->ACPInterval = 1;
+ table->SAMUInterval = 1;
+ table->GraphicsVoltageChangeEnable = 1;
+ table->GraphicsThermThrottleEnable = 1;
+ table->GraphicsInterval = 1;
+ table->VoltageInterval = 1;
+ table->ThermalInterval = 1;
+
+ table->TemperatureLimitHigh =
+ (data->thermal_temp_setting.temperature_high *
+ SMU7_Q88_FORMAT_CONVERSION_UNIT) / PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ table->TemperatureLimitLow =
+ (data->thermal_temp_setting.temperature_low *
+ SMU7_Q88_FORMAT_CONVERSION_UNIT) / PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+ table->MemoryVoltageChangeEnable = 1;
+ table->MemoryInterval = 1;
+ table->VoltageResponseTime = 0;
+ table->VddcVddciDelta = 4000;
+ table->PhaseResponseTime = 0;
+ table->MemoryThermThrottleEnable = 1;
+
+ PP_ASSERT_WITH_CODE((1 <= data->dpm_table.pcie_speed_table.count),
+ "There must be 1 or more PCIE levels defined in PPTable.",
+ return -EINVAL);
+
+ table->PCIeBootLinkLevel = (uint8_t)data->dpm_table.pcie_speed_table.count;
+ table->PCIeGenInterval = 1;
+
+ result = ci_populate_vr_config(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to populate VRConfig setting!", return result);
+ data->vr_config = table->VRConfig;
+
+ ci_populate_smc_svi2_config(hwmgr, table);
+
+ for (i = 0; i < SMU7_MAX_ENTRIES_SMIO; i++)
+ CONVERT_FROM_HOST_TO_SMC_UL(table->Smio[i]);
+
+ table->ThermGpio = 17;
+ table->SclkStepSize = 0x4000;
+ if (atomctrl_get_pp_assign_pin(hwmgr, VDDC_VRHOT_GPIO_PINID, &gpio_pin)) {
+ table->VRHotGpio = gpio_pin.uc_gpio_pin_bit_shift;
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_RegulatorHot);
+ } else {
+ table->VRHotGpio = SMU7_UNUSED_GPIO_PIN;
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_RegulatorHot);
+ }
+
+ table->AcDcGpio = SMU7_UNUSED_GPIO_PIN;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SystemFlags);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VRConfig);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMaskVddcVid);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMaskVddcPhase);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMaskVddciVid);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMaskMvddVid);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SclkStepSize);
+ CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitHigh);
+ CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitLow);
+ table->VddcVddciDelta = PP_HOST_TO_SMC_US(table->VddcVddciDelta);
+ CONVERT_FROM_HOST_TO_SMC_US(table->VoltageResponseTime);
+ CONVERT_FROM_HOST_TO_SMC_US(table->PhaseResponseTime);
+
+ table->BootVddc = PP_HOST_TO_SMC_US(table->BootVddc * VOLTAGE_SCALE);
+ table->BootVddci = PP_HOST_TO_SMC_US(table->BootVddci * VOLTAGE_SCALE);
+ table->BootMVdd = PP_HOST_TO_SMC_US(table->BootMVdd * VOLTAGE_SCALE);
+
+ /* Upload all dpm data to SMC memory.(dpm level, dpm level count etc) */
+ result = ci_copy_bytes_to_smc(hwmgr, smu_data->dpm_table_start +
+ offsetof(SMU7_Discrete_DpmTable, SystemFlags),
+ (uint8_t *)&(table->SystemFlags),
+ sizeof(SMU7_Discrete_DpmTable)-3 * sizeof(SMU7_PIDController),
+ SMC_RAM_END);
+
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to upload dpm data to SMC memory!", return result;);
+
+ result = ci_populate_initial_mc_reg_table(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Failed to populate initialize MC Reg table!", return result);
+
+ result = ci_populate_pm_fuses(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to populate PM fuses to SMC memory!", return result);
+
+ ci_start_smc(hwmgr);
+
+ return 0;
+}
+
+static int ci_thermal_setup_fan_table(struct pp_hwmgr *hwmgr)
+{
+ struct ci_smumgr *ci_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ SMU7_Discrete_FanTable fan_table = { FDO_MODE_HARDWARE };
+ uint32_t duty100;
+ uint32_t t_diff1, t_diff2, pwm_diff1, pwm_diff2;
+ uint16_t fdo_min, slope1, slope2;
+ uint32_t reference_clock;
+ int res;
+ uint64_t tmp64;
+
+ if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl))
+ return 0;
+
+ if (hwmgr->thermal_controller.fanInfo.bNoFan) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
+ if (0 == ci_data->fan_table_start) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
+ duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_FDO_CTRL1, FMAX_DUTY100);
+
+ if (0 == duty100) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
+ tmp64 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin * duty100;
+ do_div(tmp64, 10000);
+ fdo_min = (uint16_t)tmp64;
+
+ t_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usTMed - hwmgr->thermal_controller.advanceFanControlParameters.usTMin;
+ t_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usTHigh - hwmgr->thermal_controller.advanceFanControlParameters.usTMed;
+
+ pwm_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed - hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin;
+ pwm_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh - hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed;
+
+ slope1 = (uint16_t)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100);
+ slope2 = (uint16_t)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100);
+
+ fan_table.TempMin = cpu_to_be16((50 + hwmgr->thermal_controller.advanceFanControlParameters.usTMin) / 100);
+ fan_table.TempMed = cpu_to_be16((50 + hwmgr->thermal_controller.advanceFanControlParameters.usTMed) / 100);
+ fan_table.TempMax = cpu_to_be16((50 + hwmgr->thermal_controller.advanceFanControlParameters.usTMax) / 100);
+
+ fan_table.Slope1 = cpu_to_be16(slope1);
+ fan_table.Slope2 = cpu_to_be16(slope2);
+
+ fan_table.FdoMin = cpu_to_be16(fdo_min);
+
+ fan_table.HystDown = cpu_to_be16(hwmgr->thermal_controller.advanceFanControlParameters.ucTHyst);
+
+ fan_table.HystUp = cpu_to_be16(1);
+
+ fan_table.HystSlope = cpu_to_be16(1);
+
+ fan_table.TempRespLim = cpu_to_be16(5);
+
+ reference_clock = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev);
+
+ fan_table.RefreshPeriod = cpu_to_be32((hwmgr->thermal_controller.advanceFanControlParameters.ulCycleDelay * reference_clock) / 1600);
+
+ fan_table.FdoMax = cpu_to_be16((uint16_t)duty100);
+
+ fan_table.TempSrc = (uint8_t)PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_MULT_THERMAL_CTRL, TEMP_SEL);
+
+ res = ci_copy_bytes_to_smc(hwmgr, ci_data->fan_table_start, (uint8_t *)&fan_table, (uint32_t)sizeof(fan_table), SMC_RAM_END);
+
+ return 0;
+}
+
+static int ci_program_mem_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (data->need_update_smu7_dpm_table &
+ (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_OD_UPDATE_MCLK))
+ return ci_program_memory_timing_parameters(hwmgr);
+
+ return 0;
+}
+
+static int ci_update_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+
+ int result = 0;
+ uint32_t low_sclk_interrupt_threshold = 0;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkThrottleLowNotification)
+ && (data->low_sclk_interrupt_threshold != 0)) {
+ low_sclk_interrupt_threshold =
+ data->low_sclk_interrupt_threshold;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(low_sclk_interrupt_threshold);
+
+ result = ci_copy_bytes_to_smc(
+ hwmgr,
+ smu_data->dpm_table_start +
+ offsetof(SMU7_Discrete_DpmTable,
+ LowSclkInterruptT),
+ (uint8_t *)&low_sclk_interrupt_threshold,
+ sizeof(uint32_t),
+ SMC_RAM_END);
+ }
+
+ result = ci_update_and_upload_mc_reg_table(hwmgr);
+
+ PP_ASSERT_WITH_CODE((0 == result), "Failed to upload MC reg table!", return result);
+
+ result = ci_program_mem_timing_parameters(hwmgr);
+ PP_ASSERT_WITH_CODE((result == 0),
+ "Failed to program memory timing parameters!",
+ );
+
+ return result;
+}
+
+static uint32_t ci_get_offsetof(uint32_t type, uint32_t member)
+{
+ switch (type) {
+ case SMU_SoftRegisters:
+ switch (member) {
+ case HandshakeDisables:
+ return offsetof(SMU7_SoftRegisters, HandshakeDisables);
+ case VoltageChangeTimeout:
+ return offsetof(SMU7_SoftRegisters, VoltageChangeTimeout);
+ case AverageGraphicsActivity:
+ return offsetof(SMU7_SoftRegisters, AverageGraphicsA);
+ case AverageMemoryActivity:
+ return offsetof(SMU7_SoftRegisters, AverageMemoryA);
+ case PreVBlankGap:
+ return offsetof(SMU7_SoftRegisters, PreVBlankGap);
+ case VBlankTimeout:
+ return offsetof(SMU7_SoftRegisters, VBlankTimeout);
+ case DRAM_LOG_ADDR_H:
+ return offsetof(SMU7_SoftRegisters, DRAM_LOG_ADDR_H);
+ case DRAM_LOG_ADDR_L:
+ return offsetof(SMU7_SoftRegisters, DRAM_LOG_ADDR_L);
+ case DRAM_LOG_PHY_ADDR_H:
+ return offsetof(SMU7_SoftRegisters, DRAM_LOG_PHY_ADDR_H);
+ case DRAM_LOG_PHY_ADDR_L:
+ return offsetof(SMU7_SoftRegisters, DRAM_LOG_PHY_ADDR_L);
+ case DRAM_LOG_BUFF_SIZE:
+ return offsetof(SMU7_SoftRegisters, DRAM_LOG_BUFF_SIZE);
+ }
+ break;
+ case SMU_Discrete_DpmTable:
+ switch (member) {
+ case LowSclkInterruptThreshold:
+ return offsetof(SMU7_Discrete_DpmTable, LowSclkInterruptT);
+ }
+ break;
+ }
+ pr_debug("can't get the offset of type %x member %x\n", type, member);
+ return 0;
+}
+
+static uint32_t ci_get_mac_definition(uint32_t value)
+{
+ switch (value) {
+ case SMU_MAX_LEVELS_GRAPHICS:
+ return SMU7_MAX_LEVELS_GRAPHICS;
+ case SMU_MAX_LEVELS_MEMORY:
+ return SMU7_MAX_LEVELS_MEMORY;
+ case SMU_MAX_LEVELS_LINK:
+ return SMU7_MAX_LEVELS_LINK;
+ case SMU_MAX_ENTRIES_SMIO:
+ return SMU7_MAX_ENTRIES_SMIO;
+ case SMU_MAX_LEVELS_VDDC:
+ return SMU7_MAX_LEVELS_VDDC;
+ case SMU_MAX_LEVELS_VDDCI:
+ return SMU7_MAX_LEVELS_VDDCI;
+ case SMU_MAX_LEVELS_MVDD:
+ return SMU7_MAX_LEVELS_MVDD;
+ }
+
+ pr_debug("can't get the mac of %x\n", value);
+ return 0;
+}
+
+static int ci_load_smc_ucode(struct pp_hwmgr *hwmgr)
+{
+ uint32_t byte_count, start_addr;
+ uint8_t *src;
+ uint32_t data;
+
+ struct cgs_firmware_info info = {0};
+
+ cgs_get_firmware_info(hwmgr->device, CGS_UCODE_ID_SMU, &info);
+
+ hwmgr->is_kicker = info.is_kicker;
+ hwmgr->smu_version = info.version;
+ byte_count = info.image_size;
+ src = (uint8_t *)info.kptr;
+ start_addr = info.ucode_start_address;
+
+ if (byte_count > SMC_RAM_END) {
+ pr_err("SMC address is beyond the SMC RAM area.\n");
+ return -EINVAL;
+ }
+
+ cgs_write_register(hwmgr->device, mmSMC_IND_INDEX_0, start_addr);
+ PHM_WRITE_FIELD(hwmgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 1);
+
+ for (; byte_count >= 4; byte_count -= 4) {
+ data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
+ cgs_write_register(hwmgr->device, mmSMC_IND_DATA_0, data);
+ src += 4;
+ }
+ PHM_WRITE_FIELD(hwmgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
+
+ if (0 != byte_count) {
+ pr_err("SMC size must be divisible by 4\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ci_upload_firmware(struct pp_hwmgr *hwmgr)
+{
+ if (ci_is_smc_ram_running(hwmgr)) {
+ pr_info("smc is running, no need to load smc firmware\n");
+ return 0;
+ }
+ PHM_WAIT_INDIRECT_FIELD(hwmgr, SMC_IND, RCU_UC_EVENTS,
+ boot_seq_done, 1);
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SMC_SYSCON_MISC_CNTL,
+ pre_fetcher_en, 1);
+
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 1);
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SMC_SYSCON_RESET_CNTL, rst_reg, 1);
+ return ci_load_smc_ucode(hwmgr);
+}
+
+static int ci_process_firmware_header(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct ci_smumgr *ci_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+
+ uint32_t tmp = 0;
+ int result;
+ bool error = false;
+
+ if (ci_upload_firmware(hwmgr))
+ return -EINVAL;
+
+ result = ci_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU7_Firmware_Header, DpmTable),
+ &tmp, SMC_RAM_END);
+
+ if (0 == result)
+ ci_data->dpm_table_start = tmp;
+
+ error |= (0 != result);
+
+ result = ci_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU7_Firmware_Header, SoftRegisters),
+ &tmp, SMC_RAM_END);
+
+ if (0 == result) {
+ data->soft_regs_start = tmp;
+ ci_data->soft_regs_start = tmp;
+ }
+
+ error |= (0 != result);
+
+ result = ci_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU7_Firmware_Header, mcRegisterTable),
+ &tmp, SMC_RAM_END);
+
+ if (0 == result)
+ ci_data->mc_reg_table_start = tmp;
+
+ result = ci_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU7_Firmware_Header, FanTable),
+ &tmp, SMC_RAM_END);
+
+ if (0 == result)
+ ci_data->fan_table_start = tmp;
+
+ error |= (0 != result);
+
+ result = ci_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU7_Firmware_Header, mcArbDramTimingTable),
+ &tmp, SMC_RAM_END);
+
+ if (0 == result)
+ ci_data->arb_table_start = tmp;
+
+ error |= (0 != result);
+
+ result = ci_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU7_Firmware_Header, Version),
+ &tmp, SMC_RAM_END);
+
+ if (0 == result)
+ hwmgr->microcode_version_info.SMC = tmp;
+
+ error |= (0 != result);
+
+ return error ? 1 : 0;
+}
+
+static uint8_t ci_get_memory_modile_index(struct pp_hwmgr *hwmgr)
+{
+ return (uint8_t) (0xFF & (cgs_read_register(hwmgr->device, mmBIOS_SCRATCH_4) >> 16));
+}
+
+static bool ci_check_s0_mc_reg_index(uint16_t in_reg, uint16_t *out_reg)
+{
+ bool result = true;
+
+ switch (in_reg) {
+ case mmMC_SEQ_RAS_TIMING:
+ *out_reg = mmMC_SEQ_RAS_TIMING_LP;
+ break;
+
+ case mmMC_SEQ_DLL_STBY:
+ *out_reg = mmMC_SEQ_DLL_STBY_LP;
+ break;
+
+ case mmMC_SEQ_G5PDX_CMD0:
+ *out_reg = mmMC_SEQ_G5PDX_CMD0_LP;
+ break;
+
+ case mmMC_SEQ_G5PDX_CMD1:
+ *out_reg = mmMC_SEQ_G5PDX_CMD1_LP;
+ break;
+
+ case mmMC_SEQ_G5PDX_CTRL:
+ *out_reg = mmMC_SEQ_G5PDX_CTRL_LP;
+ break;
+
+ case mmMC_SEQ_CAS_TIMING:
+ *out_reg = mmMC_SEQ_CAS_TIMING_LP;
+ break;
+
+ case mmMC_SEQ_MISC_TIMING:
+ *out_reg = mmMC_SEQ_MISC_TIMING_LP;
+ break;
+
+ case mmMC_SEQ_MISC_TIMING2:
+ *out_reg = mmMC_SEQ_MISC_TIMING2_LP;
+ break;
+
+ case mmMC_SEQ_PMG_DVS_CMD:
+ *out_reg = mmMC_SEQ_PMG_DVS_CMD_LP;
+ break;
+
+ case mmMC_SEQ_PMG_DVS_CTL:
+ *out_reg = mmMC_SEQ_PMG_DVS_CTL_LP;
+ break;
+
+ case mmMC_SEQ_RD_CTL_D0:
+ *out_reg = mmMC_SEQ_RD_CTL_D0_LP;
+ break;
+
+ case mmMC_SEQ_RD_CTL_D1:
+ *out_reg = mmMC_SEQ_RD_CTL_D1_LP;
+ break;
+
+ case mmMC_SEQ_WR_CTL_D0:
+ *out_reg = mmMC_SEQ_WR_CTL_D0_LP;
+ break;
+
+ case mmMC_SEQ_WR_CTL_D1:
+ *out_reg = mmMC_SEQ_WR_CTL_D1_LP;
+ break;
+
+ case mmMC_PMG_CMD_EMRS:
+ *out_reg = mmMC_SEQ_PMG_CMD_EMRS_LP;
+ break;
+
+ case mmMC_PMG_CMD_MRS:
+ *out_reg = mmMC_SEQ_PMG_CMD_MRS_LP;
+ break;
+
+ case mmMC_PMG_CMD_MRS1:
+ *out_reg = mmMC_SEQ_PMG_CMD_MRS1_LP;
+ break;
+
+ case mmMC_SEQ_PMG_TIMING:
+ *out_reg = mmMC_SEQ_PMG_TIMING_LP;
+ break;
+
+ case mmMC_PMG_CMD_MRS2:
+ *out_reg = mmMC_SEQ_PMG_CMD_MRS2_LP;
+ break;
+
+ case mmMC_SEQ_WR_CTL_2:
+ *out_reg = mmMC_SEQ_WR_CTL_2_LP;
+ break;
+
+ default:
+ result = false;
+ break;
+ }
+
+ return result;
+}
+
+static int ci_set_s0_mc_reg_index(struct ci_mc_reg_table *table)
+{
+ uint32_t i;
+ uint16_t address;
+
+ for (i = 0; i < table->last; i++) {
+ table->mc_reg_address[i].s0 =
+ ci_check_s0_mc_reg_index(table->mc_reg_address[i].s1, &address)
+ ? address : table->mc_reg_address[i].s1;
+ }
+ return 0;
+}
+
+static int ci_copy_vbios_smc_reg_table(const pp_atomctrl_mc_reg_table *table,
+ struct ci_mc_reg_table *ni_table)
+{
+ uint8_t i, j;
+
+ PP_ASSERT_WITH_CODE((table->last <= SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+ "Invalid VramInfo table.", return -EINVAL);
+ PP_ASSERT_WITH_CODE((table->num_entries <= MAX_AC_TIMING_ENTRIES),
+ "Invalid VramInfo table.", return -EINVAL);
+
+ for (i = 0; i < table->last; i++)
+ ni_table->mc_reg_address[i].s1 = table->mc_reg_address[i].s1;
+
+ ni_table->last = table->last;
+
+ for (i = 0; i < table->num_entries; i++) {
+ ni_table->mc_reg_table_entry[i].mclk_max =
+ table->mc_reg_table_entry[i].mclk_max;
+ for (j = 0; j < table->last; j++) {
+ ni_table->mc_reg_table_entry[i].mc_data[j] =
+ table->mc_reg_table_entry[i].mc_data[j];
+ }
+ }
+
+ ni_table->num_entries = table->num_entries;
+
+ return 0;
+}
+
+static int ci_set_mc_special_registers(struct pp_hwmgr *hwmgr,
+ struct ci_mc_reg_table *table)
+{
+ uint8_t i, j, k;
+ uint32_t temp_reg;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ for (i = 0, j = table->last; i < table->last; i++) {
+ PP_ASSERT_WITH_CODE((j < SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+ "Invalid VramInfo table.", return -EINVAL);
+
+ switch (table->mc_reg_address[i].s1) {
+
+ case mmMC_SEQ_MISC1:
+ temp_reg = cgs_read_register(hwmgr->device, mmMC_PMG_CMD_EMRS);
+ table->mc_reg_address[j].s1 = mmMC_PMG_CMD_EMRS;
+ table->mc_reg_address[j].s0 = mmMC_SEQ_PMG_CMD_EMRS_LP;
+ for (k = 0; k < table->num_entries; k++) {
+ table->mc_reg_table_entry[k].mc_data[j] =
+ ((temp_reg & 0xffff0000)) |
+ ((table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16);
+ }
+ j++;
+
+ PP_ASSERT_WITH_CODE((j < SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+ "Invalid VramInfo table.", return -EINVAL);
+ temp_reg = cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS);
+ table->mc_reg_address[j].s1 = mmMC_PMG_CMD_MRS;
+ table->mc_reg_address[j].s0 = mmMC_SEQ_PMG_CMD_MRS_LP;
+ for (k = 0; k < table->num_entries; k++) {
+ table->mc_reg_table_entry[k].mc_data[j] =
+ (temp_reg & 0xffff0000) |
+ (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
+
+ if (!data->is_memory_gddr5)
+ table->mc_reg_table_entry[k].mc_data[j] |= 0x100;
+ }
+ j++;
+
+ if (!data->is_memory_gddr5) {
+ PP_ASSERT_WITH_CODE((j < SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+ "Invalid VramInfo table.", return -EINVAL);
+ table->mc_reg_address[j].s1 = mmMC_PMG_AUTO_CMD;
+ table->mc_reg_address[j].s0 = mmMC_PMG_AUTO_CMD;
+ for (k = 0; k < table->num_entries; k++) {
+ table->mc_reg_table_entry[k].mc_data[j] =
+ (table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16;
+ }
+ j++;
+ }
+
+ break;
+
+ case mmMC_SEQ_RESERVE_M:
+ temp_reg = cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS1);
+ table->mc_reg_address[j].s1 = mmMC_PMG_CMD_MRS1;
+ table->mc_reg_address[j].s0 = mmMC_SEQ_PMG_CMD_MRS1_LP;
+ for (k = 0; k < table->num_entries; k++) {
+ table->mc_reg_table_entry[k].mc_data[j] =
+ (temp_reg & 0xffff0000) |
+ (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
+ }
+ j++;
+ break;
+
+ default:
+ break;
+ }
+
+ }
+
+ table->last = j;
+
+ return 0;
+}
+
+static int ci_set_valid_flag(struct ci_mc_reg_table *table)
+{
+ uint8_t i, j;
+
+ for (i = 0; i < table->last; i++) {
+ for (j = 1; j < table->num_entries; j++) {
+ if (table->mc_reg_table_entry[j-1].mc_data[i] !=
+ table->mc_reg_table_entry[j].mc_data[i]) {
+ table->validflag |= (1 << i);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int ci_initialize_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)(hwmgr->smu_backend);
+ pp_atomctrl_mc_reg_table *table;
+ struct ci_mc_reg_table *ni_table = &smu_data->mc_reg_table;
+ uint8_t module_index = ci_get_memory_modile_index(hwmgr);
+
+ table = kzalloc(sizeof(pp_atomctrl_mc_reg_table), GFP_KERNEL);
+
+ if (NULL == table)
+ return -ENOMEM;
+
+ /* Program additional LP registers that are no longer programmed by VBIOS */
+ cgs_write_register(hwmgr->device, mmMC_SEQ_RAS_TIMING_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_RAS_TIMING));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_CAS_TIMING_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_CAS_TIMING));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_DLL_STBY_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_DLL_STBY));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD0_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD0));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD1_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD1));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_G5PDX_CTRL_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_G5PDX_CTRL));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CMD_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CMD));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CTL_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CTL));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_MISC_TIMING_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_MISC_TIMING));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_MISC_TIMING2_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_MISC_TIMING2));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_EMRS_LP, cgs_read_register(hwmgr->device, mmMC_PMG_CMD_EMRS));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_MRS_LP, cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_MRS1_LP, cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS1));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_D0_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_D0));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_D1_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_D1));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_RD_CTL_D0_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_RD_CTL_D0));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_RD_CTL_D1_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_RD_CTL_D1));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_TIMING_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_TIMING));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_MRS2_LP, cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS2));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_2_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_2));
+
+ result = atomctrl_initialize_mc_reg_table(hwmgr, module_index, table);
+
+ if (0 == result)
+ result = ci_copy_vbios_smc_reg_table(table, ni_table);
+
+ if (0 == result) {
+ ci_set_s0_mc_reg_index(ni_table);
+ result = ci_set_mc_special_registers(hwmgr, ni_table);
+ }
+
+ if (0 == result)
+ ci_set_valid_flag(ni_table);
+
+ kfree(table);
+
+ return result;
+}
+
+static bool ci_is_dpm_running(struct pp_hwmgr *hwmgr)
+{
+ return ci_is_smc_ram_running(hwmgr);
+}
+
+static int ci_smu_init(struct pp_hwmgr *hwmgr)
+{
+ struct ci_smumgr *ci_priv = NULL;
+
+ ci_priv = kzalloc(sizeof(struct ci_smumgr), GFP_KERNEL);
+
+ if (ci_priv == NULL)
+ return -ENOMEM;
+
+ hwmgr->smu_backend = ci_priv;
+
+ return 0;
+}
+
+static int ci_smu_fini(struct pp_hwmgr *hwmgr)
+{
+ kfree(hwmgr->smu_backend);
+ hwmgr->smu_backend = NULL;
+ return 0;
+}
+
+static int ci_start_smu(struct pp_hwmgr *hwmgr)
+{
+ return 0;
+}
+
+static int ci_update_dpm_settings(struct pp_hwmgr *hwmgr,
+ void *profile_setting)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct ci_smumgr *smu_data = (struct ci_smumgr *)
+ (hwmgr->smu_backend);
+ struct profile_mode_setting *setting;
+ struct SMU7_Discrete_GraphicsLevel *levels =
+ smu_data->smc_state_table.GraphicsLevel;
+ uint32_t array = smu_data->dpm_table_start +
+ offsetof(SMU7_Discrete_DpmTable, GraphicsLevel);
+
+ uint32_t mclk_array = smu_data->dpm_table_start +
+ offsetof(SMU7_Discrete_DpmTable, MemoryLevel);
+ struct SMU7_Discrete_MemoryLevel *mclk_levels =
+ smu_data->smc_state_table.MemoryLevel;
+ uint32_t i;
+ uint32_t offset, up_hyst_offset, down_hyst_offset, clk_activity_offset, tmp;
+
+ if (profile_setting == NULL)
+ return -EINVAL;
+
+ setting = (struct profile_mode_setting *)profile_setting;
+
+ if (setting->bupdate_sclk) {
+ if (!data->sclk_dpm_key_disabled)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_SCLKDPM_FreezeLevel, NULL);
+ for (i = 0; i < smu_data->smc_state_table.GraphicsDpmLevelCount; i++) {
+ if (levels[i].ActivityLevel !=
+ cpu_to_be16(setting->sclk_activity)) {
+ levels[i].ActivityLevel = cpu_to_be16(setting->sclk_activity);
+
+ clk_activity_offset = array + (sizeof(SMU7_Discrete_GraphicsLevel) * i)
+ + offsetof(SMU7_Discrete_GraphicsLevel, ActivityLevel);
+ offset = clk_activity_offset & ~0x3;
+ tmp = PP_HOST_TO_SMC_UL(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset));
+ tmp = phm_set_field_to_u32(clk_activity_offset, tmp, levels[i].ActivityLevel, sizeof(uint16_t));
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset, PP_HOST_TO_SMC_UL(tmp));
+
+ }
+ if (levels[i].UpH != setting->sclk_up_hyst ||
+ levels[i].DownH != setting->sclk_down_hyst) {
+ levels[i].UpH = setting->sclk_up_hyst;
+ levels[i].DownH = setting->sclk_down_hyst;
+ up_hyst_offset = array + (sizeof(SMU7_Discrete_GraphicsLevel) * i)
+ + offsetof(SMU7_Discrete_GraphicsLevel, UpH);
+ down_hyst_offset = array + (sizeof(SMU7_Discrete_GraphicsLevel) * i)
+ + offsetof(SMU7_Discrete_GraphicsLevel, DownH);
+ offset = up_hyst_offset & ~0x3;
+ tmp = PP_HOST_TO_SMC_UL(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset));
+ tmp = phm_set_field_to_u32(up_hyst_offset, tmp, levels[i].UpH, sizeof(uint8_t));
+ tmp = phm_set_field_to_u32(down_hyst_offset, tmp, levels[i].DownH, sizeof(uint8_t));
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset, PP_HOST_TO_SMC_UL(tmp));
+ }
+ }
+ if (!data->sclk_dpm_key_disabled)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_SCLKDPM_UnfreezeLevel, NULL);
+ }
+
+ if (setting->bupdate_mclk) {
+ if (!data->mclk_dpm_key_disabled)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_MCLKDPM_FreezeLevel, NULL);
+ for (i = 0; i < smu_data->smc_state_table.MemoryDpmLevelCount; i++) {
+ if (mclk_levels[i].ActivityLevel !=
+ cpu_to_be16(setting->mclk_activity)) {
+ mclk_levels[i].ActivityLevel = cpu_to_be16(setting->mclk_activity);
+
+ clk_activity_offset = mclk_array + (sizeof(SMU7_Discrete_MemoryLevel) * i)
+ + offsetof(SMU7_Discrete_MemoryLevel, ActivityLevel);
+ offset = clk_activity_offset & ~0x3;
+ tmp = PP_HOST_TO_SMC_UL(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset));
+ tmp = phm_set_field_to_u32(clk_activity_offset, tmp, mclk_levels[i].ActivityLevel, sizeof(uint16_t));
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset, PP_HOST_TO_SMC_UL(tmp));
+
+ }
+ if (mclk_levels[i].UpH != setting->mclk_up_hyst ||
+ mclk_levels[i].DownH != setting->mclk_down_hyst) {
+ mclk_levels[i].UpH = setting->mclk_up_hyst;
+ mclk_levels[i].DownH = setting->mclk_down_hyst;
+ up_hyst_offset = mclk_array + (sizeof(SMU7_Discrete_MemoryLevel) * i)
+ + offsetof(SMU7_Discrete_MemoryLevel, UpH);
+ down_hyst_offset = mclk_array + (sizeof(SMU7_Discrete_MemoryLevel) * i)
+ + offsetof(SMU7_Discrete_MemoryLevel, DownH);
+ offset = up_hyst_offset & ~0x3;
+ tmp = PP_HOST_TO_SMC_UL(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset));
+ tmp = phm_set_field_to_u32(up_hyst_offset, tmp, mclk_levels[i].UpH, sizeof(uint8_t));
+ tmp = phm_set_field_to_u32(down_hyst_offset, tmp, mclk_levels[i].DownH, sizeof(uint8_t));
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset, PP_HOST_TO_SMC_UL(tmp));
+ }
+ }
+ if (!data->mclk_dpm_key_disabled)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_MCLKDPM_UnfreezeLevel, NULL);
+ }
+ return 0;
+}
+
+static int ci_update_uvd_smc_table(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ struct smu7_hwmgr *data = hwmgr->backend;
+ struct ci_smumgr *smu_data = hwmgr->smu_backend;
+ struct phm_uvd_clock_voltage_dependency_table *uvd_table =
+ hwmgr->dyn_state.uvd_clock_voltage_dependency_table;
+ uint32_t profile_mode_mask = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD |
+ AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK |
+ AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK |
+ AMD_DPM_FORCED_LEVEL_PROFILE_PEAK;
+ uint32_t max_vddc = adev->pm.ac_power ? hwmgr->dyn_state.max_clock_voltage_on_ac.vddc :
+ hwmgr->dyn_state.max_clock_voltage_on_dc.vddc;
+ int32_t i;
+
+ if (PP_CAP(PHM_PlatformCaps_UVDDPM) || uvd_table->count <= 0)
+ smu_data->smc_state_table.UvdBootLevel = 0;
+ else
+ smu_data->smc_state_table.UvdBootLevel = uvd_table->count - 1;
+
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, DPM_TABLE_475,
+ UvdBootLevel, smu_data->smc_state_table.UvdBootLevel);
+
+ data->dpm_level_enable_mask.uvd_dpm_enable_mask = 0;
+
+ for (i = uvd_table->count - 1; i >= 0; i--) {
+ if (uvd_table->entries[i].v <= max_vddc)
+ data->dpm_level_enable_mask.uvd_dpm_enable_mask |= 1 << i;
+ if (hwmgr->dpm_level & profile_mode_mask || !PP_CAP(PHM_PlatformCaps_UVDDPM))
+ break;
+ }
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_UVDDPM_SetEnabledMask,
+ data->dpm_level_enable_mask.uvd_dpm_enable_mask,
+ NULL);
+
+ return 0;
+}
+
+static int ci_update_vce_smc_table(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ struct smu7_hwmgr *data = hwmgr->backend;
+ struct phm_vce_clock_voltage_dependency_table *vce_table =
+ hwmgr->dyn_state.vce_clock_voltage_dependency_table;
+ uint32_t profile_mode_mask = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD |
+ AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK |
+ AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK |
+ AMD_DPM_FORCED_LEVEL_PROFILE_PEAK;
+ uint32_t max_vddc = adev->pm.ac_power ? hwmgr->dyn_state.max_clock_voltage_on_ac.vddc :
+ hwmgr->dyn_state.max_clock_voltage_on_dc.vddc;
+ int32_t i;
+
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, DPM_TABLE_475,
+ VceBootLevel, 0); /* temp hard code to level 0, vce can set min evclk*/
+
+ data->dpm_level_enable_mask.vce_dpm_enable_mask = 0;
+
+ for (i = vce_table->count - 1; i >= 0; i--) {
+ if (vce_table->entries[i].v <= max_vddc)
+ data->dpm_level_enable_mask.vce_dpm_enable_mask |= 1 << i;
+ if (hwmgr->dpm_level & profile_mode_mask || !PP_CAP(PHM_PlatformCaps_VCEDPM))
+ break;
+ }
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_VCEDPM_SetEnabledMask,
+ data->dpm_level_enable_mask.vce_dpm_enable_mask,
+ NULL);
+
+ return 0;
+}
+
+static int ci_update_smc_table(struct pp_hwmgr *hwmgr, uint32_t type)
+{
+ switch (type) {
+ case SMU_UVD_TABLE:
+ ci_update_uvd_smc_table(hwmgr);
+ break;
+ case SMU_VCE_TABLE:
+ ci_update_vce_smc_table(hwmgr);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void ci_reset_smc(struct pp_hwmgr *hwmgr)
+{
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL,
+ rst_reg, 1);
+}
+
+
+static void ci_stop_smc_clock(struct pp_hwmgr *hwmgr)
+{
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_CLOCK_CNTL_0,
+ ck_disable, 1);
+}
+
+static int ci_stop_smc(struct pp_hwmgr *hwmgr)
+{
+ ci_reset_smc(hwmgr);
+ ci_stop_smc_clock(hwmgr);
+
+ return 0;
+}
+
+const struct pp_smumgr_func ci_smu_funcs = {
+ .name = "ci_smu",
+ .smu_init = ci_smu_init,
+ .smu_fini = ci_smu_fini,
+ .start_smu = ci_start_smu,
+ .check_fw_load_finish = NULL,
+ .request_smu_load_fw = NULL,
+ .request_smu_load_specific_fw = NULL,
+ .send_msg_to_smc = ci_send_msg_to_smc,
+ .send_msg_to_smc_with_parameter = ci_send_msg_to_smc_with_parameter,
+ .get_argument = smu7_get_argument,
+ .download_pptable_settings = NULL,
+ .upload_pptable_settings = NULL,
+ .get_offsetof = ci_get_offsetof,
+ .process_firmware_header = ci_process_firmware_header,
+ .init_smc_table = ci_init_smc_table,
+ .update_sclk_threshold = ci_update_sclk_threshold,
+ .thermal_setup_fan_table = ci_thermal_setup_fan_table,
+ .populate_all_graphic_levels = ci_populate_all_graphic_levels,
+ .populate_all_memory_levels = ci_populate_all_memory_levels,
+ .get_mac_definition = ci_get_mac_definition,
+ .initialize_mc_reg_table = ci_initialize_mc_reg_table,
+ .is_dpm_running = ci_is_dpm_running,
+ .update_dpm_settings = ci_update_dpm_settings,
+ .update_smc_table = ci_update_smc_table,
+ .stop_smc = ci_stop_smc,
+};
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.h b/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.h
new file mode 100644
index 000000000000..a8282705c569
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _CI_SMUMANAGER_H_
+#define _CI_SMUMANAGER_H_
+
+#define SMU__NUM_SCLK_DPM_STATE 8
+#define SMU__NUM_MCLK_DPM_LEVELS 6
+#define SMU__NUM_LCLK_DPM_LEVELS 8
+#define SMU__NUM_PCIE_DPM_LEVELS 8
+
+#include "smu7_discrete.h"
+#include <pp_endian.h>
+#include "ppatomctrl.h"
+
+struct ci_pt_defaults {
+ u8 svi_load_line_en;
+ u8 svi_load_line_vddc;
+ u8 tdc_vddc_throttle_release_limit_perc;
+ u8 tdc_mawt;
+ u8 tdc_waterfall_ctl;
+ u8 dte_ambient_temp_base;
+ u32 display_cac;
+ u32 bapm_temp_gradient;
+ u16 bapmti_r[SMU7_DTE_ITERATIONS * SMU7_DTE_SOURCES * SMU7_DTE_SINKS];
+ u16 bapmti_rc[SMU7_DTE_ITERATIONS * SMU7_DTE_SOURCES * SMU7_DTE_SINKS];
+};
+
+struct ci_mc_reg_entry {
+ uint32_t mclk_max;
+ uint32_t mc_data[SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct ci_mc_reg_table {
+ uint8_t last;
+ uint8_t num_entries;
+ uint16_t validflag;
+ struct ci_mc_reg_entry mc_reg_table_entry[MAX_AC_TIMING_ENTRIES];
+ SMU7_Discrete_MCRegisterAddress mc_reg_address[SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct ci_smumgr {
+ uint32_t soft_regs_start;
+ uint32_t dpm_table_start;
+ uint32_t mc_reg_table_start;
+ uint32_t fan_table_start;
+ uint32_t arb_table_start;
+ uint32_t ulv_setting_starts;
+ struct SMU7_Discrete_DpmTable smc_state_table;
+ struct SMU7_Discrete_PmFuses power_tune_table;
+ const struct ci_pt_defaults *power_tune_defaults;
+ SMU7_Discrete_MCRegisters mc_regs;
+ struct ci_mc_reg_table mc_reg_table;
+};
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/fiji_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/fiji_smumgr.c
new file mode 100644
index 000000000000..ecb9ee46d6b3
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/fiji_smumgr.c
@@ -0,0 +1,2673 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "pp_debug.h"
+#include "smumgr.h"
+#include "smu7_dyn_defaults.h"
+#include "smu73.h"
+#include "smu_ucode_xfer_vi.h"
+#include "fiji_smumgr.h"
+#include "fiji_ppsmc.h"
+#include "smu73_discrete.h"
+#include "ppatomctrl.h"
+#include "smu/smu_7_1_3_d.h"
+#include "smu/smu_7_1_3_sh_mask.h"
+#include "gmc/gmc_8_1_d.h"
+#include "gmc/gmc_8_1_sh_mask.h"
+#include "oss/oss_3_0_d.h"
+#include "gca/gfx_8_0_d.h"
+#include "bif/bif_5_0_d.h"
+#include "bif/bif_5_0_sh_mask.h"
+#include "dce/dce_10_0_d.h"
+#include "dce/dce_10_0_sh_mask.h"
+#include "hardwaremanager.h"
+#include "cgs_common.h"
+#include "atombios.h"
+#include "pppcielanes.h"
+#include "hwmgr.h"
+#include "smu7_hwmgr.h"
+
+
+#define AVFS_EN_MSB 1568
+#define AVFS_EN_LSB 1568
+
+#define FIJI_SMC_SIZE 0x20000
+
+#define POWERTUNE_DEFAULT_SET_MAX 1
+#define VDDC_VDDCI_DELTA 300
+#define MC_CG_ARB_FREQ_F1 0x0b
+
+/* [2.5%,~2.5%] Clock stretched is multiple of 2.5% vs
+ * not and [Fmin, Fmax, LDO_REFSEL, USE_FOR_LOW_FREQ]
+ */
+static const uint16_t fiji_clock_stretcher_lookup_table[2][4] = {
+ {600, 1050, 3, 0}, {600, 1050, 6, 1} };
+
+/* [FF, SS] type, [] 4 voltage ranges, and
+ * [Floor Freq, Boundary Freq, VID min , VID max]
+ */
+static const uint32_t fiji_clock_stretcher_ddt_table[2][4][4] = {
+ { {265, 529, 120, 128}, {325, 650, 96, 119}, {430, 860, 32, 95}, {0, 0, 0, 31} },
+ { {275, 550, 104, 112}, {319, 638, 96, 103}, {360, 720, 64, 95}, {384, 768, 32, 63} } };
+
+/* [Use_For_Low_freq] value, [0%, 5%, 10%, 7.14%, 14.28%, 20%]
+ * (coming from PWR_CKS_CNTL.stretch_amount reg spec)
+ */
+static const uint8_t fiji_clock_stretch_amount_conversion[2][6] = {
+ {0, 1, 3, 2, 4, 5}, {0, 2, 4, 5, 6, 5} };
+
+static const struct fiji_pt_defaults fiji_power_tune_data_set_array[POWERTUNE_DEFAULT_SET_MAX] = {
+ /*sviLoadLIneEn, SviLoadLineVddC, TDC_VDDC_ThrottleReleaseLimitPerc */
+ {1, 0xF, 0xFD,
+ /* TDC_MAWt, TdcWaterfallCtl, DTEAmbientTempBase */
+ 0x19, 5, 45}
+};
+
+static const struct SMU73_Discrete_GraphicsLevel avfs_graphics_level[8] = {
+ /* Min Sclk pcie DeepSleep Activity CgSpll CgSpll spllSpread SpllSpread CcPwr CcPwr Sclk Display Enabled Enabled Voltage Power */
+ /* Voltage, Frequency, DpmLevel, DivId, Level, FuncCntl3, FuncCntl4, Spectrum, Spectrum2, DynRm, DynRm1 Did, Watermark, ForActivity, ForThrottle, UpHyst, DownHyst, DownHyst, Throttle */
+ { 0x3c0fd047, 0x30750000, 0x00, 0x03, 0x1e00, 0x00200410, 0x87020000, 0x21680000, 0x0c000000, 0, 0, 0x16, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 },
+ { 0xa00fd047, 0x409c0000, 0x01, 0x04, 0x1e00, 0x00800510, 0x87020000, 0x21680000, 0x11000000, 0, 0, 0x16, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 },
+ { 0x0410d047, 0x50c30000, 0x01, 0x00, 0x1e00, 0x00600410, 0x87020000, 0x21680000, 0x0d000000, 0, 0, 0x0e, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 },
+ { 0x6810d047, 0x60ea0000, 0x01, 0x00, 0x1e00, 0x00800410, 0x87020000, 0x21680000, 0x0e000000, 0, 0, 0x0c, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 },
+ { 0xcc10d047, 0xe8fd0000, 0x01, 0x00, 0x1e00, 0x00e00410, 0x87020000, 0x21680000, 0x0f000000, 0, 0, 0x0c, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 },
+ { 0x3011d047, 0x70110100, 0x01, 0x00, 0x1e00, 0x00400510, 0x87020000, 0x21680000, 0x10000000, 0, 0, 0x0c, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 },
+ { 0x9411d047, 0xf8240100, 0x01, 0x00, 0x1e00, 0x00a00510, 0x87020000, 0x21680000, 0x11000000, 0, 0, 0x0c, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 },
+ { 0xf811d047, 0x80380100, 0x01, 0x00, 0x1e00, 0x00000610, 0x87020000, 0x21680000, 0x12000000, 0, 0, 0x0c, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 }
+};
+
+static int fiji_start_smu_in_protection_mode(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+
+ /* Wait for smc boot up */
+ /* PHM_WAIT_INDIRECT_FIELD_UNEQUAL(hwmgr, SMC_IND,
+ RCU_UC_EVENTS, boot_seq_done, 0); */
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 1);
+
+ result = smu7_upload_smu_firmware_image(hwmgr);
+ if (result)
+ return result;
+
+ /* Clear status */
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixSMU_STATUS, 0);
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
+
+ /* De-assert reset */
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 0);
+
+ /* Wait for ROM firmware to initialize interrupt hendler */
+ /*SMUM_WAIT_VFPF_INDIRECT_REGISTER(hwmgr, SMC_IND,
+ SMC_INTR_CNTL_MASK_0, 0x10040, 0xFFFFFFFF); */
+
+ /* Set SMU Auto Start */
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMU_INPUT_DATA, AUTO_START, 1);
+
+ /* Clear firmware interrupt enable flag */
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixFIRMWARE_FLAGS, 0);
+
+ PHM_WAIT_VFPF_INDIRECT_FIELD(hwmgr, SMC_IND, RCU_UC_EVENTS,
+ INTERRUPTS_ENABLED, 1);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_Test, 0x20000, NULL);
+
+ /* Wait for done bit to be set */
+ PHM_WAIT_VFPF_INDIRECT_FIELD_UNEQUAL(hwmgr, SMC_IND,
+ SMU_STATUS, SMU_DONE, 0);
+
+ /* Check pass/failed indicator */
+ if (PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMU_STATUS, SMU_PASS) != 1) {
+ PP_ASSERT_WITH_CODE(false,
+ "SMU Firmware start failed!", return -1);
+ }
+
+ /* Wait for firmware to initialize */
+ PHM_WAIT_VFPF_INDIRECT_FIELD(hwmgr, SMC_IND,
+ FIRMWARE_FLAGS, INTERRUPTS_ENABLED, 1);
+
+ return result;
+}
+
+static int fiji_start_smu_in_non_protection_mode(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+
+ /* wait for smc boot up */
+ PHM_WAIT_VFPF_INDIRECT_FIELD_UNEQUAL(hwmgr, SMC_IND,
+ RCU_UC_EVENTS, boot_seq_done, 0);
+
+ /* Clear firmware interrupt enable flag */
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixFIRMWARE_FLAGS, 0);
+
+ /* Assert reset */
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 1);
+
+ result = smu7_upload_smu_firmware_image(hwmgr);
+ if (result)
+ return result;
+
+ /* Set smc instruct start point at 0x0 */
+ smu7_program_jump_on_start(hwmgr);
+
+ /* Enable clock */
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
+
+ /* De-assert reset */
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 0);
+
+ /* Wait for firmware to initialize */
+ PHM_WAIT_VFPF_INDIRECT_FIELD(hwmgr, SMC_IND,
+ FIRMWARE_FLAGS, INTERRUPTS_ENABLED, 1);
+
+ return result;
+}
+
+static int fiji_start_avfs_btc(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+ struct smu7_smumgr *smu_data = (struct smu7_smumgr *)(hwmgr->smu_backend);
+
+ if (0 != smu_data->avfs_btc_param) {
+ if (0 != smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_PerformBtc, smu_data->avfs_btc_param,
+ NULL)) {
+ pr_info("[AVFS][Fiji_PerformBtc] PerformBTC SMU msg failed");
+ result = -EINVAL;
+ }
+ }
+ /* Soft-Reset to reset the engine before loading uCode */
+ /* halt */
+ cgs_write_register(hwmgr->device, mmCP_MEC_CNTL, 0x50000000);
+ /* reset everything */
+ cgs_write_register(hwmgr->device, mmGRBM_SOFT_RESET, 0xffffffff);
+ /* clear reset */
+ cgs_write_register(hwmgr->device, mmGRBM_SOFT_RESET, 0);
+
+ return result;
+}
+
+static int fiji_setup_graphics_level_structure(struct pp_hwmgr *hwmgr)
+{
+ int32_t vr_config;
+ uint32_t table_start;
+ uint32_t level_addr, vr_config_addr;
+ uint32_t level_size = sizeof(avfs_graphics_level);
+
+ PP_ASSERT_WITH_CODE(0 == smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU73_Firmware_Header, DpmTable),
+ &table_start, 0x40000),
+ "[AVFS][Fiji_SetupGfxLvlStruct] SMU could not "
+ "communicate starting address of DPM table",
+ return -1;);
+
+ /* Default value for vr_config =
+ * VR_MERGED_WITH_VDDC + VR_STATIC_VOLTAGE(VDDCI) */
+ vr_config = 0x01000500; /* Real value:0x50001 */
+
+ vr_config_addr = table_start +
+ offsetof(SMU73_Discrete_DpmTable, VRConfig);
+
+ PP_ASSERT_WITH_CODE(0 == smu7_copy_bytes_to_smc(hwmgr, vr_config_addr,
+ (uint8_t *)&vr_config, sizeof(int32_t), 0x40000),
+ "[AVFS][Fiji_SetupGfxLvlStruct] Problems copying "
+ "vr_config value over to SMC",
+ return -1;);
+
+ level_addr = table_start + offsetof(SMU73_Discrete_DpmTable, GraphicsLevel);
+
+ PP_ASSERT_WITH_CODE(0 == smu7_copy_bytes_to_smc(hwmgr, level_addr,
+ (uint8_t *)(&avfs_graphics_level), level_size, 0x40000),
+ "[AVFS][Fiji_SetupGfxLvlStruct] Copying of DPM table failed!",
+ return -1;);
+
+ return 0;
+}
+
+static int fiji_avfs_event_mgr(struct pp_hwmgr *hwmgr)
+{
+ if (!hwmgr->avfs_supported)
+ return 0;
+
+ PP_ASSERT_WITH_CODE(0 == fiji_setup_graphics_level_structure(hwmgr),
+ "[AVFS][fiji_avfs_event_mgr] Could not Copy Graphics Level"
+ " table over to SMU",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(0 == smu7_setup_pwr_virus(hwmgr),
+ "[AVFS][fiji_avfs_event_mgr] Could not setup "
+ "Pwr Virus for AVFS ",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(0 == fiji_start_avfs_btc(hwmgr),
+ "[AVFS][fiji_avfs_event_mgr] Failure at "
+ "fiji_start_avfs_btc. AVFS Disabled",
+ return -EINVAL);
+
+ return 0;
+}
+
+static int fiji_start_smu(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+ struct fiji_smumgr *priv = (struct fiji_smumgr *)(hwmgr->smu_backend);
+
+ /* Only start SMC if SMC RAM is not running */
+ if (!smu7_is_smc_ram_running(hwmgr) && hwmgr->not_vf) {
+ /* Check if SMU is running in protected mode */
+ if (0 == PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device,
+ CGS_IND_REG__SMC,
+ SMU_FIRMWARE, SMU_MODE)) {
+ result = fiji_start_smu_in_non_protection_mode(hwmgr);
+ if (result)
+ return result;
+ } else {
+ result = fiji_start_smu_in_protection_mode(hwmgr);
+ if (result)
+ return result;
+ }
+ if (fiji_avfs_event_mgr(hwmgr))
+ hwmgr->avfs_supported = false;
+ }
+
+ /* Setup SoftRegsStart here for register lookup in case
+ * DummyBackEnd is used and ProcessFirmwareHeader is not executed
+ */
+ smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU73_Firmware_Header, SoftRegisters),
+ &(priv->smu7_data.soft_regs_start), 0x40000);
+
+ result = smu7_request_smu_load_fw(hwmgr);
+
+ return result;
+}
+
+static bool fiji_is_hw_avfs_present(struct pp_hwmgr *hwmgr)
+{
+
+ uint32_t efuse = 0;
+ uint32_t mask = (1 << ((AVFS_EN_MSB - AVFS_EN_LSB) + 1)) - 1;
+
+ if (!hwmgr->not_vf)
+ return false;
+
+ if (!atomctrl_read_efuse(hwmgr, AVFS_EN_LSB, AVFS_EN_MSB,
+ mask, &efuse)) {
+ if (efuse)
+ return true;
+ }
+ return false;
+}
+
+static int fiji_smu_init(struct pp_hwmgr *hwmgr)
+{
+ struct fiji_smumgr *fiji_priv = NULL;
+
+ fiji_priv = kzalloc(sizeof(struct fiji_smumgr), GFP_KERNEL);
+
+ if (fiji_priv == NULL)
+ return -ENOMEM;
+
+ hwmgr->smu_backend = fiji_priv;
+
+ if (smu7_init(hwmgr)) {
+ kfree(fiji_priv);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fiji_get_dependency_volt_by_clk(struct pp_hwmgr *hwmgr,
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_table,
+ uint32_t clock, uint32_t *voltage, uint32_t *mvdd)
+{
+ uint32_t i;
+ uint16_t vddci;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ *voltage = *mvdd = 0;
+
+
+ /* clock - voltage dependency table is empty table */
+ if (dep_table->count == 0)
+ return -EINVAL;
+
+ for (i = 0; i < dep_table->count; i++) {
+ /* find first sclk bigger than request */
+ if (dep_table->entries[i].clk >= clock) {
+ *voltage |= (dep_table->entries[i].vddc *
+ VOLTAGE_SCALE) << VDDC_SHIFT;
+ if (SMU7_VOLTAGE_CONTROL_NONE == data->vddci_control)
+ *voltage |= (data->vbios_boot_state.vddci_bootup_value *
+ VOLTAGE_SCALE) << VDDCI_SHIFT;
+ else if (dep_table->entries[i].vddci)
+ *voltage |= (dep_table->entries[i].vddci *
+ VOLTAGE_SCALE) << VDDCI_SHIFT;
+ else {
+ vddci = phm_find_closest_vddci(&(data->vddci_voltage_table),
+ (dep_table->entries[i].vddc -
+ VDDC_VDDCI_DELTA));
+ *voltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
+ }
+
+ if (SMU7_VOLTAGE_CONTROL_NONE == data->mvdd_control)
+ *mvdd = data->vbios_boot_state.mvdd_bootup_value *
+ VOLTAGE_SCALE;
+ else if (dep_table->entries[i].mvdd)
+ *mvdd = (uint32_t) dep_table->entries[i].mvdd *
+ VOLTAGE_SCALE;
+
+ *voltage |= 1 << PHASES_SHIFT;
+ return 0;
+ }
+ }
+
+ /* sclk is bigger than max sclk in the dependence table */
+ *voltage |= (dep_table->entries[i - 1].vddc * VOLTAGE_SCALE) << VDDC_SHIFT;
+
+ if (SMU7_VOLTAGE_CONTROL_NONE == data->vddci_control)
+ *voltage |= (data->vbios_boot_state.vddci_bootup_value *
+ VOLTAGE_SCALE) << VDDCI_SHIFT;
+ else if (dep_table->entries[i-1].vddci) {
+ vddci = phm_find_closest_vddci(&(data->vddci_voltage_table),
+ (dep_table->entries[i].vddc -
+ VDDC_VDDCI_DELTA));
+ *voltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
+ }
+
+ if (SMU7_VOLTAGE_CONTROL_NONE == data->mvdd_control)
+ *mvdd = data->vbios_boot_state.mvdd_bootup_value * VOLTAGE_SCALE;
+ else if (dep_table->entries[i].mvdd)
+ *mvdd = (uint32_t) dep_table->entries[i - 1].mvdd * VOLTAGE_SCALE;
+
+ return 0;
+}
+
+
+static uint16_t scale_fan_gain_settings(uint16_t raw_setting)
+{
+ uint32_t tmp;
+ tmp = raw_setting * 4096 / 100;
+ return (uint16_t)tmp;
+}
+
+static void get_scl_sda_value(uint8_t line, uint8_t *scl, uint8_t *sda)
+{
+ switch (line) {
+ case SMU7_I2CLineID_DDC1:
+ *scl = SMU7_I2C_DDC1CLK;
+ *sda = SMU7_I2C_DDC1DATA;
+ break;
+ case SMU7_I2CLineID_DDC2:
+ *scl = SMU7_I2C_DDC2CLK;
+ *sda = SMU7_I2C_DDC2DATA;
+ break;
+ case SMU7_I2CLineID_DDC3:
+ *scl = SMU7_I2C_DDC3CLK;
+ *sda = SMU7_I2C_DDC3DATA;
+ break;
+ case SMU7_I2CLineID_DDC4:
+ *scl = SMU7_I2C_DDC4CLK;
+ *sda = SMU7_I2C_DDC4DATA;
+ break;
+ case SMU7_I2CLineID_DDC5:
+ *scl = SMU7_I2C_DDC5CLK;
+ *sda = SMU7_I2C_DDC5DATA;
+ break;
+ case SMU7_I2CLineID_DDC6:
+ *scl = SMU7_I2C_DDC6CLK;
+ *sda = SMU7_I2C_DDC6DATA;
+ break;
+ case SMU7_I2CLineID_SCLSDA:
+ *scl = SMU7_I2C_SCL;
+ *sda = SMU7_I2C_SDA;
+ break;
+ case SMU7_I2CLineID_DDCVGA:
+ *scl = SMU7_I2C_DDCVGACLK;
+ *sda = SMU7_I2C_DDCVGADATA;
+ break;
+ default:
+ *scl = 0;
+ *sda = 0;
+ break;
+ }
+}
+
+static void fiji_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr)
+{
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ if (table_info &&
+ table_info->cac_dtp_table->usPowerTuneDataSetID <= POWERTUNE_DEFAULT_SET_MAX &&
+ table_info->cac_dtp_table->usPowerTuneDataSetID)
+ smu_data->power_tune_defaults =
+ &fiji_power_tune_data_set_array
+ [table_info->cac_dtp_table->usPowerTuneDataSetID - 1];
+ else
+ smu_data->power_tune_defaults = &fiji_power_tune_data_set_array[0];
+
+}
+
+static int fiji_populate_bapm_parameters_in_dpm_table(struct pp_hwmgr *hwmgr)
+{
+
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+ const struct fiji_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+ SMU73_Discrete_DpmTable *dpm_table = &(smu_data->smc_state_table);
+
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_cac_tdp_table *cac_dtp_table = table_info->cac_dtp_table;
+ struct pp_advance_fan_control_parameters *fan_table =
+ &hwmgr->thermal_controller.advanceFanControlParameters;
+ uint8_t uc_scl, uc_sda;
+
+ /* TDP number of fraction bits are changed from 8 to 7 for Fiji
+ * as requested by SMC team
+ */
+ dpm_table->DefaultTdp = PP_HOST_TO_SMC_US(
+ (uint16_t)(cac_dtp_table->usTDP * 128));
+ dpm_table->TargetTdp = PP_HOST_TO_SMC_US(
+ (uint16_t)(cac_dtp_table->usTDP * 128));
+
+ PP_ASSERT_WITH_CODE(cac_dtp_table->usTargetOperatingTemp <= 255,
+ "Target Operating Temp is out of Range!",
+ );
+
+ dpm_table->GpuTjMax = (uint8_t)(cac_dtp_table->usTargetOperatingTemp);
+ dpm_table->GpuTjHyst = 8;
+
+ dpm_table->DTEAmbientTempBase = defaults->DTEAmbientTempBase;
+
+ /* The following are for new Fiji Multi-input fan/thermal control */
+ dpm_table->TemperatureLimitEdge = PP_HOST_TO_SMC_US(
+ cac_dtp_table->usTargetOperatingTemp * 256);
+ dpm_table->TemperatureLimitHotspot = PP_HOST_TO_SMC_US(
+ cac_dtp_table->usTemperatureLimitHotspot * 256);
+ dpm_table->TemperatureLimitLiquid1 = PP_HOST_TO_SMC_US(
+ cac_dtp_table->usTemperatureLimitLiquid1 * 256);
+ dpm_table->TemperatureLimitLiquid2 = PP_HOST_TO_SMC_US(
+ cac_dtp_table->usTemperatureLimitLiquid2 * 256);
+ dpm_table->TemperatureLimitVrVddc = PP_HOST_TO_SMC_US(
+ cac_dtp_table->usTemperatureLimitVrVddc * 256);
+ dpm_table->TemperatureLimitVrMvdd = PP_HOST_TO_SMC_US(
+ cac_dtp_table->usTemperatureLimitVrMvdd * 256);
+ dpm_table->TemperatureLimitPlx = PP_HOST_TO_SMC_US(
+ cac_dtp_table->usTemperatureLimitPlx * 256);
+
+ dpm_table->FanGainEdge = PP_HOST_TO_SMC_US(
+ scale_fan_gain_settings(fan_table->usFanGainEdge));
+ dpm_table->FanGainHotspot = PP_HOST_TO_SMC_US(
+ scale_fan_gain_settings(fan_table->usFanGainHotspot));
+ dpm_table->FanGainLiquid = PP_HOST_TO_SMC_US(
+ scale_fan_gain_settings(fan_table->usFanGainLiquid));
+ dpm_table->FanGainVrVddc = PP_HOST_TO_SMC_US(
+ scale_fan_gain_settings(fan_table->usFanGainVrVddc));
+ dpm_table->FanGainVrMvdd = PP_HOST_TO_SMC_US(
+ scale_fan_gain_settings(fan_table->usFanGainVrMvdd));
+ dpm_table->FanGainPlx = PP_HOST_TO_SMC_US(
+ scale_fan_gain_settings(fan_table->usFanGainPlx));
+ dpm_table->FanGainHbm = PP_HOST_TO_SMC_US(
+ scale_fan_gain_settings(fan_table->usFanGainHbm));
+
+ dpm_table->Liquid1_I2C_address = cac_dtp_table->ucLiquid1_I2C_address;
+ dpm_table->Liquid2_I2C_address = cac_dtp_table->ucLiquid2_I2C_address;
+ dpm_table->Vr_I2C_address = cac_dtp_table->ucVr_I2C_address;
+ dpm_table->Plx_I2C_address = cac_dtp_table->ucPlx_I2C_address;
+
+ get_scl_sda_value(cac_dtp_table->ucLiquid_I2C_Line, &uc_scl, &uc_sda);
+ dpm_table->Liquid_I2C_LineSCL = uc_scl;
+ dpm_table->Liquid_I2C_LineSDA = uc_sda;
+
+ get_scl_sda_value(cac_dtp_table->ucVr_I2C_Line, &uc_scl, &uc_sda);
+ dpm_table->Vr_I2C_LineSCL = uc_scl;
+ dpm_table->Vr_I2C_LineSDA = uc_sda;
+
+ get_scl_sda_value(cac_dtp_table->ucPlx_I2C_Line, &uc_scl, &uc_sda);
+ dpm_table->Plx_I2C_LineSCL = uc_scl;
+ dpm_table->Plx_I2C_LineSDA = uc_sda;
+
+ return 0;
+}
+
+
+static int fiji_populate_svi_load_line(struct pp_hwmgr *hwmgr)
+{
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+ const struct fiji_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+ smu_data->power_tune_table.SviLoadLineEn = defaults->SviLoadLineEn;
+ smu_data->power_tune_table.SviLoadLineVddC = defaults->SviLoadLineVddC;
+ smu_data->power_tune_table.SviLoadLineTrimVddC = 3;
+ smu_data->power_tune_table.SviLoadLineOffsetVddC = 0;
+
+ return 0;
+}
+
+
+static int fiji_populate_tdc_limit(struct pp_hwmgr *hwmgr)
+{
+ uint16_t tdc_limit;
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ const struct fiji_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+ /* TDC number of fraction bits are changed from 8 to 7
+ * for Fiji as requested by SMC team
+ */
+ tdc_limit = (uint16_t)(table_info->cac_dtp_table->usTDC * 128);
+ smu_data->power_tune_table.TDC_VDDC_PkgLimit =
+ CONVERT_FROM_HOST_TO_SMC_US(tdc_limit);
+ smu_data->power_tune_table.TDC_VDDC_ThrottleReleaseLimitPerc =
+ defaults->TDC_VDDC_ThrottleReleaseLimitPerc;
+ smu_data->power_tune_table.TDC_MAWt = defaults->TDC_MAWt;
+
+ return 0;
+}
+
+static int fiji_populate_dw8(struct pp_hwmgr *hwmgr, uint32_t fuse_table_offset)
+{
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+ const struct fiji_pt_defaults *defaults = smu_data->power_tune_defaults;
+ uint32_t temp;
+
+ if (smu7_read_smc_sram_dword(hwmgr,
+ fuse_table_offset +
+ offsetof(SMU73_Discrete_PmFuses, TdcWaterfallCtl),
+ (uint32_t *)&temp, SMC_RAM_END))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to read PmFuses.DW6 (SviLoadLineEn) from SMC Failed!",
+ return -EINVAL);
+ else {
+ smu_data->power_tune_table.TdcWaterfallCtl = defaults->TdcWaterfallCtl;
+ smu_data->power_tune_table.LPMLTemperatureMin =
+ (uint8_t)((temp >> 16) & 0xff);
+ smu_data->power_tune_table.LPMLTemperatureMax =
+ (uint8_t)((temp >> 8) & 0xff);
+ smu_data->power_tune_table.Reserved = (uint8_t)(temp & 0xff);
+ }
+ return 0;
+}
+
+static int fiji_populate_temperature_scaler(struct pp_hwmgr *hwmgr)
+{
+ int i;
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+
+ /* Currently not used. Set all to zero. */
+ for (i = 0; i < 16; i++)
+ smu_data->power_tune_table.LPMLTemperatureScaler[i] = 0;
+
+ return 0;
+}
+
+static int fiji_populate_fuzzy_fan(struct pp_hwmgr *hwmgr)
+{
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+
+ if ((hwmgr->thermal_controller.advanceFanControlParameters.
+ usFanOutputSensitivity & (1 << 15)) ||
+ 0 == hwmgr->thermal_controller.advanceFanControlParameters.
+ usFanOutputSensitivity)
+ hwmgr->thermal_controller.advanceFanControlParameters.
+ usFanOutputSensitivity = hwmgr->thermal_controller.
+ advanceFanControlParameters.usDefaultFanOutputSensitivity;
+
+ smu_data->power_tune_table.FuzzyFan_PwmSetDelta =
+ PP_HOST_TO_SMC_US(hwmgr->thermal_controller.
+ advanceFanControlParameters.usFanOutputSensitivity);
+ return 0;
+}
+
+static int fiji_populate_gnb_lpml(struct pp_hwmgr *hwmgr)
+{
+ int i;
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+
+ /* Currently not used. Set all to zero. */
+ for (i = 0; i < 16; i++)
+ smu_data->power_tune_table.GnbLPML[i] = 0;
+
+ return 0;
+}
+
+static int fiji_populate_bapm_vddc_base_leakage_sidd(struct pp_hwmgr *hwmgr)
+{
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ uint16_t HiSidd = smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd;
+ uint16_t LoSidd = smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd;
+ struct phm_cac_tdp_table *cac_table = table_info->cac_dtp_table;
+
+ HiSidd = (uint16_t)(cac_table->usHighCACLeakage / 100 * 256);
+ LoSidd = (uint16_t)(cac_table->usLowCACLeakage / 100 * 256);
+
+ smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd =
+ CONVERT_FROM_HOST_TO_SMC_US(HiSidd);
+ smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd =
+ CONVERT_FROM_HOST_TO_SMC_US(LoSidd);
+
+ return 0;
+}
+
+static int fiji_populate_pm_fuses(struct pp_hwmgr *hwmgr)
+{
+ uint32_t pm_fuse_table_offset;
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PowerContainment)) {
+ if (smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU73_Firmware_Header, PmFuseTable),
+ &pm_fuse_table_offset, SMC_RAM_END))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to get pm_fuse_table_offset Failed!",
+ return -EINVAL);
+
+ /* DW6 */
+ if (fiji_populate_svi_load_line(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate SviLoadLine Failed!",
+ return -EINVAL);
+ /* DW7 */
+ if (fiji_populate_tdc_limit(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate TDCLimit Failed!", return -EINVAL);
+ /* DW8 */
+ if (fiji_populate_dw8(hwmgr, pm_fuse_table_offset))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate TdcWaterfallCtl, "
+ "LPMLTemperature Min and Max Failed!",
+ return -EINVAL);
+
+ /* DW9-DW12 */
+ if (0 != fiji_populate_temperature_scaler(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate LPMLTemperatureScaler Failed!",
+ return -EINVAL);
+
+ /* DW13-DW14 */
+ if (fiji_populate_fuzzy_fan(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate Fuzzy Fan Control parameters Failed!",
+ return -EINVAL);
+
+ /* DW15-DW18 */
+ if (fiji_populate_gnb_lpml(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate GnbLPML Failed!",
+ return -EINVAL);
+
+ /* DW20 */
+ if (fiji_populate_bapm_vddc_base_leakage_sidd(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate BapmVddCBaseLeakage Hi and Lo "
+ "Sidd Failed!", return -EINVAL);
+
+ if (smu7_copy_bytes_to_smc(hwmgr, pm_fuse_table_offset,
+ (uint8_t *)&smu_data->power_tune_table,
+ sizeof(struct SMU73_Discrete_PmFuses), SMC_RAM_END))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to download PmFuseTable Failed!",
+ return -EINVAL);
+ }
+ return 0;
+}
+
+static int fiji_populate_cac_table(struct pp_hwmgr *hwmgr,
+ struct SMU73_Discrete_DpmTable *table)
+{
+ uint32_t count;
+ uint8_t index;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_voltage_lookup_table *lookup_table =
+ table_info->vddc_lookup_table;
+ /* tables is already swapped, so in order to use the value from it,
+ * we need to swap it back.
+ * We are populating vddc CAC data to BapmVddc table
+ * in split and merged mode
+ */
+
+ for (count = 0; count < lookup_table->count; count++) {
+ index = phm_get_voltage_index(lookup_table,
+ data->vddc_voltage_table.entries[count].value);
+ table->BapmVddcVidLoSidd[count] =
+ convert_to_vid(lookup_table->entries[index].us_cac_low);
+ table->BapmVddcVidHiSidd[count] =
+ convert_to_vid(lookup_table->entries[index].us_cac_high);
+ }
+
+ return 0;
+}
+
+static int fiji_populate_smc_voltage_tables(struct pp_hwmgr *hwmgr,
+ struct SMU73_Discrete_DpmTable *table)
+{
+ int result;
+
+ result = fiji_populate_cac_table(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "can not populate CAC voltage tables to SMC",
+ return -EINVAL);
+
+ return 0;
+}
+
+static int fiji_populate_ulv_level(struct pp_hwmgr *hwmgr,
+ struct SMU73_Discrete_Ulv *state)
+{
+ int result = 0;
+
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ state->CcPwrDynRm = 0;
+ state->CcPwrDynRm1 = 0;
+
+ state->VddcOffset = (uint16_t) table_info->us_ulv_voltage_offset;
+ state->VddcOffsetVid = (uint8_t)(table_info->us_ulv_voltage_offset *
+ VOLTAGE_VID_OFFSET_SCALE2 / VOLTAGE_VID_OFFSET_SCALE1);
+
+ state->VddcPhase = 1;
+
+ if (!result) {
+ CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm);
+ CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm1);
+ CONVERT_FROM_HOST_TO_SMC_US(state->VddcOffset);
+ }
+ return result;
+}
+
+static int fiji_populate_ulv_state(struct pp_hwmgr *hwmgr,
+ struct SMU73_Discrete_DpmTable *table)
+{
+ return fiji_populate_ulv_level(hwmgr, &table->Ulv);
+}
+
+static int fiji_populate_smc_link_level(struct pp_hwmgr *hwmgr,
+ struct SMU73_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_dpm_table *dpm_table = &data->dpm_table;
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+ int i;
+
+ /* Index (dpm_table->pcie_speed_table.count)
+ * is reserved for PCIE boot level. */
+ for (i = 0; i <= dpm_table->pcie_speed_table.count; i++) {
+ table->LinkLevel[i].PcieGenSpeed =
+ (uint8_t)dpm_table->pcie_speed_table.dpm_levels[i].value;
+ table->LinkLevel[i].PcieLaneCount = (uint8_t)encode_pcie_lane_width(
+ dpm_table->pcie_speed_table.dpm_levels[i].param1);
+ table->LinkLevel[i].EnabledForActivity = 1;
+ table->LinkLevel[i].SPC = (uint8_t)(data->pcie_spc_cap & 0xff);
+ table->LinkLevel[i].DownThreshold = PP_HOST_TO_SMC_UL(5);
+ table->LinkLevel[i].UpThreshold = PP_HOST_TO_SMC_UL(30);
+ }
+
+ smu_data->smc_state_table.LinkLevelCount =
+ (uint8_t)dpm_table->pcie_speed_table.count;
+ data->dpm_level_enable_mask.pcie_dpm_enable_mask =
+ phm_get_dpm_level_enable_mask_value(&dpm_table->pcie_speed_table);
+
+ return 0;
+}
+
+static int fiji_calculate_sclk_params(struct pp_hwmgr *hwmgr,
+ uint32_t clock, struct SMU73_Discrete_GraphicsLevel *sclk)
+{
+ const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct pp_atomctrl_clock_dividers_vi dividers;
+ uint32_t spll_func_cntl = data->clock_registers.vCG_SPLL_FUNC_CNTL;
+ uint32_t spll_func_cntl_3 = data->clock_registers.vCG_SPLL_FUNC_CNTL_3;
+ uint32_t spll_func_cntl_4 = data->clock_registers.vCG_SPLL_FUNC_CNTL_4;
+ uint32_t cg_spll_spread_spectrum = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM;
+ uint32_t cg_spll_spread_spectrum_2 = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2;
+ uint32_t ref_clock;
+ uint32_t ref_divider;
+ uint32_t fbdiv;
+ int result;
+
+ /* get the engine clock dividers for this clock value */
+ result = atomctrl_get_engine_pll_dividers_vi(hwmgr, clock, &dividers);
+
+ PP_ASSERT_WITH_CODE(result == 0,
+ "Error retrieving Engine Clock dividers from VBIOS.",
+ return result);
+
+ /* To get FBDIV we need to multiply this by 16384 and divide it by Fref. */
+ ref_clock = atomctrl_get_reference_clock(hwmgr);
+ ref_divider = 1 + dividers.uc_pll_ref_div;
+
+ /* low 14 bits is fraction and high 12 bits is divider */
+ fbdiv = dividers.ul_fb_div.ul_fb_divider & 0x3FFFFFF;
+
+ /* SPLL_FUNC_CNTL setup */
+ spll_func_cntl = PHM_SET_FIELD(spll_func_cntl, CG_SPLL_FUNC_CNTL,
+ SPLL_REF_DIV, dividers.uc_pll_ref_div);
+ spll_func_cntl = PHM_SET_FIELD(spll_func_cntl, CG_SPLL_FUNC_CNTL,
+ SPLL_PDIV_A, dividers.uc_pll_post_div);
+
+ /* SPLL_FUNC_CNTL_3 setup*/
+ spll_func_cntl_3 = PHM_SET_FIELD(spll_func_cntl_3, CG_SPLL_FUNC_CNTL_3,
+ SPLL_FB_DIV, fbdiv);
+
+ /* set to use fractional accumulation*/
+ spll_func_cntl_3 = PHM_SET_FIELD(spll_func_cntl_3, CG_SPLL_FUNC_CNTL_3,
+ SPLL_DITHEN, 1);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EngineSpreadSpectrumSupport)) {
+ struct pp_atomctrl_internal_ss_info ssInfo;
+
+ uint32_t vco_freq = clock * dividers.uc_pll_post_div;
+ if (!atomctrl_get_engine_clock_spread_spectrum(hwmgr,
+ vco_freq, &ssInfo)) {
+ /*
+ * ss_info.speed_spectrum_percentage -- in unit of 0.01%
+ * ss_info.speed_spectrum_rate -- in unit of khz
+ *
+ * clks = reference_clock * 10 / (REFDIV + 1) / speed_spectrum_rate / 2
+ */
+ uint32_t clk_s = ref_clock * 5 /
+ (ref_divider * ssInfo.speed_spectrum_rate);
+ /* clkv = 2 * D * fbdiv / NS */
+ uint32_t clk_v = 4 * ssInfo.speed_spectrum_percentage *
+ fbdiv / (clk_s * 10000);
+
+ cg_spll_spread_spectrum = PHM_SET_FIELD(cg_spll_spread_spectrum,
+ CG_SPLL_SPREAD_SPECTRUM, CLKS, clk_s);
+ cg_spll_spread_spectrum = PHM_SET_FIELD(cg_spll_spread_spectrum,
+ CG_SPLL_SPREAD_SPECTRUM, SSEN, 1);
+ cg_spll_spread_spectrum_2 = PHM_SET_FIELD(cg_spll_spread_spectrum_2,
+ CG_SPLL_SPREAD_SPECTRUM_2, CLKV, clk_v);
+ }
+ }
+
+ sclk->SclkFrequency = clock;
+ sclk->CgSpllFuncCntl3 = spll_func_cntl_3;
+ sclk->CgSpllFuncCntl4 = spll_func_cntl_4;
+ sclk->SpllSpreadSpectrum = cg_spll_spread_spectrum;
+ sclk->SpllSpreadSpectrum2 = cg_spll_spread_spectrum_2;
+ sclk->SclkDid = (uint8_t)dividers.pll_post_divider;
+
+ return 0;
+}
+
+static int fiji_populate_single_graphic_level(struct pp_hwmgr *hwmgr,
+ uint32_t clock, struct SMU73_Discrete_GraphicsLevel *level)
+{
+ int result;
+ /* PP_Clocks minClocks; */
+ uint32_t mvdd;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ phm_ppt_v1_clock_voltage_dependency_table *vdd_dep_table = NULL;
+
+ result = fiji_calculate_sclk_params(hwmgr, clock, level);
+
+ if (hwmgr->od_enabled)
+ vdd_dep_table = (phm_ppt_v1_clock_voltage_dependency_table *)&data->odn_dpm_table.vdd_dependency_on_sclk;
+ else
+ vdd_dep_table = table_info->vdd_dep_on_sclk;
+
+ /* populate graphics levels */
+ result = fiji_get_dependency_volt_by_clk(hwmgr,
+ vdd_dep_table, clock,
+ (uint32_t *)(&level->MinVoltage), &mvdd);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find VDDC voltage value for "
+ "VDDC engine clock dependency table",
+ return result);
+
+ level->SclkFrequency = clock;
+ level->ActivityLevel = data->current_profile_setting.sclk_activity;
+ level->CcPwrDynRm = 0;
+ level->CcPwrDynRm1 = 0;
+ level->EnabledForActivity = 0;
+ level->EnabledForThrottle = 1;
+ level->UpHyst = data->current_profile_setting.sclk_up_hyst;
+ level->DownHyst = data->current_profile_setting.sclk_down_hyst;
+ level->VoltageDownHyst = 0;
+ level->PowerThrottle = 0;
+
+ data->display_timing.min_clock_in_sr = hwmgr->display_config->min_core_set_clock_in_sr;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SclkDeepSleep))
+ level->DeepSleepDivId = smu7_get_sleep_divider_id_from_clock(clock,
+ hwmgr->display_config->min_core_set_clock_in_sr);
+
+
+ /* Default to slow, highest DPM level will be
+ * set to PPSMC_DISPLAY_WATERMARK_LOW later.
+ */
+ level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(level->MinVoltage);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->SclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_US(level->ActivityLevel);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->CgSpllFuncCntl3);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->CgSpllFuncCntl4);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->SpllSpreadSpectrum);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->SpllSpreadSpectrum2);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->CcPwrDynRm);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->CcPwrDynRm1);
+
+ return 0;
+}
+
+static int fiji_populate_all_graphic_levels(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+
+ struct smu7_dpm_table *dpm_table = &data->dpm_table;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_pcie_table *pcie_table = table_info->pcie_table;
+ uint8_t pcie_entry_cnt = (uint8_t) data->dpm_table.pcie_speed_table.count;
+ int result = 0;
+ uint32_t array = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU73_Discrete_DpmTable, GraphicsLevel);
+ uint32_t array_size = sizeof(struct SMU73_Discrete_GraphicsLevel) *
+ SMU73_MAX_LEVELS_GRAPHICS;
+ struct SMU73_Discrete_GraphicsLevel *levels =
+ smu_data->smc_state_table.GraphicsLevel;
+ uint32_t i, max_entry;
+ uint8_t hightest_pcie_level_enabled = 0,
+ lowest_pcie_level_enabled = 0,
+ mid_pcie_level_enabled = 0,
+ count = 0;
+
+ for (i = 0; i < dpm_table->sclk_table.count; i++) {
+ result = fiji_populate_single_graphic_level(hwmgr,
+ dpm_table->sclk_table.dpm_levels[i].value,
+ &levels[i]);
+ if (result)
+ return result;
+
+ /* Making sure only DPM level 0-1 have Deep Sleep Div ID populated. */
+ if (i > 1)
+ levels[i].DeepSleepDivId = 0;
+ }
+
+ /* Only enable level 0 for now.*/
+ levels[0].EnabledForActivity = 1;
+
+ /* set highest level watermark to high */
+ levels[dpm_table->sclk_table.count - 1].DisplayWatermark =
+ PPSMC_DISPLAY_WATERMARK_HIGH;
+
+ smu_data->smc_state_table.GraphicsDpmLevelCount =
+ (uint8_t)dpm_table->sclk_table.count;
+ data->dpm_level_enable_mask.sclk_dpm_enable_mask =
+ phm_get_dpm_level_enable_mask_value(&dpm_table->sclk_table);
+
+ if (pcie_table != NULL) {
+ PP_ASSERT_WITH_CODE((1 <= pcie_entry_cnt),
+ "There must be 1 or more PCIE levels defined in PPTable.",
+ return -EINVAL);
+ max_entry = pcie_entry_cnt - 1;
+ for (i = 0; i < dpm_table->sclk_table.count; i++)
+ levels[i].pcieDpmLevel =
+ (uint8_t) ((i < max_entry) ? i : max_entry);
+ } else {
+ while (data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
+ ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+ (1 << (hightest_pcie_level_enabled + 1))) != 0))
+ hightest_pcie_level_enabled++;
+
+ while (data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
+ ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+ (1 << lowest_pcie_level_enabled)) == 0))
+ lowest_pcie_level_enabled++;
+
+ while ((count < hightest_pcie_level_enabled) &&
+ ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+ (1 << (lowest_pcie_level_enabled + 1 + count))) == 0))
+ count++;
+
+ mid_pcie_level_enabled = (lowest_pcie_level_enabled + 1 + count) <
+ hightest_pcie_level_enabled ?
+ (lowest_pcie_level_enabled + 1 + count) :
+ hightest_pcie_level_enabled;
+
+ /* set pcieDpmLevel to hightest_pcie_level_enabled */
+ for (i = 2; i < dpm_table->sclk_table.count; i++)
+ levels[i].pcieDpmLevel = hightest_pcie_level_enabled;
+
+ /* set pcieDpmLevel to lowest_pcie_level_enabled */
+ levels[0].pcieDpmLevel = lowest_pcie_level_enabled;
+
+ /* set pcieDpmLevel to mid_pcie_level_enabled */
+ levels[1].pcieDpmLevel = mid_pcie_level_enabled;
+ }
+ /* level count will send to smc once at init smc table and never change */
+ result = smu7_copy_bytes_to_smc(hwmgr, array, (uint8_t *)levels,
+ (uint32_t)array_size, SMC_RAM_END);
+
+ return result;
+}
+
+
+/**
+ * MCLK Frequency Ratio
+ * SEQ_CG_RESP Bit[31:24] - 0x0
+ * Bit[27:24] \96 DDR3 Frequency ratio
+ * 0x0 <= 100MHz, 450 < 0x8 <= 500MHz
+ * 100 < 0x1 <= 150MHz, 500 < 0x9 <= 550MHz
+ * 150 < 0x2 <= 200MHz, 550 < 0xA <= 600MHz
+ * 200 < 0x3 <= 250MHz, 600 < 0xB <= 650MHz
+ * 250 < 0x4 <= 300MHz, 650 < 0xC <= 700MHz
+ * 300 < 0x5 <= 350MHz, 700 < 0xD <= 750MHz
+ * 350 < 0x6 <= 400MHz, 750 < 0xE <= 800MHz
+ * 400 < 0x7 <= 450MHz, 800 < 0xF
+ */
+static uint8_t fiji_get_mclk_frequency_ratio(uint32_t mem_clock)
+{
+ if (mem_clock <= 10000)
+ return 0x0;
+ if (mem_clock <= 15000)
+ return 0x1;
+ if (mem_clock <= 20000)
+ return 0x2;
+ if (mem_clock <= 25000)
+ return 0x3;
+ if (mem_clock <= 30000)
+ return 0x4;
+ if (mem_clock <= 35000)
+ return 0x5;
+ if (mem_clock <= 40000)
+ return 0x6;
+ if (mem_clock <= 45000)
+ return 0x7;
+ if (mem_clock <= 50000)
+ return 0x8;
+ if (mem_clock <= 55000)
+ return 0x9;
+ if (mem_clock <= 60000)
+ return 0xa;
+ if (mem_clock <= 65000)
+ return 0xb;
+ if (mem_clock <= 70000)
+ return 0xc;
+ if (mem_clock <= 75000)
+ return 0xd;
+ if (mem_clock <= 80000)
+ return 0xe;
+ /* mem_clock > 800MHz */
+ return 0xf;
+}
+
+static int fiji_calculate_mclk_params(struct pp_hwmgr *hwmgr,
+ uint32_t clock, struct SMU73_Discrete_MemoryLevel *mclk)
+{
+ struct pp_atomctrl_memory_clock_param mem_param;
+ int result;
+
+ result = atomctrl_get_memory_pll_dividers_vi(hwmgr, clock, &mem_param);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Failed to get Memory PLL Dividers.",
+ );
+
+ /* Save the result data to outpupt memory level structure */
+ mclk->MclkFrequency = clock;
+ mclk->MclkDivider = (uint8_t)mem_param.mpll_post_divider;
+ mclk->FreqRange = fiji_get_mclk_frequency_ratio(clock);
+
+ return result;
+}
+
+static int fiji_populate_single_memory_level(struct pp_hwmgr *hwmgr,
+ uint32_t clock, struct SMU73_Discrete_MemoryLevel *mem_level)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ int result = 0;
+ uint32_t mclk_stutter_mode_threshold = 60000;
+ phm_ppt_v1_clock_voltage_dependency_table *vdd_dep_table = NULL;
+
+ if (hwmgr->od_enabled)
+ vdd_dep_table = (phm_ppt_v1_clock_voltage_dependency_table *)&data->odn_dpm_table.vdd_dependency_on_mclk;
+ else
+ vdd_dep_table = table_info->vdd_dep_on_mclk;
+
+ if (vdd_dep_table) {
+ result = fiji_get_dependency_volt_by_clk(hwmgr,
+ vdd_dep_table, clock,
+ (uint32_t *)(&mem_level->MinVoltage), &mem_level->MinMvdd);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find MinVddc voltage value from memory "
+ "VDDC voltage dependency table", return result);
+ }
+
+ mem_level->EnabledForThrottle = 1;
+ mem_level->EnabledForActivity = 0;
+ mem_level->UpHyst = data->current_profile_setting.mclk_up_hyst;
+ mem_level->DownHyst = data->current_profile_setting.mclk_down_hyst;
+ mem_level->VoltageDownHyst = 0;
+ mem_level->ActivityLevel = data->current_profile_setting.mclk_activity;
+ mem_level->StutterEnable = false;
+
+ mem_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+ /* enable stutter mode if all the follow condition applied
+ * PECI_GetNumberOfActiveDisplays(hwmgr->pPECI,
+ * &(data->DisplayTiming.numExistingDisplays));
+ */
+ data->display_timing.num_existing_displays = hwmgr->display_config->num_display;
+ data->display_timing.vrefresh = hwmgr->display_config->vrefresh;
+
+ if (mclk_stutter_mode_threshold &&
+ (clock <= mclk_stutter_mode_threshold) &&
+ (!data->is_uvd_enabled) &&
+ (PHM_READ_FIELD(hwmgr->device, DPG_PIPE_STUTTER_CONTROL,
+ STUTTER_ENABLE) & 0x1))
+ mem_level->StutterEnable = true;
+
+ result = fiji_calculate_mclk_params(hwmgr, clock, mem_level);
+ if (!result) {
+ CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MinMvdd);
+ CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_US(mem_level->ActivityLevel);
+ CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MinVoltage);
+ }
+ return result;
+}
+
+static int fiji_populate_all_memory_levels(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+ struct smu7_dpm_table *dpm_table = &data->dpm_table;
+ int result;
+ /* populate MCLK dpm table to SMU7 */
+ uint32_t array = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU73_Discrete_DpmTable, MemoryLevel);
+ uint32_t array_size = sizeof(SMU73_Discrete_MemoryLevel) *
+ SMU73_MAX_LEVELS_MEMORY;
+ struct SMU73_Discrete_MemoryLevel *levels =
+ smu_data->smc_state_table.MemoryLevel;
+ uint32_t i;
+
+ for (i = 0; i < dpm_table->mclk_table.count; i++) {
+ PP_ASSERT_WITH_CODE((0 != dpm_table->mclk_table.dpm_levels[i].value),
+ "can not populate memory level as memory clock is zero",
+ return -EINVAL);
+ result = fiji_populate_single_memory_level(hwmgr,
+ dpm_table->mclk_table.dpm_levels[i].value,
+ &levels[i]);
+ if (result)
+ return result;
+ }
+
+ /* Only enable level 0 for now. */
+ levels[0].EnabledForActivity = 1;
+
+ /* in order to prevent MC activity from stutter mode to push DPM up.
+ * the UVD change complements this by putting the MCLK in
+ * a higher state by default such that we are not effected by
+ * up threshold or and MCLK DPM latency.
+ */
+ levels[0].ActivityLevel = (uint16_t)data->mclk_dpm0_activity_target;
+ CONVERT_FROM_HOST_TO_SMC_US(levels[0].ActivityLevel);
+
+ smu_data->smc_state_table.MemoryDpmLevelCount =
+ (uint8_t)dpm_table->mclk_table.count;
+ data->dpm_level_enable_mask.mclk_dpm_enable_mask =
+ phm_get_dpm_level_enable_mask_value(&dpm_table->mclk_table);
+ /* set highest level watermark to high */
+ levels[dpm_table->mclk_table.count - 1].DisplayWatermark =
+ PPSMC_DISPLAY_WATERMARK_HIGH;
+
+ /* level count will send to smc once at init smc table and never change */
+ result = smu7_copy_bytes_to_smc(hwmgr, array, (uint8_t *)levels,
+ (uint32_t)array_size, SMC_RAM_END);
+
+ return result;
+}
+
+static int fiji_populate_mvdd_value(struct pp_hwmgr *hwmgr,
+ uint32_t mclk, SMIO_Pattern *smio_pat)
+{
+ const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ uint32_t i = 0;
+
+ if (SMU7_VOLTAGE_CONTROL_NONE != data->mvdd_control) {
+ /* find mvdd value which clock is more than request */
+ for (i = 0; i < table_info->vdd_dep_on_mclk->count; i++) {
+ if (mclk <= table_info->vdd_dep_on_mclk->entries[i].clk) {
+ smio_pat->Voltage = data->mvdd_voltage_table.entries[i].value;
+ break;
+ }
+ }
+ PP_ASSERT_WITH_CODE(i < table_info->vdd_dep_on_mclk->count,
+ "MVDD Voltage is outside the supported range.",
+ return -EINVAL);
+ } else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int fiji_populate_smc_acpi_level(struct pp_hwmgr *hwmgr,
+ SMU73_Discrete_DpmTable *table)
+{
+ int result = 0;
+ const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct pp_atomctrl_clock_dividers_vi dividers;
+ SMIO_Pattern vol_level;
+ uint32_t mvdd;
+ uint16_t us_mvdd;
+ uint32_t spll_func_cntl = data->clock_registers.vCG_SPLL_FUNC_CNTL;
+ uint32_t spll_func_cntl_2 = data->clock_registers.vCG_SPLL_FUNC_CNTL_2;
+
+ table->ACPILevel.Flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+ if (!data->sclk_dpm_key_disabled) {
+ /* Get MinVoltage and Frequency from DPM0,
+ * already converted to SMC_UL */
+ table->ACPILevel.SclkFrequency =
+ data->dpm_table.sclk_table.dpm_levels[0].value;
+ result = fiji_get_dependency_volt_by_clk(hwmgr,
+ table_info->vdd_dep_on_sclk,
+ table->ACPILevel.SclkFrequency,
+ (uint32_t *)(&table->ACPILevel.MinVoltage), &mvdd);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Cannot find ACPI VDDC voltage value " \
+ "in Clock Dependency Table",
+ );
+ } else {
+ table->ACPILevel.SclkFrequency =
+ data->vbios_boot_state.sclk_bootup_value;
+ table->ACPILevel.MinVoltage =
+ data->vbios_boot_state.vddc_bootup_value * VOLTAGE_SCALE;
+ }
+
+ /* get the engine clock dividers for this clock value */
+ result = atomctrl_get_engine_pll_dividers_vi(hwmgr,
+ table->ACPILevel.SclkFrequency, &dividers);
+ PP_ASSERT_WITH_CODE(result == 0,
+ "Error retrieving Engine Clock dividers from VBIOS.",
+ return result);
+
+ table->ACPILevel.SclkDid = (uint8_t)dividers.pll_post_divider;
+ table->ACPILevel.DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+ table->ACPILevel.DeepSleepDivId = 0;
+
+ spll_func_cntl = PHM_SET_FIELD(spll_func_cntl, CG_SPLL_FUNC_CNTL,
+ SPLL_PWRON, 0);
+ spll_func_cntl = PHM_SET_FIELD(spll_func_cntl, CG_SPLL_FUNC_CNTL,
+ SPLL_RESET, 1);
+ spll_func_cntl_2 = PHM_SET_FIELD(spll_func_cntl_2, CG_SPLL_FUNC_CNTL_2,
+ SCLK_MUX_SEL, 4);
+
+ table->ACPILevel.CgSpllFuncCntl = spll_func_cntl;
+ table->ACPILevel.CgSpllFuncCntl2 = spll_func_cntl_2;
+ table->ACPILevel.CgSpllFuncCntl3 = data->clock_registers.vCG_SPLL_FUNC_CNTL_3;
+ table->ACPILevel.CgSpllFuncCntl4 = data->clock_registers.vCG_SPLL_FUNC_CNTL_4;
+ table->ACPILevel.SpllSpreadSpectrum = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM;
+ table->ACPILevel.SpllSpreadSpectrum2 = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2;
+ table->ACPILevel.CcPwrDynRm = 0;
+ table->ACPILevel.CcPwrDynRm1 = 0;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.Flags);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.MinVoltage);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl2);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl3);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl4);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum2);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm1);
+
+ if (!data->mclk_dpm_key_disabled) {
+ /* Get MinVoltage and Frequency from DPM0, already converted to SMC_UL */
+ table->MemoryACPILevel.MclkFrequency =
+ data->dpm_table.mclk_table.dpm_levels[0].value;
+ result = fiji_get_dependency_volt_by_clk(hwmgr,
+ table_info->vdd_dep_on_mclk,
+ table->MemoryACPILevel.MclkFrequency,
+ (uint32_t *)(&table->MemoryACPILevel.MinVoltage), &mvdd);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Cannot find ACPI VDDCI voltage value in Clock Dependency Table",
+ );
+ } else {
+ table->MemoryACPILevel.MclkFrequency =
+ data->vbios_boot_state.mclk_bootup_value;
+ table->MemoryACPILevel.MinVoltage =
+ data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE;
+ }
+
+ us_mvdd = 0;
+ if ((SMU7_VOLTAGE_CONTROL_NONE == data->mvdd_control) ||
+ (data->mclk_dpm_key_disabled))
+ us_mvdd = data->vbios_boot_state.mvdd_bootup_value;
+ else {
+ if (!fiji_populate_mvdd_value(hwmgr,
+ data->dpm_table.mclk_table.dpm_levels[0].value,
+ &vol_level))
+ us_mvdd = vol_level.Voltage;
+ }
+
+ table->MemoryACPILevel.MinMvdd =
+ PP_HOST_TO_SMC_UL(us_mvdd * VOLTAGE_SCALE);
+
+ table->MemoryACPILevel.EnabledForThrottle = 0;
+ table->MemoryACPILevel.EnabledForActivity = 0;
+ table->MemoryACPILevel.UpHyst = 0;
+ table->MemoryACPILevel.DownHyst = 100;
+ table->MemoryACPILevel.VoltageDownHyst = 0;
+ table->MemoryACPILevel.ActivityLevel =
+ PP_HOST_TO_SMC_US(data->current_profile_setting.mclk_activity);
+
+ table->MemoryACPILevel.StutterEnable = false;
+ CONVERT_FROM_HOST_TO_SMC_UL(table->MemoryACPILevel.MclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->MemoryACPILevel.MinVoltage);
+
+ return result;
+}
+
+static int fiji_populate_smc_vce_level(struct pp_hwmgr *hwmgr,
+ SMU73_Discrete_DpmTable *table)
+{
+ int result = -EINVAL;
+ uint8_t count;
+ struct pp_atomctrl_clock_dividers_vi dividers;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+ table_info->mm_dep_table;
+
+ table->VceLevelCount = (uint8_t)(mm_table->count);
+ table->VceBootLevel = 0;
+
+ for (count = 0; count < table->VceLevelCount; count++) {
+ table->VceLevel[count].Frequency = mm_table->entries[count].eclk;
+ table->VceLevel[count].MinVoltage = 0;
+ table->VceLevel[count].MinVoltage |=
+ (mm_table->entries[count].vddc * VOLTAGE_SCALE) << VDDC_SHIFT;
+ table->VceLevel[count].MinVoltage |=
+ ((mm_table->entries[count].vddc - VDDC_VDDCI_DELTA) *
+ VOLTAGE_SCALE) << VDDCI_SHIFT;
+ table->VceLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
+
+ /*retrieve divider value for VBIOS */
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+ table->VceLevel[count].Frequency, &dividers);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find divide id for VCE engine clock",
+ return result);
+
+ table->VceLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VceLevel[count].Frequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VceLevel[count].MinVoltage);
+ }
+ return result;
+}
+
+static int fiji_populate_smc_acp_level(struct pp_hwmgr *hwmgr,
+ SMU73_Discrete_DpmTable *table)
+{
+ int result = -EINVAL;
+ uint8_t count;
+ struct pp_atomctrl_clock_dividers_vi dividers;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+ table_info->mm_dep_table;
+
+ table->AcpLevelCount = (uint8_t)(mm_table->count);
+ table->AcpBootLevel = 0;
+
+ for (count = 0; count < table->AcpLevelCount; count++) {
+ table->AcpLevel[count].Frequency = mm_table->entries[count].aclk;
+ table->AcpLevel[count].MinVoltage |= (mm_table->entries[count].vddc *
+ VOLTAGE_SCALE) << VDDC_SHIFT;
+ table->AcpLevel[count].MinVoltage |= ((mm_table->entries[count].vddc -
+ VDDC_VDDCI_DELTA) * VOLTAGE_SCALE) << VDDCI_SHIFT;
+ table->AcpLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
+
+ /* retrieve divider value for VBIOS */
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+ table->AcpLevel[count].Frequency, &dividers);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find divide id for engine clock", return result);
+
+ table->AcpLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->AcpLevel[count].Frequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->AcpLevel[count].MinVoltage);
+ }
+ return result;
+}
+
+static int fiji_populate_memory_timing_parameters(struct pp_hwmgr *hwmgr,
+ int32_t eng_clock, int32_t mem_clock,
+ struct SMU73_Discrete_MCArbDramTimingTableEntry *arb_regs)
+{
+ uint32_t dram_timing;
+ uint32_t dram_timing2;
+ uint32_t burstTime;
+ ULONG trrds, trrdl;
+ int result;
+
+ result = atomctrl_set_engine_dram_timings_rv770(hwmgr,
+ eng_clock, mem_clock);
+ PP_ASSERT_WITH_CODE(result == 0,
+ "Error calling VBIOS to set DRAM_TIMING.", return result);
+
+ dram_timing = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING);
+ dram_timing2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2);
+ burstTime = cgs_read_register(hwmgr->device, mmMC_ARB_BURST_TIME);
+
+ trrds = PHM_GET_FIELD(burstTime, MC_ARB_BURST_TIME, TRRDS0);
+ trrdl = PHM_GET_FIELD(burstTime, MC_ARB_BURST_TIME, TRRDL0);
+
+ arb_regs->McArbDramTiming = PP_HOST_TO_SMC_UL(dram_timing);
+ arb_regs->McArbDramTiming2 = PP_HOST_TO_SMC_UL(dram_timing2);
+ arb_regs->McArbBurstTime = (uint8_t)burstTime;
+ arb_regs->TRRDS = (uint8_t)trrds;
+ arb_regs->TRRDL = (uint8_t)trrdl;
+
+ return 0;
+}
+
+static int fiji_program_memory_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+ struct SMU73_Discrete_MCArbDramTimingTable arb_regs;
+ uint32_t i, j;
+ int result = 0;
+
+ for (i = 0; i < data->dpm_table.sclk_table.count; i++) {
+ for (j = 0; j < data->dpm_table.mclk_table.count; j++) {
+ result = fiji_populate_memory_timing_parameters(hwmgr,
+ data->dpm_table.sclk_table.dpm_levels[i].value,
+ data->dpm_table.mclk_table.dpm_levels[j].value,
+ &arb_regs.entries[i][j]);
+ if (result)
+ break;
+ }
+ }
+
+ if (!result)
+ result = smu7_copy_bytes_to_smc(
+ hwmgr,
+ smu_data->smu7_data.arb_table_start,
+ (uint8_t *)&arb_regs,
+ sizeof(SMU73_Discrete_MCArbDramTimingTable),
+ SMC_RAM_END);
+ return result;
+}
+
+static int fiji_populate_smc_uvd_level(struct pp_hwmgr *hwmgr,
+ struct SMU73_Discrete_DpmTable *table)
+{
+ int result = -EINVAL;
+ uint8_t count;
+ struct pp_atomctrl_clock_dividers_vi dividers;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+ table_info->mm_dep_table;
+
+ table->UvdLevelCount = (uint8_t)(mm_table->count);
+ table->UvdBootLevel = 0;
+
+ for (count = 0; count < table->UvdLevelCount; count++) {
+ table->UvdLevel[count].MinVoltage = 0;
+ table->UvdLevel[count].VclkFrequency = mm_table->entries[count].vclk;
+ table->UvdLevel[count].DclkFrequency = mm_table->entries[count].dclk;
+ table->UvdLevel[count].MinVoltage |= (mm_table->entries[count].vddc *
+ VOLTAGE_SCALE) << VDDC_SHIFT;
+ table->UvdLevel[count].MinVoltage |= ((mm_table->entries[count].vddc -
+ VDDC_VDDCI_DELTA) * VOLTAGE_SCALE) << VDDCI_SHIFT;
+ table->UvdLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
+
+ /* retrieve divider value for VBIOS */
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+ table->UvdLevel[count].VclkFrequency, &dividers);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find divide id for Vclk clock", return result);
+
+ table->UvdLevel[count].VclkDivider = (uint8_t)dividers.pll_post_divider;
+
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+ table->UvdLevel[count].DclkFrequency, &dividers);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find divide id for Dclk clock", return result);
+
+ table->UvdLevel[count].DclkDivider = (uint8_t)dividers.pll_post_divider;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].VclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].DclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].MinVoltage);
+
+ }
+ return result;
+}
+
+static int fiji_populate_smc_boot_level(struct pp_hwmgr *hwmgr,
+ struct SMU73_Discrete_DpmTable *table)
+{
+ int result = 0;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ table->GraphicsBootLevel = 0;
+ table->MemoryBootLevel = 0;
+
+ /* find boot level from dpm table */
+ result = phm_find_boot_level(&(data->dpm_table.sclk_table),
+ data->vbios_boot_state.sclk_bootup_value,
+ (uint32_t *)&(table->GraphicsBootLevel));
+
+ result = phm_find_boot_level(&(data->dpm_table.mclk_table),
+ data->vbios_boot_state.mclk_bootup_value,
+ (uint32_t *)&(table->MemoryBootLevel));
+
+ table->BootVddc = data->vbios_boot_state.vddc_bootup_value *
+ VOLTAGE_SCALE;
+ table->BootVddci = data->vbios_boot_state.vddci_bootup_value *
+ VOLTAGE_SCALE;
+ table->BootMVdd = data->vbios_boot_state.mvdd_bootup_value *
+ VOLTAGE_SCALE;
+
+ CONVERT_FROM_HOST_TO_SMC_US(table->BootVddc);
+ CONVERT_FROM_HOST_TO_SMC_US(table->BootVddci);
+ CONVERT_FROM_HOST_TO_SMC_US(table->BootMVdd);
+
+ return 0;
+}
+
+static int fiji_populate_smc_initailial_state(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ uint8_t count, level;
+
+ count = (uint8_t)(table_info->vdd_dep_on_sclk->count);
+ for (level = 0; level < count; level++) {
+ if (table_info->vdd_dep_on_sclk->entries[level].clk >=
+ data->vbios_boot_state.sclk_bootup_value) {
+ smu_data->smc_state_table.GraphicsBootLevel = level;
+ break;
+ }
+ }
+
+ count = (uint8_t)(table_info->vdd_dep_on_mclk->count);
+ for (level = 0; level < count; level++) {
+ if (table_info->vdd_dep_on_mclk->entries[level].clk >=
+ data->vbios_boot_state.mclk_bootup_value) {
+ smu_data->smc_state_table.MemoryBootLevel = level;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int fiji_populate_clock_stretcher_data_table(struct pp_hwmgr *hwmgr)
+{
+ uint32_t ro, efuse, efuse2, clock_freq, volt_without_cks,
+ volt_with_cks, value;
+ uint16_t clock_freq_u16;
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+ uint8_t type, i, j, cks_setting, stretch_amount, stretch_amount2,
+ volt_offset = 0;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
+ table_info->vdd_dep_on_sclk;
+
+ stretch_amount = (uint8_t)table_info->cac_dtp_table->usClockStretchAmount;
+
+ /* Read SMU_Eefuse to read and calculate RO and determine
+ * if the part is SS or FF. if RO >= 1660MHz, part is FF.
+ */
+ efuse = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixSMU_EFUSE_0 + (146 * 4));
+ efuse2 = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixSMU_EFUSE_0 + (148 * 4));
+ efuse &= 0xFF000000;
+ efuse = efuse >> 24;
+ efuse2 &= 0xF;
+
+ if (efuse2 == 1)
+ ro = (2300 - 1350) * efuse / 255 + 1350;
+ else
+ ro = (2500 - 1000) * efuse / 255 + 1000;
+
+ if (ro >= 1660)
+ type = 0;
+ else
+ type = 1;
+
+ /* Populate Stretch amount */
+ smu_data->smc_state_table.ClockStretcherAmount = stretch_amount;
+
+ /* Populate Sclk_CKS_masterEn0_7 and Sclk_voltageOffset */
+ for (i = 0; i < sclk_table->count; i++) {
+ smu_data->smc_state_table.Sclk_CKS_masterEn0_7 |=
+ sclk_table->entries[i].cks_enable << i;
+ volt_without_cks = (uint32_t)((14041 *
+ (sclk_table->entries[i].clk/100) / 10000 + 3571 + 75 - ro) * 1000 /
+ (4026 - (13924 * (sclk_table->entries[i].clk/100) / 10000)));
+ volt_with_cks = (uint32_t)((13946 *
+ (sclk_table->entries[i].clk/100) / 10000 + 3320 + 45 - ro) * 1000 /
+ (3664 - (11454 * (sclk_table->entries[i].clk/100) / 10000)));
+ if (volt_without_cks >= volt_with_cks)
+ volt_offset = (uint8_t)(((volt_without_cks - volt_with_cks +
+ sclk_table->entries[i].cks_voffset) * 100 / 625) + 1);
+ smu_data->smc_state_table.Sclk_voltageOffset[i] = volt_offset;
+ }
+
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
+ STRETCH_ENABLE, 0x0);
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
+ masterReset, 0x1);
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
+ staticEnable, 0x1);
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
+ masterReset, 0x0);
+
+ /* Populate CKS Lookup Table */
+ if (stretch_amount == 1 || stretch_amount == 2 || stretch_amount == 5)
+ stretch_amount2 = 0;
+ else if (stretch_amount == 3 || stretch_amount == 4)
+ stretch_amount2 = 1;
+ else {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ClockStretcher);
+ PP_ASSERT_WITH_CODE(false,
+ "Stretch Amount in PPTable not supported",
+ return -EINVAL);
+ }
+
+ value = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixPWR_CKS_CNTL);
+ value &= 0xFFC2FF87;
+ smu_data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].minFreq =
+ fiji_clock_stretcher_lookup_table[stretch_amount2][0];
+ smu_data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].maxFreq =
+ fiji_clock_stretcher_lookup_table[stretch_amount2][1];
+ clock_freq_u16 = (uint16_t)(PP_SMC_TO_HOST_UL(smu_data->smc_state_table.
+ GraphicsLevel[smu_data->smc_state_table.GraphicsDpmLevelCount - 1].
+ SclkFrequency) / 100);
+ if (fiji_clock_stretcher_lookup_table[stretch_amount2][0] <
+ clock_freq_u16 &&
+ fiji_clock_stretcher_lookup_table[stretch_amount2][1] >
+ clock_freq_u16) {
+ /* Program PWR_CKS_CNTL. CKS_USE_FOR_LOW_FREQ */
+ value |= (fiji_clock_stretcher_lookup_table[stretch_amount2][3]) << 16;
+ /* Program PWR_CKS_CNTL. CKS_LDO_REFSEL */
+ value |= (fiji_clock_stretcher_lookup_table[stretch_amount2][2]) << 18;
+ /* Program PWR_CKS_CNTL. CKS_STRETCH_AMOUNT */
+ value |= (fiji_clock_stretch_amount_conversion
+ [fiji_clock_stretcher_lookup_table[stretch_amount2][3]]
+ [stretch_amount]) << 3;
+ }
+ CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.CKS_LOOKUPTable.
+ CKS_LOOKUPTableEntry[0].minFreq);
+ CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.CKS_LOOKUPTable.
+ CKS_LOOKUPTableEntry[0].maxFreq);
+ smu_data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].setting =
+ fiji_clock_stretcher_lookup_table[stretch_amount2][2] & 0x7F;
+ smu_data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].setting |=
+ (fiji_clock_stretcher_lookup_table[stretch_amount2][3]) << 7;
+
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixPWR_CKS_CNTL, value);
+
+ /* Populate DDT Lookup Table */
+ for (i = 0; i < 4; i++) {
+ /* Assign the minimum and maximum VID stored
+ * in the last row of Clock Stretcher Voltage Table.
+ */
+ smu_data->smc_state_table.ClockStretcherDataTable.
+ ClockStretcherDataTableEntry[i].minVID =
+ (uint8_t) fiji_clock_stretcher_ddt_table[type][i][2];
+ smu_data->smc_state_table.ClockStretcherDataTable.
+ ClockStretcherDataTableEntry[i].maxVID =
+ (uint8_t) fiji_clock_stretcher_ddt_table[type][i][3];
+ /* Loop through each SCLK and check the frequency
+ * to see if it lies within the frequency for clock stretcher.
+ */
+ for (j = 0; j < smu_data->smc_state_table.GraphicsDpmLevelCount; j++) {
+ cks_setting = 0;
+ clock_freq = PP_SMC_TO_HOST_UL(
+ smu_data->smc_state_table.GraphicsLevel[j].SclkFrequency);
+ /* Check the allowed frequency against the sclk level[j].
+ * Sclk's endianness has already been converted,
+ * and it's in 10Khz unit,
+ * as opposed to Data table, which is in Mhz unit.
+ */
+ if (clock_freq >=
+ (fiji_clock_stretcher_ddt_table[type][i][0]) * 100) {
+ cks_setting |= 0x2;
+ if (clock_freq <
+ (fiji_clock_stretcher_ddt_table[type][i][1]) * 100)
+ cks_setting |= 0x1;
+ }
+ smu_data->smc_state_table.ClockStretcherDataTable.
+ ClockStretcherDataTableEntry[i].setting |= cks_setting << (j * 2);
+ }
+ CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.
+ ClockStretcherDataTable.
+ ClockStretcherDataTableEntry[i].setting);
+ }
+
+ value = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixPWR_CKS_CNTL);
+ value &= 0xFFFFFFFE;
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixPWR_CKS_CNTL, value);
+
+ return 0;
+}
+
+static int fiji_populate_vr_config(struct pp_hwmgr *hwmgr,
+ struct SMU73_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint16_t config;
+
+ config = VR_MERGED_WITH_VDDC;
+ table->VRConfig |= (config << VRCONF_VDDGFX_SHIFT);
+
+ /* Set Vddc Voltage Controller */
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
+ config = VR_SVI2_PLANE_1;
+ table->VRConfig |= config;
+ } else {
+ PP_ASSERT_WITH_CODE(false,
+ "VDDC should be on SVI2 control in merged mode!",
+ );
+ }
+ /* Set Vddci Voltage Controller */
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) {
+ config = VR_SVI2_PLANE_2; /* only in merged mode */
+ table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
+ } else if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
+ config = VR_SMIO_PATTERN_1;
+ table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
+ } else {
+ config = VR_STATIC_VOLTAGE;
+ table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
+ }
+ /* Set Mvdd Voltage Controller */
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->mvdd_control) {
+ config = VR_SVI2_PLANE_2;
+ table->VRConfig |= (config << VRCONF_MVDD_SHIFT);
+ } else if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
+ config = VR_SMIO_PATTERN_2;
+ table->VRConfig |= (config << VRCONF_MVDD_SHIFT);
+ } else {
+ config = VR_STATIC_VOLTAGE;
+ table->VRConfig |= (config << VRCONF_MVDD_SHIFT);
+ }
+
+ return 0;
+}
+
+static int fiji_init_arb_table_index(struct pp_hwmgr *hwmgr)
+{
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+ uint32_t tmp;
+ int result;
+
+ /* This is a read-modify-write on the first byte of the ARB table.
+ * The first byte in the SMU73_Discrete_MCArbDramTimingTable structure
+ * is the field 'current'.
+ * This solution is ugly, but we never write the whole table only
+ * individual fields in it.
+ * In reality this field should not be in that structure
+ * but in a soft register.
+ */
+ result = smu7_read_smc_sram_dword(hwmgr,
+ smu_data->smu7_data.arb_table_start, &tmp, SMC_RAM_END);
+
+ if (result)
+ return result;
+
+ tmp &= 0x00FFFFFF;
+ tmp |= ((uint32_t)MC_CG_ARB_FREQ_F1) << 24;
+
+ return smu7_write_smc_sram_dword(hwmgr,
+ smu_data->smu7_data.arb_table_start, tmp, SMC_RAM_END);
+}
+
+static int fiji_setup_dpm_led_config(struct pp_hwmgr *hwmgr)
+{
+ pp_atomctrl_voltage_table param_led_dpm;
+ int result = 0;
+ u32 mask = 0;
+
+ result = atomctrl_get_voltage_table_v3(hwmgr,
+ VOLTAGE_TYPE_LEDDPM, VOLTAGE_OBJ_GPIO_LUT,
+ &param_led_dpm);
+ if (result == 0) {
+ int i, j;
+ u32 tmp = param_led_dpm.mask_low;
+
+ for (i = 0, j = 0; i < 32; i++) {
+ if (tmp & 1) {
+ mask |= (i << (8 * j));
+ if (++j >= 3)
+ break;
+ }
+ tmp >>= 1;
+ }
+ }
+ if (mask)
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_LedConfig,
+ mask,
+ NULL);
+ return 0;
+}
+
+static int fiji_init_smc_table(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct SMU73_Discrete_DpmTable *table = &(smu_data->smc_state_table);
+ uint8_t i;
+ struct pp_atomctrl_gpio_pin_assignment gpio_pin;
+
+ fiji_initialize_power_tune_defaults(hwmgr);
+
+ if (SMU7_VOLTAGE_CONTROL_NONE != data->voltage_control)
+ fiji_populate_smc_voltage_tables(hwmgr, table);
+
+ table->SystemFlags = 0;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition))
+ table->SystemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_StepVddc))
+ table->SystemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+ if (data->is_memory_gddr5)
+ table->SystemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
+
+ if (data->ulv_supported && table_info->us_ulv_voltage_offset) {
+ result = fiji_populate_ulv_state(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize ULV state!", return result);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixCG_ULV_PARAMETER, 0x40035);
+ }
+
+ result = fiji_populate_smc_link_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize Link Level!", return result);
+
+ result = fiji_populate_all_graphic_levels(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize Graphics Level!", return result);
+
+ result = fiji_populate_all_memory_levels(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize Memory Level!", return result);
+
+ result = fiji_populate_smc_acpi_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize ACPI Level!", return result);
+
+ result = fiji_populate_smc_vce_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize VCE Level!", return result);
+
+ result = fiji_populate_smc_acp_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize ACP Level!", return result);
+
+ /* Since only the initial state is completely set up at this point
+ * (the other states are just copies of the boot state) we only
+ * need to populate the ARB settings for the initial state.
+ */
+ result = fiji_program_memory_timing_parameters(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to Write ARB settings for the initial state.", return result);
+
+ result = fiji_populate_smc_uvd_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize UVD Level!", return result);
+
+ result = fiji_populate_smc_boot_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize Boot Level!", return result);
+
+ result = fiji_populate_smc_initailial_state(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize Boot State!", return result);
+
+ result = fiji_populate_bapm_parameters_in_dpm_table(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to populate BAPM Parameters!", return result);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ClockStretcher)) {
+ result = fiji_populate_clock_stretcher_data_table(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to populate Clock Stretcher Data Table!",
+ return result);
+ }
+
+ table->GraphicsVoltageChangeEnable = 1;
+ table->GraphicsThermThrottleEnable = 1;
+ table->GraphicsInterval = 1;
+ table->VoltageInterval = 1;
+ table->ThermalInterval = 1;
+ table->TemperatureLimitHigh =
+ table_info->cac_dtp_table->usTargetOperatingTemp *
+ SMU7_Q88_FORMAT_CONVERSION_UNIT;
+ table->TemperatureLimitLow =
+ (table_info->cac_dtp_table->usTargetOperatingTemp - 1) *
+ SMU7_Q88_FORMAT_CONVERSION_UNIT;
+ table->MemoryVoltageChangeEnable = 1;
+ table->MemoryInterval = 1;
+ table->VoltageResponseTime = 0;
+ table->PhaseResponseTime = 0;
+ table->MemoryThermThrottleEnable = 1;
+ table->PCIeBootLinkLevel = 0; /* 0:Gen1 1:Gen2 2:Gen3*/
+ table->PCIeGenInterval = 1;
+ table->VRConfig = 0;
+
+ result = fiji_populate_vr_config(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to populate VRConfig setting!", return result);
+ data->vr_config = table->VRConfig;
+ table->ThermGpio = 17;
+ table->SclkStepSize = 0x4000;
+
+ if (atomctrl_get_pp_assign_pin(hwmgr, VDDC_VRHOT_GPIO_PINID, &gpio_pin)) {
+ table->VRHotGpio = gpio_pin.uc_gpio_pin_bit_shift;
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_RegulatorHot);
+ } else {
+ table->VRHotGpio = SMU7_UNUSED_GPIO_PIN;
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_RegulatorHot);
+ }
+
+ if (atomctrl_get_pp_assign_pin(hwmgr, PP_AC_DC_SWITCH_GPIO_PINID,
+ &gpio_pin)) {
+ table->AcDcGpio = gpio_pin.uc_gpio_pin_bit_shift;
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition);
+ } else {
+ table->AcDcGpio = SMU7_UNUSED_GPIO_PIN;
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition);
+ }
+
+ /* Thermal Output GPIO */
+ if (atomctrl_get_pp_assign_pin(hwmgr, THERMAL_INT_OUTPUT_GPIO_PINID,
+ &gpio_pin)) {
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ThermalOutGPIO);
+
+ table->ThermOutGpio = gpio_pin.uc_gpio_pin_bit_shift;
+
+ /* For porlarity read GPIOPAD_A with assigned Gpio pin
+ * since VBIOS will program this register to set 'inactive state',
+ * driver can then determine 'active state' from this and
+ * program SMU with correct polarity
+ */
+ table->ThermOutPolarity = (0 == (cgs_read_register(hwmgr->device, mmGPIOPAD_A) &
+ (1 << gpio_pin.uc_gpio_pin_bit_shift))) ? 1:0;
+ table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_ONLY;
+
+ /* if required, combine VRHot/PCC with thermal out GPIO */
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_RegulatorHot) &&
+ phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_CombinePCCWithThermalSignal))
+ table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_VRHOT;
+ } else {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ThermalOutGPIO);
+ table->ThermOutGpio = 17;
+ table->ThermOutPolarity = 1;
+ table->ThermOutMode = SMU7_THERM_OUT_MODE_DISABLE;
+ }
+
+ for (i = 0; i < SMU73_MAX_ENTRIES_SMIO; i++)
+ table->Smio[i] = PP_HOST_TO_SMC_UL(table->Smio[i]);
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SystemFlags);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VRConfig);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask1);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask2);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SclkStepSize);
+ CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitHigh);
+ CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitLow);
+ CONVERT_FROM_HOST_TO_SMC_US(table->VoltageResponseTime);
+ CONVERT_FROM_HOST_TO_SMC_US(table->PhaseResponseTime);
+
+ /* Upload all dpm data to SMC memory.(dpm level, dpm level count etc) */
+ result = smu7_copy_bytes_to_smc(hwmgr,
+ smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU73_Discrete_DpmTable, SystemFlags),
+ (uint8_t *)&(table->SystemFlags),
+ sizeof(SMU73_Discrete_DpmTable) - 3 * sizeof(SMU73_PIDController),
+ SMC_RAM_END);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to upload dpm data to SMC memory!", return result);
+
+ result = fiji_init_arb_table_index(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to upload arb data to SMC memory!", return result);
+
+ result = fiji_populate_pm_fuses(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to populate PM fuses to SMC memory!", return result);
+
+ result = fiji_setup_dpm_led_config(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to setup dpm led config", return result);
+
+ return 0;
+}
+
+static int fiji_thermal_setup_fan_table(struct pp_hwmgr *hwmgr)
+{
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+
+ SMU73_Discrete_FanTable fan_table = { FDO_MODE_HARDWARE };
+ uint32_t duty100;
+ uint32_t t_diff1, t_diff2, pwm_diff1, pwm_diff2;
+ uint16_t fdo_min, slope1, slope2;
+ uint32_t reference_clock;
+ int res;
+ uint64_t tmp64;
+
+ if (hwmgr->thermal_controller.fanInfo.bNoFan) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
+ if (smu_data->smu7_data.fan_table_start == 0) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
+ duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_FDO_CTRL1, FMAX_DUTY100);
+
+ if (duty100 == 0) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
+ tmp64 = hwmgr->thermal_controller.advanceFanControlParameters.
+ usPWMMin * duty100;
+ do_div(tmp64, 10000);
+ fdo_min = (uint16_t)tmp64;
+
+ t_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usTMed -
+ hwmgr->thermal_controller.advanceFanControlParameters.usTMin;
+ t_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usTHigh -
+ hwmgr->thermal_controller.advanceFanControlParameters.usTMed;
+
+ pwm_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed -
+ hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin;
+ pwm_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh -
+ hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed;
+
+ slope1 = (uint16_t)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100);
+ slope2 = (uint16_t)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100);
+
+ fan_table.TempMin = cpu_to_be16((50 + hwmgr->
+ thermal_controller.advanceFanControlParameters.usTMin) / 100);
+ fan_table.TempMed = cpu_to_be16((50 + hwmgr->
+ thermal_controller.advanceFanControlParameters.usTMed) / 100);
+ fan_table.TempMax = cpu_to_be16((50 + hwmgr->
+ thermal_controller.advanceFanControlParameters.usTMax) / 100);
+
+ fan_table.Slope1 = cpu_to_be16(slope1);
+ fan_table.Slope2 = cpu_to_be16(slope2);
+
+ fan_table.FdoMin = cpu_to_be16(fdo_min);
+
+ fan_table.HystDown = cpu_to_be16(hwmgr->
+ thermal_controller.advanceFanControlParameters.ucTHyst);
+
+ fan_table.HystUp = cpu_to_be16(1);
+
+ fan_table.HystSlope = cpu_to_be16(1);
+
+ fan_table.TempRespLim = cpu_to_be16(5);
+
+ reference_clock = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev);
+
+ fan_table.RefreshPeriod = cpu_to_be32((hwmgr->
+ thermal_controller.advanceFanControlParameters.ulCycleDelay *
+ reference_clock) / 1600);
+
+ fan_table.FdoMax = cpu_to_be16((uint16_t)duty100);
+
+ fan_table.TempSrc = (uint8_t)PHM_READ_VFPF_INDIRECT_FIELD(
+ hwmgr->device, CGS_IND_REG__SMC,
+ CG_MULT_THERMAL_CTRL, TEMP_SEL);
+
+ res = smu7_copy_bytes_to_smc(hwmgr, smu_data->smu7_data.fan_table_start,
+ (uint8_t *)&fan_table, (uint32_t)sizeof(fan_table),
+ SMC_RAM_END);
+
+ if (!res && hwmgr->thermal_controller.
+ advanceFanControlParameters.ucMinimumPWMLimit)
+ res = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetFanMinPwm,
+ hwmgr->thermal_controller.
+ advanceFanControlParameters.ucMinimumPWMLimit,
+ NULL);
+
+ if (!res && hwmgr->thermal_controller.
+ advanceFanControlParameters.ulMinFanSCLKAcousticLimit)
+ res = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetFanSclkTarget,
+ hwmgr->thermal_controller.
+ advanceFanControlParameters.ulMinFanSCLKAcousticLimit,
+ NULL);
+
+ if (res)
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+
+ return 0;
+}
+
+
+static int fiji_thermal_avfs_enable(struct pp_hwmgr *hwmgr)
+{
+ if (!hwmgr->avfs_supported)
+ return 0;
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_EnableAvfs, NULL);
+
+ return 0;
+}
+
+static int fiji_program_mem_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (data->need_update_smu7_dpm_table &
+ (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_OD_UPDATE_MCLK))
+ return fiji_program_memory_timing_parameters(hwmgr);
+
+ return 0;
+}
+
+static int fiji_update_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+
+ int result = 0;
+ uint32_t low_sclk_interrupt_threshold = 0;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkThrottleLowNotification)
+ && (data->low_sclk_interrupt_threshold != 0)) {
+ low_sclk_interrupt_threshold =
+ data->low_sclk_interrupt_threshold;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(low_sclk_interrupt_threshold);
+
+ result = smu7_copy_bytes_to_smc(
+ hwmgr,
+ smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU73_Discrete_DpmTable,
+ LowSclkInterruptThreshold),
+ (uint8_t *)&low_sclk_interrupt_threshold,
+ sizeof(uint32_t),
+ SMC_RAM_END);
+ }
+ result = fiji_program_mem_timing_parameters(hwmgr);
+ PP_ASSERT_WITH_CODE((result == 0),
+ "Failed to program memory timing parameters!",
+ );
+ return result;
+}
+
+static uint32_t fiji_get_offsetof(uint32_t type, uint32_t member)
+{
+ switch (type) {
+ case SMU_SoftRegisters:
+ switch (member) {
+ case HandshakeDisables:
+ return offsetof(SMU73_SoftRegisters, HandshakeDisables);
+ case VoltageChangeTimeout:
+ return offsetof(SMU73_SoftRegisters, VoltageChangeTimeout);
+ case AverageGraphicsActivity:
+ return offsetof(SMU73_SoftRegisters, AverageGraphicsActivity);
+ case AverageMemoryActivity:
+ return offsetof(SMU73_SoftRegisters, AverageMemoryActivity);
+ case PreVBlankGap:
+ return offsetof(SMU73_SoftRegisters, PreVBlankGap);
+ case VBlankTimeout:
+ return offsetof(SMU73_SoftRegisters, VBlankTimeout);
+ case UcodeLoadStatus:
+ return offsetof(SMU73_SoftRegisters, UcodeLoadStatus);
+ case DRAM_LOG_ADDR_H:
+ return offsetof(SMU73_SoftRegisters, DRAM_LOG_ADDR_H);
+ case DRAM_LOG_ADDR_L:
+ return offsetof(SMU73_SoftRegisters, DRAM_LOG_ADDR_L);
+ case DRAM_LOG_PHY_ADDR_H:
+ return offsetof(SMU73_SoftRegisters, DRAM_LOG_PHY_ADDR_H);
+ case DRAM_LOG_PHY_ADDR_L:
+ return offsetof(SMU73_SoftRegisters, DRAM_LOG_PHY_ADDR_L);
+ case DRAM_LOG_BUFF_SIZE:
+ return offsetof(SMU73_SoftRegisters, DRAM_LOG_BUFF_SIZE);
+ }
+ break;
+ case SMU_Discrete_DpmTable:
+ switch (member) {
+ case UvdBootLevel:
+ return offsetof(SMU73_Discrete_DpmTable, UvdBootLevel);
+ case VceBootLevel:
+ return offsetof(SMU73_Discrete_DpmTable, VceBootLevel);
+ case LowSclkInterruptThreshold:
+ return offsetof(SMU73_Discrete_DpmTable, LowSclkInterruptThreshold);
+ }
+ break;
+ }
+ pr_warn("can't get the offset of type %x member %x\n", type, member);
+ return 0;
+}
+
+static uint32_t fiji_get_mac_definition(uint32_t value)
+{
+ switch (value) {
+ case SMU_MAX_LEVELS_GRAPHICS:
+ return SMU73_MAX_LEVELS_GRAPHICS;
+ case SMU_MAX_LEVELS_MEMORY:
+ return SMU73_MAX_LEVELS_MEMORY;
+ case SMU_MAX_LEVELS_LINK:
+ return SMU73_MAX_LEVELS_LINK;
+ case SMU_MAX_ENTRIES_SMIO:
+ return SMU73_MAX_ENTRIES_SMIO;
+ case SMU_MAX_LEVELS_VDDC:
+ return SMU73_MAX_LEVELS_VDDC;
+ case SMU_MAX_LEVELS_VDDGFX:
+ return SMU73_MAX_LEVELS_VDDGFX;
+ case SMU_MAX_LEVELS_VDDCI:
+ return SMU73_MAX_LEVELS_VDDCI;
+ case SMU_MAX_LEVELS_MVDD:
+ return SMU73_MAX_LEVELS_MVDD;
+ }
+
+ pr_warn("can't get the mac of %x\n", value);
+ return 0;
+}
+
+
+static int fiji_update_uvd_smc_table(struct pp_hwmgr *hwmgr)
+{
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+ uint32_t mm_boot_level_offset, mm_boot_level_value;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ smu_data->smc_state_table.UvdBootLevel = 0;
+ if (table_info->mm_dep_table->count > 0)
+ smu_data->smc_state_table.UvdBootLevel =
+ (uint8_t) (table_info->mm_dep_table->count - 1);
+ mm_boot_level_offset = smu_data->smu7_data.dpm_table_start + offsetof(SMU73_Discrete_DpmTable,
+ UvdBootLevel);
+ mm_boot_level_offset /= 4;
+ mm_boot_level_offset *= 4;
+ mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, mm_boot_level_offset);
+ mm_boot_level_value &= 0x00FFFFFF;
+ mm_boot_level_value |= smu_data->smc_state_table.UvdBootLevel << 24;
+ cgs_write_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
+
+ if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UVDDPM) ||
+ phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_StablePState))
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_UVDDPM_SetEnabledMask,
+ (uint32_t)(1 << smu_data->smc_state_table.UvdBootLevel),
+ NULL);
+ return 0;
+}
+
+static int fiji_update_vce_smc_table(struct pp_hwmgr *hwmgr)
+{
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+ uint32_t mm_boot_level_offset, mm_boot_level_value;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_StablePState))
+ smu_data->smc_state_table.VceBootLevel =
+ (uint8_t) (table_info->mm_dep_table->count - 1);
+ else
+ smu_data->smc_state_table.VceBootLevel = 0;
+
+ mm_boot_level_offset = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU73_Discrete_DpmTable, VceBootLevel);
+ mm_boot_level_offset /= 4;
+ mm_boot_level_offset *= 4;
+ mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, mm_boot_level_offset);
+ mm_boot_level_value &= 0xFF00FFFF;
+ mm_boot_level_value |= smu_data->smc_state_table.VceBootLevel << 16;
+ cgs_write_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_StablePState))
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_VCEDPM_SetEnabledMask,
+ (uint32_t)1 << smu_data->smc_state_table.VceBootLevel,
+ NULL);
+ return 0;
+}
+
+static int fiji_update_smc_table(struct pp_hwmgr *hwmgr, uint32_t type)
+{
+ switch (type) {
+ case SMU_UVD_TABLE:
+ fiji_update_uvd_smc_table(hwmgr);
+ break;
+ case SMU_VCE_TABLE:
+ fiji_update_vce_smc_table(hwmgr);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int fiji_process_firmware_header(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smu_backend);
+ uint32_t tmp;
+ int result;
+ bool error = false;
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU73_Firmware_Header, DpmTable),
+ &tmp, SMC_RAM_END);
+
+ if (0 == result)
+ smu_data->smu7_data.dpm_table_start = tmp;
+
+ error |= (0 != result);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU73_Firmware_Header, SoftRegisters),
+ &tmp, SMC_RAM_END);
+
+ if (!result) {
+ data->soft_regs_start = tmp;
+ smu_data->smu7_data.soft_regs_start = tmp;
+ }
+
+ error |= (0 != result);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU73_Firmware_Header, mcRegisterTable),
+ &tmp, SMC_RAM_END);
+
+ if (!result)
+ smu_data->smu7_data.mc_reg_table_start = tmp;
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU73_Firmware_Header, FanTable),
+ &tmp, SMC_RAM_END);
+
+ if (!result)
+ smu_data->smu7_data.fan_table_start = tmp;
+
+ error |= (0 != result);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU73_Firmware_Header, mcArbDramTimingTable),
+ &tmp, SMC_RAM_END);
+
+ if (!result)
+ smu_data->smu7_data.arb_table_start = tmp;
+
+ error |= (0 != result);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU73_Firmware_Header, Version),
+ &tmp, SMC_RAM_END);
+
+ if (!result)
+ hwmgr->microcode_version_info.SMC = tmp;
+
+ error |= (0 != result);
+
+ return error ? -1 : 0;
+}
+
+static int fiji_initialize_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+
+ /* Program additional LP registers
+ * that are no longer programmed by VBIOS
+ */
+ cgs_write_register(hwmgr->device, mmMC_SEQ_RAS_TIMING_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_RAS_TIMING));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_CAS_TIMING_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_CAS_TIMING));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_MISC_TIMING2_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_MISC_TIMING2));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_D1_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_D1));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_RD_CTL_D0_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_RD_CTL_D0));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_RD_CTL_D1_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_RD_CTL_D1));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_TIMING_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_TIMING));
+
+ return 0;
+}
+
+static bool fiji_is_dpm_running(struct pp_hwmgr *hwmgr)
+{
+ return (1 == PHM_READ_INDIRECT_FIELD(hwmgr->device,
+ CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON))
+ ? true : false;
+}
+
+static int fiji_update_dpm_settings(struct pp_hwmgr *hwmgr,
+ void *profile_setting)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct fiji_smumgr *smu_data = (struct fiji_smumgr *)
+ (hwmgr->smu_backend);
+ struct profile_mode_setting *setting;
+ struct SMU73_Discrete_GraphicsLevel *levels =
+ smu_data->smc_state_table.GraphicsLevel;
+ uint32_t array = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU73_Discrete_DpmTable, GraphicsLevel);
+
+ uint32_t mclk_array = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU73_Discrete_DpmTable, MemoryLevel);
+ struct SMU73_Discrete_MemoryLevel *mclk_levels =
+ smu_data->smc_state_table.MemoryLevel;
+ uint32_t i;
+ uint32_t offset, up_hyst_offset, down_hyst_offset, clk_activity_offset, tmp;
+
+ if (profile_setting == NULL)
+ return -EINVAL;
+
+ setting = (struct profile_mode_setting *)profile_setting;
+
+ if (setting->bupdate_sclk) {
+ if (!data->sclk_dpm_key_disabled)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_SCLKDPM_FreezeLevel, NULL);
+ for (i = 0; i < smu_data->smc_state_table.GraphicsDpmLevelCount; i++) {
+ if (levels[i].ActivityLevel !=
+ cpu_to_be16(setting->sclk_activity)) {
+ levels[i].ActivityLevel = cpu_to_be16(setting->sclk_activity);
+
+ clk_activity_offset = array + (sizeof(SMU73_Discrete_GraphicsLevel) * i)
+ + offsetof(SMU73_Discrete_GraphicsLevel, ActivityLevel);
+ offset = clk_activity_offset & ~0x3;
+ tmp = PP_HOST_TO_SMC_UL(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset));
+ tmp = phm_set_field_to_u32(clk_activity_offset, tmp, levels[i].ActivityLevel, sizeof(uint16_t));
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset, PP_HOST_TO_SMC_UL(tmp));
+
+ }
+ if (levels[i].UpHyst != setting->sclk_up_hyst ||
+ levels[i].DownHyst != setting->sclk_down_hyst) {
+ levels[i].UpHyst = setting->sclk_up_hyst;
+ levels[i].DownHyst = setting->sclk_down_hyst;
+ up_hyst_offset = array + (sizeof(SMU73_Discrete_GraphicsLevel) * i)
+ + offsetof(SMU73_Discrete_GraphicsLevel, UpHyst);
+ down_hyst_offset = array + (sizeof(SMU73_Discrete_GraphicsLevel) * i)
+ + offsetof(SMU73_Discrete_GraphicsLevel, DownHyst);
+ offset = up_hyst_offset & ~0x3;
+ tmp = PP_HOST_TO_SMC_UL(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset));
+ tmp = phm_set_field_to_u32(up_hyst_offset, tmp, levels[i].UpHyst, sizeof(uint8_t));
+ tmp = phm_set_field_to_u32(down_hyst_offset, tmp, levels[i].DownHyst, sizeof(uint8_t));
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset, PP_HOST_TO_SMC_UL(tmp));
+ }
+ }
+ if (!data->sclk_dpm_key_disabled)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_SCLKDPM_UnfreezeLevel, NULL);
+ }
+
+ if (setting->bupdate_mclk) {
+ if (!data->mclk_dpm_key_disabled)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_MCLKDPM_FreezeLevel, NULL);
+ for (i = 0; i < smu_data->smc_state_table.MemoryDpmLevelCount; i++) {
+ if (mclk_levels[i].ActivityLevel !=
+ cpu_to_be16(setting->mclk_activity)) {
+ mclk_levels[i].ActivityLevel = cpu_to_be16(setting->mclk_activity);
+
+ clk_activity_offset = mclk_array + (sizeof(SMU73_Discrete_MemoryLevel) * i)
+ + offsetof(SMU73_Discrete_MemoryLevel, ActivityLevel);
+ offset = clk_activity_offset & ~0x3;
+ tmp = PP_HOST_TO_SMC_UL(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset));
+ tmp = phm_set_field_to_u32(clk_activity_offset, tmp, mclk_levels[i].ActivityLevel, sizeof(uint16_t));
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset, PP_HOST_TO_SMC_UL(tmp));
+
+ }
+ if (mclk_levels[i].UpHyst != setting->mclk_up_hyst ||
+ mclk_levels[i].DownHyst != setting->mclk_down_hyst) {
+ mclk_levels[i].UpHyst = setting->mclk_up_hyst;
+ mclk_levels[i].DownHyst = setting->mclk_down_hyst;
+ up_hyst_offset = mclk_array + (sizeof(SMU73_Discrete_MemoryLevel) * i)
+ + offsetof(SMU73_Discrete_MemoryLevel, UpHyst);
+ down_hyst_offset = mclk_array + (sizeof(SMU73_Discrete_MemoryLevel) * i)
+ + offsetof(SMU73_Discrete_MemoryLevel, DownHyst);
+ offset = up_hyst_offset & ~0x3;
+ tmp = PP_HOST_TO_SMC_UL(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset));
+ tmp = phm_set_field_to_u32(up_hyst_offset, tmp, mclk_levels[i].UpHyst, sizeof(uint8_t));
+ tmp = phm_set_field_to_u32(down_hyst_offset, tmp, mclk_levels[i].DownHyst, sizeof(uint8_t));
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset, PP_HOST_TO_SMC_UL(tmp));
+ }
+ }
+ if (!data->mclk_dpm_key_disabled)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_MCLKDPM_UnfreezeLevel, NULL);
+ }
+ return 0;
+}
+
+const struct pp_smumgr_func fiji_smu_funcs = {
+ .name = "fiji_smu",
+ .smu_init = &fiji_smu_init,
+ .smu_fini = &smu7_smu_fini,
+ .start_smu = &fiji_start_smu,
+ .check_fw_load_finish = &smu7_check_fw_load_finish,
+ .request_smu_load_fw = &smu7_reload_firmware,
+ .request_smu_load_specific_fw = NULL,
+ .send_msg_to_smc = &smu7_send_msg_to_smc,
+ .send_msg_to_smc_with_parameter = &smu7_send_msg_to_smc_with_parameter,
+ .get_argument = smu7_get_argument,
+ .download_pptable_settings = NULL,
+ .upload_pptable_settings = NULL,
+ .update_smc_table = fiji_update_smc_table,
+ .get_offsetof = fiji_get_offsetof,
+ .process_firmware_header = fiji_process_firmware_header,
+ .init_smc_table = fiji_init_smc_table,
+ .update_sclk_threshold = fiji_update_sclk_threshold,
+ .thermal_setup_fan_table = fiji_thermal_setup_fan_table,
+ .thermal_avfs_enable = fiji_thermal_avfs_enable,
+ .populate_all_graphic_levels = fiji_populate_all_graphic_levels,
+ .populate_all_memory_levels = fiji_populate_all_memory_levels,
+ .get_mac_definition = fiji_get_mac_definition,
+ .initialize_mc_reg_table = fiji_initialize_mc_reg_table,
+ .is_dpm_running = fiji_is_dpm_running,
+ .is_hw_avfs_present = fiji_is_hw_avfs_present,
+ .update_dpm_settings = fiji_update_dpm_settings,
+};
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/fiji_smumgr.h b/drivers/gpu/drm/amd/pm/powerplay/smumgr/fiji_smumgr.h
new file mode 100644
index 000000000000..6d3746268ccf
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/fiji_smumgr.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _FIJI_SMUMANAGER_H_
+#define _FIJI_SMUMANAGER_H_
+
+#include "smu73_discrete.h"
+#include <pp_endian.h>
+#include "smu7_smumgr.h"
+
+
+struct fiji_pt_defaults {
+ uint8_t SviLoadLineEn;
+ uint8_t SviLoadLineVddC;
+ uint8_t TDC_VDDC_ThrottleReleaseLimitPerc;
+ uint8_t TDC_MAWt;
+ uint8_t TdcWaterfallCtl;
+ uint8_t DTEAmbientTempBase;
+};
+
+struct fiji_smumgr {
+ struct smu7_smumgr smu7_data;
+ struct SMU73_Discrete_DpmTable smc_state_table;
+ struct SMU73_Discrete_Ulv ulv_setting;
+ struct SMU73_Discrete_PmFuses power_tune_table;
+ const struct fiji_pt_defaults *power_tune_defaults;
+};
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/iceland_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/iceland_smumgr.c
new file mode 100644
index 000000000000..431ad2fd38df
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/iceland_smumgr.c
@@ -0,0 +1,2686 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Author: Huang Rui <ray.huang@amd.com>
+ *
+ */
+#include "pp_debug.h"
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/gfp.h>
+
+#include "smumgr.h"
+#include "iceland_smumgr.h"
+
+#include "ppsmc.h"
+
+#include "cgs_common.h"
+
+#include "smu7_dyn_defaults.h"
+#include "smu7_hwmgr.h"
+#include "hardwaremanager.h"
+#include "ppatomctrl.h"
+#include "atombios.h"
+#include "pppcielanes.h"
+#include "pp_endian.h"
+#include "processpptables.h"
+
+
+#include "smu/smu_7_1_1_d.h"
+#include "smu/smu_7_1_1_sh_mask.h"
+#include "smu71_discrete.h"
+
+#include "smu_ucode_xfer_vi.h"
+#include "gmc/gmc_8_1_d.h"
+#include "gmc/gmc_8_1_sh_mask.h"
+#include "bif/bif_5_0_d.h"
+#include "bif/bif_5_0_sh_mask.h"
+#include "dce/dce_10_0_d.h"
+#include "dce/dce_10_0_sh_mask.h"
+
+
+#define ICELAND_SMC_SIZE 0x20000
+
+#define POWERTUNE_DEFAULT_SET_MAX 1
+#define MC_CG_ARB_FREQ_F1 0x0b
+#define VDDC_VDDCI_DELTA 200
+
+#define DEVICE_ID_VI_ICELAND_M_6900 0x6900
+#define DEVICE_ID_VI_ICELAND_M_6901 0x6901
+#define DEVICE_ID_VI_ICELAND_M_6902 0x6902
+#define DEVICE_ID_VI_ICELAND_M_6903 0x6903
+
+static const struct iceland_pt_defaults defaults_iceland = {
+ /*
+ * sviLoadLIneEn, SviLoadLineVddC, TDC_VDDC_ThrottleReleaseLimitPerc,
+ * TDC_MAWt, TdcWaterfallCtl, DTEAmbientTempBase, DisplayCac, BAPM_TEMP_GRADIENT
+ */
+ 1, 0xF, 0xFD, 0x19, 5, 45, 0, 0xB0000,
+ { 0x79, 0x253, 0x25D, 0xAE, 0x72, 0x80, 0x83, 0x86, 0x6F, 0xC8, 0xC9, 0xC9, 0x2F, 0x4D, 0x61 },
+ { 0x17C, 0x172, 0x180, 0x1BC, 0x1B3, 0x1BD, 0x206, 0x200, 0x203, 0x25D, 0x25A, 0x255, 0x2C3, 0x2C5, 0x2B4 }
+};
+
+/* 35W - XT, XTL */
+static const struct iceland_pt_defaults defaults_icelandxt = {
+ /*
+ * sviLoadLIneEn, SviLoadLineVddC,
+ * TDC_VDDC_ThrottleReleaseLimitPerc, TDC_MAWt,
+ * TdcWaterfallCtl, DTEAmbientTempBase, DisplayCac,
+ * BAPM_TEMP_GRADIENT
+ */
+ 1, 0xF, 0xFD, 0x19, 5, 45, 0, 0x0,
+ { 0xA7, 0x0, 0x0, 0xB5, 0x0, 0x0, 0x9F, 0x0, 0x0, 0xD6, 0x0, 0x0, 0xD7, 0x0, 0x0},
+ { 0x1EA, 0x0, 0x0, 0x224, 0x0, 0x0, 0x25E, 0x0, 0x0, 0x28E, 0x0, 0x0, 0x2AB, 0x0, 0x0}
+};
+
+/* 25W - PRO, LE */
+static const struct iceland_pt_defaults defaults_icelandpro = {
+ /*
+ * sviLoadLIneEn, SviLoadLineVddC,
+ * TDC_VDDC_ThrottleReleaseLimitPerc, TDC_MAWt,
+ * TdcWaterfallCtl, DTEAmbientTempBase, DisplayCac,
+ * BAPM_TEMP_GRADIENT
+ */
+ 1, 0xF, 0xFD, 0x19, 5, 45, 0, 0x0,
+ { 0xB7, 0x0, 0x0, 0xC3, 0x0, 0x0, 0xB5, 0x0, 0x0, 0xEA, 0x0, 0x0, 0xE6, 0x0, 0x0},
+ { 0x1EA, 0x0, 0x0, 0x224, 0x0, 0x0, 0x25E, 0x0, 0x0, 0x28E, 0x0, 0x0, 0x2AB, 0x0, 0x0}
+};
+
+static int iceland_start_smc(struct pp_hwmgr *hwmgr)
+{
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 0);
+
+ return 0;
+}
+
+static void iceland_reset_smc(struct pp_hwmgr *hwmgr)
+{
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL,
+ rst_reg, 1);
+}
+
+
+static void iceland_stop_smc_clock(struct pp_hwmgr *hwmgr)
+{
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_CLOCK_CNTL_0,
+ ck_disable, 1);
+}
+
+static void iceland_start_smc_clock(struct pp_hwmgr *hwmgr)
+{
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_CLOCK_CNTL_0,
+ ck_disable, 0);
+}
+
+static int iceland_smu_start_smc(struct pp_hwmgr *hwmgr)
+{
+ /* set smc instruct start point at 0x0 */
+ smu7_program_jump_on_start(hwmgr);
+
+ /* enable smc clock */
+ iceland_start_smc_clock(hwmgr);
+
+ /* de-assert reset */
+ iceland_start_smc(hwmgr);
+
+ PHM_WAIT_INDIRECT_FIELD(hwmgr, SMC_IND, FIRMWARE_FLAGS,
+ INTERRUPTS_ENABLED, 1);
+
+ return 0;
+}
+
+
+static int iceland_upload_smc_firmware_data(struct pp_hwmgr *hwmgr,
+ uint32_t length, const uint8_t *src,
+ uint32_t limit, uint32_t start_addr)
+{
+ uint32_t byte_count = length;
+ uint32_t data;
+
+ PP_ASSERT_WITH_CODE((limit >= byte_count), "SMC address is beyond the SMC RAM area.", return -EINVAL);
+
+ cgs_write_register(hwmgr->device, mmSMC_IND_INDEX_0, start_addr);
+ PHM_WRITE_FIELD(hwmgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 1);
+
+ while (byte_count >= 4) {
+ data = src[0] * 0x1000000 + src[1] * 0x10000 + src[2] * 0x100 + src[3];
+ cgs_write_register(hwmgr->device, mmSMC_IND_DATA_0, data);
+ src += 4;
+ byte_count -= 4;
+ }
+
+ PHM_WRITE_FIELD(hwmgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
+
+ PP_ASSERT_WITH_CODE((0 == byte_count), "SMC size must be divisible by 4.", return -EINVAL);
+
+ return 0;
+}
+
+
+static int iceland_smu_upload_firmware_image(struct pp_hwmgr *hwmgr)
+{
+ uint32_t val;
+ struct cgs_firmware_info info = {0};
+
+ if (hwmgr == NULL || hwmgr->device == NULL)
+ return -EINVAL;
+
+ /* load SMC firmware */
+ cgs_get_firmware_info(hwmgr->device,
+ smu7_convert_fw_type_to_cgs(UCODE_ID_SMU), &info);
+
+ if (info.image_size & 3) {
+ pr_err("[ powerplay ] SMC ucode is not 4 bytes aligned\n");
+ return -EINVAL;
+ }
+
+ if (info.image_size > ICELAND_SMC_SIZE) {
+ pr_err("[ powerplay ] SMC address is beyond the SMC RAM area\n");
+ return -EINVAL;
+ }
+ hwmgr->smu_version = info.version;
+ /* wait for smc boot up */
+ PHM_WAIT_INDIRECT_FIELD_UNEQUAL(hwmgr, SMC_IND,
+ RCU_UC_EVENTS, boot_seq_done, 0);
+
+ /* clear firmware interrupt enable flag */
+ val = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixSMC_SYSCON_MISC_CNTL);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixSMC_SYSCON_MISC_CNTL, val | 1);
+
+ /* stop smc clock */
+ iceland_stop_smc_clock(hwmgr);
+
+ /* reset smc */
+ iceland_reset_smc(hwmgr);
+ iceland_upload_smc_firmware_data(hwmgr, info.image_size,
+ (uint8_t *)info.kptr, ICELAND_SMC_SIZE,
+ info.ucode_start_address);
+
+ return 0;
+}
+
+static int iceland_request_smu_load_specific_fw(struct pp_hwmgr *hwmgr,
+ uint32_t firmwareType)
+{
+ return 0;
+}
+
+static int iceland_start_smu(struct pp_hwmgr *hwmgr)
+{
+ struct iceland_smumgr *priv = hwmgr->smu_backend;
+ int result;
+
+ if (!smu7_is_smc_ram_running(hwmgr)) {
+ result = iceland_smu_upload_firmware_image(hwmgr);
+ if (result)
+ return result;
+
+ iceland_smu_start_smc(hwmgr);
+ }
+
+ /* Setup SoftRegsStart here to visit the register UcodeLoadStatus
+ * to check fw loading state
+ */
+ smu7_read_smc_sram_dword(hwmgr,
+ SMU71_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU71_Firmware_Header, SoftRegisters),
+ &(priv->smu7_data.soft_regs_start), 0x40000);
+
+ result = smu7_request_smu_load_fw(hwmgr);
+
+ return result;
+}
+
+static int iceland_smu_init(struct pp_hwmgr *hwmgr)
+{
+ struct iceland_smumgr *iceland_priv = NULL;
+
+ iceland_priv = kzalloc(sizeof(struct iceland_smumgr), GFP_KERNEL);
+
+ if (iceland_priv == NULL)
+ return -ENOMEM;
+
+ hwmgr->smu_backend = iceland_priv;
+
+ if (smu7_init(hwmgr)) {
+ kfree(iceland_priv);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static void iceland_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr)
+{
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t dev_id;
+
+ dev_id = adev->pdev->device;
+
+ switch (dev_id) {
+ case DEVICE_ID_VI_ICELAND_M_6900:
+ case DEVICE_ID_VI_ICELAND_M_6903:
+ smu_data->power_tune_defaults = &defaults_icelandxt;
+ break;
+
+ case DEVICE_ID_VI_ICELAND_M_6901:
+ case DEVICE_ID_VI_ICELAND_M_6902:
+ smu_data->power_tune_defaults = &defaults_icelandpro;
+ break;
+ default:
+ smu_data->power_tune_defaults = &defaults_iceland;
+ pr_warn("Unknown V.I. Device ID.\n");
+ break;
+ }
+ return;
+}
+
+static int iceland_populate_svi_load_line(struct pp_hwmgr *hwmgr)
+{
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ const struct iceland_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+ smu_data->power_tune_table.SviLoadLineEn = defaults->svi_load_line_en;
+ smu_data->power_tune_table.SviLoadLineVddC = defaults->svi_load_line_vddc;
+ smu_data->power_tune_table.SviLoadLineTrimVddC = 3;
+ smu_data->power_tune_table.SviLoadLineOffsetVddC = 0;
+
+ return 0;
+}
+
+static int iceland_populate_tdc_limit(struct pp_hwmgr *hwmgr)
+{
+ uint16_t tdc_limit;
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ const struct iceland_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+ tdc_limit = (uint16_t)(hwmgr->dyn_state.cac_dtp_table->usTDC * 256);
+ smu_data->power_tune_table.TDC_VDDC_PkgLimit =
+ CONVERT_FROM_HOST_TO_SMC_US(tdc_limit);
+ smu_data->power_tune_table.TDC_VDDC_ThrottleReleaseLimitPerc =
+ defaults->tdc_vddc_throttle_release_limit_perc;
+ smu_data->power_tune_table.TDC_MAWt = defaults->tdc_mawt;
+
+ return 0;
+}
+
+static int iceland_populate_dw8(struct pp_hwmgr *hwmgr, uint32_t fuse_table_offset)
+{
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ const struct iceland_pt_defaults *defaults = smu_data->power_tune_defaults;
+ uint32_t temp;
+
+ if (smu7_read_smc_sram_dword(hwmgr,
+ fuse_table_offset +
+ offsetof(SMU71_Discrete_PmFuses, TdcWaterfallCtl),
+ (uint32_t *)&temp, SMC_RAM_END))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to read PmFuses.DW6 (SviLoadLineEn) from SMC Failed!",
+ return -EINVAL);
+ else
+ smu_data->power_tune_table.TdcWaterfallCtl = defaults->tdc_waterfall_ctl;
+
+ return 0;
+}
+
+static int iceland_populate_temperature_scaler(struct pp_hwmgr *hwmgr)
+{
+ return 0;
+}
+
+static int iceland_populate_gnb_lpml(struct pp_hwmgr *hwmgr)
+{
+ int i;
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+
+ /* Currently not used. Set all to zero. */
+ for (i = 0; i < 8; i++)
+ smu_data->power_tune_table.GnbLPML[i] = 0;
+
+ return 0;
+}
+
+static int iceland_populate_bapm_vddc_base_leakage_sidd(struct pp_hwmgr *hwmgr)
+{
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ uint16_t HiSidd = smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd;
+ uint16_t LoSidd = smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd;
+ struct phm_cac_tdp_table *cac_table = hwmgr->dyn_state.cac_dtp_table;
+
+ HiSidd = (uint16_t)(cac_table->usHighCACLeakage / 100 * 256);
+ LoSidd = (uint16_t)(cac_table->usLowCACLeakage / 100 * 256);
+
+ smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd =
+ CONVERT_FROM_HOST_TO_SMC_US(HiSidd);
+ smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd =
+ CONVERT_FROM_HOST_TO_SMC_US(LoSidd);
+
+ return 0;
+}
+
+static int iceland_populate_bapm_vddc_vid_sidd(struct pp_hwmgr *hwmgr)
+{
+ int i;
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ uint8_t *hi_vid = smu_data->power_tune_table.BapmVddCVidHiSidd;
+ uint8_t *lo_vid = smu_data->power_tune_table.BapmVddCVidLoSidd;
+
+ PP_ASSERT_WITH_CODE(NULL != hwmgr->dyn_state.cac_leakage_table,
+ "The CAC Leakage table does not exist!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(hwmgr->dyn_state.cac_leakage_table->count <= 8,
+ "There should never be more than 8 entries for BapmVddcVid!!!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(hwmgr->dyn_state.cac_leakage_table->count == hwmgr->dyn_state.vddc_dependency_on_sclk->count,
+ "CACLeakageTable->count and VddcDependencyOnSCLk->count not equal", return -EINVAL);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_EVV)) {
+ for (i = 0; (uint32_t) i < hwmgr->dyn_state.cac_leakage_table->count; i++) {
+ lo_vid[i] = convert_to_vid(hwmgr->dyn_state.cac_leakage_table->entries[i].Vddc1);
+ hi_vid[i] = convert_to_vid(hwmgr->dyn_state.cac_leakage_table->entries[i].Vddc2);
+ }
+ } else {
+ PP_ASSERT_WITH_CODE(false, "Iceland should always support EVV", return -EINVAL);
+ }
+
+ return 0;
+}
+
+static int iceland_populate_vddc_vid(struct pp_hwmgr *hwmgr)
+{
+ int i;
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ uint8_t *vid = smu_data->power_tune_table.VddCVid;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ PP_ASSERT_WITH_CODE(data->vddc_voltage_table.count <= 8,
+ "There should never be more than 8 entries for VddcVid!!!",
+ return -EINVAL);
+
+ for (i = 0; i < (int)data->vddc_voltage_table.count; i++) {
+ vid[i] = convert_to_vid(data->vddc_voltage_table.entries[i].value);
+ }
+
+ return 0;
+}
+
+
+
+static int iceland_populate_pm_fuses(struct pp_hwmgr *hwmgr)
+{
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ uint32_t pm_fuse_table_offset;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PowerContainment)) {
+ if (smu7_read_smc_sram_dword(hwmgr,
+ SMU71_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU71_Firmware_Header, PmFuseTable),
+ &pm_fuse_table_offset, SMC_RAM_END))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to get pm_fuse_table_offset Failed!",
+ return -EINVAL);
+
+ /* DW0 - DW3 */
+ if (iceland_populate_bapm_vddc_vid_sidd(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate bapm vddc vid Failed!",
+ return -EINVAL);
+
+ /* DW4 - DW5 */
+ if (iceland_populate_vddc_vid(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate vddc vid Failed!",
+ return -EINVAL);
+
+ /* DW6 */
+ if (iceland_populate_svi_load_line(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate SviLoadLine Failed!",
+ return -EINVAL);
+ /* DW7 */
+ if (iceland_populate_tdc_limit(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate TDCLimit Failed!", return -EINVAL);
+ /* DW8 */
+ if (iceland_populate_dw8(hwmgr, pm_fuse_table_offset))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate TdcWaterfallCtl, "
+ "LPMLTemperature Min and Max Failed!",
+ return -EINVAL);
+
+ /* DW9-DW12 */
+ if (0 != iceland_populate_temperature_scaler(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate LPMLTemperatureScaler Failed!",
+ return -EINVAL);
+
+ /* DW13-DW16 */
+ if (iceland_populate_gnb_lpml(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate GnbLPML Failed!",
+ return -EINVAL);
+
+ /* DW18 */
+ if (iceland_populate_bapm_vddc_base_leakage_sidd(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate BapmVddCBaseLeakage Hi and Lo Sidd Failed!",
+ return -EINVAL);
+
+ if (smu7_copy_bytes_to_smc(hwmgr, pm_fuse_table_offset,
+ (uint8_t *)&smu_data->power_tune_table,
+ sizeof(struct SMU71_Discrete_PmFuses), SMC_RAM_END))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to download PmFuseTable Failed!",
+ return -EINVAL);
+ }
+ return 0;
+}
+
+static int iceland_get_dependency_volt_by_clk(struct pp_hwmgr *hwmgr,
+ struct phm_clock_voltage_dependency_table *allowed_clock_voltage_table,
+ uint32_t clock, uint32_t *vol)
+{
+ uint32_t i = 0;
+
+ /* clock - voltage dependency table is empty table */
+ if (allowed_clock_voltage_table->count == 0)
+ return -EINVAL;
+
+ for (i = 0; i < allowed_clock_voltage_table->count; i++) {
+ /* find first sclk bigger than request */
+ if (allowed_clock_voltage_table->entries[i].clk >= clock) {
+ *vol = allowed_clock_voltage_table->entries[i].v;
+ return 0;
+ }
+ }
+
+ /* sclk is bigger than max sclk in the dependence table */
+ *vol = allowed_clock_voltage_table->entries[i - 1].v;
+
+ return 0;
+}
+
+static int iceland_get_std_voltage_value_sidd(struct pp_hwmgr *hwmgr,
+ pp_atomctrl_voltage_table_entry *tab, uint16_t *hi,
+ uint16_t *lo)
+{
+ uint16_t v_index;
+ bool vol_found = false;
+ *hi = tab->value * VOLTAGE_SCALE;
+ *lo = tab->value * VOLTAGE_SCALE;
+
+ /* SCLK/VDDC Dependency Table has to exist. */
+ PP_ASSERT_WITH_CODE(NULL != hwmgr->dyn_state.vddc_dependency_on_sclk,
+ "The SCLK/VDDC Dependency Table does not exist.",
+ return -EINVAL);
+
+ if (NULL == hwmgr->dyn_state.cac_leakage_table) {
+ pr_warn("CAC Leakage Table does not exist, using vddc.\n");
+ return 0;
+ }
+
+ /*
+ * Since voltage in the sclk/vddc dependency table is not
+ * necessarily in ascending order because of ELB voltage
+ * patching, loop through entire list to find exact voltage.
+ */
+ for (v_index = 0; (uint32_t)v_index < hwmgr->dyn_state.vddc_dependency_on_sclk->count; v_index++) {
+ if (tab->value == hwmgr->dyn_state.vddc_dependency_on_sclk->entries[v_index].v) {
+ vol_found = true;
+ if ((uint32_t)v_index < hwmgr->dyn_state.cac_leakage_table->count) {
+ *lo = hwmgr->dyn_state.cac_leakage_table->entries[v_index].Vddc * VOLTAGE_SCALE;
+ *hi = (uint16_t)(hwmgr->dyn_state.cac_leakage_table->entries[v_index].Leakage * VOLTAGE_SCALE);
+ } else {
+ pr_warn("Index from SCLK/VDDC Dependency Table exceeds the CAC Leakage Table index, using maximum index from CAC table.\n");
+ *lo = hwmgr->dyn_state.cac_leakage_table->entries[hwmgr->dyn_state.cac_leakage_table->count - 1].Vddc * VOLTAGE_SCALE;
+ *hi = (uint16_t)(hwmgr->dyn_state.cac_leakage_table->entries[hwmgr->dyn_state.cac_leakage_table->count - 1].Leakage * VOLTAGE_SCALE);
+ }
+ break;
+ }
+ }
+
+ /*
+ * If voltage is not found in the first pass, loop again to
+ * find the best match, equal or higher value.
+ */
+ if (!vol_found) {
+ for (v_index = 0; (uint32_t)v_index < hwmgr->dyn_state.vddc_dependency_on_sclk->count; v_index++) {
+ if (tab->value <= hwmgr->dyn_state.vddc_dependency_on_sclk->entries[v_index].v) {
+ vol_found = true;
+ if ((uint32_t)v_index < hwmgr->dyn_state.cac_leakage_table->count) {
+ *lo = hwmgr->dyn_state.cac_leakage_table->entries[v_index].Vddc * VOLTAGE_SCALE;
+ *hi = (uint16_t)(hwmgr->dyn_state.cac_leakage_table->entries[v_index].Leakage) * VOLTAGE_SCALE;
+ } else {
+ pr_warn("Index from SCLK/VDDC Dependency Table exceeds the CAC Leakage Table index in second look up, using maximum index from CAC table.");
+ *lo = hwmgr->dyn_state.cac_leakage_table->entries[hwmgr->dyn_state.cac_leakage_table->count - 1].Vddc * VOLTAGE_SCALE;
+ *hi = (uint16_t)(hwmgr->dyn_state.cac_leakage_table->entries[hwmgr->dyn_state.cac_leakage_table->count - 1].Leakage * VOLTAGE_SCALE);
+ }
+ break;
+ }
+ }
+
+ if (!vol_found)
+ pr_warn("Unable to get std_vddc from SCLK/VDDC Dependency Table, using vddc.\n");
+ }
+
+ return 0;
+}
+
+static int iceland_populate_smc_voltage_table(struct pp_hwmgr *hwmgr,
+ pp_atomctrl_voltage_table_entry *tab,
+ SMU71_Discrete_VoltageLevel *smc_voltage_tab)
+{
+ int result;
+
+ result = iceland_get_std_voltage_value_sidd(hwmgr, tab,
+ &smc_voltage_tab->StdVoltageHiSidd,
+ &smc_voltage_tab->StdVoltageLoSidd);
+ if (0 != result) {
+ smc_voltage_tab->StdVoltageHiSidd = tab->value * VOLTAGE_SCALE;
+ smc_voltage_tab->StdVoltageLoSidd = tab->value * VOLTAGE_SCALE;
+ }
+
+ smc_voltage_tab->Voltage = PP_HOST_TO_SMC_US(tab->value * VOLTAGE_SCALE);
+ CONVERT_FROM_HOST_TO_SMC_US(smc_voltage_tab->StdVoltageHiSidd);
+ CONVERT_FROM_HOST_TO_SMC_US(smc_voltage_tab->StdVoltageHiSidd);
+
+ return 0;
+}
+
+static int iceland_populate_smc_vddc_table(struct pp_hwmgr *hwmgr,
+ SMU71_Discrete_DpmTable *table)
+{
+ unsigned int count;
+ int result;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ table->VddcLevelCount = data->vddc_voltage_table.count;
+ for (count = 0; count < table->VddcLevelCount; count++) {
+ result = iceland_populate_smc_voltage_table(hwmgr,
+ &(data->vddc_voltage_table.entries[count]),
+ &(table->VddcLevel[count]));
+ PP_ASSERT_WITH_CODE(0 == result, "do not populate SMC VDDC voltage table", return -EINVAL);
+
+ /* GPIO voltage control */
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->voltage_control)
+ table->VddcLevel[count].Smio |= data->vddc_voltage_table.entries[count].smio_low;
+ else if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control)
+ table->VddcLevel[count].Smio = 0;
+ }
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VddcLevelCount);
+
+ return 0;
+}
+
+static int iceland_populate_smc_vdd_ci_table(struct pp_hwmgr *hwmgr,
+ SMU71_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t count;
+ int result;
+
+ table->VddciLevelCount = data->vddci_voltage_table.count;
+
+ for (count = 0; count < table->VddciLevelCount; count++) {
+ result = iceland_populate_smc_voltage_table(hwmgr,
+ &(data->vddci_voltage_table.entries[count]),
+ &(table->VddciLevel[count]));
+ PP_ASSERT_WITH_CODE(result == 0, "do not populate SMC VDDCI voltage table", return -EINVAL);
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control)
+ table->VddciLevel[count].Smio |= data->vddci_voltage_table.entries[count].smio_low;
+ else
+ table->VddciLevel[count].Smio |= 0;
+ }
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VddciLevelCount);
+
+ return 0;
+}
+
+static int iceland_populate_smc_mvdd_table(struct pp_hwmgr *hwmgr,
+ SMU71_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t count;
+ int result;
+
+ table->MvddLevelCount = data->mvdd_voltage_table.count;
+
+ for (count = 0; count < table->VddciLevelCount; count++) {
+ result = iceland_populate_smc_voltage_table(hwmgr,
+ &(data->mvdd_voltage_table.entries[count]),
+ &table->MvddLevel[count]);
+ PP_ASSERT_WITH_CODE(result == 0, "do not populate SMC mvdd voltage table", return -EINVAL);
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control)
+ table->MvddLevel[count].Smio |= data->mvdd_voltage_table.entries[count].smio_low;
+ else
+ table->MvddLevel[count].Smio |= 0;
+ }
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->MvddLevelCount);
+
+ return 0;
+}
+
+
+static int iceland_populate_smc_voltage_tables(struct pp_hwmgr *hwmgr,
+ SMU71_Discrete_DpmTable *table)
+{
+ int result;
+
+ result = iceland_populate_smc_vddc_table(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "can not populate VDDC voltage table to SMC", return -EINVAL);
+
+ result = iceland_populate_smc_vdd_ci_table(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "can not populate VDDCI voltage table to SMC", return -EINVAL);
+
+ result = iceland_populate_smc_mvdd_table(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "can not populate MVDD voltage table to SMC", return -EINVAL);
+
+ return 0;
+}
+
+static int iceland_populate_ulv_level(struct pp_hwmgr *hwmgr,
+ struct SMU71_Discrete_Ulv *state)
+{
+ uint32_t voltage_response_time, ulv_voltage;
+ int result;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ state->CcPwrDynRm = 0;
+ state->CcPwrDynRm1 = 0;
+
+ result = pp_tables_get_response_times(hwmgr, &voltage_response_time, &ulv_voltage);
+ PP_ASSERT_WITH_CODE((0 == result), "can not get ULV voltage value", return result;);
+
+ if (ulv_voltage == 0) {
+ data->ulv_supported = false;
+ return 0;
+ }
+
+ if (data->voltage_control != SMU7_VOLTAGE_CONTROL_BY_SVID2) {
+ /* use minimum voltage if ulv voltage in pptable is bigger than minimum voltage */
+ if (ulv_voltage > hwmgr->dyn_state.vddc_dependency_on_sclk->entries[0].v)
+ state->VddcOffset = 0;
+ else
+ /* used in SMIO Mode. not implemented for now. this is backup only for CI. */
+ state->VddcOffset = (uint16_t)(hwmgr->dyn_state.vddc_dependency_on_sclk->entries[0].v - ulv_voltage);
+ } else {
+ /* use minimum voltage if ulv voltage in pptable is bigger than minimum voltage */
+ if (ulv_voltage > hwmgr->dyn_state.vddc_dependency_on_sclk->entries[0].v)
+ state->VddcOffsetVid = 0;
+ else /* used in SVI2 Mode */
+ state->VddcOffsetVid = (uint8_t)(
+ (hwmgr->dyn_state.vddc_dependency_on_sclk->entries[0].v - ulv_voltage)
+ * VOLTAGE_VID_OFFSET_SCALE2
+ / VOLTAGE_VID_OFFSET_SCALE1);
+ }
+ state->VddcPhase = 1;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm);
+ CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm1);
+ CONVERT_FROM_HOST_TO_SMC_US(state->VddcOffset);
+
+ return 0;
+}
+
+static int iceland_populate_ulv_state(struct pp_hwmgr *hwmgr,
+ SMU71_Discrete_Ulv *ulv_level)
+{
+ return iceland_populate_ulv_level(hwmgr, ulv_level);
+}
+
+static int iceland_populate_smc_link_level(struct pp_hwmgr *hwmgr, SMU71_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_dpm_table *dpm_table = &data->dpm_table;
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ uint32_t i;
+
+ /* Index (dpm_table->pcie_speed_table.count) is reserved for PCIE boot level. */
+ for (i = 0; i <= dpm_table->pcie_speed_table.count; i++) {
+ table->LinkLevel[i].PcieGenSpeed =
+ (uint8_t)dpm_table->pcie_speed_table.dpm_levels[i].value;
+ table->LinkLevel[i].PcieLaneCount =
+ (uint8_t)encode_pcie_lane_width(dpm_table->pcie_speed_table.dpm_levels[i].param1);
+ table->LinkLevel[i].EnabledForActivity =
+ 1;
+ table->LinkLevel[i].SPC =
+ (uint8_t)(data->pcie_spc_cap & 0xff);
+ table->LinkLevel[i].DownThreshold =
+ PP_HOST_TO_SMC_UL(5);
+ table->LinkLevel[i].UpThreshold =
+ PP_HOST_TO_SMC_UL(30);
+ }
+
+ smu_data->smc_state_table.LinkLevelCount =
+ (uint8_t)dpm_table->pcie_speed_table.count;
+ data->dpm_level_enable_mask.pcie_dpm_enable_mask =
+ phm_get_dpm_level_enable_mask_value(&dpm_table->pcie_speed_table);
+
+ return 0;
+}
+
+static int iceland_calculate_sclk_params(struct pp_hwmgr *hwmgr,
+ uint32_t engine_clock, SMU71_Discrete_GraphicsLevel *sclk)
+{
+ const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ pp_atomctrl_clock_dividers_vi dividers;
+ uint32_t spll_func_cntl = data->clock_registers.vCG_SPLL_FUNC_CNTL;
+ uint32_t spll_func_cntl_3 = data->clock_registers.vCG_SPLL_FUNC_CNTL_3;
+ uint32_t spll_func_cntl_4 = data->clock_registers.vCG_SPLL_FUNC_CNTL_4;
+ uint32_t cg_spll_spread_spectrum = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM;
+ uint32_t cg_spll_spread_spectrum_2 = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2;
+ uint32_t reference_clock;
+ uint32_t reference_divider;
+ uint32_t fbdiv;
+ int result;
+
+ /* get the engine clock dividers for this clock value*/
+ result = atomctrl_get_engine_pll_dividers_vi(hwmgr, engine_clock, &dividers);
+
+ PP_ASSERT_WITH_CODE(result == 0,
+ "Error retrieving Engine Clock dividers from VBIOS.", return result);
+
+ /* To get FBDIV we need to multiply this by 16384 and divide it by Fref.*/
+ reference_clock = atomctrl_get_reference_clock(hwmgr);
+
+ reference_divider = 1 + dividers.uc_pll_ref_div;
+
+ /* low 14 bits is fraction and high 12 bits is divider*/
+ fbdiv = dividers.ul_fb_div.ul_fb_divider & 0x3FFFFFF;
+
+ /* SPLL_FUNC_CNTL setup*/
+ spll_func_cntl = PHM_SET_FIELD(spll_func_cntl,
+ CG_SPLL_FUNC_CNTL, SPLL_REF_DIV, dividers.uc_pll_ref_div);
+ spll_func_cntl = PHM_SET_FIELD(spll_func_cntl,
+ CG_SPLL_FUNC_CNTL, SPLL_PDIV_A, dividers.uc_pll_post_div);
+
+ /* SPLL_FUNC_CNTL_3 setup*/
+ spll_func_cntl_3 = PHM_SET_FIELD(spll_func_cntl_3,
+ CG_SPLL_FUNC_CNTL_3, SPLL_FB_DIV, fbdiv);
+
+ /* set to use fractional accumulation*/
+ spll_func_cntl_3 = PHM_SET_FIELD(spll_func_cntl_3,
+ CG_SPLL_FUNC_CNTL_3, SPLL_DITHEN, 1);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EngineSpreadSpectrumSupport)) {
+ pp_atomctrl_internal_ss_info ss_info;
+
+ uint32_t vcoFreq = engine_clock * dividers.uc_pll_post_div;
+ if (0 == atomctrl_get_engine_clock_spread_spectrum(hwmgr, vcoFreq, &ss_info)) {
+ /*
+ * ss_info.speed_spectrum_percentage -- in unit of 0.01%
+ * ss_info.speed_spectrum_rate -- in unit of khz
+ */
+ /* clks = reference_clock * 10 / (REFDIV + 1) / speed_spectrum_rate / 2 */
+ uint32_t clkS = reference_clock * 5 / (reference_divider * ss_info.speed_spectrum_rate);
+
+ /* clkv = 2 * D * fbdiv / NS */
+ uint32_t clkV = 4 * ss_info.speed_spectrum_percentage * fbdiv / (clkS * 10000);
+
+ cg_spll_spread_spectrum =
+ PHM_SET_FIELD(cg_spll_spread_spectrum, CG_SPLL_SPREAD_SPECTRUM, CLKS, clkS);
+ cg_spll_spread_spectrum =
+ PHM_SET_FIELD(cg_spll_spread_spectrum, CG_SPLL_SPREAD_SPECTRUM, SSEN, 1);
+ cg_spll_spread_spectrum_2 =
+ PHM_SET_FIELD(cg_spll_spread_spectrum_2, CG_SPLL_SPREAD_SPECTRUM_2, CLKV, clkV);
+ }
+ }
+
+ sclk->SclkFrequency = engine_clock;
+ sclk->CgSpllFuncCntl3 = spll_func_cntl_3;
+ sclk->CgSpllFuncCntl4 = spll_func_cntl_4;
+ sclk->SpllSpreadSpectrum = cg_spll_spread_spectrum;
+ sclk->SpllSpreadSpectrum2 = cg_spll_spread_spectrum_2;
+ sclk->SclkDid = (uint8_t)dividers.pll_post_divider;
+
+ return 0;
+}
+
+static int iceland_populate_phase_value_based_on_sclk(struct pp_hwmgr *hwmgr,
+ const struct phm_phase_shedding_limits_table *pl,
+ uint32_t sclk, uint32_t *p_shed)
+{
+ unsigned int i;
+
+ /* use the minimum phase shedding */
+ *p_shed = 1;
+
+ for (i = 0; i < pl->count; i++) {
+ if (sclk < pl->entries[i].Sclk) {
+ *p_shed = i;
+ break;
+ }
+ }
+ return 0;
+}
+
+static int iceland_populate_single_graphic_level(struct pp_hwmgr *hwmgr,
+ uint32_t engine_clock,
+ SMU71_Discrete_GraphicsLevel *graphic_level)
+{
+ int result;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ result = iceland_calculate_sclk_params(hwmgr, engine_clock, graphic_level);
+
+ /* populate graphics levels*/
+ result = iceland_get_dependency_volt_by_clk(hwmgr,
+ hwmgr->dyn_state.vddc_dependency_on_sclk, engine_clock,
+ &graphic_level->MinVddc);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find VDDC voltage value for VDDC engine clock dependency table", return result);
+
+ /* SCLK frequency in units of 10KHz*/
+ graphic_level->SclkFrequency = engine_clock;
+ graphic_level->MinVddcPhases = 1;
+
+ if (data->vddc_phase_shed_control)
+ iceland_populate_phase_value_based_on_sclk(hwmgr,
+ hwmgr->dyn_state.vddc_phase_shed_limits_table,
+ engine_clock,
+ &graphic_level->MinVddcPhases);
+
+ /* Indicates maximum activity level for this performance level. 50% for now*/
+ graphic_level->ActivityLevel = data->current_profile_setting.sclk_activity;
+
+ graphic_level->CcPwrDynRm = 0;
+ graphic_level->CcPwrDynRm1 = 0;
+ /* this level can be used if activity is high enough.*/
+ graphic_level->EnabledForActivity = 0;
+ /* this level can be used for throttling.*/
+ graphic_level->EnabledForThrottle = 1;
+ graphic_level->UpHyst = data->current_profile_setting.sclk_up_hyst;
+ graphic_level->DownHyst = data->current_profile_setting.sclk_down_hyst;
+ graphic_level->VoltageDownHyst = 0;
+ graphic_level->PowerThrottle = 0;
+
+ data->display_timing.min_clock_in_sr =
+ hwmgr->display_config->min_core_set_clock_in_sr;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkDeepSleep))
+ graphic_level->DeepSleepDivId =
+ smu7_get_sleep_divider_id_from_clock(engine_clock,
+ data->display_timing.min_clock_in_sr);
+
+ /* Default to slow, highest DPM level will be set to PPSMC_DISPLAY_WATERMARK_LOW later.*/
+ graphic_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+ if (0 == result) {
+ graphic_level->MinVddc = PP_HOST_TO_SMC_UL(graphic_level->MinVddc * VOLTAGE_SCALE);
+ CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->MinVddcPhases);
+ CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->SclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_US(graphic_level->ActivityLevel);
+ CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CgSpllFuncCntl3);
+ CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CgSpllFuncCntl4);
+ CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->SpllSpreadSpectrum);
+ CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->SpllSpreadSpectrum2);
+ CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CcPwrDynRm);
+ CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CcPwrDynRm1);
+ }
+
+ return result;
+}
+
+static int iceland_populate_all_graphic_levels(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ struct smu7_dpm_table *dpm_table = &data->dpm_table;
+ uint32_t level_array_adress = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU71_Discrete_DpmTable, GraphicsLevel);
+
+ uint32_t level_array_size = sizeof(SMU71_Discrete_GraphicsLevel) *
+ SMU71_MAX_LEVELS_GRAPHICS;
+
+ SMU71_Discrete_GraphicsLevel *levels = smu_data->smc_state_table.GraphicsLevel;
+
+ uint32_t i;
+ uint8_t highest_pcie_level_enabled = 0;
+ uint8_t lowest_pcie_level_enabled = 0, mid_pcie_level_enabled = 0;
+ uint8_t count = 0;
+ int result = 0;
+
+ memset(levels, 0x00, level_array_size);
+
+ for (i = 0; i < dpm_table->sclk_table.count; i++) {
+ result = iceland_populate_single_graphic_level(hwmgr,
+ dpm_table->sclk_table.dpm_levels[i].value,
+ &(smu_data->smc_state_table.GraphicsLevel[i]));
+ if (result != 0)
+ return result;
+
+ /* Making sure only DPM level 0-1 have Deep Sleep Div ID populated. */
+ if (i > 1)
+ smu_data->smc_state_table.GraphicsLevel[i].DeepSleepDivId = 0;
+ }
+
+ /* Only enable level 0 for now. */
+ smu_data->smc_state_table.GraphicsLevel[0].EnabledForActivity = 1;
+
+ /* set highest level watermark to high */
+ if (dpm_table->sclk_table.count > 1)
+ smu_data->smc_state_table.GraphicsLevel[dpm_table->sclk_table.count-1].DisplayWatermark =
+ PPSMC_DISPLAY_WATERMARK_HIGH;
+
+ smu_data->smc_state_table.GraphicsDpmLevelCount =
+ (uint8_t)dpm_table->sclk_table.count;
+ data->dpm_level_enable_mask.sclk_dpm_enable_mask =
+ phm_get_dpm_level_enable_mask_value(&dpm_table->sclk_table);
+
+ while ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+ (1 << (highest_pcie_level_enabled + 1))) != 0) {
+ highest_pcie_level_enabled++;
+ }
+
+ while ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+ (1 << lowest_pcie_level_enabled)) == 0) {
+ lowest_pcie_level_enabled++;
+ }
+
+ while ((count < highest_pcie_level_enabled) &&
+ ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+ (1 << (lowest_pcie_level_enabled + 1 + count))) == 0)) {
+ count++;
+ }
+
+ mid_pcie_level_enabled = (lowest_pcie_level_enabled+1+count) < highest_pcie_level_enabled ?
+ (lowest_pcie_level_enabled+1+count) : highest_pcie_level_enabled;
+
+
+ /* set pcieDpmLevel to highest_pcie_level_enabled*/
+ for (i = 2; i < dpm_table->sclk_table.count; i++) {
+ smu_data->smc_state_table.GraphicsLevel[i].pcieDpmLevel = highest_pcie_level_enabled;
+ }
+
+ /* set pcieDpmLevel to lowest_pcie_level_enabled*/
+ smu_data->smc_state_table.GraphicsLevel[0].pcieDpmLevel = lowest_pcie_level_enabled;
+
+ /* set pcieDpmLevel to mid_pcie_level_enabled*/
+ smu_data->smc_state_table.GraphicsLevel[1].pcieDpmLevel = mid_pcie_level_enabled;
+
+ /* level count will send to smc once at init smc table and never change*/
+ result = smu7_copy_bytes_to_smc(hwmgr, level_array_adress,
+ (uint8_t *)levels, (uint32_t)level_array_size,
+ SMC_RAM_END);
+
+ return result;
+}
+
+static int iceland_calculate_mclk_params(
+ struct pp_hwmgr *hwmgr,
+ uint32_t memory_clock,
+ SMU71_Discrete_MemoryLevel *mclk,
+ bool strobe_mode,
+ bool dllStateOn
+ )
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ uint32_t dll_cntl = data->clock_registers.vDLL_CNTL;
+ uint32_t mclk_pwrmgt_cntl = data->clock_registers.vMCLK_PWRMGT_CNTL;
+ uint32_t mpll_ad_func_cntl = data->clock_registers.vMPLL_AD_FUNC_CNTL;
+ uint32_t mpll_dq_func_cntl = data->clock_registers.vMPLL_DQ_FUNC_CNTL;
+ uint32_t mpll_func_cntl = data->clock_registers.vMPLL_FUNC_CNTL;
+ uint32_t mpll_func_cntl_1 = data->clock_registers.vMPLL_FUNC_CNTL_1;
+ uint32_t mpll_func_cntl_2 = data->clock_registers.vMPLL_FUNC_CNTL_2;
+ uint32_t mpll_ss1 = data->clock_registers.vMPLL_SS1;
+ uint32_t mpll_ss2 = data->clock_registers.vMPLL_SS2;
+
+ pp_atomctrl_memory_clock_param mpll_param;
+ int result;
+
+ result = atomctrl_get_memory_pll_dividers_si(hwmgr,
+ memory_clock, &mpll_param, strobe_mode);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Error retrieving Memory Clock Parameters from VBIOS.", return result);
+
+ /* MPLL_FUNC_CNTL setup*/
+ mpll_func_cntl = PHM_SET_FIELD(mpll_func_cntl, MPLL_FUNC_CNTL, BWCTRL, mpll_param.bw_ctrl);
+
+ /* MPLL_FUNC_CNTL_1 setup*/
+ mpll_func_cntl_1 = PHM_SET_FIELD(mpll_func_cntl_1,
+ MPLL_FUNC_CNTL_1, CLKF, mpll_param.mpll_fb_divider.cl_kf);
+ mpll_func_cntl_1 = PHM_SET_FIELD(mpll_func_cntl_1,
+ MPLL_FUNC_CNTL_1, CLKFRAC, mpll_param.mpll_fb_divider.clk_frac);
+ mpll_func_cntl_1 = PHM_SET_FIELD(mpll_func_cntl_1,
+ MPLL_FUNC_CNTL_1, VCO_MODE, mpll_param.vco_mode);
+
+ /* MPLL_AD_FUNC_CNTL setup*/
+ mpll_ad_func_cntl = PHM_SET_FIELD(mpll_ad_func_cntl,
+ MPLL_AD_FUNC_CNTL, YCLK_POST_DIV, mpll_param.mpll_post_divider);
+
+ if (data->is_memory_gddr5) {
+ /* MPLL_DQ_FUNC_CNTL setup*/
+ mpll_dq_func_cntl = PHM_SET_FIELD(mpll_dq_func_cntl,
+ MPLL_DQ_FUNC_CNTL, YCLK_SEL, mpll_param.yclk_sel);
+ mpll_dq_func_cntl = PHM_SET_FIELD(mpll_dq_func_cntl,
+ MPLL_DQ_FUNC_CNTL, YCLK_POST_DIV, mpll_param.mpll_post_divider);
+ }
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MemorySpreadSpectrumSupport)) {
+ /*
+ ************************************
+ Fref = Reference Frequency
+ NF = Feedback divider ratio
+ NR = Reference divider ratio
+ Fnom = Nominal VCO output frequency = Fref * NF / NR
+ Fs = Spreading Rate
+ D = Percentage down-spread / 2
+ Fint = Reference input frequency to PFD = Fref / NR
+ NS = Spreading rate divider ratio = int(Fint / (2 * Fs))
+ CLKS = NS - 1 = ISS_STEP_NUM[11:0]
+ NV = D * Fs / Fnom * 4 * ((Fnom/Fref * NR) ^ 2)
+ CLKV = 65536 * NV = ISS_STEP_SIZE[25:0]
+ *************************************
+ */
+ pp_atomctrl_internal_ss_info ss_info;
+ uint32_t freq_nom;
+ uint32_t tmp;
+ uint32_t reference_clock = atomctrl_get_mpll_reference_clock(hwmgr);
+
+ /* for GDDR5 for all modes and DDR3 */
+ if (1 == mpll_param.qdr)
+ freq_nom = memory_clock * 4 * (1 << mpll_param.mpll_post_divider);
+ else
+ freq_nom = memory_clock * 2 * (1 << mpll_param.mpll_post_divider);
+
+ /* tmp = (freq_nom / reference_clock * reference_divider) ^ 2 Note: S.I. reference_divider = 1*/
+ tmp = (freq_nom / reference_clock);
+ tmp = tmp * tmp;
+
+ if (0 == atomctrl_get_memory_clock_spread_spectrum(hwmgr, freq_nom, &ss_info)) {
+ /* ss_info.speed_spectrum_percentage -- in unit of 0.01% */
+ /* ss.Info.speed_spectrum_rate -- in unit of khz */
+ /* CLKS = reference_clock / (2 * speed_spectrum_rate * reference_divider) * 10 */
+ /* = reference_clock * 5 / speed_spectrum_rate */
+ uint32_t clks = reference_clock * 5 / ss_info.speed_spectrum_rate;
+
+ /* CLKV = 65536 * speed_spectrum_percentage / 2 * spreadSpecrumRate / freq_nom * 4 / 100000 * ((freq_nom / reference_clock) ^ 2) */
+ /* = 131 * speed_spectrum_percentage * speed_spectrum_rate / 100 * ((freq_nom / reference_clock) ^ 2) / freq_nom */
+ uint32_t clkv =
+ (uint32_t)((((131 * ss_info.speed_spectrum_percentage *
+ ss_info.speed_spectrum_rate) / 100) * tmp) / freq_nom);
+
+ mpll_ss1 = PHM_SET_FIELD(mpll_ss1, MPLL_SS1, CLKV, clkv);
+ mpll_ss2 = PHM_SET_FIELD(mpll_ss2, MPLL_SS2, CLKS, clks);
+ }
+ }
+
+ /* MCLK_PWRMGT_CNTL setup */
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, DLL_SPEED, mpll_param.dll_speed);
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, MRDCK0_PDNB, dllStateOn);
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, MRDCK1_PDNB, dllStateOn);
+
+
+ /* Save the result data to outpupt memory level structure */
+ mclk->MclkFrequency = memory_clock;
+ mclk->MpllFuncCntl = mpll_func_cntl;
+ mclk->MpllFuncCntl_1 = mpll_func_cntl_1;
+ mclk->MpllFuncCntl_2 = mpll_func_cntl_2;
+ mclk->MpllAdFuncCntl = mpll_ad_func_cntl;
+ mclk->MpllDqFuncCntl = mpll_dq_func_cntl;
+ mclk->MclkPwrmgtCntl = mclk_pwrmgt_cntl;
+ mclk->DllCntl = dll_cntl;
+ mclk->MpllSs1 = mpll_ss1;
+ mclk->MpllSs2 = mpll_ss2;
+
+ return 0;
+}
+
+static uint8_t iceland_get_mclk_frequency_ratio(uint32_t memory_clock,
+ bool strobe_mode)
+{
+ uint8_t mc_para_index;
+
+ if (strobe_mode) {
+ if (memory_clock < 12500) {
+ mc_para_index = 0x00;
+ } else if (memory_clock > 47500) {
+ mc_para_index = 0x0f;
+ } else {
+ mc_para_index = (uint8_t)((memory_clock - 10000) / 2500);
+ }
+ } else {
+ if (memory_clock < 65000) {
+ mc_para_index = 0x00;
+ } else if (memory_clock > 135000) {
+ mc_para_index = 0x0f;
+ } else {
+ mc_para_index = (uint8_t)((memory_clock - 60000) / 5000);
+ }
+ }
+
+ return mc_para_index;
+}
+
+static uint8_t iceland_get_ddr3_mclk_frequency_ratio(uint32_t memory_clock)
+{
+ uint8_t mc_para_index;
+
+ if (memory_clock < 10000) {
+ mc_para_index = 0;
+ } else if (memory_clock >= 80000) {
+ mc_para_index = 0x0f;
+ } else {
+ mc_para_index = (uint8_t)((memory_clock - 10000) / 5000 + 1);
+ }
+
+ return mc_para_index;
+}
+
+static int iceland_populate_phase_value_based_on_mclk(struct pp_hwmgr *hwmgr, const struct phm_phase_shedding_limits_table *pl,
+ uint32_t memory_clock, uint32_t *p_shed)
+{
+ unsigned int i;
+
+ *p_shed = 1;
+
+ for (i = 0; i < pl->count; i++) {
+ if (memory_clock < pl->entries[i].Mclk) {
+ *p_shed = i;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int iceland_populate_single_memory_level(
+ struct pp_hwmgr *hwmgr,
+ uint32_t memory_clock,
+ SMU71_Discrete_MemoryLevel *memory_level
+ )
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ int result = 0;
+ bool dll_state_on;
+ uint32_t mclk_edc_wr_enable_threshold = 40000;
+ uint32_t mclk_edc_enable_threshold = 40000;
+ uint32_t mclk_strobe_mode_threshold = 40000;
+
+ if (hwmgr->dyn_state.vddc_dependency_on_mclk != NULL) {
+ result = iceland_get_dependency_volt_by_clk(hwmgr,
+ hwmgr->dyn_state.vddc_dependency_on_mclk, memory_clock, &memory_level->MinVddc);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find MinVddc voltage value from memory VDDC voltage dependency table", return result);
+ }
+
+ if (data->vddci_control == SMU7_VOLTAGE_CONTROL_NONE) {
+ memory_level->MinVddci = memory_level->MinVddc;
+ } else if (NULL != hwmgr->dyn_state.vddci_dependency_on_mclk) {
+ result = iceland_get_dependency_volt_by_clk(hwmgr,
+ hwmgr->dyn_state.vddci_dependency_on_mclk,
+ memory_clock,
+ &memory_level->MinVddci);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find MinVddci voltage value from memory VDDCI voltage dependency table", return result);
+ }
+
+ memory_level->MinVddcPhases = 1;
+
+ if (data->vddc_phase_shed_control) {
+ iceland_populate_phase_value_based_on_mclk(hwmgr, hwmgr->dyn_state.vddc_phase_shed_limits_table,
+ memory_clock, &memory_level->MinVddcPhases);
+ }
+
+ memory_level->EnabledForThrottle = 1;
+ memory_level->EnabledForActivity = 0;
+ memory_level->UpHyst = data->current_profile_setting.mclk_up_hyst;
+ memory_level->DownHyst = data->current_profile_setting.mclk_down_hyst;
+ memory_level->VoltageDownHyst = 0;
+
+ /* Indicates maximum activity level for this performance level.*/
+ memory_level->ActivityLevel = data->current_profile_setting.mclk_activity;
+ memory_level->StutterEnable = 0;
+ memory_level->StrobeEnable = 0;
+ memory_level->EdcReadEnable = 0;
+ memory_level->EdcWriteEnable = 0;
+ memory_level->RttEnable = 0;
+
+ /* default set to low watermark. Highest level will be set to high later.*/
+ memory_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+ data->display_timing.num_existing_displays = hwmgr->display_config->num_display;
+ data->display_timing.vrefresh = hwmgr->display_config->vrefresh;
+
+ /* stutter mode not support on iceland */
+
+ /* decide strobe mode*/
+ memory_level->StrobeEnable = (mclk_strobe_mode_threshold != 0) &&
+ (memory_clock <= mclk_strobe_mode_threshold);
+
+ /* decide EDC mode and memory clock ratio*/
+ if (data->is_memory_gddr5) {
+ memory_level->StrobeRatio = iceland_get_mclk_frequency_ratio(memory_clock,
+ memory_level->StrobeEnable);
+
+ if ((mclk_edc_enable_threshold != 0) &&
+ (memory_clock > mclk_edc_enable_threshold)) {
+ memory_level->EdcReadEnable = 1;
+ }
+
+ if ((mclk_edc_wr_enable_threshold != 0) &&
+ (memory_clock > mclk_edc_wr_enable_threshold)) {
+ memory_level->EdcWriteEnable = 1;
+ }
+
+ if (memory_level->StrobeEnable) {
+ if (iceland_get_mclk_frequency_ratio(memory_clock, 1) >=
+ ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC7) >> 16) & 0xf))
+ dll_state_on = ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC5) >> 1) & 0x1) ? 1 : 0;
+ else
+ dll_state_on = ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC6) >> 1) & 0x1) ? 1 : 0;
+ } else
+ dll_state_on = data->dll_default_on;
+ } else {
+ memory_level->StrobeRatio =
+ iceland_get_ddr3_mclk_frequency_ratio(memory_clock);
+ dll_state_on = ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC5) >> 1) & 0x1) ? 1 : 0;
+ }
+
+ result = iceland_calculate_mclk_params(hwmgr,
+ memory_clock, memory_level, memory_level->StrobeEnable, dll_state_on);
+
+ if (0 == result) {
+ memory_level->MinVddc = PP_HOST_TO_SMC_UL(memory_level->MinVddc * VOLTAGE_SCALE);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MinVddcPhases);
+ memory_level->MinVddci = PP_HOST_TO_SMC_UL(memory_level->MinVddci * VOLTAGE_SCALE);
+ memory_level->MinMvdd = PP_HOST_TO_SMC_UL(memory_level->MinMvdd * VOLTAGE_SCALE);
+ /* MCLK frequency in units of 10KHz*/
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MclkFrequency);
+ /* Indicates maximum activity level for this performance level.*/
+ CONVERT_FROM_HOST_TO_SMC_US(memory_level->ActivityLevel);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl_1);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl_2);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllAdFuncCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllDqFuncCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MclkPwrmgtCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->DllCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllSs1);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllSs2);
+ }
+
+ return result;
+}
+
+static int iceland_populate_all_memory_levels(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ struct smu7_dpm_table *dpm_table = &data->dpm_table;
+ int result;
+
+ /* populate MCLK dpm table to SMU7 */
+ uint32_t level_array_adress = smu_data->smu7_data.dpm_table_start + offsetof(SMU71_Discrete_DpmTable, MemoryLevel);
+ uint32_t level_array_size = sizeof(SMU71_Discrete_MemoryLevel) * SMU71_MAX_LEVELS_MEMORY;
+ SMU71_Discrete_MemoryLevel *levels = smu_data->smc_state_table.MemoryLevel;
+ uint32_t i;
+
+ memset(levels, 0x00, level_array_size);
+
+ for (i = 0; i < dpm_table->mclk_table.count; i++) {
+ PP_ASSERT_WITH_CODE((0 != dpm_table->mclk_table.dpm_levels[i].value),
+ "can not populate memory level as memory clock is zero", return -EINVAL);
+ result = iceland_populate_single_memory_level(hwmgr, dpm_table->mclk_table.dpm_levels[i].value,
+ &(smu_data->smc_state_table.MemoryLevel[i]));
+ if (0 != result) {
+ return result;
+ }
+ }
+
+ /* Only enable level 0 for now.*/
+ smu_data->smc_state_table.MemoryLevel[0].EnabledForActivity = 1;
+
+ /*
+ * in order to prevent MC activity from stutter mode to push DPM up.
+ * the UVD change complements this by putting the MCLK in a higher state
+ * by default such that we are not effected by up threshold or and MCLK DPM latency.
+ */
+ smu_data->smc_state_table.MemoryLevel[0].ActivityLevel = 0x1F;
+ CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.MemoryLevel[0].ActivityLevel);
+
+ smu_data->smc_state_table.MemoryDpmLevelCount = (uint8_t)dpm_table->mclk_table.count;
+ data->dpm_level_enable_mask.mclk_dpm_enable_mask = phm_get_dpm_level_enable_mask_value(&dpm_table->mclk_table);
+ /* set highest level watermark to high*/
+ smu_data->smc_state_table.MemoryLevel[dpm_table->mclk_table.count-1].DisplayWatermark = PPSMC_DISPLAY_WATERMARK_HIGH;
+
+ /* level count will send to smc once at init smc table and never change*/
+ result = smu7_copy_bytes_to_smc(hwmgr,
+ level_array_adress, (uint8_t *)levels, (uint32_t)level_array_size,
+ SMC_RAM_END);
+
+ return result;
+}
+
+static int iceland_populate_mvdd_value(struct pp_hwmgr *hwmgr, uint32_t mclk,
+ SMU71_Discrete_VoltageLevel *voltage)
+{
+ const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ uint32_t i = 0;
+
+ if (SMU7_VOLTAGE_CONTROL_NONE != data->mvdd_control) {
+ /* find mvdd value which clock is more than request */
+ for (i = 0; i < hwmgr->dyn_state.mvdd_dependency_on_mclk->count; i++) {
+ if (mclk <= hwmgr->dyn_state.mvdd_dependency_on_mclk->entries[i].clk) {
+ /* Always round to higher voltage. */
+ voltage->Voltage = data->mvdd_voltage_table.entries[i].value;
+ break;
+ }
+ }
+
+ PP_ASSERT_WITH_CODE(i < hwmgr->dyn_state.mvdd_dependency_on_mclk->count,
+ "MVDD Voltage is outside the supported range.", return -EINVAL);
+
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int iceland_populate_smc_acpi_level(struct pp_hwmgr *hwmgr,
+ SMU71_Discrete_DpmTable *table)
+{
+ int result = 0;
+ const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct pp_atomctrl_clock_dividers_vi dividers;
+ uint32_t vddc_phase_shed_control = 0;
+
+ SMU71_Discrete_VoltageLevel voltage_level;
+ uint32_t spll_func_cntl = data->clock_registers.vCG_SPLL_FUNC_CNTL;
+ uint32_t spll_func_cntl_2 = data->clock_registers.vCG_SPLL_FUNC_CNTL_2;
+ uint32_t dll_cntl = data->clock_registers.vDLL_CNTL;
+ uint32_t mclk_pwrmgt_cntl = data->clock_registers.vMCLK_PWRMGT_CNTL;
+
+
+ /* The ACPI state should not do DPM on DC (or ever).*/
+ table->ACPILevel.Flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+ if (data->acpi_vddc)
+ table->ACPILevel.MinVddc = PP_HOST_TO_SMC_UL(data->acpi_vddc * VOLTAGE_SCALE);
+ else
+ table->ACPILevel.MinVddc = PP_HOST_TO_SMC_UL(data->min_vddc_in_pptable * VOLTAGE_SCALE);
+
+ table->ACPILevel.MinVddcPhases = vddc_phase_shed_control ? 0 : 1;
+ /* assign zero for now*/
+ table->ACPILevel.SclkFrequency = atomctrl_get_reference_clock(hwmgr);
+
+ /* get the engine clock dividers for this clock value*/
+ result = atomctrl_get_engine_pll_dividers_vi(hwmgr,
+ table->ACPILevel.SclkFrequency, &dividers);
+
+ PP_ASSERT_WITH_CODE(result == 0,
+ "Error retrieving Engine Clock dividers from VBIOS.", return result);
+
+ /* divider ID for required SCLK*/
+ table->ACPILevel.SclkDid = (uint8_t)dividers.pll_post_divider;
+ table->ACPILevel.DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+ table->ACPILevel.DeepSleepDivId = 0;
+
+ spll_func_cntl = PHM_SET_FIELD(spll_func_cntl,
+ CG_SPLL_FUNC_CNTL, SPLL_PWRON, 0);
+ spll_func_cntl = PHM_SET_FIELD(spll_func_cntl,
+ CG_SPLL_FUNC_CNTL, SPLL_RESET, 1);
+ spll_func_cntl_2 = PHM_SET_FIELD(spll_func_cntl_2,
+ CG_SPLL_FUNC_CNTL_2, SCLK_MUX_SEL, 4);
+
+ table->ACPILevel.CgSpllFuncCntl = spll_func_cntl;
+ table->ACPILevel.CgSpllFuncCntl2 = spll_func_cntl_2;
+ table->ACPILevel.CgSpllFuncCntl3 = data->clock_registers.vCG_SPLL_FUNC_CNTL_3;
+ table->ACPILevel.CgSpllFuncCntl4 = data->clock_registers.vCG_SPLL_FUNC_CNTL_4;
+ table->ACPILevel.SpllSpreadSpectrum = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM;
+ table->ACPILevel.SpllSpreadSpectrum2 = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2;
+ table->ACPILevel.CcPwrDynRm = 0;
+ table->ACPILevel.CcPwrDynRm1 = 0;
+
+
+ /* For various features to be enabled/disabled while this level is active.*/
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.Flags);
+ /* SCLK frequency in units of 10KHz*/
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl2);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl3);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl4);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum2);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm1);
+
+ /* table->MemoryACPILevel.MinVddcPhases = table->ACPILevel.MinVddcPhases;*/
+ table->MemoryACPILevel.MinVddc = table->ACPILevel.MinVddc;
+ table->MemoryACPILevel.MinVddcPhases = table->ACPILevel.MinVddcPhases;
+
+ if (SMU7_VOLTAGE_CONTROL_NONE == data->vddci_control)
+ table->MemoryACPILevel.MinVddci = table->MemoryACPILevel.MinVddc;
+ else {
+ if (data->acpi_vddci != 0)
+ table->MemoryACPILevel.MinVddci = PP_HOST_TO_SMC_UL(data->acpi_vddci * VOLTAGE_SCALE);
+ else
+ table->MemoryACPILevel.MinVddci = PP_HOST_TO_SMC_UL(data->min_vddci_in_pptable * VOLTAGE_SCALE);
+ }
+
+ if (0 == iceland_populate_mvdd_value(hwmgr, 0, &voltage_level))
+ table->MemoryACPILevel.MinMvdd =
+ PP_HOST_TO_SMC_UL(voltage_level.Voltage * VOLTAGE_SCALE);
+ else
+ table->MemoryACPILevel.MinMvdd = 0;
+
+ /* Force reset on DLL*/
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, MRDCK0_RESET, 0x1);
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, MRDCK1_RESET, 0x1);
+
+ /* Disable DLL in ACPIState*/
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, MRDCK0_PDNB, 0);
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, MRDCK1_PDNB, 0);
+
+ /* Enable DLL bypass signal*/
+ dll_cntl = PHM_SET_FIELD(dll_cntl,
+ DLL_CNTL, MRDCK0_BYPASS, 0);
+ dll_cntl = PHM_SET_FIELD(dll_cntl,
+ DLL_CNTL, MRDCK1_BYPASS, 0);
+
+ table->MemoryACPILevel.DllCntl =
+ PP_HOST_TO_SMC_UL(dll_cntl);
+ table->MemoryACPILevel.MclkPwrmgtCntl =
+ PP_HOST_TO_SMC_UL(mclk_pwrmgt_cntl);
+ table->MemoryACPILevel.MpllAdFuncCntl =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_AD_FUNC_CNTL);
+ table->MemoryACPILevel.MpllDqFuncCntl =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_DQ_FUNC_CNTL);
+ table->MemoryACPILevel.MpllFuncCntl =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_FUNC_CNTL);
+ table->MemoryACPILevel.MpllFuncCntl_1 =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_FUNC_CNTL_1);
+ table->MemoryACPILevel.MpllFuncCntl_2 =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_FUNC_CNTL_2);
+ table->MemoryACPILevel.MpllSs1 =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_SS1);
+ table->MemoryACPILevel.MpllSs2 =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_SS2);
+
+ table->MemoryACPILevel.EnabledForThrottle = 0;
+ table->MemoryACPILevel.EnabledForActivity = 0;
+ table->MemoryACPILevel.UpHyst = 0;
+ table->MemoryACPILevel.DownHyst = 100;
+ table->MemoryACPILevel.VoltageDownHyst = 0;
+ /* Indicates maximum activity level for this performance level.*/
+ table->MemoryACPILevel.ActivityLevel = PP_HOST_TO_SMC_US(data->current_profile_setting.mclk_activity);
+
+ table->MemoryACPILevel.StutterEnable = 0;
+ table->MemoryACPILevel.StrobeEnable = 0;
+ table->MemoryACPILevel.EdcReadEnable = 0;
+ table->MemoryACPILevel.EdcWriteEnable = 0;
+ table->MemoryACPILevel.RttEnable = 0;
+
+ return result;
+}
+
+static int iceland_populate_smc_uvd_level(struct pp_hwmgr *hwmgr,
+ SMU71_Discrete_DpmTable *table)
+{
+ return 0;
+}
+
+static int iceland_populate_smc_vce_level(struct pp_hwmgr *hwmgr,
+ SMU71_Discrete_DpmTable *table)
+{
+ return 0;
+}
+
+static int iceland_populate_smc_acp_level(struct pp_hwmgr *hwmgr,
+ SMU71_Discrete_DpmTable *table)
+{
+ return 0;
+}
+
+static int iceland_populate_memory_timing_parameters(
+ struct pp_hwmgr *hwmgr,
+ uint32_t engine_clock,
+ uint32_t memory_clock,
+ struct SMU71_Discrete_MCArbDramTimingTableEntry *arb_regs
+ )
+{
+ uint32_t dramTiming;
+ uint32_t dramTiming2;
+ uint32_t burstTime;
+ int result;
+
+ result = atomctrl_set_engine_dram_timings_rv770(hwmgr,
+ engine_clock, memory_clock);
+
+ PP_ASSERT_WITH_CODE(result == 0,
+ "Error calling VBIOS to set DRAM_TIMING.", return result);
+
+ dramTiming = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING);
+ dramTiming2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2);
+ burstTime = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0);
+
+ arb_regs->McArbDramTiming = PP_HOST_TO_SMC_UL(dramTiming);
+ arb_regs->McArbDramTiming2 = PP_HOST_TO_SMC_UL(dramTiming2);
+ arb_regs->McArbBurstTime = (uint8_t)burstTime;
+
+ return 0;
+}
+
+static int iceland_program_memory_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ int result = 0;
+ SMU71_Discrete_MCArbDramTimingTable arb_regs;
+ uint32_t i, j;
+
+ memset(&arb_regs, 0x00, sizeof(SMU71_Discrete_MCArbDramTimingTable));
+
+ for (i = 0; i < data->dpm_table.sclk_table.count; i++) {
+ for (j = 0; j < data->dpm_table.mclk_table.count; j++) {
+ result = iceland_populate_memory_timing_parameters
+ (hwmgr, data->dpm_table.sclk_table.dpm_levels[i].value,
+ data->dpm_table.mclk_table.dpm_levels[j].value,
+ &arb_regs.entries[i][j]);
+
+ if (0 != result) {
+ break;
+ }
+ }
+ }
+
+ if (0 == result) {
+ result = smu7_copy_bytes_to_smc(
+ hwmgr,
+ smu_data->smu7_data.arb_table_start,
+ (uint8_t *)&arb_regs,
+ sizeof(SMU71_Discrete_MCArbDramTimingTable),
+ SMC_RAM_END
+ );
+ }
+
+ return result;
+}
+
+static int iceland_populate_smc_boot_level(struct pp_hwmgr *hwmgr,
+ SMU71_Discrete_DpmTable *table)
+{
+ int result = 0;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ table->GraphicsBootLevel = 0;
+ table->MemoryBootLevel = 0;
+
+ /* find boot level from dpm table*/
+ result = phm_find_boot_level(&(data->dpm_table.sclk_table),
+ data->vbios_boot_state.sclk_bootup_value,
+ (uint32_t *)&(smu_data->smc_state_table.GraphicsBootLevel));
+
+ if (0 != result) {
+ smu_data->smc_state_table.GraphicsBootLevel = 0;
+ pr_err("VBIOS did not find boot engine clock value in dependency table. Using Graphics DPM level 0!\n");
+ result = 0;
+ }
+
+ result = phm_find_boot_level(&(data->dpm_table.mclk_table),
+ data->vbios_boot_state.mclk_bootup_value,
+ (uint32_t *)&(smu_data->smc_state_table.MemoryBootLevel));
+
+ if (0 != result) {
+ smu_data->smc_state_table.MemoryBootLevel = 0;
+ pr_err("VBIOS did not find boot engine clock value in dependency table. Using Memory DPM level 0!\n");
+ result = 0;
+ }
+
+ table->BootVddc = data->vbios_boot_state.vddc_bootup_value;
+ if (SMU7_VOLTAGE_CONTROL_NONE == data->vddci_control)
+ table->BootVddci = table->BootVddc;
+ else
+ table->BootVddci = data->vbios_boot_state.vddci_bootup_value;
+
+ table->BootMVdd = data->vbios_boot_state.mvdd_bootup_value;
+
+ return result;
+}
+
+static int iceland_populate_mc_reg_address(struct pp_hwmgr *hwmgr,
+ SMU71_Discrete_MCRegisters *mc_reg_table)
+{
+ const struct iceland_smumgr *smu_data = (struct iceland_smumgr *)hwmgr->smu_backend;
+
+ uint32_t i, j;
+
+ for (i = 0, j = 0; j < smu_data->mc_reg_table.last; j++) {
+ if (smu_data->mc_reg_table.validflag & 1<<j) {
+ PP_ASSERT_WITH_CODE(i < SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE,
+ "Index of mc_reg_table->address[] array out of boundary", return -EINVAL);
+ mc_reg_table->address[i].s0 =
+ PP_HOST_TO_SMC_US(smu_data->mc_reg_table.mc_reg_address[j].s0);
+ mc_reg_table->address[i].s1 =
+ PP_HOST_TO_SMC_US(smu_data->mc_reg_table.mc_reg_address[j].s1);
+ i++;
+ }
+ }
+
+ mc_reg_table->last = (uint8_t)i;
+
+ return 0;
+}
+
+/*convert register values from driver to SMC format */
+static void iceland_convert_mc_registers(
+ const struct iceland_mc_reg_entry *entry,
+ SMU71_Discrete_MCRegisterSet *data,
+ uint32_t num_entries, uint32_t valid_flag)
+{
+ uint32_t i, j;
+
+ for (i = 0, j = 0; j < num_entries; j++) {
+ if (valid_flag & 1<<j) {
+ data->value[i] = PP_HOST_TO_SMC_UL(entry->mc_data[j]);
+ i++;
+ }
+ }
+}
+
+static int iceland_convert_mc_reg_table_entry_to_smc(struct pp_hwmgr *hwmgr,
+ const uint32_t memory_clock,
+ SMU71_Discrete_MCRegisterSet *mc_reg_table_data
+ )
+{
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ uint32_t i = 0;
+
+ for (i = 0; i < smu_data->mc_reg_table.num_entries; i++) {
+ if (memory_clock <=
+ smu_data->mc_reg_table.mc_reg_table_entry[i].mclk_max) {
+ break;
+ }
+ }
+
+ if ((i == smu_data->mc_reg_table.num_entries) && (i > 0))
+ --i;
+
+ iceland_convert_mc_registers(&smu_data->mc_reg_table.mc_reg_table_entry[i],
+ mc_reg_table_data, smu_data->mc_reg_table.last,
+ smu_data->mc_reg_table.validflag);
+
+ return 0;
+}
+
+static int iceland_convert_mc_reg_table_to_smc(struct pp_hwmgr *hwmgr,
+ SMU71_Discrete_MCRegisters *mc_regs)
+{
+ int result = 0;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ int res;
+ uint32_t i;
+
+ for (i = 0; i < data->dpm_table.mclk_table.count; i++) {
+ res = iceland_convert_mc_reg_table_entry_to_smc(
+ hwmgr,
+ data->dpm_table.mclk_table.dpm_levels[i].value,
+ &mc_regs->data[i]
+ );
+
+ if (0 != res)
+ result = res;
+ }
+
+ return result;
+}
+
+static int iceland_update_and_upload_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t address;
+ int32_t result;
+
+ if (0 == (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK))
+ return 0;
+
+
+ memset(&smu_data->mc_regs, 0, sizeof(SMU71_Discrete_MCRegisters));
+
+ result = iceland_convert_mc_reg_table_to_smc(hwmgr, &(smu_data->mc_regs));
+
+ if (result != 0)
+ return result;
+
+
+ address = smu_data->smu7_data.mc_reg_table_start + (uint32_t)offsetof(SMU71_Discrete_MCRegisters, data[0]);
+
+ return smu7_copy_bytes_to_smc(hwmgr, address,
+ (uint8_t *)&smu_data->mc_regs.data[0],
+ sizeof(SMU71_Discrete_MCRegisterSet) * data->dpm_table.mclk_table.count,
+ SMC_RAM_END);
+}
+
+static int iceland_populate_initial_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+
+ memset(&smu_data->mc_regs, 0x00, sizeof(SMU71_Discrete_MCRegisters));
+ result = iceland_populate_mc_reg_address(hwmgr, &(smu_data->mc_regs));
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize MCRegTable for the MC register addresses!", return result;);
+
+ result = iceland_convert_mc_reg_table_to_smc(hwmgr, &smu_data->mc_regs);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize MCRegTable for driver state!", return result;);
+
+ return smu7_copy_bytes_to_smc(hwmgr, smu_data->smu7_data.mc_reg_table_start,
+ (uint8_t *)&smu_data->mc_regs, sizeof(SMU71_Discrete_MCRegisters), SMC_RAM_END);
+}
+
+static int iceland_populate_smc_initial_state(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ uint8_t count, level;
+
+ count = (uint8_t)(hwmgr->dyn_state.vddc_dependency_on_sclk->count);
+
+ for (level = 0; level < count; level++) {
+ if (hwmgr->dyn_state.vddc_dependency_on_sclk->entries[level].clk
+ >= data->vbios_boot_state.sclk_bootup_value) {
+ smu_data->smc_state_table.GraphicsBootLevel = level;
+ break;
+ }
+ }
+
+ count = (uint8_t)(hwmgr->dyn_state.vddc_dependency_on_mclk->count);
+
+ for (level = 0; level < count; level++) {
+ if (hwmgr->dyn_state.vddc_dependency_on_mclk->entries[level].clk
+ >= data->vbios_boot_state.mclk_bootup_value) {
+ smu_data->smc_state_table.MemoryBootLevel = level;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int iceland_populate_bapm_parameters_in_dpm_table(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ const struct iceland_pt_defaults *defaults = smu_data->power_tune_defaults;
+ SMU71_Discrete_DpmTable *dpm_table = &(smu_data->smc_state_table);
+ struct phm_cac_tdp_table *cac_dtp_table = hwmgr->dyn_state.cac_dtp_table;
+ struct phm_ppm_table *ppm = hwmgr->dyn_state.ppm_parameter_table;
+ const uint16_t *def1, *def2;
+ int i, j, k;
+
+
+ /*
+ * TDP number of fraction bits are changed from 8 to 7 for Iceland
+ * as requested by SMC team
+ */
+
+ dpm_table->DefaultTdp = PP_HOST_TO_SMC_US((uint16_t)(cac_dtp_table->usTDP * 256));
+ dpm_table->TargetTdp = PP_HOST_TO_SMC_US((uint16_t)(cac_dtp_table->usConfigurableTDP * 256));
+
+
+ dpm_table->DTETjOffset = 0;
+
+ dpm_table->GpuTjMax = (uint8_t)(data->thermal_temp_setting.temperature_high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES);
+ dpm_table->GpuTjHyst = 8;
+
+ dpm_table->DTEAmbientTempBase = defaults->dte_ambient_temp_base;
+
+ /* The following are for new Iceland Multi-input fan/thermal control */
+ if (NULL != ppm) {
+ dpm_table->PPM_PkgPwrLimit = (uint16_t)ppm->dgpu_tdp * 256 / 1000;
+ dpm_table->PPM_TemperatureLimit = (uint16_t)ppm->tj_max * 256;
+ } else {
+ dpm_table->PPM_PkgPwrLimit = 0;
+ dpm_table->PPM_TemperatureLimit = 0;
+ }
+
+ CONVERT_FROM_HOST_TO_SMC_US(dpm_table->PPM_PkgPwrLimit);
+ CONVERT_FROM_HOST_TO_SMC_US(dpm_table->PPM_TemperatureLimit);
+
+ dpm_table->BAPM_TEMP_GRADIENT = PP_HOST_TO_SMC_UL(defaults->bapm_temp_gradient);
+ def1 = defaults->bapmti_r;
+ def2 = defaults->bapmti_rc;
+
+ for (i = 0; i < SMU71_DTE_ITERATIONS; i++) {
+ for (j = 0; j < SMU71_DTE_SOURCES; j++) {
+ for (k = 0; k < SMU71_DTE_SINKS; k++) {
+ dpm_table->BAPMTI_R[i][j][k] = PP_HOST_TO_SMC_US(*def1);
+ dpm_table->BAPMTI_RC[i][j][k] = PP_HOST_TO_SMC_US(*def2);
+ def1++;
+ def2++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int iceland_populate_smc_svi2_config(struct pp_hwmgr *hwmgr,
+ SMU71_Discrete_DpmTable *tab)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control)
+ tab->SVI2Enable |= VDDC_ON_SVI2;
+
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control)
+ tab->SVI2Enable |= VDDCI_ON_SVI2;
+ else
+ tab->MergedVddci = 1;
+
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->mvdd_control)
+ tab->SVI2Enable |= MVDD_ON_SVI2;
+
+ PP_ASSERT_WITH_CODE(tab->SVI2Enable != (VDDC_ON_SVI2 | VDDCI_ON_SVI2 | MVDD_ON_SVI2) &&
+ (tab->SVI2Enable & VDDC_ON_SVI2), "SVI2 domain configuration is incorrect!", return -EINVAL);
+
+ return 0;
+}
+
+static int iceland_init_smc_table(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ SMU71_Discrete_DpmTable *table = &(smu_data->smc_state_table);
+
+
+ iceland_initialize_power_tune_defaults(hwmgr);
+ memset(&(smu_data->smc_state_table), 0x00, sizeof(smu_data->smc_state_table));
+
+ if (SMU7_VOLTAGE_CONTROL_NONE != data->voltage_control) {
+ iceland_populate_smc_voltage_tables(hwmgr, table);
+ }
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition))
+ table->SystemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_StepVddc))
+ table->SystemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+ if (data->is_memory_gddr5)
+ table->SystemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
+
+
+ if (data->ulv_supported) {
+ result = iceland_populate_ulv_state(hwmgr, &(smu_data->ulv_setting));
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize ULV state!", return result;);
+
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixCG_ULV_PARAMETER, 0x40035);
+ }
+
+ result = iceland_populate_smc_link_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize Link Level!", return result;);
+
+ result = iceland_populate_all_graphic_levels(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize Graphics Level!", return result;);
+
+ result = iceland_populate_all_memory_levels(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize Memory Level!", return result;);
+
+ result = iceland_populate_smc_acpi_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize ACPI Level!", return result;);
+
+ result = iceland_populate_smc_vce_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize VCE Level!", return result;);
+
+ result = iceland_populate_smc_acp_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize ACP Level!", return result;);
+
+ /* Since only the initial state is completely set up at this point (the other states are just copies of the boot state) we only */
+ /* need to populate the ARB settings for the initial state. */
+ result = iceland_program_memory_timing_parameters(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to Write ARB settings for the initial state.", return result;);
+
+ result = iceland_populate_smc_uvd_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize UVD Level!", return result;);
+
+ table->GraphicsBootLevel = 0;
+ table->MemoryBootLevel = 0;
+
+ result = iceland_populate_smc_boot_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize Boot Level!", return result;);
+
+ result = iceland_populate_smc_initial_state(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result, "Failed to initialize Boot State!", return result);
+
+ result = iceland_populate_bapm_parameters_in_dpm_table(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result, "Failed to populate BAPM Parameters!", return result);
+
+ table->GraphicsVoltageChangeEnable = 1;
+ table->GraphicsThermThrottleEnable = 1;
+ table->GraphicsInterval = 1;
+ table->VoltageInterval = 1;
+ table->ThermalInterval = 1;
+
+ table->TemperatureLimitHigh =
+ (data->thermal_temp_setting.temperature_high *
+ SMU7_Q88_FORMAT_CONVERSION_UNIT) / PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ table->TemperatureLimitLow =
+ (data->thermal_temp_setting.temperature_low *
+ SMU7_Q88_FORMAT_CONVERSION_UNIT) / PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+ table->MemoryVoltageChangeEnable = 1;
+ table->MemoryInterval = 1;
+ table->VoltageResponseTime = 0;
+ table->PhaseResponseTime = 0;
+ table->MemoryThermThrottleEnable = 1;
+ table->PCIeBootLinkLevel = 0;
+ table->PCIeGenInterval = 1;
+
+ result = iceland_populate_smc_svi2_config(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to populate SVI2 setting!", return result);
+
+ table->ThermGpio = 17;
+ table->SclkStepSize = 0x4000;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SystemFlags);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMaskVddcVid);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMaskVddcPhase);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMaskVddciVid);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMaskMvddVid);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SclkStepSize);
+ CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitHigh);
+ CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitLow);
+ CONVERT_FROM_HOST_TO_SMC_US(table->VoltageResponseTime);
+ CONVERT_FROM_HOST_TO_SMC_US(table->PhaseResponseTime);
+
+ table->BootVddc = PP_HOST_TO_SMC_US(table->BootVddc * VOLTAGE_SCALE);
+ table->BootVddci = PP_HOST_TO_SMC_US(table->BootVddci * VOLTAGE_SCALE);
+ table->BootMVdd = PP_HOST_TO_SMC_US(table->BootMVdd * VOLTAGE_SCALE);
+
+ /* Upload all dpm data to SMC memory.(dpm level, dpm level count etc) */
+ result = smu7_copy_bytes_to_smc(hwmgr, smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU71_Discrete_DpmTable, SystemFlags),
+ (uint8_t *)&(table->SystemFlags),
+ sizeof(SMU71_Discrete_DpmTable)-3 * sizeof(SMU71_PIDController),
+ SMC_RAM_END);
+
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to upload dpm data to SMC memory!", return result;);
+
+ /* Upload all ulv setting to SMC memory.(dpm level, dpm level count etc) */
+ result = smu7_copy_bytes_to_smc(hwmgr,
+ smu_data->smu7_data.ulv_setting_starts,
+ (uint8_t *)&(smu_data->ulv_setting),
+ sizeof(SMU71_Discrete_Ulv),
+ SMC_RAM_END);
+
+
+ result = iceland_populate_initial_mc_reg_table(hwmgr);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Failed to populate initialize MC Reg table!", return result);
+
+ result = iceland_populate_pm_fuses(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to populate PM fuses to SMC memory!", return result);
+
+ return 0;
+}
+
+int iceland_thermal_setup_fan_table(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_smumgr *smu7_data = (struct smu7_smumgr *)(hwmgr->smu_backend);
+ SMU71_Discrete_FanTable fan_table = { FDO_MODE_HARDWARE };
+ uint32_t duty100;
+ uint32_t t_diff1, t_diff2, pwm_diff1, pwm_diff2;
+ uint16_t fdo_min, slope1, slope2;
+ uint32_t reference_clock;
+ int res;
+ uint64_t tmp64;
+
+ if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl))
+ return 0;
+
+ if (hwmgr->thermal_controller.fanInfo.bNoFan) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
+ if (0 == smu7_data->fan_table_start) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
+ duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_FDO_CTRL1, FMAX_DUTY100);
+
+ if (0 == duty100) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
+ tmp64 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin * duty100;
+ do_div(tmp64, 10000);
+ fdo_min = (uint16_t)tmp64;
+
+ t_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usTMed - hwmgr->thermal_controller.advanceFanControlParameters.usTMin;
+ t_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usTHigh - hwmgr->thermal_controller.advanceFanControlParameters.usTMed;
+
+ pwm_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed - hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin;
+ pwm_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh - hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed;
+
+ slope1 = (uint16_t)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100);
+ slope2 = (uint16_t)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100);
+
+ fan_table.TempMin = cpu_to_be16((50 + hwmgr->thermal_controller.advanceFanControlParameters.usTMin) / 100);
+ fan_table.TempMed = cpu_to_be16((50 + hwmgr->thermal_controller.advanceFanControlParameters.usTMed) / 100);
+ fan_table.TempMax = cpu_to_be16((50 + hwmgr->thermal_controller.advanceFanControlParameters.usTMax) / 100);
+
+ fan_table.Slope1 = cpu_to_be16(slope1);
+ fan_table.Slope2 = cpu_to_be16(slope2);
+
+ fan_table.FdoMin = cpu_to_be16(fdo_min);
+
+ fan_table.HystDown = cpu_to_be16(hwmgr->thermal_controller.advanceFanControlParameters.ucTHyst);
+
+ fan_table.HystUp = cpu_to_be16(1);
+
+ fan_table.HystSlope = cpu_to_be16(1);
+
+ fan_table.TempRespLim = cpu_to_be16(5);
+
+ reference_clock = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev);
+
+ fan_table.RefreshPeriod = cpu_to_be32((hwmgr->thermal_controller.advanceFanControlParameters.ulCycleDelay * reference_clock) / 1600);
+
+ fan_table.FdoMax = cpu_to_be16((uint16_t)duty100);
+
+ fan_table.TempSrc = (uint8_t)PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_MULT_THERMAL_CTRL, TEMP_SEL);
+
+ /* fan_table.FanControl_GL_Flag = 1; */
+
+ res = smu7_copy_bytes_to_smc(hwmgr, smu7_data->fan_table_start, (uint8_t *)&fan_table, (uint32_t)sizeof(fan_table), SMC_RAM_END);
+
+ return 0;
+}
+
+
+static int iceland_program_mem_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (data->need_update_smu7_dpm_table &
+ (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_OD_UPDATE_MCLK))
+ return iceland_program_memory_timing_parameters(hwmgr);
+
+ return 0;
+}
+
+static int iceland_update_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+
+ int result = 0;
+ uint32_t low_sclk_interrupt_threshold = 0;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkThrottleLowNotification)
+ && (data->low_sclk_interrupt_threshold != 0)) {
+ low_sclk_interrupt_threshold =
+ data->low_sclk_interrupt_threshold;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(low_sclk_interrupt_threshold);
+
+ result = smu7_copy_bytes_to_smc(
+ hwmgr,
+ smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU71_Discrete_DpmTable,
+ LowSclkInterruptThreshold),
+ (uint8_t *)&low_sclk_interrupt_threshold,
+ sizeof(uint32_t),
+ SMC_RAM_END);
+ }
+
+ result = iceland_update_and_upload_mc_reg_table(hwmgr);
+
+ PP_ASSERT_WITH_CODE((0 == result), "Failed to upload MC reg table!", return result);
+
+ result = iceland_program_mem_timing_parameters(hwmgr);
+ PP_ASSERT_WITH_CODE((result == 0),
+ "Failed to program memory timing parameters!",
+ );
+
+ return result;
+}
+
+static uint32_t iceland_get_offsetof(uint32_t type, uint32_t member)
+{
+ switch (type) {
+ case SMU_SoftRegisters:
+ switch (member) {
+ case HandshakeDisables:
+ return offsetof(SMU71_SoftRegisters, HandshakeDisables);
+ case VoltageChangeTimeout:
+ return offsetof(SMU71_SoftRegisters, VoltageChangeTimeout);
+ case AverageGraphicsActivity:
+ return offsetof(SMU71_SoftRegisters, AverageGraphicsActivity);
+ case AverageMemoryActivity:
+ return offsetof(SMU71_SoftRegisters, AverageMemoryActivity);
+ case PreVBlankGap:
+ return offsetof(SMU71_SoftRegisters, PreVBlankGap);
+ case VBlankTimeout:
+ return offsetof(SMU71_SoftRegisters, VBlankTimeout);
+ case UcodeLoadStatus:
+ return offsetof(SMU71_SoftRegisters, UcodeLoadStatus);
+ case DRAM_LOG_ADDR_H:
+ return offsetof(SMU71_SoftRegisters, DRAM_LOG_ADDR_H);
+ case DRAM_LOG_ADDR_L:
+ return offsetof(SMU71_SoftRegisters, DRAM_LOG_ADDR_L);
+ case DRAM_LOG_PHY_ADDR_H:
+ return offsetof(SMU71_SoftRegisters, DRAM_LOG_PHY_ADDR_H);
+ case DRAM_LOG_PHY_ADDR_L:
+ return offsetof(SMU71_SoftRegisters, DRAM_LOG_PHY_ADDR_L);
+ case DRAM_LOG_BUFF_SIZE:
+ return offsetof(SMU71_SoftRegisters, DRAM_LOG_BUFF_SIZE);
+ }
+ break;
+ case SMU_Discrete_DpmTable:
+ switch (member) {
+ case LowSclkInterruptThreshold:
+ return offsetof(SMU71_Discrete_DpmTable, LowSclkInterruptThreshold);
+ }
+ break;
+ }
+ pr_warn("can't get the offset of type %x member %x\n", type, member);
+ return 0;
+}
+
+static uint32_t iceland_get_mac_definition(uint32_t value)
+{
+ switch (value) {
+ case SMU_MAX_LEVELS_GRAPHICS:
+ return SMU71_MAX_LEVELS_GRAPHICS;
+ case SMU_MAX_LEVELS_MEMORY:
+ return SMU71_MAX_LEVELS_MEMORY;
+ case SMU_MAX_LEVELS_LINK:
+ return SMU71_MAX_LEVELS_LINK;
+ case SMU_MAX_ENTRIES_SMIO:
+ return SMU71_MAX_ENTRIES_SMIO;
+ case SMU_MAX_LEVELS_VDDC:
+ return SMU71_MAX_LEVELS_VDDC;
+ case SMU_MAX_LEVELS_VDDCI:
+ return SMU71_MAX_LEVELS_VDDCI;
+ case SMU_MAX_LEVELS_MVDD:
+ return SMU71_MAX_LEVELS_MVDD;
+ }
+
+ pr_warn("can't get the mac of %x\n", value);
+ return 0;
+}
+
+static int iceland_process_firmware_header(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_smumgr *smu7_data = (struct smu7_smumgr *)(hwmgr->smu_backend);
+
+ uint32_t tmp;
+ int result;
+ bool error = false;
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU71_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU71_Firmware_Header, DpmTable),
+ &tmp, SMC_RAM_END);
+
+ if (0 == result) {
+ smu7_data->dpm_table_start = tmp;
+ }
+
+ error |= (0 != result);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU71_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU71_Firmware_Header, SoftRegisters),
+ &tmp, SMC_RAM_END);
+
+ if (0 == result) {
+ data->soft_regs_start = tmp;
+ smu7_data->soft_regs_start = tmp;
+ }
+
+ error |= (0 != result);
+
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU71_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU71_Firmware_Header, mcRegisterTable),
+ &tmp, SMC_RAM_END);
+
+ if (0 == result) {
+ smu7_data->mc_reg_table_start = tmp;
+ }
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU71_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU71_Firmware_Header, FanTable),
+ &tmp, SMC_RAM_END);
+
+ if (0 == result) {
+ smu7_data->fan_table_start = tmp;
+ }
+
+ error |= (0 != result);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU71_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU71_Firmware_Header, mcArbDramTimingTable),
+ &tmp, SMC_RAM_END);
+
+ if (0 == result) {
+ smu7_data->arb_table_start = tmp;
+ }
+
+ error |= (0 != result);
+
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU71_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU71_Firmware_Header, Version),
+ &tmp, SMC_RAM_END);
+
+ if (0 == result) {
+ hwmgr->microcode_version_info.SMC = tmp;
+ }
+
+ error |= (0 != result);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU71_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU71_Firmware_Header, UlvSettings),
+ &tmp, SMC_RAM_END);
+
+ if (0 == result) {
+ smu7_data->ulv_setting_starts = tmp;
+ }
+
+ error |= (0 != result);
+
+ return error ? 1 : 0;
+}
+
+/*---------------------------MC----------------------------*/
+
+static uint8_t iceland_get_memory_modile_index(struct pp_hwmgr *hwmgr)
+{
+ return (uint8_t) (0xFF & (cgs_read_register(hwmgr->device, mmBIOS_SCRATCH_4) >> 16));
+}
+
+static bool iceland_check_s0_mc_reg_index(uint16_t in_reg, uint16_t *out_reg)
+{
+ bool result = true;
+
+ switch (in_reg) {
+ case mmMC_SEQ_RAS_TIMING:
+ *out_reg = mmMC_SEQ_RAS_TIMING_LP;
+ break;
+
+ case mmMC_SEQ_DLL_STBY:
+ *out_reg = mmMC_SEQ_DLL_STBY_LP;
+ break;
+
+ case mmMC_SEQ_G5PDX_CMD0:
+ *out_reg = mmMC_SEQ_G5PDX_CMD0_LP;
+ break;
+
+ case mmMC_SEQ_G5PDX_CMD1:
+ *out_reg = mmMC_SEQ_G5PDX_CMD1_LP;
+ break;
+
+ case mmMC_SEQ_G5PDX_CTRL:
+ *out_reg = mmMC_SEQ_G5PDX_CTRL_LP;
+ break;
+
+ case mmMC_SEQ_CAS_TIMING:
+ *out_reg = mmMC_SEQ_CAS_TIMING_LP;
+ break;
+
+ case mmMC_SEQ_MISC_TIMING:
+ *out_reg = mmMC_SEQ_MISC_TIMING_LP;
+ break;
+
+ case mmMC_SEQ_MISC_TIMING2:
+ *out_reg = mmMC_SEQ_MISC_TIMING2_LP;
+ break;
+
+ case mmMC_SEQ_PMG_DVS_CMD:
+ *out_reg = mmMC_SEQ_PMG_DVS_CMD_LP;
+ break;
+
+ case mmMC_SEQ_PMG_DVS_CTL:
+ *out_reg = mmMC_SEQ_PMG_DVS_CTL_LP;
+ break;
+
+ case mmMC_SEQ_RD_CTL_D0:
+ *out_reg = mmMC_SEQ_RD_CTL_D0_LP;
+ break;
+
+ case mmMC_SEQ_RD_CTL_D1:
+ *out_reg = mmMC_SEQ_RD_CTL_D1_LP;
+ break;
+
+ case mmMC_SEQ_WR_CTL_D0:
+ *out_reg = mmMC_SEQ_WR_CTL_D0_LP;
+ break;
+
+ case mmMC_SEQ_WR_CTL_D1:
+ *out_reg = mmMC_SEQ_WR_CTL_D1_LP;
+ break;
+
+ case mmMC_PMG_CMD_EMRS:
+ *out_reg = mmMC_SEQ_PMG_CMD_EMRS_LP;
+ break;
+
+ case mmMC_PMG_CMD_MRS:
+ *out_reg = mmMC_SEQ_PMG_CMD_MRS_LP;
+ break;
+
+ case mmMC_PMG_CMD_MRS1:
+ *out_reg = mmMC_SEQ_PMG_CMD_MRS1_LP;
+ break;
+
+ case mmMC_SEQ_PMG_TIMING:
+ *out_reg = mmMC_SEQ_PMG_TIMING_LP;
+ break;
+
+ case mmMC_PMG_CMD_MRS2:
+ *out_reg = mmMC_SEQ_PMG_CMD_MRS2_LP;
+ break;
+
+ case mmMC_SEQ_WR_CTL_2:
+ *out_reg = mmMC_SEQ_WR_CTL_2_LP;
+ break;
+
+ default:
+ result = false;
+ break;
+ }
+
+ return result;
+}
+
+static int iceland_set_s0_mc_reg_index(struct iceland_mc_reg_table *table)
+{
+ uint32_t i;
+ uint16_t address;
+
+ for (i = 0; i < table->last; i++) {
+ table->mc_reg_address[i].s0 =
+ iceland_check_s0_mc_reg_index(table->mc_reg_address[i].s1, &address)
+ ? address : table->mc_reg_address[i].s1;
+ }
+ return 0;
+}
+
+static int iceland_copy_vbios_smc_reg_table(const pp_atomctrl_mc_reg_table *table,
+ struct iceland_mc_reg_table *ni_table)
+{
+ uint8_t i, j;
+
+ PP_ASSERT_WITH_CODE((table->last <= SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+ "Invalid VramInfo table.", return -EINVAL);
+ PP_ASSERT_WITH_CODE((table->num_entries <= MAX_AC_TIMING_ENTRIES),
+ "Invalid VramInfo table.", return -EINVAL);
+
+ for (i = 0; i < table->last; i++) {
+ ni_table->mc_reg_address[i].s1 = table->mc_reg_address[i].s1;
+ }
+ ni_table->last = table->last;
+
+ for (i = 0; i < table->num_entries; i++) {
+ ni_table->mc_reg_table_entry[i].mclk_max =
+ table->mc_reg_table_entry[i].mclk_max;
+ for (j = 0; j < table->last; j++) {
+ ni_table->mc_reg_table_entry[i].mc_data[j] =
+ table->mc_reg_table_entry[i].mc_data[j];
+ }
+ }
+
+ ni_table->num_entries = table->num_entries;
+
+ return 0;
+}
+
+static int iceland_set_mc_special_registers(struct pp_hwmgr *hwmgr,
+ struct iceland_mc_reg_table *table)
+{
+ uint8_t i, j, k;
+ uint32_t temp_reg;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ for (i = 0, j = table->last; i < table->last; i++) {
+ PP_ASSERT_WITH_CODE((j < SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+ "Invalid VramInfo table.", return -EINVAL);
+
+ switch (table->mc_reg_address[i].s1) {
+
+ case mmMC_SEQ_MISC1:
+ temp_reg = cgs_read_register(hwmgr->device, mmMC_PMG_CMD_EMRS);
+ table->mc_reg_address[j].s1 = mmMC_PMG_CMD_EMRS;
+ table->mc_reg_address[j].s0 = mmMC_SEQ_PMG_CMD_EMRS_LP;
+ for (k = 0; k < table->num_entries; k++) {
+ table->mc_reg_table_entry[k].mc_data[j] =
+ ((temp_reg & 0xffff0000)) |
+ ((table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16);
+ }
+ j++;
+
+ PP_ASSERT_WITH_CODE((j < SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+ "Invalid VramInfo table.", return -EINVAL);
+ temp_reg = cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS);
+ table->mc_reg_address[j].s1 = mmMC_PMG_CMD_MRS;
+ table->mc_reg_address[j].s0 = mmMC_SEQ_PMG_CMD_MRS_LP;
+ for (k = 0; k < table->num_entries; k++) {
+ table->mc_reg_table_entry[k].mc_data[j] =
+ (temp_reg & 0xffff0000) |
+ (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
+
+ if (!data->is_memory_gddr5) {
+ table->mc_reg_table_entry[k].mc_data[j] |= 0x100;
+ }
+ }
+ j++;
+
+ if (!data->is_memory_gddr5) {
+ PP_ASSERT_WITH_CODE((j < SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+ "Invalid VramInfo table.", return -EINVAL);
+ table->mc_reg_address[j].s1 = mmMC_PMG_AUTO_CMD;
+ table->mc_reg_address[j].s0 = mmMC_PMG_AUTO_CMD;
+ for (k = 0; k < table->num_entries; k++) {
+ table->mc_reg_table_entry[k].mc_data[j] =
+ (table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16;
+ }
+ j++;
+ }
+
+ break;
+
+ case mmMC_SEQ_RESERVE_M:
+ temp_reg = cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS1);
+ table->mc_reg_address[j].s1 = mmMC_PMG_CMD_MRS1;
+ table->mc_reg_address[j].s0 = mmMC_SEQ_PMG_CMD_MRS1_LP;
+ for (k = 0; k < table->num_entries; k++) {
+ table->mc_reg_table_entry[k].mc_data[j] =
+ (temp_reg & 0xffff0000) |
+ (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
+ }
+ j++;
+ break;
+
+ default:
+ break;
+ }
+
+ }
+
+ table->last = j;
+
+ return 0;
+}
+
+static int iceland_set_valid_flag(struct iceland_mc_reg_table *table)
+{
+ uint8_t i, j;
+ for (i = 0; i < table->last; i++) {
+ for (j = 1; j < table->num_entries; j++) {
+ if (table->mc_reg_table_entry[j-1].mc_data[i] !=
+ table->mc_reg_table_entry[j].mc_data[i]) {
+ table->validflag |= (1<<i);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int iceland_initialize_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smu_backend);
+ pp_atomctrl_mc_reg_table *table;
+ struct iceland_mc_reg_table *ni_table = &smu_data->mc_reg_table;
+ uint8_t module_index = iceland_get_memory_modile_index(hwmgr);
+
+ table = kzalloc(sizeof(pp_atomctrl_mc_reg_table), GFP_KERNEL);
+
+ if (NULL == table)
+ return -ENOMEM;
+
+ /* Program additional LP registers that are no longer programmed by VBIOS */
+ cgs_write_register(hwmgr->device, mmMC_SEQ_RAS_TIMING_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_RAS_TIMING));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_CAS_TIMING_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_CAS_TIMING));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_DLL_STBY_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_DLL_STBY));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD0_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD0));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD1_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD1));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_G5PDX_CTRL_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_G5PDX_CTRL));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CMD_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CMD));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CTL_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CTL));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_MISC_TIMING_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_MISC_TIMING));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_MISC_TIMING2_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_MISC_TIMING2));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_EMRS_LP, cgs_read_register(hwmgr->device, mmMC_PMG_CMD_EMRS));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_MRS_LP, cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_MRS1_LP, cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS1));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_D0_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_D0));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_D1_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_D1));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_RD_CTL_D0_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_RD_CTL_D0));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_RD_CTL_D1_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_RD_CTL_D1));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_TIMING_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_TIMING));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_MRS2_LP, cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS2));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_2_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_2));
+
+ result = atomctrl_initialize_mc_reg_table(hwmgr, module_index, table);
+
+ if (0 == result)
+ result = iceland_copy_vbios_smc_reg_table(table, ni_table);
+
+ if (0 == result) {
+ iceland_set_s0_mc_reg_index(ni_table);
+ result = iceland_set_mc_special_registers(hwmgr, ni_table);
+ }
+
+ if (0 == result)
+ iceland_set_valid_flag(ni_table);
+
+ kfree(table);
+
+ return result;
+}
+
+static bool iceland_is_dpm_running(struct pp_hwmgr *hwmgr)
+{
+ return (1 == PHM_READ_INDIRECT_FIELD(hwmgr->device,
+ CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON))
+ ? true : false;
+}
+
+const struct pp_smumgr_func iceland_smu_funcs = {
+ .name = "iceland_smu",
+ .smu_init = &iceland_smu_init,
+ .smu_fini = &smu7_smu_fini,
+ .start_smu = &iceland_start_smu,
+ .check_fw_load_finish = &smu7_check_fw_load_finish,
+ .request_smu_load_fw = &smu7_request_smu_load_fw,
+ .request_smu_load_specific_fw = &iceland_request_smu_load_specific_fw,
+ .send_msg_to_smc = &smu7_send_msg_to_smc,
+ .send_msg_to_smc_with_parameter = &smu7_send_msg_to_smc_with_parameter,
+ .get_argument = smu7_get_argument,
+ .download_pptable_settings = NULL,
+ .upload_pptable_settings = NULL,
+ .get_offsetof = iceland_get_offsetof,
+ .process_firmware_header = iceland_process_firmware_header,
+ .init_smc_table = iceland_init_smc_table,
+ .update_sclk_threshold = iceland_update_sclk_threshold,
+ .thermal_setup_fan_table = iceland_thermal_setup_fan_table,
+ .populate_all_graphic_levels = iceland_populate_all_graphic_levels,
+ .populate_all_memory_levels = iceland_populate_all_memory_levels,
+ .get_mac_definition = iceland_get_mac_definition,
+ .initialize_mc_reg_table = iceland_initialize_mc_reg_table,
+ .is_dpm_running = iceland_is_dpm_running,
+};
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/iceland_smumgr.h b/drivers/gpu/drm/amd/pm/powerplay/smumgr/iceland_smumgr.h
new file mode 100644
index 000000000000..f32c506779c9
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/iceland_smumgr.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Author: Huang Rui <ray.huang@amd.com>
+ *
+ */
+
+#ifndef _ICELAND_SMUMGR_H_
+#define _ICELAND_SMUMGR_H_
+
+
+#include "smu7_smumgr.h"
+#include "pp_endian.h"
+#include "smu71_discrete.h"
+
+struct iceland_pt_defaults {
+ uint8_t svi_load_line_en;
+ uint8_t svi_load_line_vddc;
+ uint8_t tdc_vddc_throttle_release_limit_perc;
+ uint8_t tdc_mawt;
+ uint8_t tdc_waterfall_ctl;
+ uint8_t dte_ambient_temp_base;
+ uint32_t display_cac;
+ uint32_t bapm_temp_gradient;
+ uint16_t bapmti_r[SMU71_DTE_ITERATIONS * SMU71_DTE_SOURCES * SMU71_DTE_SINKS];
+ uint16_t bapmti_rc[SMU71_DTE_ITERATIONS * SMU71_DTE_SOURCES * SMU71_DTE_SINKS];
+};
+
+struct iceland_mc_reg_entry {
+ uint32_t mclk_max;
+ uint32_t mc_data[SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct iceland_mc_reg_table {
+ uint8_t last; /* number of registers*/
+ uint8_t num_entries; /* number of entries in mc_reg_table_entry used*/
+ uint16_t validflag; /* indicate the corresponding register is valid or not. 1: valid, 0: invalid. bit0->address[0], bit1->address[1], etc.*/
+ struct iceland_mc_reg_entry mc_reg_table_entry[MAX_AC_TIMING_ENTRIES];
+ SMU71_Discrete_MCRegisterAddress mc_reg_address[SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct iceland_smumgr {
+ struct smu7_smumgr smu7_data;
+ struct SMU71_Discrete_DpmTable smc_state_table;
+ struct SMU71_Discrete_PmFuses power_tune_table;
+ struct SMU71_Discrete_Ulv ulv_setting;
+ const struct iceland_pt_defaults *power_tune_defaults;
+ SMU71_Discrete_MCRegisters mc_regs;
+ struct iceland_mc_reg_table mc_reg_table;
+};
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/polaris10_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/polaris10_smumgr.c
new file mode 100644
index 000000000000..c3d2e6dcf62a
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/polaris10_smumgr.c
@@ -0,0 +1,2590 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <linux/pci.h>
+
+#include "pp_debug.h"
+#include "smumgr.h"
+#include "smu74.h"
+#include "smu_ucode_xfer_vi.h"
+#include "polaris10_smumgr.h"
+#include "smu74_discrete.h"
+#include "smu/smu_7_1_3_d.h"
+#include "smu/smu_7_1_3_sh_mask.h"
+#include "gmc/gmc_8_1_d.h"
+#include "gmc/gmc_8_1_sh_mask.h"
+#include "oss/oss_3_0_d.h"
+#include "gca/gfx_8_0_d.h"
+#include "bif/bif_5_0_d.h"
+#include "bif/bif_5_0_sh_mask.h"
+#include "ppatomctrl.h"
+#include "cgs_common.h"
+#include "smu7_ppsmc.h"
+#include "smu7_smumgr.h"
+
+#include "smu7_dyn_defaults.h"
+
+#include "smu7_hwmgr.h"
+#include "hardwaremanager.h"
+#include "atombios.h"
+#include "pppcielanes.h"
+
+#include "dce/dce_10_0_d.h"
+#include "dce/dce_10_0_sh_mask.h"
+
+#define POLARIS10_SMC_SIZE 0x20000
+#define POWERTUNE_DEFAULT_SET_MAX 1
+#define VDDC_VDDCI_DELTA 200
+#define MC_CG_ARB_FREQ_F1 0x0b
+
+static const struct polaris10_pt_defaults polaris10_power_tune_data_set_array[POWERTUNE_DEFAULT_SET_MAX] = {
+ /* sviLoadLIneEn, SviLoadLineVddC, TDC_VDDC_ThrottleReleaseLimitPerc, TDC_MAWt,
+ * TdcWaterfallCtl, DTEAmbientTempBase, DisplayCac, BAPM_TEMP_GRADIENT */
+ { 1, 0xF, 0xFD, 0x19, 5, 45, 0, 0xB0000,
+ { 0x79, 0x253, 0x25D, 0xAE, 0x72, 0x80, 0x83, 0x86, 0x6F, 0xC8, 0xC9, 0xC9, 0x2F, 0x4D, 0x61},
+ { 0x17C, 0x172, 0x180, 0x1BC, 0x1B3, 0x1BD, 0x206, 0x200, 0x203, 0x25D, 0x25A, 0x255, 0x2C3, 0x2C5, 0x2B4 } },
+};
+
+static const sclkFcwRange_t Range_Table[NUM_SCLK_RANGE] = {
+ {VCO_2_4, POSTDIV_DIV_BY_16, 75, 160, 112},
+ {VCO_3_6, POSTDIV_DIV_BY_16, 112, 224, 160},
+ {VCO_2_4, POSTDIV_DIV_BY_8, 75, 160, 112},
+ {VCO_3_6, POSTDIV_DIV_BY_8, 112, 224, 160},
+ {VCO_2_4, POSTDIV_DIV_BY_4, 75, 160, 112},
+ {VCO_3_6, POSTDIV_DIV_BY_4, 112, 216, 160},
+ {VCO_2_4, POSTDIV_DIV_BY_2, 75, 160, 108},
+ {VCO_3_6, POSTDIV_DIV_BY_2, 112, 216, 160} };
+
+#define PPPOLARIS10_TARGETACTIVITY_DFLT 50
+
+static const SMU74_Discrete_GraphicsLevel avfs_graphics_level_polaris10[8] = {
+ /* Min pcie DeepSleep Activity CgSpll CgSpll CcPwr CcPwr Sclk Enabled Enabled Voltage Power */
+ /* Voltage, DpmLevel, DivId, Level, FuncCntl3, FuncCntl4, DynRm, DynRm1 Did, Padding,ForActivity, ForThrottle, UpHyst, DownHyst, DownHyst, Throttle */
+ { 0x100ea446, 0x00, 0x03, 0x3200, 0, 0, 0, 0, 0, 0, 0x01, 0x01, 0x0a, 0x00, 0x00, 0x00, { 0x30750000, 0x3000, 0, 0x2600, 0, 0, 0x0004, 0x8f02, 0xffff, 0x2f00, 0x300e, 0x2700 } },
+ { 0x400ea446, 0x01, 0x04, 0x3200, 0, 0, 0, 0, 0, 0, 0x01, 0x01, 0x0a, 0x00, 0x00, 0x00, { 0x409c0000, 0x2000, 0, 0x1e00, 1, 1, 0x0004, 0x8300, 0xffff, 0x1f00, 0xcb5e, 0x1a00 } },
+ { 0x740ea446, 0x01, 0x00, 0x3200, 0, 0, 0, 0, 0, 0, 0x01, 0x01, 0x0a, 0x00, 0x00, 0x00, { 0x50c30000, 0x2800, 0, 0x2000, 1, 1, 0x0004, 0x0c02, 0xffff, 0x2700, 0x6433, 0x2100 } },
+ { 0xa40ea446, 0x01, 0x00, 0x3200, 0, 0, 0, 0, 0, 0, 0x01, 0x01, 0x0a, 0x00, 0x00, 0x00, { 0x60ea0000, 0x3000, 0, 0x2600, 1, 1, 0x0004, 0x8f02, 0xffff, 0x2f00, 0x300e, 0x2700 } },
+ { 0xd80ea446, 0x01, 0x00, 0x3200, 0, 0, 0, 0, 0, 0, 0x01, 0x01, 0x0a, 0x00, 0x00, 0x00, { 0x70110100, 0x3800, 0, 0x2c00, 1, 1, 0x0004, 0x1203, 0xffff, 0x3600, 0xc9e2, 0x2e00 } },
+ { 0x3c0fa446, 0x01, 0x00, 0x3200, 0, 0, 0, 0, 0, 0, 0x01, 0x01, 0x0a, 0x00, 0x00, 0x00, { 0x80380100, 0x2000, 0, 0x1e00, 2, 1, 0x0004, 0x8300, 0xffff, 0x1f00, 0xcb5e, 0x1a00 } },
+ { 0x6c0fa446, 0x01, 0x00, 0x3200, 0, 0, 0, 0, 0, 0, 0x01, 0x01, 0x0a, 0x00, 0x00, 0x00, { 0x905f0100, 0x2400, 0, 0x1e00, 2, 1, 0x0004, 0x8901, 0xffff, 0x2300, 0x314c, 0x1d00 } },
+ { 0xa00fa446, 0x01, 0x00, 0x3200, 0, 0, 0, 0, 0, 0, 0x01, 0x01, 0x0a, 0x00, 0x00, 0x00, { 0xa0860100, 0x2800, 0, 0x2000, 2, 1, 0x0004, 0x0c02, 0xffff, 0x2700, 0x6433, 0x2100 } }
+};
+
+static const SMU74_Discrete_MemoryLevel avfs_memory_level_polaris10 = {
+ 0x100ea446, 0, 0x30750000, 0x01, 0x01, 0x01, 0x00, 0x00, 0x64, 0x00, 0x00, 0x1f00, 0x00, 0x00};
+
+static int polaris10_perform_btc(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+ struct smu7_smumgr *smu_data = (struct smu7_smumgr *)(hwmgr->smu_backend);
+
+ if (0 != smu_data->avfs_btc_param) {
+ if (0 != smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_PerformBtc, smu_data->avfs_btc_param,
+ NULL)) {
+ pr_info("[AVFS][SmuPolaris10_PerformBtc] PerformBTC SMU msg failed");
+ result = -1;
+ }
+ }
+ if (smu_data->avfs_btc_param > 1) {
+ /* Soft-Reset to reset the engine before loading uCode */
+ /* halt */
+ cgs_write_register(hwmgr->device, mmCP_MEC_CNTL, 0x50000000);
+ /* reset everything */
+ cgs_write_register(hwmgr->device, mmGRBM_SOFT_RESET, 0xffffffff);
+ cgs_write_register(hwmgr->device, mmGRBM_SOFT_RESET, 0);
+ }
+ return result;
+}
+
+
+static int polaris10_setup_graphics_level_structure(struct pp_hwmgr *hwmgr)
+{
+ uint32_t vr_config;
+ uint32_t dpm_table_start;
+
+ uint16_t u16_boot_mvdd;
+ uint32_t graphics_level_address, vr_config_address, graphics_level_size;
+
+ graphics_level_size = sizeof(avfs_graphics_level_polaris10);
+ u16_boot_mvdd = PP_HOST_TO_SMC_US(1300 * VOLTAGE_SCALE);
+
+ PP_ASSERT_WITH_CODE(0 == smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION + offsetof(SMU74_Firmware_Header, DpmTable),
+ &dpm_table_start, 0x40000),
+ "[AVFS][Polaris10_SetupGfxLvlStruct] SMU could not communicate starting address of DPM table",
+ return -1);
+
+ /* Default value for VRConfig = VR_MERGED_WITH_VDDC + VR_STATIC_VOLTAGE(VDDCI) */
+ vr_config = 0x01000500; /* Real value:0x50001 */
+
+ vr_config_address = dpm_table_start + offsetof(SMU74_Discrete_DpmTable, VRConfig);
+
+ PP_ASSERT_WITH_CODE(0 == smu7_copy_bytes_to_smc(hwmgr, vr_config_address,
+ (uint8_t *)&vr_config, sizeof(uint32_t), 0x40000),
+ "[AVFS][Polaris10_SetupGfxLvlStruct] Problems copying VRConfig value over to SMC",
+ return -1);
+
+ graphics_level_address = dpm_table_start + offsetof(SMU74_Discrete_DpmTable, GraphicsLevel);
+
+ PP_ASSERT_WITH_CODE(0 == smu7_copy_bytes_to_smc(hwmgr, graphics_level_address,
+ (uint8_t *)(&avfs_graphics_level_polaris10),
+ graphics_level_size, 0x40000),
+ "[AVFS][Polaris10_SetupGfxLvlStruct] Copying of SCLK DPM table failed!",
+ return -1);
+
+ graphics_level_address = dpm_table_start + offsetof(SMU74_Discrete_DpmTable, MemoryLevel);
+
+ PP_ASSERT_WITH_CODE(0 == smu7_copy_bytes_to_smc(hwmgr, graphics_level_address,
+ (uint8_t *)(&avfs_memory_level_polaris10), sizeof(avfs_memory_level_polaris10), 0x40000),
+ "[AVFS][Polaris10_SetupGfxLvlStruct] Copying of MCLK DPM table failed!",
+ return -1);
+
+ /* MVDD Boot value - neccessary for getting rid of the hang that occurs during Mclk DPM enablement */
+
+ graphics_level_address = dpm_table_start + offsetof(SMU74_Discrete_DpmTable, BootMVdd);
+
+ PP_ASSERT_WITH_CODE(0 == smu7_copy_bytes_to_smc(hwmgr, graphics_level_address,
+ (uint8_t *)(&u16_boot_mvdd), sizeof(u16_boot_mvdd), 0x40000),
+ "[AVFS][Polaris10_SetupGfxLvlStruct] Copying of DPM table failed!",
+ return -1);
+
+ return 0;
+}
+
+
+static int polaris10_avfs_event_mgr(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_smumgr *smu_data = (struct smu7_smumgr *)(hwmgr->smu_backend);
+
+ if (!hwmgr->avfs_supported)
+ return 0;
+
+ PP_ASSERT_WITH_CODE(0 == polaris10_setup_graphics_level_structure(hwmgr),
+ "[AVFS][Polaris10_AVFSEventMgr] Could not Copy Graphics Level table over to SMU",
+ return -EINVAL);
+
+ if (smu_data->avfs_btc_param > 1) {
+ pr_info("[AVFS][Polaris10_AVFSEventMgr] AC BTC has not been successfully verified on Fiji. There may be in this setting.");
+ PP_ASSERT_WITH_CODE(0 == smu7_setup_pwr_virus(hwmgr),
+ "[AVFS][Polaris10_AVFSEventMgr] Could not setup Pwr Virus for AVFS ",
+ return -EINVAL);
+ }
+
+ PP_ASSERT_WITH_CODE(0 == polaris10_perform_btc(hwmgr),
+ "[AVFS][Polaris10_AVFSEventMgr] Failure at SmuPolaris10_PerformBTC. AVFS Disabled",
+ return -EINVAL);
+
+ return 0;
+}
+
+static int polaris10_start_smu_in_protection_mode(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+
+ /* Wait for smc boot up */
+ /* PHM_WAIT_VFPF_INDIRECT_FIELD_UNEQUAL(smumgr, SMC_IND, RCU_UC_EVENTS, boot_seq_done, 0) */
+
+ /* Assert reset */
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 1);
+
+ result = smu7_upload_smu_firmware_image(hwmgr);
+ if (result != 0)
+ return result;
+
+ /* Clear status */
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixSMU_STATUS, 0);
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
+
+ /* De-assert reset */
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 0);
+
+
+ PHM_WAIT_VFPF_INDIRECT_FIELD(hwmgr, SMC_IND, RCU_UC_EVENTS, INTERRUPTS_ENABLED, 1);
+
+
+ /* Call Test SMU message with 0x20000 offset to trigger SMU start */
+ smu7_send_msg_to_smc_offset(hwmgr);
+
+ /* Wait done bit to be set */
+ /* Check pass/failed indicator */
+
+ PHM_WAIT_VFPF_INDIRECT_FIELD_UNEQUAL(hwmgr, SMC_IND, SMU_STATUS, SMU_DONE, 0);
+
+ if (1 != PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMU_STATUS, SMU_PASS))
+ PP_ASSERT_WITH_CODE(false, "SMU Firmware start failed!", return -1);
+
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixFIRMWARE_FLAGS, 0);
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 1);
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 0);
+
+ /* Wait for firmware to initialize */
+ PHM_WAIT_VFPF_INDIRECT_FIELD(hwmgr, SMC_IND, FIRMWARE_FLAGS, INTERRUPTS_ENABLED, 1);
+
+ return result;
+}
+
+static int polaris10_start_smu_in_non_protection_mode(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+
+ /* wait for smc boot up */
+ PHM_WAIT_VFPF_INDIRECT_FIELD_UNEQUAL(hwmgr, SMC_IND, RCU_UC_EVENTS, boot_seq_done, 0);
+
+ /* Clear firmware interrupt enable flag */
+ /* PHM_WRITE_VFPF_INDIRECT_FIELD(pSmuMgr, SMC_IND, SMC_SYSCON_MISC_CNTL, pre_fetcher_en, 1); */
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixFIRMWARE_FLAGS, 0);
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL,
+ rst_reg, 1);
+
+ result = smu7_upload_smu_firmware_image(hwmgr);
+ if (result != 0)
+ return result;
+
+ /* Set smc instruct start point at 0x0 */
+ smu7_program_jump_on_start(hwmgr);
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 0);
+
+ /* Wait for firmware to initialize */
+
+ PHM_WAIT_VFPF_INDIRECT_FIELD(hwmgr, SMC_IND,
+ FIRMWARE_FLAGS, INTERRUPTS_ENABLED, 1);
+
+ return result;
+}
+
+static int polaris10_start_smu(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+
+ /* Only start SMC if SMC RAM is not running */
+ if (!smu7_is_smc_ram_running(hwmgr) && hwmgr->not_vf) {
+ smu_data->protected_mode = (uint8_t) (PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SMU_FIRMWARE, SMU_MODE));
+ smu_data->smu7_data.security_hard_key = (uint8_t) (PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SMU_FIRMWARE, SMU_SEL));
+
+ /* Check if SMU is running in protected mode */
+ if (smu_data->protected_mode == 0)
+ result = polaris10_start_smu_in_non_protection_mode(hwmgr);
+ else
+ result = polaris10_start_smu_in_protection_mode(hwmgr);
+
+ if (result != 0)
+ PP_ASSERT_WITH_CODE(0, "Failed to load SMU ucode.", return result);
+
+ polaris10_avfs_event_mgr(hwmgr);
+ }
+
+ /* Setup SoftRegsStart here for register lookup in case DummyBackEnd is used and ProcessFirmwareHeader is not executed */
+ smu7_read_smc_sram_dword(hwmgr, SMU7_FIRMWARE_HEADER_LOCATION + offsetof(SMU74_Firmware_Header, SoftRegisters),
+ &(smu_data->smu7_data.soft_regs_start), 0x40000);
+
+ result = smu7_request_smu_load_fw(hwmgr);
+
+ return result;
+}
+
+static bool polaris10_is_hw_avfs_present(struct pp_hwmgr *hwmgr)
+{
+ uint32_t efuse;
+
+ efuse = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixSMU_EFUSE_0 + (49*4));
+ efuse &= 0x00000001;
+ if (efuse)
+ return true;
+
+ return false;
+}
+
+static int polaris10_smu_init(struct pp_hwmgr *hwmgr)
+{
+ struct polaris10_smumgr *smu_data;
+
+ smu_data = kzalloc(sizeof(struct polaris10_smumgr), GFP_KERNEL);
+ if (smu_data == NULL)
+ return -ENOMEM;
+
+ hwmgr->smu_backend = smu_data;
+
+ if (smu7_init(hwmgr)) {
+ kfree(smu_data);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int polaris10_get_dependency_volt_by_clk(struct pp_hwmgr *hwmgr,
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_table,
+ uint32_t clock, SMU_VoltageLevel *voltage, uint32_t *mvdd)
+{
+ uint32_t i;
+ uint16_t vddci;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ *voltage = *mvdd = 0;
+
+ /* clock - voltage dependency table is empty table */
+ if (dep_table->count == 0)
+ return -EINVAL;
+
+ for (i = 0; i < dep_table->count; i++) {
+ /* find first sclk bigger than request */
+ if (dep_table->entries[i].clk >= clock) {
+ *voltage |= (dep_table->entries[i].vddc *
+ VOLTAGE_SCALE) << VDDC_SHIFT;
+ if (SMU7_VOLTAGE_CONTROL_NONE == data->vddci_control)
+ *voltage |= (data->vbios_boot_state.vddci_bootup_value *
+ VOLTAGE_SCALE) << VDDCI_SHIFT;
+ else if (dep_table->entries[i].vddci)
+ *voltage |= (dep_table->entries[i].vddci *
+ VOLTAGE_SCALE) << VDDCI_SHIFT;
+ else {
+ vddci = phm_find_closest_vddci(&(data->vddci_voltage_table),
+ (dep_table->entries[i].vddc -
+ (uint16_t)VDDC_VDDCI_DELTA));
+ *voltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
+ }
+
+ if (SMU7_VOLTAGE_CONTROL_NONE == data->mvdd_control)
+ *mvdd = data->vbios_boot_state.mvdd_bootup_value *
+ VOLTAGE_SCALE;
+ else if (dep_table->entries[i].mvdd)
+ *mvdd = (uint32_t) dep_table->entries[i].mvdd *
+ VOLTAGE_SCALE;
+
+ *voltage |= 1 << PHASES_SHIFT;
+ return 0;
+ }
+ }
+
+ /* sclk is bigger than max sclk in the dependence table */
+ *voltage |= (dep_table->entries[i - 1].vddc * VOLTAGE_SCALE) << VDDC_SHIFT;
+
+ if (SMU7_VOLTAGE_CONTROL_NONE == data->vddci_control)
+ *voltage |= (data->vbios_boot_state.vddci_bootup_value *
+ VOLTAGE_SCALE) << VDDCI_SHIFT;
+ else if (dep_table->entries[i-1].vddci) {
+ vddci = phm_find_closest_vddci(&(data->vddci_voltage_table),
+ (dep_table->entries[i].vddc -
+ (uint16_t)VDDC_VDDCI_DELTA));
+ *voltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
+ }
+
+ if (SMU7_VOLTAGE_CONTROL_NONE == data->mvdd_control)
+ *mvdd = data->vbios_boot_state.mvdd_bootup_value * VOLTAGE_SCALE;
+ else if (dep_table->entries[i].mvdd)
+ *mvdd = (uint32_t) dep_table->entries[i - 1].mvdd * VOLTAGE_SCALE;
+
+ return 0;
+}
+
+static uint16_t scale_fan_gain_settings(uint16_t raw_setting)
+{
+ uint32_t tmp;
+ tmp = raw_setting * 4096 / 100;
+ return (uint16_t)tmp;
+}
+
+static int polaris10_populate_bapm_parameters_in_dpm_table(struct pp_hwmgr *hwmgr)
+{
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+
+ const struct polaris10_pt_defaults *defaults = smu_data->power_tune_defaults;
+ SMU74_Discrete_DpmTable *table = &(smu_data->smc_state_table);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_cac_tdp_table *cac_dtp_table = table_info->cac_dtp_table;
+ struct pp_advance_fan_control_parameters *fan_table =
+ &hwmgr->thermal_controller.advanceFanControlParameters;
+ int i, j, k;
+ const uint16_t *pdef1;
+ const uint16_t *pdef2;
+
+ table->DefaultTdp = PP_HOST_TO_SMC_US((uint16_t)(cac_dtp_table->usTDP * 128));
+ table->TargetTdp = PP_HOST_TO_SMC_US((uint16_t)(cac_dtp_table->usTDP * 128));
+
+ PP_ASSERT_WITH_CODE(cac_dtp_table->usTargetOperatingTemp <= 255,
+ "Target Operating Temp is out of Range!",
+ );
+
+ table->TemperatureLimitEdge = PP_HOST_TO_SMC_US(
+ cac_dtp_table->usTargetOperatingTemp * 256);
+ table->TemperatureLimitHotspot = PP_HOST_TO_SMC_US(
+ cac_dtp_table->usTemperatureLimitHotspot * 256);
+ table->FanGainEdge = PP_HOST_TO_SMC_US(
+ scale_fan_gain_settings(fan_table->usFanGainEdge));
+ table->FanGainHotspot = PP_HOST_TO_SMC_US(
+ scale_fan_gain_settings(fan_table->usFanGainHotspot));
+
+ pdef1 = defaults->BAPMTI_R;
+ pdef2 = defaults->BAPMTI_RC;
+
+ for (i = 0; i < SMU74_DTE_ITERATIONS; i++) {
+ for (j = 0; j < SMU74_DTE_SOURCES; j++) {
+ for (k = 0; k < SMU74_DTE_SINKS; k++) {
+ table->BAPMTI_R[i][j][k] = PP_HOST_TO_SMC_US(*pdef1);
+ table->BAPMTI_RC[i][j][k] = PP_HOST_TO_SMC_US(*pdef2);
+ pdef1++;
+ pdef2++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int polaris10_populate_svi_load_line(struct pp_hwmgr *hwmgr)
+{
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ const struct polaris10_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+ smu_data->power_tune_table.SviLoadLineEn = defaults->SviLoadLineEn;
+ smu_data->power_tune_table.SviLoadLineVddC = defaults->SviLoadLineVddC;
+ smu_data->power_tune_table.SviLoadLineTrimVddC = 3;
+ smu_data->power_tune_table.SviLoadLineOffsetVddC = 0;
+
+ return 0;
+}
+
+static int polaris10_populate_tdc_limit(struct pp_hwmgr *hwmgr)
+{
+ uint16_t tdc_limit;
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ const struct polaris10_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+ tdc_limit = (uint16_t)(table_info->cac_dtp_table->usTDC * 128);
+ smu_data->power_tune_table.TDC_VDDC_PkgLimit =
+ CONVERT_FROM_HOST_TO_SMC_US(tdc_limit);
+ smu_data->power_tune_table.TDC_VDDC_ThrottleReleaseLimitPerc =
+ defaults->TDC_VDDC_ThrottleReleaseLimitPerc;
+ smu_data->power_tune_table.TDC_MAWt = defaults->TDC_MAWt;
+
+ return 0;
+}
+
+static int polaris10_populate_dw8(struct pp_hwmgr *hwmgr, uint32_t fuse_table_offset)
+{
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ const struct polaris10_pt_defaults *defaults = smu_data->power_tune_defaults;
+ uint32_t temp;
+
+ if (smu7_read_smc_sram_dword(hwmgr,
+ fuse_table_offset +
+ offsetof(SMU74_Discrete_PmFuses, TdcWaterfallCtl),
+ (uint32_t *)&temp, SMC_RAM_END))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to read PmFuses.DW6 (SviLoadLineEn) from SMC Failed!",
+ return -EINVAL);
+ else {
+ smu_data->power_tune_table.TdcWaterfallCtl = defaults->TdcWaterfallCtl;
+ smu_data->power_tune_table.LPMLTemperatureMin =
+ (uint8_t)((temp >> 16) & 0xff);
+ smu_data->power_tune_table.LPMLTemperatureMax =
+ (uint8_t)((temp >> 8) & 0xff);
+ smu_data->power_tune_table.Reserved = (uint8_t)(temp & 0xff);
+ }
+ return 0;
+}
+
+static int polaris10_populate_temperature_scaler(struct pp_hwmgr *hwmgr)
+{
+ int i;
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+
+ /* Currently not used. Set all to zero. */
+ for (i = 0; i < 16; i++)
+ smu_data->power_tune_table.LPMLTemperatureScaler[i] = 0;
+
+ return 0;
+}
+
+static int polaris10_populate_fuzzy_fan(struct pp_hwmgr *hwmgr)
+{
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+
+/* TO DO move to hwmgr */
+ if ((hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity & (1 << 15))
+ || 0 == hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity)
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity =
+ hwmgr->thermal_controller.advanceFanControlParameters.usDefaultFanOutputSensitivity;
+
+ smu_data->power_tune_table.FuzzyFan_PwmSetDelta = PP_HOST_TO_SMC_US(
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity);
+ return 0;
+}
+
+static int polaris10_populate_gnb_lpml(struct pp_hwmgr *hwmgr)
+{
+ int i;
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+
+ /* Currently not used. Set all to zero. */
+ for (i = 0; i < 16; i++)
+ smu_data->power_tune_table.GnbLPML[i] = 0;
+
+ return 0;
+}
+
+static int polaris10_populate_bapm_vddc_base_leakage_sidd(struct pp_hwmgr *hwmgr)
+{
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ uint16_t hi_sidd = smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd;
+ uint16_t lo_sidd = smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd;
+ struct phm_cac_tdp_table *cac_table = table_info->cac_dtp_table;
+
+ hi_sidd = (uint16_t)(cac_table->usHighCACLeakage / 100 * 256);
+ lo_sidd = (uint16_t)(cac_table->usLowCACLeakage / 100 * 256);
+
+ smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd =
+ CONVERT_FROM_HOST_TO_SMC_US(hi_sidd);
+ smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd =
+ CONVERT_FROM_HOST_TO_SMC_US(lo_sidd);
+
+ return 0;
+}
+
+static int polaris10_populate_pm_fuses(struct pp_hwmgr *hwmgr)
+{
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ uint32_t pm_fuse_table_offset;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PowerContainment)) {
+ if (smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU74_Firmware_Header, PmFuseTable),
+ &pm_fuse_table_offset, SMC_RAM_END))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to get pm_fuse_table_offset Failed!",
+ return -EINVAL);
+
+ if (polaris10_populate_svi_load_line(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate SviLoadLine Failed!",
+ return -EINVAL);
+
+ if (polaris10_populate_tdc_limit(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate TDCLimit Failed!", return -EINVAL);
+
+ if (polaris10_populate_dw8(hwmgr, pm_fuse_table_offset))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate TdcWaterfallCtl, "
+ "LPMLTemperature Min and Max Failed!",
+ return -EINVAL);
+
+ if (0 != polaris10_populate_temperature_scaler(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate LPMLTemperatureScaler Failed!",
+ return -EINVAL);
+
+ if (polaris10_populate_fuzzy_fan(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate Fuzzy Fan Control parameters Failed!",
+ return -EINVAL);
+
+ if (polaris10_populate_gnb_lpml(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate GnbLPML Failed!",
+ return -EINVAL);
+
+ if (polaris10_populate_bapm_vddc_base_leakage_sidd(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate BapmVddCBaseLeakage Hi and Lo "
+ "Sidd Failed!", return -EINVAL);
+
+ if (smu7_copy_bytes_to_smc(hwmgr, pm_fuse_table_offset,
+ (uint8_t *)&smu_data->power_tune_table,
+ (sizeof(struct SMU74_Discrete_PmFuses) - 92), SMC_RAM_END))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to download PmFuseTable Failed!",
+ return -EINVAL);
+ }
+ return 0;
+}
+
+static int polaris10_populate_smc_mvdd_table(struct pp_hwmgr *hwmgr,
+ SMU74_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t count, level;
+
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
+ count = data->mvdd_voltage_table.count;
+ if (count > SMU_MAX_SMIO_LEVELS)
+ count = SMU_MAX_SMIO_LEVELS;
+ for (level = 0; level < count; level++) {
+ table->SmioTable2.Pattern[level].Voltage =
+ PP_HOST_TO_SMC_US(data->mvdd_voltage_table.entries[level].value * VOLTAGE_SCALE);
+ /* Index into DpmTable.Smio. Drive bits from Smio entry to get this voltage level.*/
+ table->SmioTable2.Pattern[level].Smio =
+ (uint8_t) level;
+ table->Smio[level] |=
+ data->mvdd_voltage_table.entries[level].smio_low;
+ }
+ table->SmioMask2 = data->mvdd_voltage_table.mask_low;
+
+ table->MvddLevelCount = (uint32_t) PP_HOST_TO_SMC_UL(count);
+ }
+
+ return 0;
+}
+
+static int polaris10_populate_smc_vddci_table(struct pp_hwmgr *hwmgr,
+ struct SMU74_Discrete_DpmTable *table)
+{
+ uint32_t count, level;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ count = data->vddci_voltage_table.count;
+
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
+ if (count > SMU_MAX_SMIO_LEVELS)
+ count = SMU_MAX_SMIO_LEVELS;
+ for (level = 0; level < count; ++level) {
+ table->SmioTable1.Pattern[level].Voltage =
+ PP_HOST_TO_SMC_US(data->vddci_voltage_table.entries[level].value * VOLTAGE_SCALE);
+ table->SmioTable1.Pattern[level].Smio = (uint8_t) level;
+
+ table->Smio[level] |= data->vddci_voltage_table.entries[level].smio_low;
+ }
+ }
+
+ table->SmioMask1 = data->vddci_voltage_table.mask_low;
+
+ return 0;
+}
+
+static int polaris10_populate_cac_table(struct pp_hwmgr *hwmgr,
+ struct SMU74_Discrete_DpmTable *table)
+{
+ uint32_t count;
+ uint8_t index;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_voltage_lookup_table *lookup_table =
+ table_info->vddc_lookup_table;
+ /* tables is already swapped, so in order to use the value from it,
+ * we need to swap it back.
+ * We are populating vddc CAC data to BapmVddc table
+ * in split and merged mode
+ */
+ for (count = 0; count < lookup_table->count; count++) {
+ index = phm_get_voltage_index(lookup_table,
+ data->vddc_voltage_table.entries[count].value);
+ table->BapmVddcVidLoSidd[count] = convert_to_vid(lookup_table->entries[index].us_cac_low);
+ table->BapmVddcVidHiSidd[count] = convert_to_vid(lookup_table->entries[index].us_cac_mid);
+ table->BapmVddcVidHiSidd2[count] = convert_to_vid(lookup_table->entries[index].us_cac_high);
+ }
+
+ return 0;
+}
+
+static int polaris10_populate_smc_voltage_tables(struct pp_hwmgr *hwmgr,
+ struct SMU74_Discrete_DpmTable *table)
+{
+ polaris10_populate_smc_vddci_table(hwmgr, table);
+ polaris10_populate_smc_mvdd_table(hwmgr, table);
+ polaris10_populate_cac_table(hwmgr, table);
+
+ return 0;
+}
+
+static int polaris10_populate_ulv_level(struct pp_hwmgr *hwmgr,
+ struct SMU74_Discrete_Ulv *state)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ state->CcPwrDynRm = 0;
+ state->CcPwrDynRm1 = 0;
+
+ state->VddcOffset = (uint16_t) table_info->us_ulv_voltage_offset;
+ state->VddcOffsetVid = (uint8_t)(table_info->us_ulv_voltage_offset *
+ VOLTAGE_VID_OFFSET_SCALE2 / VOLTAGE_VID_OFFSET_SCALE1);
+
+ if (hwmgr->chip_id == CHIP_POLARIS12 || hwmgr->is_kicker)
+ state->VddcPhase = data->vddc_phase_shed_control ^ 0x3;
+ else
+ state->VddcPhase = (data->vddc_phase_shed_control) ? 0 : 1;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm);
+ CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm1);
+ CONVERT_FROM_HOST_TO_SMC_US(state->VddcOffset);
+
+ return 0;
+}
+
+static int polaris10_populate_ulv_state(struct pp_hwmgr *hwmgr,
+ struct SMU74_Discrete_DpmTable *table)
+{
+ return polaris10_populate_ulv_level(hwmgr, &table->Ulv);
+}
+
+static int polaris10_populate_smc_link_level(struct pp_hwmgr *hwmgr,
+ struct SMU74_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ struct smu7_dpm_table *dpm_table = &data->dpm_table;
+ int i;
+
+ /* Index (dpm_table->pcie_speed_table.count)
+ * is reserved for PCIE boot level. */
+ for (i = 0; i <= dpm_table->pcie_speed_table.count; i++) {
+ table->LinkLevel[i].PcieGenSpeed =
+ (uint8_t)dpm_table->pcie_speed_table.dpm_levels[i].value;
+ table->LinkLevel[i].PcieLaneCount = (uint8_t)encode_pcie_lane_width(
+ dpm_table->pcie_speed_table.dpm_levels[i].param1);
+ table->LinkLevel[i].EnabledForActivity = 1;
+ table->LinkLevel[i].SPC = (uint8_t)(data->pcie_spc_cap & 0xff);
+ table->LinkLevel[i].DownThreshold = PP_HOST_TO_SMC_UL(5);
+ table->LinkLevel[i].UpThreshold = PP_HOST_TO_SMC_UL(30);
+ }
+
+ smu_data->smc_state_table.LinkLevelCount =
+ (uint8_t)dpm_table->pcie_speed_table.count;
+
+/* To Do move to hwmgr */
+ data->dpm_level_enable_mask.pcie_dpm_enable_mask =
+ phm_get_dpm_level_enable_mask_value(&dpm_table->pcie_speed_table);
+
+ return 0;
+}
+
+
+static void polaris10_get_sclk_range_table(struct pp_hwmgr *hwmgr,
+ SMU74_Discrete_DpmTable *table)
+{
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ uint32_t i, ref_clk;
+
+ struct pp_atom_ctrl_sclk_range_table range_table_from_vbios = { { {0} } };
+
+ ref_clk = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev);
+
+ if (0 == atomctrl_get_smc_sclk_range_table(hwmgr, &range_table_from_vbios)) {
+ for (i = 0; i < NUM_SCLK_RANGE; i++) {
+ table->SclkFcwRangeTable[i].vco_setting = range_table_from_vbios.entry[i].ucVco_setting;
+ table->SclkFcwRangeTable[i].postdiv = range_table_from_vbios.entry[i].ucPostdiv;
+ table->SclkFcwRangeTable[i].fcw_pcc = range_table_from_vbios.entry[i].usFcw_pcc;
+
+ table->SclkFcwRangeTable[i].fcw_trans_upper = range_table_from_vbios.entry[i].usFcw_trans_upper;
+ table->SclkFcwRangeTable[i].fcw_trans_lower = range_table_from_vbios.entry[i].usRcw_trans_lower;
+
+ CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_pcc);
+ CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_trans_upper);
+ CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_trans_lower);
+ }
+ return;
+ }
+
+ for (i = 0; i < NUM_SCLK_RANGE; i++) {
+ smu_data->range_table[i].trans_lower_frequency = (ref_clk * Range_Table[i].fcw_trans_lower) >> Range_Table[i].postdiv;
+ smu_data->range_table[i].trans_upper_frequency = (ref_clk * Range_Table[i].fcw_trans_upper) >> Range_Table[i].postdiv;
+
+ table->SclkFcwRangeTable[i].vco_setting = Range_Table[i].vco_setting;
+ table->SclkFcwRangeTable[i].postdiv = Range_Table[i].postdiv;
+ table->SclkFcwRangeTable[i].fcw_pcc = Range_Table[i].fcw_pcc;
+
+ table->SclkFcwRangeTable[i].fcw_trans_upper = Range_Table[i].fcw_trans_upper;
+ table->SclkFcwRangeTable[i].fcw_trans_lower = Range_Table[i].fcw_trans_lower;
+
+ CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_pcc);
+ CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_trans_upper);
+ CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_trans_lower);
+ }
+}
+
+static int polaris10_calculate_sclk_params(struct pp_hwmgr *hwmgr,
+ uint32_t clock, SMU_SclkSetting *sclk_setting)
+{
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ const SMU74_Discrete_DpmTable *table = &(smu_data->smc_state_table);
+ struct pp_atomctrl_clock_dividers_ai dividers;
+ uint32_t ref_clock;
+ uint32_t pcc_target_percent, pcc_target_freq, ss_target_percent, ss_target_freq;
+ uint8_t i;
+ int result;
+ uint64_t temp;
+
+ sclk_setting->SclkFrequency = clock;
+ /* get the engine clock dividers for this clock value */
+ result = atomctrl_get_engine_pll_dividers_ai(hwmgr, clock, &dividers);
+ if (result == 0) {
+ sclk_setting->Fcw_int = dividers.usSclk_fcw_int;
+ sclk_setting->Fcw_frac = dividers.usSclk_fcw_frac;
+ sclk_setting->Pcc_fcw_int = dividers.usPcc_fcw_int;
+ sclk_setting->PllRange = dividers.ucSclkPllRange;
+ sclk_setting->Sclk_slew_rate = 0x400;
+ sclk_setting->Pcc_up_slew_rate = dividers.usPcc_fcw_slew_frac;
+ sclk_setting->Pcc_down_slew_rate = 0xffff;
+ sclk_setting->SSc_En = dividers.ucSscEnable;
+ sclk_setting->Fcw1_int = dividers.usSsc_fcw1_int;
+ sclk_setting->Fcw1_frac = dividers.usSsc_fcw1_frac;
+ sclk_setting->Sclk_ss_slew_rate = dividers.usSsc_fcw_slew_frac;
+ return result;
+ }
+
+ ref_clock = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev);
+
+ for (i = 0; i < NUM_SCLK_RANGE; i++) {
+ if (clock > smu_data->range_table[i].trans_lower_frequency
+ && clock <= smu_data->range_table[i].trans_upper_frequency) {
+ sclk_setting->PllRange = i;
+ break;
+ }
+ }
+
+ sclk_setting->Fcw_int = (uint16_t)((clock << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv) / ref_clock);
+ temp = clock << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv;
+ temp <<= 0x10;
+ do_div(temp, ref_clock);
+ sclk_setting->Fcw_frac = temp & 0xffff;
+
+ pcc_target_percent = 10; /* Hardcode 10% for now. */
+ pcc_target_freq = clock - (clock * pcc_target_percent / 100);
+ sclk_setting->Pcc_fcw_int = (uint16_t)((pcc_target_freq << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv) / ref_clock);
+
+ ss_target_percent = 2; /* Hardcode 2% for now. */
+ sclk_setting->SSc_En = 0;
+ if (ss_target_percent) {
+ sclk_setting->SSc_En = 1;
+ ss_target_freq = clock - (clock * ss_target_percent / 100);
+ sclk_setting->Fcw1_int = (uint16_t)((ss_target_freq << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv) / ref_clock);
+ temp = ss_target_freq << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv;
+ temp <<= 0x10;
+ do_div(temp, ref_clock);
+ sclk_setting->Fcw1_frac = temp & 0xffff;
+ }
+
+ return 0;
+}
+
+static int polaris10_populate_single_graphic_level(struct pp_hwmgr *hwmgr,
+ uint32_t clock, struct SMU74_Discrete_GraphicsLevel *level)
+{
+ int result;
+ /* PP_Clocks minClocks; */
+ uint32_t mvdd;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ SMU_SclkSetting curr_sclk_setting = { 0 };
+ phm_ppt_v1_clock_voltage_dependency_table *vdd_dep_table = NULL;
+
+ result = polaris10_calculate_sclk_params(hwmgr, clock, &curr_sclk_setting);
+
+ if (hwmgr->od_enabled)
+ vdd_dep_table = (phm_ppt_v1_clock_voltage_dependency_table *)&data->odn_dpm_table.vdd_dependency_on_sclk;
+ else
+ vdd_dep_table = table_info->vdd_dep_on_sclk;
+
+ /* populate graphics levels */
+ result = polaris10_get_dependency_volt_by_clk(hwmgr,
+ vdd_dep_table, clock,
+ &level->MinVoltage, &mvdd);
+
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find VDDC voltage value for "
+ "VDDC engine clock dependency table",
+ return result);
+ level->ActivityLevel = data->current_profile_setting.sclk_activity;
+
+ level->CcPwrDynRm = 0;
+ level->CcPwrDynRm1 = 0;
+ level->EnabledForActivity = 0;
+ level->EnabledForThrottle = 1;
+ level->UpHyst = data->current_profile_setting.sclk_up_hyst;
+ level->DownHyst = data->current_profile_setting.sclk_down_hyst;
+ level->VoltageDownHyst = 0;
+ level->PowerThrottle = 0;
+ data->display_timing.min_clock_in_sr = hwmgr->display_config->min_core_set_clock_in_sr;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SclkDeepSleep))
+ level->DeepSleepDivId = smu7_get_sleep_divider_id_from_clock(clock,
+ hwmgr->display_config->min_core_set_clock_in_sr);
+
+ /* Default to slow, highest DPM level will be
+ * set to PPSMC_DISPLAY_WATERMARK_LOW later.
+ */
+ if (data->update_up_hyst)
+ level->UpHyst = (uint8_t)data->up_hyst;
+ if (data->update_down_hyst)
+ level->DownHyst = (uint8_t)data->down_hyst;
+
+ level->SclkSetting = curr_sclk_setting;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(level->MinVoltage);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->CcPwrDynRm);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->CcPwrDynRm1);
+ CONVERT_FROM_HOST_TO_SMC_US(level->ActivityLevel);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->SclkSetting.SclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Fcw_int);
+ CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Fcw_frac);
+ CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Pcc_fcw_int);
+ CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Sclk_slew_rate);
+ CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Pcc_up_slew_rate);
+ CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Pcc_down_slew_rate);
+ CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Fcw1_int);
+ CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Fcw1_frac);
+ CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Sclk_ss_slew_rate);
+ return 0;
+}
+
+static int polaris10_populate_all_graphic_levels(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *hw_data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ struct smu7_dpm_table *dpm_table = &hw_data->dpm_table;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_pcie_table *pcie_table = table_info->pcie_table;
+ uint8_t pcie_entry_cnt = (uint8_t) hw_data->dpm_table.pcie_speed_table.count;
+ int result = 0;
+ uint32_t array = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU74_Discrete_DpmTable, GraphicsLevel);
+ uint32_t array_size = sizeof(struct SMU74_Discrete_GraphicsLevel) *
+ SMU74_MAX_LEVELS_GRAPHICS;
+ struct SMU74_Discrete_GraphicsLevel *levels =
+ smu_data->smc_state_table.GraphicsLevel;
+ uint32_t i, max_entry;
+ uint8_t hightest_pcie_level_enabled = 0,
+ lowest_pcie_level_enabled = 0,
+ mid_pcie_level_enabled = 0,
+ count = 0;
+
+ polaris10_get_sclk_range_table(hwmgr, &(smu_data->smc_state_table));
+
+ for (i = 0; i < dpm_table->sclk_table.count; i++) {
+
+ result = polaris10_populate_single_graphic_level(hwmgr,
+ dpm_table->sclk_table.dpm_levels[i].value,
+ &(smu_data->smc_state_table.GraphicsLevel[i]));
+ if (result)
+ return result;
+
+ /* Making sure only DPM level 0-1 have Deep Sleep Div ID populated. */
+ if (i > 1)
+ levels[i].DeepSleepDivId = 0;
+ }
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SPLLShutdownSupport))
+ smu_data->smc_state_table.GraphicsLevel[0].SclkSetting.SSc_En = 0;
+
+ smu_data->smc_state_table.GraphicsLevel[0].EnabledForActivity = 1;
+ smu_data->smc_state_table.GraphicsDpmLevelCount =
+ (uint8_t)dpm_table->sclk_table.count;
+ hw_data->dpm_level_enable_mask.sclk_dpm_enable_mask =
+ phm_get_dpm_level_enable_mask_value(&dpm_table->sclk_table);
+
+
+ if (pcie_table != NULL) {
+ PP_ASSERT_WITH_CODE((1 <= pcie_entry_cnt),
+ "There must be 1 or more PCIE levels defined in PPTable.",
+ return -EINVAL);
+ max_entry = pcie_entry_cnt - 1;
+ for (i = 0; i < dpm_table->sclk_table.count; i++)
+ levels[i].pcieDpmLevel =
+ (uint8_t) ((i < max_entry) ? i : max_entry);
+ } else {
+ while (hw_data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
+ ((hw_data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+ (1 << (hightest_pcie_level_enabled + 1))) != 0))
+ hightest_pcie_level_enabled++;
+
+ while (hw_data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
+ ((hw_data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+ (1 << lowest_pcie_level_enabled)) == 0))
+ lowest_pcie_level_enabled++;
+
+ while ((count < hightest_pcie_level_enabled) &&
+ ((hw_data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+ (1 << (lowest_pcie_level_enabled + 1 + count))) == 0))
+ count++;
+
+ mid_pcie_level_enabled = (lowest_pcie_level_enabled + 1 + count) <
+ hightest_pcie_level_enabled ?
+ (lowest_pcie_level_enabled + 1 + count) :
+ hightest_pcie_level_enabled;
+
+ /* set pcieDpmLevel to hightest_pcie_level_enabled */
+ for (i = 2; i < dpm_table->sclk_table.count; i++)
+ levels[i].pcieDpmLevel = hightest_pcie_level_enabled;
+
+ /* set pcieDpmLevel to lowest_pcie_level_enabled */
+ levels[0].pcieDpmLevel = lowest_pcie_level_enabled;
+
+ /* set pcieDpmLevel to mid_pcie_level_enabled */
+ levels[1].pcieDpmLevel = mid_pcie_level_enabled;
+ }
+ /* level count will send to smc once at init smc table and never change */
+ result = smu7_copy_bytes_to_smc(hwmgr, array, (uint8_t *)levels,
+ (uint32_t)array_size, SMC_RAM_END);
+
+ return result;
+}
+
+
+static int polaris10_populate_single_memory_level(struct pp_hwmgr *hwmgr,
+ uint32_t clock, struct SMU74_Discrete_MemoryLevel *mem_level)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ int result = 0;
+ uint32_t mclk_stutter_mode_threshold = 40000;
+ phm_ppt_v1_clock_voltage_dependency_table *vdd_dep_table = NULL;
+
+
+ if (hwmgr->od_enabled)
+ vdd_dep_table = (phm_ppt_v1_clock_voltage_dependency_table *)&data->odn_dpm_table.vdd_dependency_on_mclk;
+ else
+ vdd_dep_table = table_info->vdd_dep_on_mclk;
+
+ if (vdd_dep_table) {
+ result = polaris10_get_dependency_volt_by_clk(hwmgr,
+ vdd_dep_table, clock,
+ &mem_level->MinVoltage, &mem_level->MinMvdd);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find MinVddc voltage value from memory "
+ "VDDC voltage dependency table", return result);
+ }
+
+ mem_level->MclkFrequency = clock;
+ mem_level->EnabledForThrottle = 1;
+ mem_level->EnabledForActivity = 0;
+ mem_level->UpHyst = data->current_profile_setting.mclk_up_hyst;
+ mem_level->DownHyst = data->current_profile_setting.mclk_down_hyst;
+ mem_level->VoltageDownHyst = 0;
+ mem_level->ActivityLevel = data->current_profile_setting.mclk_activity;
+ mem_level->StutterEnable = false;
+ mem_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+ data->display_timing.num_existing_displays = hwmgr->display_config->num_display;
+ data->display_timing.vrefresh = hwmgr->display_config->vrefresh;
+
+ if (mclk_stutter_mode_threshold &&
+ (clock <= mclk_stutter_mode_threshold) &&
+ (PHM_READ_FIELD(hwmgr->device, DPG_PIPE_STUTTER_CONTROL,
+ STUTTER_ENABLE) & 0x1))
+ mem_level->StutterEnable = true;
+
+ if (!result) {
+ CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MinMvdd);
+ CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_US(mem_level->ActivityLevel);
+ CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MinVoltage);
+ }
+ return result;
+}
+
+static int polaris10_populate_all_memory_levels(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *hw_data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ struct smu7_dpm_table *dpm_table = &hw_data->dpm_table;
+ int result;
+ /* populate MCLK dpm table to SMU7 */
+ uint32_t array = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU74_Discrete_DpmTable, MemoryLevel);
+ uint32_t array_size = sizeof(SMU74_Discrete_MemoryLevel) *
+ SMU74_MAX_LEVELS_MEMORY;
+ struct SMU74_Discrete_MemoryLevel *levels =
+ smu_data->smc_state_table.MemoryLevel;
+ uint32_t i;
+
+ for (i = 0; i < dpm_table->mclk_table.count; i++) {
+ PP_ASSERT_WITH_CODE((0 != dpm_table->mclk_table.dpm_levels[i].value),
+ "can not populate memory level as memory clock is zero",
+ return -EINVAL);
+ result = polaris10_populate_single_memory_level(hwmgr,
+ dpm_table->mclk_table.dpm_levels[i].value,
+ &levels[i]);
+ if (i == dpm_table->mclk_table.count - 1) {
+ levels[i].DisplayWatermark = PPSMC_DISPLAY_WATERMARK_HIGH;
+ levels[i].EnabledForActivity = 1;
+ }
+ if (result)
+ return result;
+ }
+
+ /* In order to prevent MC activity from stutter mode to push DPM up,
+ * the UVD change complements this by putting the MCLK in
+ * a higher state by default such that we are not affected by
+ * up threshold or and MCLK DPM latency.
+ */
+ levels[0].ActivityLevel = 0x1f;
+ CONVERT_FROM_HOST_TO_SMC_US(levels[0].ActivityLevel);
+
+ smu_data->smc_state_table.MemoryDpmLevelCount =
+ (uint8_t)dpm_table->mclk_table.count;
+ hw_data->dpm_level_enable_mask.mclk_dpm_enable_mask =
+ phm_get_dpm_level_enable_mask_value(&dpm_table->mclk_table);
+
+ /* level count will send to smc once at init smc table and never change */
+ result = smu7_copy_bytes_to_smc(hwmgr, array, (uint8_t *)levels,
+ (uint32_t)array_size, SMC_RAM_END);
+
+ return result;
+}
+
+static int polaris10_populate_mvdd_value(struct pp_hwmgr *hwmgr,
+ uint32_t mclk, SMIO_Pattern *smio_pat)
+{
+ const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ uint32_t i = 0;
+
+ if (SMU7_VOLTAGE_CONTROL_NONE != data->mvdd_control) {
+ /* find mvdd value which clock is more than request */
+ for (i = 0; i < table_info->vdd_dep_on_mclk->count; i++) {
+ if (mclk <= table_info->vdd_dep_on_mclk->entries[i].clk) {
+ smio_pat->Voltage = data->mvdd_voltage_table.entries[i].value;
+ break;
+ }
+ }
+ PP_ASSERT_WITH_CODE(i < table_info->vdd_dep_on_mclk->count,
+ "MVDD Voltage is outside the supported range.",
+ return -EINVAL);
+ } else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int polaris10_populate_smc_acpi_level(struct pp_hwmgr *hwmgr,
+ SMU74_Discrete_DpmTable *table)
+{
+ int result = 0;
+ uint32_t sclk_frequency;
+ const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ SMIO_Pattern vol_level;
+ uint32_t mvdd;
+
+ table->ACPILevel.Flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+ /* Get MinVoltage and Frequency from DPM0,
+ * already converted to SMC_UL */
+ sclk_frequency = data->vbios_boot_state.sclk_bootup_value;
+ result = polaris10_get_dependency_volt_by_clk(hwmgr,
+ table_info->vdd_dep_on_sclk,
+ sclk_frequency,
+ &table->ACPILevel.MinVoltage, &mvdd);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Cannot find ACPI VDDC voltage value "
+ "in Clock Dependency Table",
+ );
+
+ result = polaris10_calculate_sclk_params(hwmgr, sclk_frequency, &(table->ACPILevel.SclkSetting));
+ PP_ASSERT_WITH_CODE(result == 0, "Error retrieving Engine Clock dividers from VBIOS.", return result);
+
+ table->ACPILevel.DeepSleepDivId = 0;
+ table->ACPILevel.CcPwrDynRm = 0;
+ table->ACPILevel.CcPwrDynRm1 = 0;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.Flags);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.MinVoltage);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm1);
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SclkSetting.SclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Fcw_int);
+ CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Fcw_frac);
+ CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Pcc_fcw_int);
+ CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Sclk_slew_rate);
+ CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Pcc_up_slew_rate);
+ CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Pcc_down_slew_rate);
+ CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Fcw1_int);
+ CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Fcw1_frac);
+ CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Sclk_ss_slew_rate);
+
+
+ /* Get MinVoltage and Frequency from DPM0, already converted to SMC_UL */
+ table->MemoryACPILevel.MclkFrequency = data->vbios_boot_state.mclk_bootup_value;
+ result = polaris10_get_dependency_volt_by_clk(hwmgr,
+ table_info->vdd_dep_on_mclk,
+ table->MemoryACPILevel.MclkFrequency,
+ &table->MemoryACPILevel.MinVoltage, &mvdd);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Cannot find ACPI VDDCI voltage value "
+ "in Clock Dependency Table",
+ );
+
+ if (!((SMU7_VOLTAGE_CONTROL_NONE == data->mvdd_control) ||
+ (data->mclk_dpm_key_disabled)))
+ polaris10_populate_mvdd_value(hwmgr,
+ data->dpm_table.mclk_table.dpm_levels[0].value,
+ &vol_level);
+
+ if (0 == polaris10_populate_mvdd_value(hwmgr, 0, &vol_level))
+ table->MemoryACPILevel.MinMvdd = PP_HOST_TO_SMC_UL(vol_level.Voltage);
+ else
+ table->MemoryACPILevel.MinMvdd = 0;
+
+ table->MemoryACPILevel.StutterEnable = false;
+
+ table->MemoryACPILevel.EnabledForThrottle = 0;
+ table->MemoryACPILevel.EnabledForActivity = 0;
+ table->MemoryACPILevel.UpHyst = 0;
+ table->MemoryACPILevel.DownHyst = 100;
+ table->MemoryACPILevel.VoltageDownHyst = 0;
+ table->MemoryACPILevel.ActivityLevel =
+ PP_HOST_TO_SMC_US(data->current_profile_setting.mclk_activity);
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->MemoryACPILevel.MclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->MemoryACPILevel.MinVoltage);
+
+ return result;
+}
+
+static int polaris10_populate_smc_vce_level(struct pp_hwmgr *hwmgr,
+ SMU74_Discrete_DpmTable *table)
+{
+ int result = -EINVAL;
+ uint8_t count;
+ struct pp_atomctrl_clock_dividers_vi dividers;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+ table_info->mm_dep_table;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t vddci;
+
+ table->VceLevelCount = (uint8_t)(mm_table->count);
+ table->VceBootLevel = 0;
+
+ for (count = 0; count < table->VceLevelCount; count++) {
+ table->VceLevel[count].Frequency = mm_table->entries[count].eclk;
+ table->VceLevel[count].MinVoltage = 0;
+ table->VceLevel[count].MinVoltage |=
+ (mm_table->entries[count].vddc * VOLTAGE_SCALE) << VDDC_SHIFT;
+
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control)
+ vddci = (uint32_t)phm_find_closest_vddci(&(data->vddci_voltage_table),
+ mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
+ else if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control)
+ vddci = mm_table->entries[count].vddc - VDDC_VDDCI_DELTA;
+ else
+ vddci = (data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE) << VDDCI_SHIFT;
+
+
+ table->VceLevel[count].MinVoltage |=
+ (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
+ table->VceLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
+
+ /*retrieve divider value for VBIOS */
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+ table->VceLevel[count].Frequency, &dividers);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find divide id for VCE engine clock",
+ return result);
+
+ table->VceLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VceLevel[count].Frequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VceLevel[count].MinVoltage);
+ }
+ return result;
+}
+
+static int polaris10_populate_memory_timing_parameters(struct pp_hwmgr *hwmgr,
+ int32_t eng_clock, int32_t mem_clock,
+ SMU74_Discrete_MCArbDramTimingTableEntry *arb_regs)
+{
+ uint32_t dram_timing;
+ uint32_t dram_timing2;
+ uint32_t burst_time;
+ int result;
+
+ result = atomctrl_set_engine_dram_timings_rv770(hwmgr,
+ eng_clock, mem_clock);
+ PP_ASSERT_WITH_CODE(result == 0,
+ "Error calling VBIOS to set DRAM_TIMING.", return result);
+
+ dram_timing = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING);
+ dram_timing2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2);
+ burst_time = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0);
+
+
+ arb_regs->McArbDramTiming = PP_HOST_TO_SMC_UL(dram_timing);
+ arb_regs->McArbDramTiming2 = PP_HOST_TO_SMC_UL(dram_timing2);
+ arb_regs->McArbBurstTime = (uint8_t)burst_time;
+
+ return 0;
+}
+
+static int polaris10_program_memory_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *hw_data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ struct SMU74_Discrete_MCArbDramTimingTable arb_regs;
+ uint32_t i, j;
+ int result = 0;
+
+ for (i = 0; i < hw_data->dpm_table.sclk_table.count; i++) {
+ for (j = 0; j < hw_data->dpm_table.mclk_table.count; j++) {
+ result = polaris10_populate_memory_timing_parameters(hwmgr,
+ hw_data->dpm_table.sclk_table.dpm_levels[i].value,
+ hw_data->dpm_table.mclk_table.dpm_levels[j].value,
+ &arb_regs.entries[i][j]);
+ if (result == 0)
+ result = atomctrl_set_ac_timing_ai(hwmgr, hw_data->dpm_table.mclk_table.dpm_levels[j].value, j);
+ if (result != 0)
+ return result;
+ }
+ }
+
+ result = smu7_copy_bytes_to_smc(
+ hwmgr,
+ smu_data->smu7_data.arb_table_start,
+ (uint8_t *)&arb_regs,
+ sizeof(SMU74_Discrete_MCArbDramTimingTable),
+ SMC_RAM_END);
+ return result;
+}
+
+static int polaris10_populate_smc_uvd_level(struct pp_hwmgr *hwmgr,
+ struct SMU74_Discrete_DpmTable *table)
+{
+ int result = -EINVAL;
+ uint8_t count;
+ struct pp_atomctrl_clock_dividers_vi dividers;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+ table_info->mm_dep_table;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t vddci;
+
+ table->UvdLevelCount = (uint8_t)(mm_table->count);
+ table->UvdBootLevel = 0;
+
+ for (count = 0; count < table->UvdLevelCount; count++) {
+ table->UvdLevel[count].MinVoltage = 0;
+ table->UvdLevel[count].VclkFrequency = mm_table->entries[count].vclk;
+ table->UvdLevel[count].DclkFrequency = mm_table->entries[count].dclk;
+ table->UvdLevel[count].MinVoltage |= (mm_table->entries[count].vddc *
+ VOLTAGE_SCALE) << VDDC_SHIFT;
+
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control)
+ vddci = (uint32_t)phm_find_closest_vddci(&(data->vddci_voltage_table),
+ mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
+ else if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control)
+ vddci = mm_table->entries[count].vddc - VDDC_VDDCI_DELTA;
+ else
+ vddci = (data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE) << VDDCI_SHIFT;
+
+ table->UvdLevel[count].MinVoltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
+ table->UvdLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
+
+ /* retrieve divider value for VBIOS */
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+ table->UvdLevel[count].VclkFrequency, &dividers);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find divide id for Vclk clock", return result);
+
+ table->UvdLevel[count].VclkDivider = (uint8_t)dividers.pll_post_divider;
+
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+ table->UvdLevel[count].DclkFrequency, &dividers);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find divide id for Dclk clock", return result);
+
+ table->UvdLevel[count].DclkDivider = (uint8_t)dividers.pll_post_divider;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].VclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].DclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].MinVoltage);
+ }
+
+ return result;
+}
+
+static int polaris10_populate_smc_boot_level(struct pp_hwmgr *hwmgr,
+ struct SMU74_Discrete_DpmTable *table)
+{
+ int result = 0;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ table->GraphicsBootLevel = 0;
+ table->MemoryBootLevel = 0;
+
+ /* find boot level from dpm table */
+ result = phm_find_boot_level(&(data->dpm_table.sclk_table),
+ data->vbios_boot_state.sclk_bootup_value,
+ (uint32_t *)&(table->GraphicsBootLevel));
+
+ result = phm_find_boot_level(&(data->dpm_table.mclk_table),
+ data->vbios_boot_state.mclk_bootup_value,
+ (uint32_t *)&(table->MemoryBootLevel));
+
+ table->BootVddc = data->vbios_boot_state.vddc_bootup_value *
+ VOLTAGE_SCALE;
+ table->BootVddci = data->vbios_boot_state.vddci_bootup_value *
+ VOLTAGE_SCALE;
+ table->BootMVdd = data->vbios_boot_state.mvdd_bootup_value *
+ VOLTAGE_SCALE;
+
+ CONVERT_FROM_HOST_TO_SMC_US(table->BootVddc);
+ CONVERT_FROM_HOST_TO_SMC_US(table->BootVddci);
+ CONVERT_FROM_HOST_TO_SMC_US(table->BootMVdd);
+
+ return 0;
+}
+
+static int polaris10_populate_smc_initailial_state(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *hw_data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ uint8_t count, level;
+
+ count = (uint8_t)(table_info->vdd_dep_on_sclk->count);
+
+ for (level = 0; level < count; level++) {
+ if (table_info->vdd_dep_on_sclk->entries[level].clk >=
+ hw_data->vbios_boot_state.sclk_bootup_value) {
+ smu_data->smc_state_table.GraphicsBootLevel = level;
+ break;
+ }
+ }
+
+ count = (uint8_t)(table_info->vdd_dep_on_mclk->count);
+ for (level = 0; level < count; level++) {
+ if (table_info->vdd_dep_on_mclk->entries[level].clk >=
+ hw_data->vbios_boot_state.mclk_bootup_value) {
+ smu_data->smc_state_table.MemoryBootLevel = level;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int polaris10_populate_clock_stretcher_data_table(struct pp_hwmgr *hwmgr)
+{
+ uint32_t ro, efuse, volt_without_cks, volt_with_cks, value, max, min;
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+
+ uint8_t i, stretch_amount, volt_offset = 0;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
+ table_info->vdd_dep_on_sclk;
+
+ stretch_amount = (uint8_t)table_info->cac_dtp_table->usClockStretchAmount;
+
+ /* Read SMU_Eefuse to read and calculate RO and determine
+ * if the part is SS or FF. if RO >= 1660MHz, part is FF.
+ */
+ efuse = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixSMU_EFUSE_0 + (67 * 4));
+ efuse &= 0xFF000000;
+ efuse = efuse >> 24;
+
+ if (hwmgr->chip_id == CHIP_POLARIS10) {
+ if (hwmgr->is_kicker) {
+ min = 1200;
+ max = 2500;
+ } else {
+ min = 1000;
+ max = 2300;
+ }
+ } else if (hwmgr->chip_id == CHIP_POLARIS11) {
+ if (hwmgr->is_kicker) {
+ min = 900;
+ max = 2100;
+ } else {
+ min = 1100;
+ max = 2100;
+ }
+ } else {
+ min = 1100;
+ max = 2100;
+ }
+
+ ro = efuse * (max - min) / 255 + min;
+
+ /* Populate Sclk_CKS_masterEn0_7 and Sclk_voltageOffset */
+ for (i = 0; i < sclk_table->count; i++) {
+ smu_data->smc_state_table.Sclk_CKS_masterEn0_7 |=
+ sclk_table->entries[i].cks_enable << i;
+ if (hwmgr->chip_id == CHIP_POLARIS10) {
+ volt_without_cks = (uint32_t)((2753594000U + (sclk_table->entries[i].clk/100) * 136418 - (ro - 70) * 1000000) / \
+ (2424180 - (sclk_table->entries[i].clk/100) * 1132925/1000));
+ volt_with_cks = (uint32_t)((2797202000U + sclk_table->entries[i].clk/100 * 3232 - (ro - 65) * 1000000) / \
+ (2522480 - sclk_table->entries[i].clk/100 * 115764/100));
+ } else {
+ volt_without_cks = (uint32_t)((2416794800U + (sclk_table->entries[i].clk/100) * 1476925/10 - (ro - 50) * 1000000) / \
+ (2625416 - (sclk_table->entries[i].clk/100) * (12586807/10000)));
+ volt_with_cks = (uint32_t)((2999656000U - sclk_table->entries[i].clk/100 * 392803 - (ro - 44) * 1000000) / \
+ (3422454 - sclk_table->entries[i].clk/100 * (18886376/10000)));
+ }
+
+ if (volt_without_cks >= volt_with_cks)
+ volt_offset = (uint8_t)(((volt_without_cks - volt_with_cks +
+ sclk_table->entries[i].cks_voffset) * 100 + 624) / 625);
+
+ smu_data->smc_state_table.Sclk_voltageOffset[i] = volt_offset;
+ }
+
+ smu_data->smc_state_table.LdoRefSel = (table_info->cac_dtp_table->ucCKS_LDO_REFSEL != 0) ? table_info->cac_dtp_table->ucCKS_LDO_REFSEL : 6;
+ /* Populate CKS Lookup Table */
+ if (stretch_amount == 0 || stretch_amount > 5) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ClockStretcher);
+ PP_ASSERT_WITH_CODE(false,
+ "Stretch Amount in PPTable not supported",
+ return -EINVAL);
+ }
+
+ value = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixPWR_CKS_CNTL);
+ value &= 0xFFFFFFFE;
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixPWR_CKS_CNTL, value);
+
+ return 0;
+}
+
+static int polaris10_populate_vr_config(struct pp_hwmgr *hwmgr,
+ struct SMU74_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ uint16_t config;
+
+ config = VR_MERGED_WITH_VDDC;
+ table->VRConfig |= (config << VRCONF_VDDGFX_SHIFT);
+
+ /* Set Vddc Voltage Controller */
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
+ config = VR_SVI2_PLANE_1;
+ table->VRConfig |= config;
+ } else {
+ PP_ASSERT_WITH_CODE(false,
+ "VDDC should be on SVI2 control in merged mode!",
+ );
+ }
+ /* Set Vddci Voltage Controller */
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) {
+ config = VR_SVI2_PLANE_2; /* only in merged mode */
+ table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
+ } else if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
+ config = VR_SMIO_PATTERN_1;
+ table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
+ } else {
+ config = VR_STATIC_VOLTAGE;
+ table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
+ }
+ /* Set Mvdd Voltage Controller */
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->mvdd_control) {
+ config = VR_SVI2_PLANE_2;
+ table->VRConfig |= (config << VRCONF_MVDD_SHIFT);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, smu_data->smu7_data.soft_regs_start +
+ offsetof(SMU74_SoftRegisters, AllowMvddSwitch), 0x1);
+ } else {
+ config = VR_STATIC_VOLTAGE;
+ table->VRConfig |= (config << VRCONF_MVDD_SHIFT);
+ }
+
+ return 0;
+}
+
+
+static int polaris10_populate_avfs_parameters(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ SMU74_Discrete_DpmTable *table = &(smu_data->smc_state_table);
+ int result = 0;
+ struct pp_atom_ctrl__avfs_parameters avfs_params = {0};
+ AVFS_meanNsigma_t AVFS_meanNsigma = { {0} };
+ AVFS_Sclk_Offset_t AVFS_SclkOffset = { {0} };
+ uint32_t tmp, i;
+
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)hwmgr->pptable;
+ struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
+ table_info->vdd_dep_on_sclk;
+
+
+ if (!hwmgr->avfs_supported)
+ return 0;
+
+ result = atomctrl_get_avfs_information(hwmgr, &avfs_params);
+
+ if (0 == result) {
+ if (((adev->pdev->device == 0x67ef) &&
+ ((adev->pdev->revision == 0xe0) ||
+ (adev->pdev->revision == 0xe5))) ||
+ ((adev->pdev->device == 0x67ff) &&
+ ((adev->pdev->revision == 0xcf) ||
+ (adev->pdev->revision == 0xef) ||
+ (adev->pdev->revision == 0xff)))) {
+ avfs_params.ucEnableApplyAVFS_CKS_OFF_Voltage = 1;
+ if ((adev->pdev->device == 0x67ef && adev->pdev->revision == 0xe5) ||
+ (adev->pdev->device == 0x67ff && adev->pdev->revision == 0xef)) {
+ if ((avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a0 == 0xEA522DD3) &&
+ (avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a1 == 0x5645A) &&
+ (avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a2 == 0x33F9E) &&
+ (avfs_params.ulAVFSGB_FUSE_TABLE_CKSOFF_m1 == 0xFFFFC5CC) &&
+ (avfs_params.usAVFSGB_FUSE_TABLE_CKSOFF_m2 == 0x1B1A) &&
+ (avfs_params.ulAVFSGB_FUSE_TABLE_CKSOFF_b == 0xFFFFFCED)) {
+ avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a0 = 0xF718F1D4;
+ avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a1 = 0x323FD;
+ avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a2 = 0x1E455;
+ avfs_params.ulAVFSGB_FUSE_TABLE_CKSOFF_m1 = 0;
+ avfs_params.usAVFSGB_FUSE_TABLE_CKSOFF_m2 = 0;
+ avfs_params.ulAVFSGB_FUSE_TABLE_CKSOFF_b = 0x23;
+ }
+ }
+ } else if (hwmgr->chip_id == CHIP_POLARIS12 && !hwmgr->is_kicker) {
+ avfs_params.ucEnableApplyAVFS_CKS_OFF_Voltage = 1;
+ avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a0 = 0xF6B024DD;
+ avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a1 = 0x3005E;
+ avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a2 = 0x18A5F;
+ avfs_params.ulAVFSGB_FUSE_TABLE_CKSOFF_m1 = 0x315;
+ avfs_params.usAVFSGB_FUSE_TABLE_CKSOFF_m2 = 0xFED1;
+ avfs_params.ulAVFSGB_FUSE_TABLE_CKSOFF_b = 0x3B;
+ } else if (((adev->pdev->device == 0x67df) &&
+ ((adev->pdev->revision == 0xe0) ||
+ (adev->pdev->revision == 0xe3) ||
+ (adev->pdev->revision == 0xe4) ||
+ (adev->pdev->revision == 0xe5) ||
+ (adev->pdev->revision == 0xe7) ||
+ (adev->pdev->revision == 0xef))) ||
+ ((adev->pdev->device == 0x6fdf) &&
+ ((adev->pdev->revision == 0xef) ||
+ (adev->pdev->revision == 0xff)))) {
+ avfs_params.ucEnableApplyAVFS_CKS_OFF_Voltage = 1;
+ avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a0 = 0xF843B66B;
+ avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a1 = 0x59CB5;
+ avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a2 = 0xFFFF287F;
+ avfs_params.ulAVFSGB_FUSE_TABLE_CKSOFF_m1 = 0;
+ avfs_params.usAVFSGB_FUSE_TABLE_CKSOFF_m2 = 0xFF23;
+ avfs_params.ulAVFSGB_FUSE_TABLE_CKSOFF_b = 0x58;
+ }
+ }
+
+ if (0 == result) {
+ table->BTCGB_VDROOP_TABLE[0].a0 = PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSON_a0);
+ table->BTCGB_VDROOP_TABLE[0].a1 = PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSON_a1);
+ table->BTCGB_VDROOP_TABLE[0].a2 = PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSON_a2);
+ table->BTCGB_VDROOP_TABLE[1].a0 = PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a0);
+ table->BTCGB_VDROOP_TABLE[1].a1 = PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a1);
+ table->BTCGB_VDROOP_TABLE[1].a2 = PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a2);
+ table->AVFSGB_VDROOP_TABLE[0].m1 = PP_HOST_TO_SMC_UL(avfs_params.ulAVFSGB_FUSE_TABLE_CKSON_m1);
+ table->AVFSGB_VDROOP_TABLE[0].m2 = PP_HOST_TO_SMC_US(avfs_params.usAVFSGB_FUSE_TABLE_CKSON_m2);
+ table->AVFSGB_VDROOP_TABLE[0].b = PP_HOST_TO_SMC_UL(avfs_params.ulAVFSGB_FUSE_TABLE_CKSON_b);
+ table->AVFSGB_VDROOP_TABLE[0].m1_shift = 24;
+ table->AVFSGB_VDROOP_TABLE[0].m2_shift = 12;
+ table->AVFSGB_VDROOP_TABLE[1].m1 = PP_HOST_TO_SMC_UL(avfs_params.ulAVFSGB_FUSE_TABLE_CKSOFF_m1);
+ table->AVFSGB_VDROOP_TABLE[1].m2 = PP_HOST_TO_SMC_US(avfs_params.usAVFSGB_FUSE_TABLE_CKSOFF_m2);
+ table->AVFSGB_VDROOP_TABLE[1].b = PP_HOST_TO_SMC_UL(avfs_params.ulAVFSGB_FUSE_TABLE_CKSOFF_b);
+ table->AVFSGB_VDROOP_TABLE[1].m1_shift = 24;
+ table->AVFSGB_VDROOP_TABLE[1].m2_shift = 12;
+ table->MaxVoltage = PP_HOST_TO_SMC_US(avfs_params.usMaxVoltage_0_25mv);
+ AVFS_meanNsigma.Aconstant[0] = PP_HOST_TO_SMC_UL(avfs_params.ulAVFS_meanNsigma_Acontant0);
+ AVFS_meanNsigma.Aconstant[1] = PP_HOST_TO_SMC_UL(avfs_params.ulAVFS_meanNsigma_Acontant1);
+ AVFS_meanNsigma.Aconstant[2] = PP_HOST_TO_SMC_UL(avfs_params.ulAVFS_meanNsigma_Acontant2);
+ AVFS_meanNsigma.DC_tol_sigma = PP_HOST_TO_SMC_US(avfs_params.usAVFS_meanNsigma_DC_tol_sigma);
+ AVFS_meanNsigma.Platform_mean = PP_HOST_TO_SMC_US(avfs_params.usAVFS_meanNsigma_Platform_mean);
+ AVFS_meanNsigma.PSM_Age_CompFactor = PP_HOST_TO_SMC_US(avfs_params.usPSM_Age_ComFactor);
+ AVFS_meanNsigma.Platform_sigma = PP_HOST_TO_SMC_US(avfs_params.usAVFS_meanNsigma_Platform_sigma);
+
+ for (i = 0; i < NUM_VFT_COLUMNS; i++) {
+ AVFS_meanNsigma.Static_Voltage_Offset[i] = (uint8_t)(sclk_table->entries[i].cks_voffset * 100 / 625);
+ AVFS_SclkOffset.Sclk_Offset[i] = PP_HOST_TO_SMC_US((uint16_t)(sclk_table->entries[i].sclk_offset) / 100);
+ }
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION + offsetof(SMU74_Firmware_Header, AvfsMeanNSigma),
+ &tmp, SMC_RAM_END);
+
+ smu7_copy_bytes_to_smc(hwmgr,
+ tmp,
+ (uint8_t *)&AVFS_meanNsigma,
+ sizeof(AVFS_meanNsigma_t),
+ SMC_RAM_END);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION + offsetof(SMU74_Firmware_Header, AvfsSclkOffsetTable),
+ &tmp, SMC_RAM_END);
+ smu7_copy_bytes_to_smc(hwmgr,
+ tmp,
+ (uint8_t *)&AVFS_SclkOffset,
+ sizeof(AVFS_Sclk_Offset_t),
+ SMC_RAM_END);
+
+ data->avfs_vdroop_override_setting = (avfs_params.ucEnableGB_VDROOP_TABLE_CKSON << BTCGB0_Vdroop_Enable_SHIFT) |
+ (avfs_params.ucEnableGB_VDROOP_TABLE_CKSOFF << BTCGB1_Vdroop_Enable_SHIFT) |
+ (avfs_params.ucEnableGB_FUSE_TABLE_CKSON << AVFSGB0_Vdroop_Enable_SHIFT) |
+ (avfs_params.ucEnableGB_FUSE_TABLE_CKSOFF << AVFSGB1_Vdroop_Enable_SHIFT);
+ data->apply_avfs_cks_off_voltage = (avfs_params.ucEnableApplyAVFS_CKS_OFF_Voltage == 1) ? true : false;
+ }
+ return result;
+}
+
+static int polaris10_init_arb_table_index(struct pp_hwmgr *hwmgr)
+{
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ uint32_t tmp;
+ int result;
+
+ /* This is a read-modify-write on the first byte of the ARB table.
+ * The first byte in the SMU73_Discrete_MCArbDramTimingTable structure
+ * is the field 'current'.
+ * This solution is ugly, but we never write the whole table only
+ * individual fields in it.
+ * In reality this field should not be in that structure
+ * but in a soft register.
+ */
+ result = smu7_read_smc_sram_dword(hwmgr,
+ smu_data->smu7_data.arb_table_start, &tmp, SMC_RAM_END);
+
+ if (result)
+ return result;
+
+ tmp &= 0x00FFFFFF;
+ tmp |= ((uint32_t)MC_CG_ARB_FREQ_F1) << 24;
+
+ return smu7_write_smc_sram_dword(hwmgr,
+ smu_data->smu7_data.arb_table_start, tmp, SMC_RAM_END);
+}
+
+static void polaris10_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr)
+{
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ if (table_info &&
+ table_info->cac_dtp_table->usPowerTuneDataSetID <= POWERTUNE_DEFAULT_SET_MAX &&
+ table_info->cac_dtp_table->usPowerTuneDataSetID)
+ smu_data->power_tune_defaults =
+ &polaris10_power_tune_data_set_array
+ [table_info->cac_dtp_table->usPowerTuneDataSetID - 1];
+ else
+ smu_data->power_tune_defaults = &polaris10_power_tune_data_set_array[0];
+
+}
+
+static int polaris10_init_smc_table(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ struct smu7_hwmgr *hw_data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct SMU74_Discrete_DpmTable *table = &(smu_data->smc_state_table);
+ uint8_t i;
+ struct pp_atomctrl_gpio_pin_assignment gpio_pin;
+ pp_atomctrl_clock_dividers_vi dividers;
+
+ polaris10_initialize_power_tune_defaults(hwmgr);
+
+ if (SMU7_VOLTAGE_CONTROL_NONE != hw_data->voltage_control)
+ polaris10_populate_smc_voltage_tables(hwmgr, table);
+
+ table->SystemFlags = 0;
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition))
+ table->SystemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_StepVddc))
+ table->SystemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+ if (hw_data->is_memory_gddr5)
+ table->SystemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
+
+ if (hw_data->ulv_supported && table_info->us_ulv_voltage_offset) {
+ result = polaris10_populate_ulv_state(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize ULV state!", return result);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixCG_ULV_PARAMETER, SMU7_CGULVPARAMETER_DFLT);
+ }
+
+ result = polaris10_populate_smc_link_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize Link Level!", return result);
+
+ result = polaris10_populate_all_graphic_levels(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize Graphics Level!", return result);
+
+ result = polaris10_populate_all_memory_levels(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize Memory Level!", return result);
+
+ result = polaris10_populate_smc_acpi_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize ACPI Level!", return result);
+
+ result = polaris10_populate_smc_vce_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize VCE Level!", return result);
+
+ /* Since only the initial state is completely set up at this point
+ * (the other states are just copies of the boot state) we only
+ * need to populate the ARB settings for the initial state.
+ */
+ result = polaris10_program_memory_timing_parameters(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to Write ARB settings for the initial state.", return result);
+
+ result = polaris10_populate_smc_uvd_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize UVD Level!", return result);
+
+ result = polaris10_populate_smc_boot_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize Boot Level!", return result);
+
+ result = polaris10_populate_smc_initailial_state(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to initialize Boot State!", return result);
+
+ result = polaris10_populate_bapm_parameters_in_dpm_table(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to populate BAPM Parameters!", return result);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ClockStretcher)) {
+ result = polaris10_populate_clock_stretcher_data_table(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to populate Clock Stretcher Data Table!",
+ return result);
+ }
+
+ result = polaris10_populate_avfs_parameters(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result, "Failed to populate AVFS Parameters!", return result;);
+
+ table->CurrSclkPllRange = 0xff;
+ table->GraphicsVoltageChangeEnable = 1;
+ table->GraphicsThermThrottleEnable = 1;
+ table->GraphicsInterval = 1;
+ table->VoltageInterval = 1;
+ table->ThermalInterval = 1;
+ table->TemperatureLimitHigh =
+ table_info->cac_dtp_table->usTargetOperatingTemp *
+ SMU7_Q88_FORMAT_CONVERSION_UNIT;
+ table->TemperatureLimitLow =
+ (table_info->cac_dtp_table->usTargetOperatingTemp - 1) *
+ SMU7_Q88_FORMAT_CONVERSION_UNIT;
+ table->MemoryVoltageChangeEnable = 1;
+ table->MemoryInterval = 1;
+ table->VoltageResponseTime = 0;
+ table->PhaseResponseTime = 0;
+ table->MemoryThermThrottleEnable = 1;
+ table->PCIeBootLinkLevel = 0;
+ table->PCIeGenInterval = 1;
+ table->VRConfig = 0;
+
+ result = polaris10_populate_vr_config(hwmgr, table);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to populate VRConfig setting!", return result);
+ hw_data->vr_config = table->VRConfig;
+ table->ThermGpio = 17;
+ table->SclkStepSize = 0x4000;
+
+ if (atomctrl_get_pp_assign_pin(hwmgr, VDDC_VRHOT_GPIO_PINID, &gpio_pin)) {
+ table->VRHotGpio = gpio_pin.uc_gpio_pin_bit_shift;
+ } else {
+ table->VRHotGpio = SMU7_UNUSED_GPIO_PIN;
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_RegulatorHot);
+ }
+
+ if (atomctrl_get_pp_assign_pin(hwmgr, PP_AC_DC_SWITCH_GPIO_PINID,
+ &gpio_pin)) {
+ table->AcDcGpio = gpio_pin.uc_gpio_pin_bit_shift;
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition);
+ } else {
+ table->AcDcGpio = SMU7_UNUSED_GPIO_PIN;
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition);
+ }
+
+ /* Thermal Output GPIO */
+ if (atomctrl_get_pp_assign_pin(hwmgr, THERMAL_INT_OUTPUT_GPIO_PINID,
+ &gpio_pin)) {
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ThermalOutGPIO);
+
+ table->ThermOutGpio = gpio_pin.uc_gpio_pin_bit_shift;
+
+ /* For porlarity read GPIOPAD_A with assigned Gpio pin
+ * since VBIOS will program this register to set 'inactive state',
+ * driver can then determine 'active state' from this and
+ * program SMU with correct polarity
+ */
+ table->ThermOutPolarity = (0 == (cgs_read_register(hwmgr->device, mmGPIOPAD_A)
+ & (1 << gpio_pin.uc_gpio_pin_bit_shift))) ? 1:0;
+ table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_ONLY;
+
+ /* if required, combine VRHot/PCC with thermal out GPIO */
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_RegulatorHot)
+ && phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_CombinePCCWithThermalSignal))
+ table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_VRHOT;
+ } else {
+ table->ThermOutGpio = 17;
+ table->ThermOutPolarity = 1;
+ table->ThermOutMode = SMU7_THERM_OUT_MODE_DISABLE;
+ }
+
+ /* Populate BIF_SCLK levels into SMC DPM table */
+ for (i = 0; i <= hw_data->dpm_table.pcie_speed_table.count; i++) {
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr, smu_data->bif_sclk_table[i], &dividers);
+ PP_ASSERT_WITH_CODE((result == 0), "Can not find DFS divide id for Sclk", return result);
+
+ if (i == 0)
+ table->Ulv.BifSclkDfs = PP_HOST_TO_SMC_US((USHORT)(dividers.pll_post_divider));
+ else
+ table->LinkLevel[i-1].BifSclkDfs = PP_HOST_TO_SMC_US((USHORT)(dividers.pll_post_divider));
+ }
+
+ for (i = 0; i < SMU74_MAX_ENTRIES_SMIO; i++)
+ table->Smio[i] = PP_HOST_TO_SMC_UL(table->Smio[i]);
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SystemFlags);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VRConfig);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask1);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask2);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SclkStepSize);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->CurrSclkPllRange);
+ CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitHigh);
+ CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitLow);
+ CONVERT_FROM_HOST_TO_SMC_US(table->VoltageResponseTime);
+ CONVERT_FROM_HOST_TO_SMC_US(table->PhaseResponseTime);
+
+ /* Upload all dpm data to SMC memory.(dpm level, dpm level count etc) */
+ result = smu7_copy_bytes_to_smc(hwmgr,
+ smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU74_Discrete_DpmTable, SystemFlags),
+ (uint8_t *)&(table->SystemFlags),
+ sizeof(SMU74_Discrete_DpmTable) - 3 * sizeof(SMU74_PIDController),
+ SMC_RAM_END);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to upload dpm data to SMC memory!", return result);
+
+ result = polaris10_init_arb_table_index(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to upload arb data to SMC memory!", return result);
+
+ result = polaris10_populate_pm_fuses(hwmgr);
+ PP_ASSERT_WITH_CODE(0 == result,
+ "Failed to populate PM fuses to SMC memory!", return result);
+
+ return 0;
+}
+
+static int polaris10_program_mem_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (data->need_update_smu7_dpm_table &
+ (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_OD_UPDATE_MCLK))
+ return polaris10_program_memory_timing_parameters(hwmgr);
+
+ return 0;
+}
+
+int polaris10_thermal_avfs_enable(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (!hwmgr->avfs_supported)
+ return 0;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetGBDroopSettings, data->avfs_vdroop_override_setting,
+ NULL);
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_EnableAvfs, NULL);
+
+ /* Apply avfs cks-off voltages to avoid the overshoot
+ * when switching to the highest sclk frequency
+ */
+ if (data->apply_avfs_cks_off_voltage)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ApplyAvfsCksOffVoltage, NULL);
+
+ return 0;
+}
+
+static int polaris10_thermal_setup_fan_table(struct pp_hwmgr *hwmgr)
+{
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ SMU74_Discrete_FanTable fan_table = { FDO_MODE_HARDWARE };
+ uint32_t duty100;
+ uint32_t t_diff1, t_diff2, pwm_diff1, pwm_diff2;
+ uint16_t fdo_min, slope1, slope2;
+ uint32_t reference_clock;
+ int res;
+ uint64_t tmp64;
+
+ if (hwmgr->thermal_controller.fanInfo.bNoFan) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
+ if (smu_data->smu7_data.fan_table_start == 0) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
+ duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ CG_FDO_CTRL1, FMAX_DUTY100);
+
+ if (duty100 == 0) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
+ /* use hardware fan control */
+ if (hwmgr->thermal_controller.use_hw_fan_control)
+ return 0;
+
+ tmp64 = hwmgr->thermal_controller.advanceFanControlParameters.
+ usPWMMin * duty100;
+ do_div(tmp64, 10000);
+ fdo_min = (uint16_t)tmp64;
+
+ t_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usTMed -
+ hwmgr->thermal_controller.advanceFanControlParameters.usTMin;
+ t_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usTHigh -
+ hwmgr->thermal_controller.advanceFanControlParameters.usTMed;
+
+ pwm_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed -
+ hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin;
+ pwm_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh -
+ hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed;
+
+ slope1 = (uint16_t)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100);
+ slope2 = (uint16_t)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100);
+
+ fan_table.TempMin = cpu_to_be16((50 + hwmgr->
+ thermal_controller.advanceFanControlParameters.usTMin) / 100);
+ fan_table.TempMed = cpu_to_be16((50 + hwmgr->
+ thermal_controller.advanceFanControlParameters.usTMed) / 100);
+ fan_table.TempMax = cpu_to_be16((50 + hwmgr->
+ thermal_controller.advanceFanControlParameters.usTMax) / 100);
+
+ fan_table.Slope1 = cpu_to_be16(slope1);
+ fan_table.Slope2 = cpu_to_be16(slope2);
+
+ fan_table.FdoMin = cpu_to_be16(fdo_min);
+
+ fan_table.HystDown = cpu_to_be16(hwmgr->
+ thermal_controller.advanceFanControlParameters.ucTHyst);
+
+ fan_table.HystUp = cpu_to_be16(1);
+
+ fan_table.HystSlope = cpu_to_be16(1);
+
+ fan_table.TempRespLim = cpu_to_be16(5);
+
+ reference_clock = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev);
+
+ fan_table.RefreshPeriod = cpu_to_be32((hwmgr->
+ thermal_controller.advanceFanControlParameters.ulCycleDelay *
+ reference_clock) / 1600);
+
+ fan_table.FdoMax = cpu_to_be16((uint16_t)duty100);
+
+ fan_table.TempSrc = (uint8_t)PHM_READ_VFPF_INDIRECT_FIELD(
+ hwmgr->device, CGS_IND_REG__SMC,
+ CG_MULT_THERMAL_CTRL, TEMP_SEL);
+
+ res = smu7_copy_bytes_to_smc(hwmgr, smu_data->smu7_data.fan_table_start,
+ (uint8_t *)&fan_table, (uint32_t)sizeof(fan_table),
+ SMC_RAM_END);
+
+ if (!res && hwmgr->thermal_controller.
+ advanceFanControlParameters.ucMinimumPWMLimit)
+ res = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetFanMinPwm,
+ hwmgr->thermal_controller.
+ advanceFanControlParameters.ucMinimumPWMLimit,
+ NULL);
+
+ if (!res && hwmgr->thermal_controller.
+ advanceFanControlParameters.ulMinFanSCLKAcousticLimit)
+ res = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetFanSclkTarget,
+ hwmgr->thermal_controller.
+ advanceFanControlParameters.ulMinFanSCLKAcousticLimit,
+ NULL);
+
+ if (res)
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+
+ return 0;
+}
+
+static int polaris10_update_uvd_smc_table(struct pp_hwmgr *hwmgr)
+{
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ uint32_t mm_boot_level_offset, mm_boot_level_value;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ smu_data->smc_state_table.UvdBootLevel = 0;
+ if (table_info->mm_dep_table->count > 0)
+ smu_data->smc_state_table.UvdBootLevel =
+ (uint8_t) (table_info->mm_dep_table->count - 1);
+ mm_boot_level_offset = smu_data->smu7_data.dpm_table_start + offsetof(SMU74_Discrete_DpmTable,
+ UvdBootLevel);
+ mm_boot_level_offset /= 4;
+ mm_boot_level_offset *= 4;
+ mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, mm_boot_level_offset);
+ mm_boot_level_value &= 0x00FFFFFF;
+ mm_boot_level_value |= smu_data->smc_state_table.UvdBootLevel << 24;
+ cgs_write_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
+
+ if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UVDDPM) ||
+ phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_StablePState))
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_UVDDPM_SetEnabledMask,
+ (uint32_t)(1 << smu_data->smc_state_table.UvdBootLevel),
+ NULL);
+ return 0;
+}
+
+static int polaris10_update_vce_smc_table(struct pp_hwmgr *hwmgr)
+{
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ uint32_t mm_boot_level_offset, mm_boot_level_value;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_StablePState))
+ smu_data->smc_state_table.VceBootLevel =
+ (uint8_t) (table_info->mm_dep_table->count - 1);
+ else
+ smu_data->smc_state_table.VceBootLevel = 0;
+
+ mm_boot_level_offset = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU74_Discrete_DpmTable, VceBootLevel);
+ mm_boot_level_offset /= 4;
+ mm_boot_level_offset *= 4;
+ mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, mm_boot_level_offset);
+ mm_boot_level_value &= 0xFF00FFFF;
+ mm_boot_level_value |= smu_data->smc_state_table.VceBootLevel << 16;
+ cgs_write_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_StablePState))
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_VCEDPM_SetEnabledMask,
+ (uint32_t)1 << smu_data->smc_state_table.VceBootLevel,
+ NULL);
+ return 0;
+}
+
+static int polaris10_update_bif_smc_table(struct pp_hwmgr *hwmgr)
+{
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_pcie_table *pcie_table = table_info->pcie_table;
+ int max_entry, i;
+
+ max_entry = (SMU74_MAX_LEVELS_LINK < pcie_table->count) ?
+ SMU74_MAX_LEVELS_LINK :
+ pcie_table->count;
+ /* Setup BIF_SCLK levels */
+ for (i = 0; i < max_entry; i++)
+ smu_data->bif_sclk_table[i] = pcie_table->entries[i].pcie_sclk;
+ return 0;
+}
+
+static int polaris10_update_smc_table(struct pp_hwmgr *hwmgr, uint32_t type)
+{
+ switch (type) {
+ case SMU_UVD_TABLE:
+ polaris10_update_uvd_smc_table(hwmgr);
+ break;
+ case SMU_VCE_TABLE:
+ polaris10_update_vce_smc_table(hwmgr);
+ break;
+ case SMU_BIF_TABLE:
+ polaris10_update_bif_smc_table(hwmgr);
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int polaris10_update_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+
+ int result = 0;
+ uint32_t low_sclk_interrupt_threshold = 0;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkThrottleLowNotification)
+ && (data->low_sclk_interrupt_threshold != 0)) {
+ low_sclk_interrupt_threshold =
+ data->low_sclk_interrupt_threshold;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(low_sclk_interrupt_threshold);
+
+ result = smu7_copy_bytes_to_smc(
+ hwmgr,
+ smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU74_Discrete_DpmTable,
+ LowSclkInterruptThreshold),
+ (uint8_t *)&low_sclk_interrupt_threshold,
+ sizeof(uint32_t),
+ SMC_RAM_END);
+ }
+ PP_ASSERT_WITH_CODE((result == 0),
+ "Failed to update SCLK threshold!", return result);
+
+ result = polaris10_program_mem_timing_parameters(hwmgr);
+ PP_ASSERT_WITH_CODE((result == 0),
+ "Failed to program memory timing parameters!",
+ );
+
+ return result;
+}
+
+static uint32_t polaris10_get_offsetof(uint32_t type, uint32_t member)
+{
+ switch (type) {
+ case SMU_SoftRegisters:
+ switch (member) {
+ case HandshakeDisables:
+ return offsetof(SMU74_SoftRegisters, HandshakeDisables);
+ case VoltageChangeTimeout:
+ return offsetof(SMU74_SoftRegisters, VoltageChangeTimeout);
+ case AverageGraphicsActivity:
+ return offsetof(SMU74_SoftRegisters, AverageGraphicsActivity);
+ case AverageMemoryActivity:
+ return offsetof(SMU74_SoftRegisters, AverageMemoryActivity);
+ case PreVBlankGap:
+ return offsetof(SMU74_SoftRegisters, PreVBlankGap);
+ case VBlankTimeout:
+ return offsetof(SMU74_SoftRegisters, VBlankTimeout);
+ case UcodeLoadStatus:
+ return offsetof(SMU74_SoftRegisters, UcodeLoadStatus);
+ case DRAM_LOG_ADDR_H:
+ return offsetof(SMU74_SoftRegisters, DRAM_LOG_ADDR_H);
+ case DRAM_LOG_ADDR_L:
+ return offsetof(SMU74_SoftRegisters, DRAM_LOG_ADDR_L);
+ case DRAM_LOG_PHY_ADDR_H:
+ return offsetof(SMU74_SoftRegisters, DRAM_LOG_PHY_ADDR_H);
+ case DRAM_LOG_PHY_ADDR_L:
+ return offsetof(SMU74_SoftRegisters, DRAM_LOG_PHY_ADDR_L);
+ case DRAM_LOG_BUFF_SIZE:
+ return offsetof(SMU74_SoftRegisters, DRAM_LOG_BUFF_SIZE);
+ }
+ break;
+ case SMU_Discrete_DpmTable:
+ switch (member) {
+ case UvdBootLevel:
+ return offsetof(SMU74_Discrete_DpmTable, UvdBootLevel);
+ case VceBootLevel:
+ return offsetof(SMU74_Discrete_DpmTable, VceBootLevel);
+ case LowSclkInterruptThreshold:
+ return offsetof(SMU74_Discrete_DpmTable, LowSclkInterruptThreshold);
+ }
+ break;
+ }
+ pr_warn("can't get the offset of type %x member %x\n", type, member);
+ return 0;
+}
+
+static uint32_t polaris10_get_mac_definition(uint32_t value)
+{
+ switch (value) {
+ case SMU_MAX_LEVELS_GRAPHICS:
+ return SMU74_MAX_LEVELS_GRAPHICS;
+ case SMU_MAX_LEVELS_MEMORY:
+ return SMU74_MAX_LEVELS_MEMORY;
+ case SMU_MAX_LEVELS_LINK:
+ return SMU74_MAX_LEVELS_LINK;
+ case SMU_MAX_ENTRIES_SMIO:
+ return SMU74_MAX_ENTRIES_SMIO;
+ case SMU_MAX_LEVELS_VDDC:
+ return SMU74_MAX_LEVELS_VDDC;
+ case SMU_MAX_LEVELS_VDDGFX:
+ return SMU74_MAX_LEVELS_VDDGFX;
+ case SMU_MAX_LEVELS_VDDCI:
+ return SMU74_MAX_LEVELS_VDDCI;
+ case SMU_MAX_LEVELS_MVDD:
+ return SMU74_MAX_LEVELS_MVDD;
+ case SMU_UVD_MCLK_HANDSHAKE_DISABLE:
+ return SMU7_UVD_MCLK_HANDSHAKE_DISABLE;
+ }
+
+ pr_warn("can't get the mac of %x\n", value);
+ return 0;
+}
+
+static int polaris10_process_firmware_header(struct pp_hwmgr *hwmgr)
+{
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smu_backend);
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t tmp;
+ int result;
+ bool error = false;
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU74_Firmware_Header, DpmTable),
+ &tmp, SMC_RAM_END);
+
+ if (0 == result)
+ smu_data->smu7_data.dpm_table_start = tmp;
+
+ error |= (0 != result);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU74_Firmware_Header, SoftRegisters),
+ &tmp, SMC_RAM_END);
+
+ if (!result) {
+ data->soft_regs_start = tmp;
+ smu_data->smu7_data.soft_regs_start = tmp;
+ }
+
+ error |= (0 != result);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU74_Firmware_Header, mcRegisterTable),
+ &tmp, SMC_RAM_END);
+
+ if (!result)
+ smu_data->smu7_data.mc_reg_table_start = tmp;
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU74_Firmware_Header, FanTable),
+ &tmp, SMC_RAM_END);
+
+ if (!result)
+ smu_data->smu7_data.fan_table_start = tmp;
+
+ error |= (0 != result);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU74_Firmware_Header, mcArbDramTimingTable),
+ &tmp, SMC_RAM_END);
+
+ if (!result)
+ smu_data->smu7_data.arb_table_start = tmp;
+
+ error |= (0 != result);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU74_Firmware_Header, Version),
+ &tmp, SMC_RAM_END);
+
+ if (!result)
+ hwmgr->microcode_version_info.SMC = tmp;
+
+ error |= (0 != result);
+
+ return error ? -1 : 0;
+}
+
+static bool polaris10_is_dpm_running(struct pp_hwmgr *hwmgr)
+{
+ return (1 == PHM_READ_INDIRECT_FIELD(hwmgr->device,
+ CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON))
+ ? true : false;
+}
+
+static int polaris10_update_dpm_settings(struct pp_hwmgr *hwmgr,
+ void *profile_setting)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)
+ (hwmgr->smu_backend);
+ struct profile_mode_setting *setting;
+ struct SMU74_Discrete_GraphicsLevel *levels =
+ smu_data->smc_state_table.GraphicsLevel;
+ uint32_t array = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU74_Discrete_DpmTable, GraphicsLevel);
+
+ uint32_t mclk_array = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU74_Discrete_DpmTable, MemoryLevel);
+ struct SMU74_Discrete_MemoryLevel *mclk_levels =
+ smu_data->smc_state_table.MemoryLevel;
+ uint32_t i;
+ uint32_t offset, up_hyst_offset, down_hyst_offset, clk_activity_offset, tmp;
+
+ if (profile_setting == NULL)
+ return -EINVAL;
+
+ setting = (struct profile_mode_setting *)profile_setting;
+
+ if (setting->bupdate_sclk) {
+ if (!data->sclk_dpm_key_disabled)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_SCLKDPM_FreezeLevel, NULL);
+ for (i = 0; i < smu_data->smc_state_table.GraphicsDpmLevelCount; i++) {
+ if (levels[i].ActivityLevel !=
+ cpu_to_be16(setting->sclk_activity)) {
+ levels[i].ActivityLevel = cpu_to_be16(setting->sclk_activity);
+
+ clk_activity_offset = array + (sizeof(SMU74_Discrete_GraphicsLevel) * i)
+ + offsetof(SMU74_Discrete_GraphicsLevel, ActivityLevel);
+ offset = clk_activity_offset & ~0x3;
+ tmp = PP_HOST_TO_SMC_UL(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset));
+ tmp = phm_set_field_to_u32(clk_activity_offset, tmp, levels[i].ActivityLevel, sizeof(uint16_t));
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset, PP_HOST_TO_SMC_UL(tmp));
+
+ }
+ if (levels[i].UpHyst != setting->sclk_up_hyst ||
+ levels[i].DownHyst != setting->sclk_down_hyst) {
+ levels[i].UpHyst = setting->sclk_up_hyst;
+ levels[i].DownHyst = setting->sclk_down_hyst;
+ up_hyst_offset = array + (sizeof(SMU74_Discrete_GraphicsLevel) * i)
+ + offsetof(SMU74_Discrete_GraphicsLevel, UpHyst);
+ down_hyst_offset = array + (sizeof(SMU74_Discrete_GraphicsLevel) * i)
+ + offsetof(SMU74_Discrete_GraphicsLevel, DownHyst);
+ offset = up_hyst_offset & ~0x3;
+ tmp = PP_HOST_TO_SMC_UL(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset));
+ tmp = phm_set_field_to_u32(up_hyst_offset, tmp, levels[i].UpHyst, sizeof(uint8_t));
+ tmp = phm_set_field_to_u32(down_hyst_offset, tmp, levels[i].DownHyst, sizeof(uint8_t));
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset, PP_HOST_TO_SMC_UL(tmp));
+ }
+ }
+ if (!data->sclk_dpm_key_disabled)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_SCLKDPM_UnfreezeLevel, NULL);
+ }
+
+ if (setting->bupdate_mclk) {
+ if (!data->mclk_dpm_key_disabled)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_MCLKDPM_FreezeLevel, NULL);
+ for (i = 0; i < smu_data->smc_state_table.MemoryDpmLevelCount; i++) {
+ if (mclk_levels[i].ActivityLevel !=
+ cpu_to_be16(setting->mclk_activity)) {
+ mclk_levels[i].ActivityLevel = cpu_to_be16(setting->mclk_activity);
+
+ clk_activity_offset = mclk_array + (sizeof(SMU74_Discrete_MemoryLevel) * i)
+ + offsetof(SMU74_Discrete_MemoryLevel, ActivityLevel);
+ offset = clk_activity_offset & ~0x3;
+ tmp = PP_HOST_TO_SMC_UL(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset));
+ tmp = phm_set_field_to_u32(clk_activity_offset, tmp, mclk_levels[i].ActivityLevel, sizeof(uint16_t));
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset, PP_HOST_TO_SMC_UL(tmp));
+
+ }
+ if (mclk_levels[i].UpHyst != setting->mclk_up_hyst ||
+ mclk_levels[i].DownHyst != setting->mclk_down_hyst) {
+ mclk_levels[i].UpHyst = setting->mclk_up_hyst;
+ mclk_levels[i].DownHyst = setting->mclk_down_hyst;
+ up_hyst_offset = mclk_array + (sizeof(SMU74_Discrete_MemoryLevel) * i)
+ + offsetof(SMU74_Discrete_MemoryLevel, UpHyst);
+ down_hyst_offset = mclk_array + (sizeof(SMU74_Discrete_MemoryLevel) * i)
+ + offsetof(SMU74_Discrete_MemoryLevel, DownHyst);
+ offset = up_hyst_offset & ~0x3;
+ tmp = PP_HOST_TO_SMC_UL(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset));
+ tmp = phm_set_field_to_u32(up_hyst_offset, tmp, mclk_levels[i].UpHyst, sizeof(uint8_t));
+ tmp = phm_set_field_to_u32(down_hyst_offset, tmp, mclk_levels[i].DownHyst, sizeof(uint8_t));
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset, PP_HOST_TO_SMC_UL(tmp));
+ }
+ }
+ if (!data->mclk_dpm_key_disabled)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_MCLKDPM_UnfreezeLevel, NULL);
+ }
+ return 0;
+}
+
+const struct pp_smumgr_func polaris10_smu_funcs = {
+ .name = "polaris10_smu",
+ .smu_init = polaris10_smu_init,
+ .smu_fini = smu7_smu_fini,
+ .start_smu = polaris10_start_smu,
+ .check_fw_load_finish = smu7_check_fw_load_finish,
+ .request_smu_load_fw = smu7_reload_firmware,
+ .request_smu_load_specific_fw = NULL,
+ .send_msg_to_smc = smu7_send_msg_to_smc,
+ .send_msg_to_smc_with_parameter = smu7_send_msg_to_smc_with_parameter,
+ .get_argument = smu7_get_argument,
+ .download_pptable_settings = NULL,
+ .upload_pptable_settings = NULL,
+ .update_smc_table = polaris10_update_smc_table,
+ .get_offsetof = polaris10_get_offsetof,
+ .process_firmware_header = polaris10_process_firmware_header,
+ .init_smc_table = polaris10_init_smc_table,
+ .update_sclk_threshold = polaris10_update_sclk_threshold,
+ .thermal_avfs_enable = polaris10_thermal_avfs_enable,
+ .thermal_setup_fan_table = polaris10_thermal_setup_fan_table,
+ .populate_all_graphic_levels = polaris10_populate_all_graphic_levels,
+ .populate_all_memory_levels = polaris10_populate_all_memory_levels,
+ .get_mac_definition = polaris10_get_mac_definition,
+ .is_dpm_running = polaris10_is_dpm_running,
+ .is_hw_avfs_present = polaris10_is_hw_avfs_present,
+ .update_dpm_settings = polaris10_update_dpm_settings,
+};
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/polaris10_smumgr.h b/drivers/gpu/drm/amd/pm/powerplay/smumgr/polaris10_smumgr.h
new file mode 100644
index 000000000000..1ec425df9eda
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/polaris10_smumgr.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _POLARIS10_SMUMANAGER_H
+#define _POLARIS10_SMUMANAGER_H
+
+
+#include <pp_endian.h>
+#include "smu74.h"
+#include "smu74_discrete.h"
+#include "smu7_smumgr.h"
+
+#define SMC_RAM_END 0x40000
+
+struct polaris10_pt_defaults {
+ uint8_t SviLoadLineEn;
+ uint8_t SviLoadLineVddC;
+ uint8_t TDC_VDDC_ThrottleReleaseLimitPerc;
+ uint8_t TDC_MAWt;
+ uint8_t TdcWaterfallCtl;
+ uint8_t DTEAmbientTempBase;
+
+ uint32_t DisplayCac;
+ uint32_t BAPM_TEMP_GRADIENT;
+ uint16_t BAPMTI_R[SMU74_DTE_ITERATIONS * SMU74_DTE_SOURCES * SMU74_DTE_SINKS];
+ uint16_t BAPMTI_RC[SMU74_DTE_ITERATIONS * SMU74_DTE_SOURCES * SMU74_DTE_SINKS];
+};
+
+struct polaris10_range_table {
+ uint32_t trans_lower_frequency; /* in 10khz */
+ uint32_t trans_upper_frequency;
+};
+
+struct polaris10_smumgr {
+ struct smu7_smumgr smu7_data;
+ uint8_t protected_mode;
+ SMU74_Discrete_DpmTable smc_state_table;
+ struct SMU74_Discrete_Ulv ulv_setting;
+ struct SMU74_Discrete_PmFuses power_tune_table;
+ struct polaris10_range_table range_table[NUM_SCLK_RANGE];
+ const struct polaris10_pt_defaults *power_tune_defaults;
+ uint32_t bif_sclk_table[SMU74_MAX_LEVELS_LINK];
+};
+
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu10_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu10_smumgr.c
new file mode 100644
index 000000000000..ea2279bb8cbf
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu10_smumgr.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <linux/pci.h>
+
+#include "smumgr.h"
+#include "smu10_inc.h"
+#include "soc15_common.h"
+#include "smu10_smumgr.h"
+#include "ppatomctrl.h"
+#include "rv_ppsmc.h"
+#include "smu10_driver_if.h"
+#include "smu10.h"
+#include "pp_debug.h"
+
+
+#define BUFFER_SIZE 80000
+#define MAX_STRING_SIZE 15
+#define BUFFER_SIZETWO 131072
+
+#define MP0_Public 0x03800000
+#define MP0_SRAM 0x03900000
+#define MP1_Public 0x03b00000
+#define MP1_SRAM 0x03c00004
+
+#define smnMP1_FIRMWARE_FLAGS 0x3010028
+
+
+static uint32_t smu10_wait_for_response(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t reg;
+
+ reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_SMN_C2PMSG_90);
+
+ phm_wait_for_register_unequal(hwmgr, reg,
+ 0, MP1_C2PMSG_90__CONTENT_MASK);
+
+ return RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90);
+}
+
+static int smu10_send_msg_to_smc_without_waiting(struct pp_hwmgr *hwmgr,
+ uint16_t msg)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_66, msg);
+
+ return 0;
+}
+
+static uint32_t smu10_read_arg_from_smc(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ return RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_82);
+}
+
+static int smu10_send_msg_to_smc(struct pp_hwmgr *hwmgr, uint16_t msg)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ smu10_wait_for_response(hwmgr);
+
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90, 0);
+
+ smu10_send_msg_to_smc_without_waiting(hwmgr, msg);
+
+ if (smu10_wait_for_response(hwmgr) == 0)
+ printk("Failed to send Message %x.\n", msg);
+
+ return 0;
+}
+
+
+static int smu10_send_msg_to_smc_with_parameter(struct pp_hwmgr *hwmgr,
+ uint16_t msg, uint32_t parameter)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ smu10_wait_for_response(hwmgr);
+
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90, 0);
+
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_82, parameter);
+
+ smu10_send_msg_to_smc_without_waiting(hwmgr, msg);
+
+
+ if (smu10_wait_for_response(hwmgr) == 0)
+ printk("Failed to send Message %x.\n", msg);
+
+ return 0;
+}
+
+static int smu10_copy_table_from_smc(struct pp_hwmgr *hwmgr,
+ uint8_t *table, int16_t table_id)
+{
+ struct smu10_smumgr *priv =
+ (struct smu10_smumgr *)(hwmgr->smu_backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ PP_ASSERT_WITH_CODE(table_id < MAX_SMU_TABLE,
+ "Invalid SMU Table ID!", return -EINVAL;);
+ PP_ASSERT_WITH_CODE(priv->smu_tables.entry[table_id].version != 0,
+ "Invalid SMU Table version!", return -EINVAL;);
+ PP_ASSERT_WITH_CODE(priv->smu_tables.entry[table_id].size != 0,
+ "Invalid SMU Table Length!", return -EINVAL;);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrHigh,
+ upper_32_bits(priv->smu_tables.entry[table_id].mc_addr),
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrLow,
+ lower_32_bits(priv->smu_tables.entry[table_id].mc_addr),
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_TransferTableSmu2Dram,
+ priv->smu_tables.entry[table_id].table_id,
+ NULL);
+
+ /* flush hdp cache */
+ amdgpu_asic_flush_hdp(adev, NULL);
+
+ memcpy(table, (uint8_t *)priv->smu_tables.entry[table_id].table,
+ priv->smu_tables.entry[table_id].size);
+
+ return 0;
+}
+
+static int smu10_copy_table_to_smc(struct pp_hwmgr *hwmgr,
+ uint8_t *table, int16_t table_id)
+{
+ struct smu10_smumgr *priv =
+ (struct smu10_smumgr *)(hwmgr->smu_backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ PP_ASSERT_WITH_CODE(table_id < MAX_SMU_TABLE,
+ "Invalid SMU Table ID!", return -EINVAL;);
+ PP_ASSERT_WITH_CODE(priv->smu_tables.entry[table_id].version != 0,
+ "Invalid SMU Table version!", return -EINVAL;);
+ PP_ASSERT_WITH_CODE(priv->smu_tables.entry[table_id].size != 0,
+ "Invalid SMU Table Length!", return -EINVAL;);
+
+ memcpy(priv->smu_tables.entry[table_id].table, table,
+ priv->smu_tables.entry[table_id].size);
+
+ amdgpu_asic_flush_hdp(adev, NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrHigh,
+ upper_32_bits(priv->smu_tables.entry[table_id].mc_addr),
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrLow,
+ lower_32_bits(priv->smu_tables.entry[table_id].mc_addr),
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_TransferTableDram2Smu,
+ priv->smu_tables.entry[table_id].table_id,
+ NULL);
+
+ return 0;
+}
+
+static int smu10_verify_smc_interface(struct pp_hwmgr *hwmgr)
+{
+ uint32_t smc_driver_if_version;
+
+ smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_GetDriverIfVersion,
+ &smc_driver_if_version);
+
+ if ((smc_driver_if_version != SMU10_DRIVER_IF_VERSION) &&
+ (smc_driver_if_version != SMU10_DRIVER_IF_VERSION + 1)) {
+ pr_err("Attempt to read SMC IF Version Number Failed!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smu10_smu_fini(struct pp_hwmgr *hwmgr)
+{
+ struct smu10_smumgr *priv =
+ (struct smu10_smumgr *)(hwmgr->smu_backend);
+
+ if (priv) {
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[SMU10_WMTABLE].handle,
+ &priv->smu_tables.entry[SMU10_WMTABLE].mc_addr,
+ &priv->smu_tables.entry[SMU10_WMTABLE].table);
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[SMU10_CLOCKTABLE].handle,
+ &priv->smu_tables.entry[SMU10_CLOCKTABLE].mc_addr,
+ &priv->smu_tables.entry[SMU10_CLOCKTABLE].table);
+ kfree(hwmgr->smu_backend);
+ hwmgr->smu_backend = NULL;
+ }
+
+ return 0;
+}
+
+static int smu10_start_smu(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetSmuVersion, &hwmgr->smu_version);
+ adev->pm.fw_version = hwmgr->smu_version >> 8;
+
+ if (!(adev->apu_flags & AMD_APU_IS_RAVEN2) &&
+ (adev->apu_flags & AMD_APU_IS_RAVEN) &&
+ adev->pm.fw_version < 0x1e45)
+ adev->pm.pp_feature &= ~PP_GFXOFF_MASK;
+
+ if (smu10_verify_smc_interface(hwmgr))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int smu10_smu_init(struct pp_hwmgr *hwmgr)
+{
+ struct smu10_smumgr *priv;
+ int r;
+
+ priv = kzalloc(sizeof(struct smu10_smumgr), GFP_KERNEL);
+
+ if (!priv)
+ return -ENOMEM;
+
+ hwmgr->smu_backend = priv;
+
+ /* allocate space for watermarks table */
+ r = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ sizeof(Watermarks_t),
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[SMU10_WMTABLE].handle,
+ &priv->smu_tables.entry[SMU10_WMTABLE].mc_addr,
+ &priv->smu_tables.entry[SMU10_WMTABLE].table);
+
+ if (r)
+ goto err0;
+
+ priv->smu_tables.entry[SMU10_WMTABLE].version = 0x01;
+ priv->smu_tables.entry[SMU10_WMTABLE].size = sizeof(Watermarks_t);
+ priv->smu_tables.entry[SMU10_WMTABLE].table_id = TABLE_WATERMARKS;
+
+ /* allocate space for watermarks table */
+ r = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ sizeof(DpmClocks_t),
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[SMU10_CLOCKTABLE].handle,
+ &priv->smu_tables.entry[SMU10_CLOCKTABLE].mc_addr,
+ &priv->smu_tables.entry[SMU10_CLOCKTABLE].table);
+
+ if (r)
+ goto err1;
+
+ priv->smu_tables.entry[SMU10_CLOCKTABLE].version = 0x01;
+ priv->smu_tables.entry[SMU10_CLOCKTABLE].size = sizeof(DpmClocks_t);
+ priv->smu_tables.entry[SMU10_CLOCKTABLE].table_id = TABLE_DPMCLOCKS;
+
+ return 0;
+
+err1:
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[SMU10_WMTABLE].handle,
+ &priv->smu_tables.entry[SMU10_WMTABLE].mc_addr,
+ &priv->smu_tables.entry[SMU10_WMTABLE].table);
+err0:
+ kfree(priv);
+ return -EINVAL;
+}
+
+static int smu10_smc_table_manager(struct pp_hwmgr *hwmgr, uint8_t *table, uint16_t table_id, bool rw)
+{
+ int ret;
+
+ if (rw)
+ ret = smu10_copy_table_from_smc(hwmgr, table, table_id);
+ else
+ ret = smu10_copy_table_to_smc(hwmgr, table, table_id);
+
+ return ret;
+}
+
+
+const struct pp_smumgr_func smu10_smu_funcs = {
+ .name = "smu10_smu",
+ .smu_init = &smu10_smu_init,
+ .smu_fini = &smu10_smu_fini,
+ .start_smu = &smu10_start_smu,
+ .request_smu_load_specific_fw = NULL,
+ .send_msg_to_smc = &smu10_send_msg_to_smc,
+ .send_msg_to_smc_with_parameter = &smu10_send_msg_to_smc_with_parameter,
+ .download_pptable_settings = NULL,
+ .upload_pptable_settings = NULL,
+ .get_argument = smu10_read_arg_from_smc,
+ .smc_table_manager = smu10_smc_table_manager,
+};
+
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu10_smumgr.h b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu10_smumgr.h
new file mode 100644
index 000000000000..9c2be74a2b2f
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu10_smumgr.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 PP_SMU10_SMUMANAGER_H
+#define PP_SMU10_SMUMANAGER_H
+
+#include "rv_ppsmc.h"
+#include "smu10_driver_if.h"
+
+#define MAX_SMU_TABLE 2
+
+struct smu_table_entry {
+ uint32_t version;
+ uint32_t size;
+ uint32_t table_id;
+ uint64_t mc_addr;
+ void *table;
+ struct amdgpu_bo *handle;
+};
+
+struct smu_table_array {
+ struct smu_table_entry entry[MAX_SMU_TABLE];
+};
+
+struct smu10_smumgr {
+ struct smu_table_array smu_tables;
+};
+
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu7_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu7_smumgr.c
new file mode 100644
index 000000000000..aae25243eb10
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu7_smumgr.c
@@ -0,0 +1,601 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "pp_debug.h"
+#include "smumgr.h"
+#include "smu_ucode_xfer_vi.h"
+#include "ppatomctrl.h"
+#include "cgs_common.h"
+#include "smu7_ppsmc.h"
+#include "smu7_smumgr.h"
+#include "smu7_common.h"
+
+#include "polaris10_pwrvirus.h"
+
+#define SMU7_SMC_SIZE 0x20000
+
+static int smu7_set_smc_sram_address(struct pp_hwmgr *hwmgr, uint32_t smc_addr, uint32_t limit)
+{
+ PP_ASSERT_WITH_CODE((0 == (3 & smc_addr)), "SMC address must be 4 byte aligned.", return -EINVAL);
+ PP_ASSERT_WITH_CODE((limit > (smc_addr + 3)), "SMC addr is beyond the SMC RAM area.", return -EINVAL);
+
+ cgs_write_register(hwmgr->device, mmSMC_IND_INDEX_11, smc_addr);
+ PHM_WRITE_FIELD(hwmgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_11, 0); /* on ci, SMC_IND_ACCESS_CNTL is different */
+ return 0;
+}
+
+
+int smu7_copy_bytes_from_smc(struct pp_hwmgr *hwmgr, uint32_t smc_start_address, uint32_t *dest, uint32_t byte_count, uint32_t limit)
+{
+ uint32_t data;
+ uint32_t addr;
+ uint8_t *dest_byte;
+ uint8_t i, data_byte[4] = {0};
+ uint32_t *pdata = (uint32_t *)&data_byte;
+
+ PP_ASSERT_WITH_CODE((0 == (3 & smc_start_address)), "SMC address must be 4 byte aligned.", return -EINVAL);
+ PP_ASSERT_WITH_CODE((limit > (smc_start_address + byte_count)), "SMC address is beyond the SMC RAM area.", return -EINVAL);
+
+ addr = smc_start_address;
+
+ while (byte_count >= 4) {
+ smu7_read_smc_sram_dword(hwmgr, addr, &data, limit);
+
+ *dest = PP_SMC_TO_HOST_UL(data);
+
+ dest += 1;
+ byte_count -= 4;
+ addr += 4;
+ }
+
+ if (byte_count) {
+ smu7_read_smc_sram_dword(hwmgr, addr, &data, limit);
+ *pdata = PP_SMC_TO_HOST_UL(data);
+ /* Cast dest into byte type in dest_byte. This way, we don't overflow if the allocated memory is not 4-byte aligned. */
+ dest_byte = (uint8_t *)dest;
+ for (i = 0; i < byte_count; i++)
+ dest_byte[i] = data_byte[i];
+ }
+
+ return 0;
+}
+
+
+int smu7_copy_bytes_to_smc(struct pp_hwmgr *hwmgr, uint32_t smc_start_address,
+ const uint8_t *src, uint32_t byte_count, uint32_t limit)
+{
+ int result;
+ uint32_t data = 0;
+ uint32_t original_data;
+ uint32_t addr = 0;
+ uint32_t extra_shift;
+
+ PP_ASSERT_WITH_CODE((0 == (3 & smc_start_address)), "SMC address must be 4 byte aligned.", return -EINVAL);
+ PP_ASSERT_WITH_CODE((limit > (smc_start_address + byte_count)), "SMC address is beyond the SMC RAM area.", return -EINVAL);
+
+ addr = smc_start_address;
+
+ while (byte_count >= 4) {
+ /* Bytes are written into the SMC addres space with the MSB first. */
+ data = src[0] * 0x1000000 + src[1] * 0x10000 + src[2] * 0x100 + src[3];
+
+ result = smu7_set_smc_sram_address(hwmgr, addr, limit);
+
+ if (0 != result)
+ return result;
+
+ cgs_write_register(hwmgr->device, mmSMC_IND_DATA_11, data);
+
+ src += 4;
+ byte_count -= 4;
+ addr += 4;
+ }
+
+ if (0 != byte_count) {
+
+ data = 0;
+
+ result = smu7_set_smc_sram_address(hwmgr, addr, limit);
+
+ if (0 != result)
+ return result;
+
+
+ original_data = cgs_read_register(hwmgr->device, mmSMC_IND_DATA_11);
+
+ extra_shift = 8 * (4 - byte_count);
+
+ while (byte_count > 0) {
+ /* Bytes are written into the SMC addres space with the MSB first. */
+ data = (0x100 * data) + *src++;
+ byte_count--;
+ }
+
+ data <<= extra_shift;
+
+ data |= (original_data & ~((~0UL) << extra_shift));
+
+ result = smu7_set_smc_sram_address(hwmgr, addr, limit);
+
+ if (0 != result)
+ return result;
+
+ cgs_write_register(hwmgr->device, mmSMC_IND_DATA_11, data);
+ }
+
+ return 0;
+}
+
+
+int smu7_program_jump_on_start(struct pp_hwmgr *hwmgr)
+{
+ static const unsigned char data[4] = { 0xE0, 0x00, 0x80, 0x40 };
+
+ smu7_copy_bytes_to_smc(hwmgr, 0x0, data, 4, sizeof(data)+1);
+
+ return 0;
+}
+
+bool smu7_is_smc_ram_running(struct pp_hwmgr *hwmgr)
+{
+ return ((0 == PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SMC_SYSCON_CLOCK_CNTL_0, ck_disable))
+ && (0x20100 <= cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixSMC_PC_C)));
+}
+
+int smu7_send_msg_to_smc(struct pp_hwmgr *hwmgr, uint16_t msg)
+{
+ int ret;
+
+ PHM_WAIT_FIELD_UNEQUAL(hwmgr, SMC_RESP_0, SMC_RESP, 0);
+
+ ret = PHM_READ_FIELD(hwmgr->device, SMC_RESP_0, SMC_RESP);
+
+ if (ret == 0xFE)
+ pr_debug("last message was not supported\n");
+ else if (ret != 1)
+ pr_info("\n last message was failed ret is %d\n", ret);
+
+ cgs_write_register(hwmgr->device, mmSMC_RESP_0, 0);
+ cgs_write_register(hwmgr->device, mmSMC_MESSAGE_0, msg);
+
+ PHM_WAIT_FIELD_UNEQUAL(hwmgr, SMC_RESP_0, SMC_RESP, 0);
+
+ ret = PHM_READ_FIELD(hwmgr->device, SMC_RESP_0, SMC_RESP);
+
+ if (ret == 0xFE)
+ pr_debug("message %x was not supported\n", msg);
+ else if (ret != 1)
+ pr_info("\n failed to send message %x ret is %d \n", msg, ret);
+
+ return 0;
+}
+
+int smu7_send_msg_to_smc_with_parameter(struct pp_hwmgr *hwmgr, uint16_t msg, uint32_t parameter)
+{
+ PHM_WAIT_FIELD_UNEQUAL(hwmgr, SMC_RESP_0, SMC_RESP, 0);
+
+ cgs_write_register(hwmgr->device, mmSMC_MSG_ARG_0, parameter);
+
+ return smu7_send_msg_to_smc(hwmgr, msg);
+}
+
+uint32_t smu7_get_argument(struct pp_hwmgr *hwmgr)
+{
+ return cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
+}
+
+int smu7_send_msg_to_smc_offset(struct pp_hwmgr *hwmgr)
+{
+ return smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_Test, 0x20000, NULL);
+}
+
+enum cgs_ucode_id smu7_convert_fw_type_to_cgs(uint32_t fw_type)
+{
+ enum cgs_ucode_id result = CGS_UCODE_ID_MAXIMUM;
+
+ switch (fw_type) {
+ case UCODE_ID_SMU:
+ result = CGS_UCODE_ID_SMU;
+ break;
+ case UCODE_ID_SMU_SK:
+ result = CGS_UCODE_ID_SMU_SK;
+ break;
+ case UCODE_ID_SDMA0:
+ result = CGS_UCODE_ID_SDMA0;
+ break;
+ case UCODE_ID_SDMA1:
+ result = CGS_UCODE_ID_SDMA1;
+ break;
+ case UCODE_ID_CP_CE:
+ result = CGS_UCODE_ID_CP_CE;
+ break;
+ case UCODE_ID_CP_PFP:
+ result = CGS_UCODE_ID_CP_PFP;
+ break;
+ case UCODE_ID_CP_ME:
+ result = CGS_UCODE_ID_CP_ME;
+ break;
+ case UCODE_ID_CP_MEC:
+ result = CGS_UCODE_ID_CP_MEC;
+ break;
+ case UCODE_ID_CP_MEC_JT1:
+ result = CGS_UCODE_ID_CP_MEC_JT1;
+ break;
+ case UCODE_ID_CP_MEC_JT2:
+ result = CGS_UCODE_ID_CP_MEC_JT2;
+ break;
+ case UCODE_ID_RLC_G:
+ result = CGS_UCODE_ID_RLC_G;
+ break;
+ case UCODE_ID_MEC_STORAGE:
+ result = CGS_UCODE_ID_STORAGE;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+
+int smu7_read_smc_sram_dword(struct pp_hwmgr *hwmgr, uint32_t smc_addr, uint32_t *value, uint32_t limit)
+{
+ int result;
+
+ result = smu7_set_smc_sram_address(hwmgr, smc_addr, limit);
+
+ *value = result ? 0 : cgs_read_register(hwmgr->device, mmSMC_IND_DATA_11);
+
+ return result;
+}
+
+int smu7_write_smc_sram_dword(struct pp_hwmgr *hwmgr, uint32_t smc_addr, uint32_t value, uint32_t limit)
+{
+ int result;
+
+ result = smu7_set_smc_sram_address(hwmgr, smc_addr, limit);
+
+ if (result)
+ return result;
+
+ cgs_write_register(hwmgr->device, mmSMC_IND_DATA_11, value);
+
+ return 0;
+}
+
+static int smu7_populate_single_firmware_entry(struct pp_hwmgr *hwmgr,
+ uint32_t fw_type,
+ struct SMU_Entry *entry)
+{
+ int result = 0;
+ struct cgs_firmware_info info = {0};
+
+ result = cgs_get_firmware_info(hwmgr->device,
+ smu7_convert_fw_type_to_cgs(fw_type),
+ &info);
+
+ if (!result) {
+ entry->version = info.fw_version;
+ entry->id = (uint16_t)fw_type;
+ entry->image_addr_high = upper_32_bits(info.mc_addr);
+ entry->image_addr_low = lower_32_bits(info.mc_addr);
+ entry->meta_data_addr_high = 0;
+ entry->meta_data_addr_low = 0;
+
+ /* digest need be excluded out */
+ if (!hwmgr->not_vf)
+ info.image_size -= 20;
+ entry->data_size_byte = info.image_size;
+ entry->num_register_entries = 0;
+ }
+
+ if ((fw_type == UCODE_ID_RLC_G)
+ || (fw_type == UCODE_ID_CP_MEC))
+ entry->flags = 1;
+ else
+ entry->flags = 0;
+
+ return 0;
+}
+
+int smu7_request_smu_load_fw(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_smumgr *smu_data = (struct smu7_smumgr *)(hwmgr->smu_backend);
+ uint32_t fw_to_load;
+ int r = 0;
+
+ amdgpu_ucode_init_bo(hwmgr->adev);
+
+ if (smu_data->soft_regs_start)
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ smu_data->soft_regs_start + smum_get_offsetof(hwmgr,
+ SMU_SoftRegisters, UcodeLoadStatus),
+ 0x0);
+
+ if (hwmgr->chip_id > CHIP_TOPAZ) { /* add support for Topaz */
+ if (hwmgr->not_vf) {
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SMU_DRAM_ADDR_HI,
+ upper_32_bits(smu_data->smu_buffer.mc_addr),
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SMU_DRAM_ADDR_LO,
+ lower_32_bits(smu_data->smu_buffer.mc_addr),
+ NULL);
+ }
+ fw_to_load = UCODE_ID_RLC_G_MASK
+ + UCODE_ID_SDMA0_MASK
+ + UCODE_ID_SDMA1_MASK
+ + UCODE_ID_CP_CE_MASK
+ + UCODE_ID_CP_ME_MASK
+ + UCODE_ID_CP_PFP_MASK
+ + UCODE_ID_CP_MEC_MASK;
+ } else {
+ fw_to_load = UCODE_ID_RLC_G_MASK
+ + UCODE_ID_SDMA0_MASK
+ + UCODE_ID_SDMA1_MASK
+ + UCODE_ID_CP_CE_MASK
+ + UCODE_ID_CP_ME_MASK
+ + UCODE_ID_CP_PFP_MASK
+ + UCODE_ID_CP_MEC_MASK
+ + UCODE_ID_CP_MEC_JT1_MASK
+ + UCODE_ID_CP_MEC_JT2_MASK;
+ }
+
+ if (!smu_data->toc) {
+ struct SMU_DRAMData_TOC *toc;
+
+ smu_data->toc = kzalloc(sizeof(struct SMU_DRAMData_TOC), GFP_KERNEL);
+ if (!smu_data->toc)
+ return -ENOMEM;
+ toc = smu_data->toc;
+ toc->num_entries = 0;
+ toc->structure_version = 1;
+
+ PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(hwmgr,
+ UCODE_ID_RLC_G, &toc->entry[toc->num_entries++]),
+ "Failed to Get Firmware Entry.", r = -EINVAL; goto failed);
+ PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(hwmgr,
+ UCODE_ID_CP_CE, &toc->entry[toc->num_entries++]),
+ "Failed to Get Firmware Entry.", r = -EINVAL; goto failed);
+ PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(hwmgr,
+ UCODE_ID_CP_PFP, &toc->entry[toc->num_entries++]),
+ "Failed to Get Firmware Entry.", r = -EINVAL; goto failed);
+ PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(hwmgr,
+ UCODE_ID_CP_ME, &toc->entry[toc->num_entries++]),
+ "Failed to Get Firmware Entry.", r = -EINVAL; goto failed);
+ PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(hwmgr,
+ UCODE_ID_CP_MEC, &toc->entry[toc->num_entries++]),
+ "Failed to Get Firmware Entry.", r = -EINVAL; goto failed);
+ PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(hwmgr,
+ UCODE_ID_CP_MEC_JT1, &toc->entry[toc->num_entries++]),
+ "Failed to Get Firmware Entry.", r = -EINVAL; goto failed);
+ PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(hwmgr,
+ UCODE_ID_CP_MEC_JT2, &toc->entry[toc->num_entries++]),
+ "Failed to Get Firmware Entry.", r = -EINVAL; goto failed);
+ PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(hwmgr,
+ UCODE_ID_SDMA0, &toc->entry[toc->num_entries++]),
+ "Failed to Get Firmware Entry.", r = -EINVAL; goto failed);
+ PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(hwmgr,
+ UCODE_ID_SDMA1, &toc->entry[toc->num_entries++]),
+ "Failed to Get Firmware Entry.", r = -EINVAL; goto failed);
+ if (!hwmgr->not_vf)
+ PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(hwmgr,
+ UCODE_ID_MEC_STORAGE, &toc->entry[toc->num_entries++]),
+ "Failed to Get Firmware Entry.", r = -EINVAL; goto failed);
+ }
+ memcpy_toio(smu_data->header_buffer.kaddr, smu_data->toc,
+ sizeof(struct SMU_DRAMData_TOC));
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DRV_DRAM_ADDR_HI,
+ upper_32_bits(smu_data->header_buffer.mc_addr),
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DRV_DRAM_ADDR_LO,
+ lower_32_bits(smu_data->header_buffer.mc_addr),
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_LoadUcodes, fw_to_load, NULL);
+
+ r = smu7_check_fw_load_finish(hwmgr, fw_to_load);
+ if (!r)
+ return 0;
+
+ pr_err("SMU load firmware failed\n");
+
+failed:
+ kfree(smu_data->toc);
+ smu_data->toc = NULL;
+ return r;
+}
+
+/* Check if the FW has been loaded, SMU will not return if loading has not finished. */
+int smu7_check_fw_load_finish(struct pp_hwmgr *hwmgr, uint32_t fw_type)
+{
+ struct smu7_smumgr *smu_data = (struct smu7_smumgr *)(hwmgr->smu_backend);
+ uint32_t ret;
+
+ ret = phm_wait_on_indirect_register(hwmgr, mmSMC_IND_INDEX_11,
+ smu_data->soft_regs_start + smum_get_offsetof(hwmgr,
+ SMU_SoftRegisters, UcodeLoadStatus),
+ fw_type, fw_type);
+ return ret;
+}
+
+int smu7_reload_firmware(struct pp_hwmgr *hwmgr)
+{
+ return hwmgr->smumgr_funcs->start_smu(hwmgr);
+}
+
+static int smu7_upload_smc_firmware_data(struct pp_hwmgr *hwmgr, uint32_t length, uint32_t *src, uint32_t limit)
+{
+ uint32_t byte_count = length;
+
+ PP_ASSERT_WITH_CODE((limit >= byte_count), "SMC address is beyond the SMC RAM area.", return -EINVAL);
+
+ cgs_write_register(hwmgr->device, mmSMC_IND_INDEX_11, 0x20000);
+ PHM_WRITE_FIELD(hwmgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_11, 1);
+
+ for (; byte_count >= 4; byte_count -= 4)
+ cgs_write_register(hwmgr->device, mmSMC_IND_DATA_11, *src++);
+
+ PHM_WRITE_FIELD(hwmgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_11, 0);
+
+ PP_ASSERT_WITH_CODE((0 == byte_count), "SMC size must be divisible by 4.", return -EINVAL);
+
+ return 0;
+}
+
+
+int smu7_upload_smu_firmware_image(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+ struct smu7_smumgr *smu_data = (struct smu7_smumgr *)(hwmgr->smu_backend);
+
+ struct cgs_firmware_info info = {0};
+
+ if (smu_data->security_hard_key == 1)
+ cgs_get_firmware_info(hwmgr->device,
+ smu7_convert_fw_type_to_cgs(UCODE_ID_SMU), &info);
+ else
+ cgs_get_firmware_info(hwmgr->device,
+ smu7_convert_fw_type_to_cgs(UCODE_ID_SMU_SK), &info);
+
+ hwmgr->is_kicker = info.is_kicker;
+ hwmgr->smu_version = info.version;
+ result = smu7_upload_smc_firmware_data(hwmgr, info.image_size, (uint32_t *)info.kptr, SMU7_SMC_SIZE);
+
+ return result;
+}
+
+static void execute_pwr_table(struct pp_hwmgr *hwmgr, const PWR_Command_Table *pvirus, int size)
+{
+ int i;
+ uint32_t reg, data;
+
+ for (i = 0; i < size; i++) {
+ reg = pvirus->reg;
+ data = pvirus->data;
+ if (reg != 0xffffffff)
+ cgs_write_register(hwmgr->device, reg, data);
+ else
+ break;
+ pvirus++;
+ }
+}
+
+static void execute_pwr_dfy_table(struct pp_hwmgr *hwmgr, const PWR_DFY_Section *section)
+{
+ int i;
+
+ cgs_write_register(hwmgr->device, mmCP_DFY_CNTL, section->dfy_cntl);
+ cgs_write_register(hwmgr->device, mmCP_DFY_ADDR_HI, section->dfy_addr_hi);
+ cgs_write_register(hwmgr->device, mmCP_DFY_ADDR_LO, section->dfy_addr_lo);
+ for (i = 0; i < section->dfy_size; i++)
+ cgs_write_register(hwmgr->device, mmCP_DFY_DATA_0, section->dfy_data[i]);
+}
+
+int smu7_setup_pwr_virus(struct pp_hwmgr *hwmgr)
+{
+ execute_pwr_table(hwmgr, pwr_virus_table_pre, ARRAY_SIZE(pwr_virus_table_pre));
+ execute_pwr_dfy_table(hwmgr, &pwr_virus_section1);
+ execute_pwr_dfy_table(hwmgr, &pwr_virus_section2);
+ execute_pwr_dfy_table(hwmgr, &pwr_virus_section3);
+ execute_pwr_dfy_table(hwmgr, &pwr_virus_section4);
+ execute_pwr_dfy_table(hwmgr, &pwr_virus_section5);
+ execute_pwr_dfy_table(hwmgr, &pwr_virus_section6);
+ execute_pwr_table(hwmgr, pwr_virus_table_post, ARRAY_SIZE(pwr_virus_table_post));
+
+ return 0;
+}
+
+int smu7_init(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_smumgr *smu_data;
+ int r;
+ /* Allocate memory for backend private data */
+ smu_data = (struct smu7_smumgr *)(hwmgr->smu_backend);
+ smu_data->header_buffer.data_size =
+ ((sizeof(struct SMU_DRAMData_TOC) / 4096) + 1) * 4096;
+
+/* Allocate FW image data structure and header buffer and
+ * send the header buffer address to SMU */
+ r = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ smu_data->header_buffer.data_size,
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &smu_data->header_buffer.handle,
+ &smu_data->header_buffer.mc_addr,
+ &smu_data->header_buffer.kaddr);
+
+ if (r)
+ return -EINVAL;
+
+ if (!hwmgr->not_vf)
+ return 0;
+
+ smu_data->smu_buffer.data_size = 200*4096;
+ r = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ smu_data->smu_buffer.data_size,
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &smu_data->smu_buffer.handle,
+ &smu_data->smu_buffer.mc_addr,
+ &smu_data->smu_buffer.kaddr);
+
+ if (r) {
+ amdgpu_bo_free_kernel(&smu_data->header_buffer.handle,
+ &smu_data->header_buffer.mc_addr,
+ &smu_data->header_buffer.kaddr);
+ return -EINVAL;
+ }
+
+ if (smum_is_hw_avfs_present(hwmgr) &&
+ (hwmgr->feature_mask & PP_AVFS_MASK))
+ hwmgr->avfs_supported = true;
+
+ return 0;
+}
+
+
+int smu7_smu_fini(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_smumgr *smu_data = (struct smu7_smumgr *)(hwmgr->smu_backend);
+
+ amdgpu_bo_free_kernel(&smu_data->header_buffer.handle,
+ &smu_data->header_buffer.mc_addr,
+ &smu_data->header_buffer.kaddr);
+
+ if (hwmgr->not_vf)
+ amdgpu_bo_free_kernel(&smu_data->smu_buffer.handle,
+ &smu_data->smu_buffer.mc_addr,
+ &smu_data->smu_buffer.kaddr);
+
+
+ kfree(smu_data->toc);
+ smu_data->toc = NULL;
+ kfree(hwmgr->smu_backend);
+ hwmgr->smu_backend = NULL;
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu7_smumgr.h b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu7_smumgr.h
new file mode 100644
index 000000000000..e7303dc8c260
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu7_smumgr.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _SMU7_SMUMANAGER_H
+#define _SMU7_SMUMANAGER_H
+
+
+#include <pp_endian.h>
+
+#define SMC_RAM_END 0x40000
+
+struct smu7_buffer_entry {
+ uint32_t data_size;
+ uint64_t mc_addr;
+ void *kaddr;
+ struct amdgpu_bo *handle;
+};
+
+struct smu7_smumgr {
+ struct smu7_buffer_entry smu_buffer;
+ struct smu7_buffer_entry header_buffer;
+ struct SMU_DRAMData_TOC *toc;
+
+ uint32_t soft_regs_start;
+ uint32_t dpm_table_start;
+ uint32_t mc_reg_table_start;
+ uint32_t fan_table_start;
+ uint32_t arb_table_start;
+ uint32_t ulv_setting_starts;
+ uint8_t security_hard_key;
+ uint32_t acpi_optimization;
+ uint32_t avfs_btc_param;
+};
+
+
+int smu7_copy_bytes_from_smc(struct pp_hwmgr *hwmgr, uint32_t smc_start_address,
+ uint32_t *dest, uint32_t byte_count, uint32_t limit);
+int smu7_copy_bytes_to_smc(struct pp_hwmgr *hwmgr, uint32_t smc_start_address,
+ const uint8_t *src, uint32_t byte_count, uint32_t limit);
+int smu7_program_jump_on_start(struct pp_hwmgr *hwmgr);
+bool smu7_is_smc_ram_running(struct pp_hwmgr *hwmgr);
+int smu7_send_msg_to_smc(struct pp_hwmgr *hwmgr, uint16_t msg);
+int smu7_send_msg_to_smc_with_parameter(struct pp_hwmgr *hwmgr, uint16_t msg,
+ uint32_t parameter);
+uint32_t smu7_get_argument(struct pp_hwmgr *hwmgr);
+int smu7_send_msg_to_smc_offset(struct pp_hwmgr *hwmgr);
+
+enum cgs_ucode_id smu7_convert_fw_type_to_cgs(uint32_t fw_type);
+int smu7_read_smc_sram_dword(struct pp_hwmgr *hwmgr, uint32_t smc_addr,
+ uint32_t *value, uint32_t limit);
+int smu7_write_smc_sram_dword(struct pp_hwmgr *hwmgr, uint32_t smc_addr,
+ uint32_t value, uint32_t limit);
+
+int smu7_request_smu_load_fw(struct pp_hwmgr *hwmgr);
+int smu7_check_fw_load_finish(struct pp_hwmgr *hwmgr, uint32_t fw_type);
+int smu7_reload_firmware(struct pp_hwmgr *hwmgr);
+int smu7_upload_smu_firmware_image(struct pp_hwmgr *hwmgr);
+int smu7_init(struct pp_hwmgr *hwmgr);
+int smu7_smu_fini(struct pp_hwmgr *hwmgr);
+
+int smu7_setup_pwr_virus(struct pp_hwmgr *hwmgr);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu8_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu8_smumgr.c
new file mode 100644
index 000000000000..76d4f12ceedf
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu8_smumgr.c
@@ -0,0 +1,910 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <linux/delay.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "cgs_common.h"
+#include "smu/smu_8_0_d.h"
+#include "smu/smu_8_0_sh_mask.h"
+#include "smu8.h"
+#include "smu8_fusion.h"
+#include "smu8_smumgr.h"
+#include "cz_ppsmc.h"
+#include "smu_ucode_xfer_cz.h"
+#include "gca/gfx_8_0_d.h"
+#include "gca/gfx_8_0_sh_mask.h"
+#include "smumgr.h"
+
+#define SIZE_ALIGN_32(x) (((x) + 31) / 32 * 32)
+
+static const enum smu8_scratch_entry firmware_list[] = {
+ SMU8_SCRATCH_ENTRY_UCODE_ID_SDMA0,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_SDMA1,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_CE,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_PFP,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_ME,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT2,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_G,
+};
+
+static uint32_t smu8_get_argument(struct pp_hwmgr *hwmgr)
+{
+ if (hwmgr == NULL || hwmgr->device == NULL)
+ return 0;
+
+ return cgs_read_register(hwmgr->device,
+ mmSMU_MP1_SRBM2P_ARG_0);
+}
+
+/* Send a message to the SMC, and wait for its response.*/
+static int smu8_send_msg_to_smc_with_parameter(struct pp_hwmgr *hwmgr,
+ uint16_t msg, uint32_t parameter)
+{
+ int result = 0;
+ ktime_t t_start;
+ s64 elapsed_us;
+
+ if (hwmgr == NULL || hwmgr->device == NULL)
+ return -EINVAL;
+
+ result = PHM_WAIT_FIELD_UNEQUAL(hwmgr,
+ SMU_MP1_SRBM2P_RESP_0, CONTENT, 0);
+ if (result != 0) {
+ /* Read the last message to SMU, to report actual cause */
+ uint32_t val = cgs_read_register(hwmgr->device,
+ mmSMU_MP1_SRBM2P_MSG_0);
+ pr_err("%s(0x%04x) aborted; SMU still servicing msg (0x%04x)\n",
+ __func__, msg, val);
+ return result;
+ }
+ t_start = ktime_get();
+
+ cgs_write_register(hwmgr->device, mmSMU_MP1_SRBM2P_ARG_0, parameter);
+
+ cgs_write_register(hwmgr->device, mmSMU_MP1_SRBM2P_RESP_0, 0);
+ cgs_write_register(hwmgr->device, mmSMU_MP1_SRBM2P_MSG_0, msg);
+
+ result = PHM_WAIT_FIELD_UNEQUAL(hwmgr,
+ SMU_MP1_SRBM2P_RESP_0, CONTENT, 0);
+
+ elapsed_us = ktime_us_delta(ktime_get(), t_start);
+
+ WARN(result, "%s(0x%04x, %#x) timed out after %lld us\n",
+ __func__, msg, parameter, elapsed_us);
+
+ return result;
+}
+
+static int smu8_send_msg_to_smc(struct pp_hwmgr *hwmgr, uint16_t msg)
+{
+ return smu8_send_msg_to_smc_with_parameter(hwmgr, msg, 0);
+}
+
+static int smu8_set_smc_sram_address(struct pp_hwmgr *hwmgr,
+ uint32_t smc_address, uint32_t limit)
+{
+ if (hwmgr == NULL || hwmgr->device == NULL)
+ return -EINVAL;
+
+ if (0 != (3 & smc_address)) {
+ pr_err("SMC address must be 4 byte aligned\n");
+ return -EINVAL;
+ }
+
+ if (limit <= (smc_address + 3)) {
+ pr_err("SMC address beyond the SMC RAM area\n");
+ return -EINVAL;
+ }
+
+ cgs_write_register(hwmgr->device, mmMP0PUB_IND_INDEX_0,
+ SMN_MP1_SRAM_START_ADDR + smc_address);
+
+ return 0;
+}
+
+static int smu8_write_smc_sram_dword(struct pp_hwmgr *hwmgr,
+ uint32_t smc_address, uint32_t value, uint32_t limit)
+{
+ int result;
+
+ if (hwmgr == NULL || hwmgr->device == NULL)
+ return -EINVAL;
+
+ result = smu8_set_smc_sram_address(hwmgr, smc_address, limit);
+ if (!result)
+ cgs_write_register(hwmgr->device, mmMP0PUB_IND_DATA_0, value);
+
+ return result;
+}
+
+static int smu8_check_fw_load_finish(struct pp_hwmgr *hwmgr,
+ uint32_t firmware)
+{
+ int i;
+ uint32_t index = SMN_MP1_SRAM_START_ADDR +
+ SMU8_FIRMWARE_HEADER_LOCATION +
+ offsetof(struct SMU8_Firmware_Header, UcodeLoadStatus);
+
+ if (hwmgr == NULL || hwmgr->device == NULL)
+ return -EINVAL;
+
+ cgs_write_register(hwmgr->device, mmMP0PUB_IND_INDEX, index);
+
+ for (i = 0; i < hwmgr->usec_timeout; i++) {
+ if (firmware ==
+ (cgs_read_register(hwmgr->device, mmMP0PUB_IND_DATA) & firmware))
+ break;
+ udelay(1);
+ }
+
+ if (i >= hwmgr->usec_timeout) {
+ pr_err("SMU check loaded firmware failed.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smu8_load_mec_firmware(struct pp_hwmgr *hwmgr)
+{
+ uint32_t reg_data;
+ uint32_t tmp;
+ int ret = 0;
+ struct cgs_firmware_info info = {0};
+
+ if (hwmgr == NULL || hwmgr->device == NULL)
+ return -EINVAL;
+
+ ret = cgs_get_firmware_info(hwmgr->device,
+ CGS_UCODE_ID_CP_MEC, &info);
+
+ if (ret)
+ return -EINVAL;
+
+ /* Disable MEC parsing/prefetching */
+ tmp = cgs_read_register(hwmgr->device,
+ mmCP_MEC_CNTL);
+ tmp = PHM_SET_FIELD(tmp, CP_MEC_CNTL, MEC_ME1_HALT, 1);
+ tmp = PHM_SET_FIELD(tmp, CP_MEC_CNTL, MEC_ME2_HALT, 1);
+ cgs_write_register(hwmgr->device, mmCP_MEC_CNTL, tmp);
+
+ tmp = cgs_read_register(hwmgr->device,
+ mmCP_CPC_IC_BASE_CNTL);
+
+ tmp = PHM_SET_FIELD(tmp, CP_CPC_IC_BASE_CNTL, VMID, 0);
+ tmp = PHM_SET_FIELD(tmp, CP_CPC_IC_BASE_CNTL, ATC, 0);
+ tmp = PHM_SET_FIELD(tmp, CP_CPC_IC_BASE_CNTL, CACHE_POLICY, 0);
+ tmp = PHM_SET_FIELD(tmp, CP_CPC_IC_BASE_CNTL, MTYPE, 1);
+ cgs_write_register(hwmgr->device, mmCP_CPC_IC_BASE_CNTL, tmp);
+
+ reg_data = lower_32_bits(info.mc_addr) &
+ PHM_FIELD_MASK(CP_CPC_IC_BASE_LO, IC_BASE_LO);
+ cgs_write_register(hwmgr->device, mmCP_CPC_IC_BASE_LO, reg_data);
+
+ reg_data = upper_32_bits(info.mc_addr) &
+ PHM_FIELD_MASK(CP_CPC_IC_BASE_HI, IC_BASE_HI);
+ cgs_write_register(hwmgr->device, mmCP_CPC_IC_BASE_HI, reg_data);
+
+ return 0;
+}
+
+static uint8_t smu8_translate_firmware_enum_to_arg(struct pp_hwmgr *hwmgr,
+ enum smu8_scratch_entry firmware_enum)
+{
+ uint8_t ret = 0;
+
+ switch (firmware_enum) {
+ case SMU8_SCRATCH_ENTRY_UCODE_ID_SDMA0:
+ ret = UCODE_ID_SDMA0;
+ break;
+ case SMU8_SCRATCH_ENTRY_UCODE_ID_SDMA1:
+ if (hwmgr->chip_id == CHIP_STONEY)
+ ret = UCODE_ID_SDMA0;
+ else
+ ret = UCODE_ID_SDMA1;
+ break;
+ case SMU8_SCRATCH_ENTRY_UCODE_ID_CP_CE:
+ ret = UCODE_ID_CP_CE;
+ break;
+ case SMU8_SCRATCH_ENTRY_UCODE_ID_CP_PFP:
+ ret = UCODE_ID_CP_PFP;
+ break;
+ case SMU8_SCRATCH_ENTRY_UCODE_ID_CP_ME:
+ ret = UCODE_ID_CP_ME;
+ break;
+ case SMU8_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1:
+ ret = UCODE_ID_CP_MEC_JT1;
+ break;
+ case SMU8_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT2:
+ if (hwmgr->chip_id == CHIP_STONEY)
+ ret = UCODE_ID_CP_MEC_JT1;
+ else
+ ret = UCODE_ID_CP_MEC_JT2;
+ break;
+ case SMU8_SCRATCH_ENTRY_UCODE_ID_GMCON_RENG:
+ ret = UCODE_ID_GMCON_RENG;
+ break;
+ case SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_G:
+ ret = UCODE_ID_RLC_G;
+ break;
+ case SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_SCRATCH:
+ ret = UCODE_ID_RLC_SCRATCH;
+ break;
+ case SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_ARAM:
+ ret = UCODE_ID_RLC_SRM_ARAM;
+ break;
+ case SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_DRAM:
+ ret = UCODE_ID_RLC_SRM_DRAM;
+ break;
+ case SMU8_SCRATCH_ENTRY_UCODE_ID_DMCU_ERAM:
+ ret = UCODE_ID_DMCU_ERAM;
+ break;
+ case SMU8_SCRATCH_ENTRY_UCODE_ID_DMCU_IRAM:
+ ret = UCODE_ID_DMCU_IRAM;
+ break;
+ case SMU8_SCRATCH_ENTRY_UCODE_ID_POWER_PROFILING:
+ ret = TASK_ARG_INIT_MM_PWR_LOG;
+ break;
+ case SMU8_SCRATCH_ENTRY_DATA_ID_SDMA_HALT:
+ case SMU8_SCRATCH_ENTRY_DATA_ID_SYS_CLOCKGATING:
+ case SMU8_SCRATCH_ENTRY_DATA_ID_SDMA_RING_REGS:
+ case SMU8_SCRATCH_ENTRY_DATA_ID_NONGFX_REINIT:
+ case SMU8_SCRATCH_ENTRY_DATA_ID_SDMA_START:
+ case SMU8_SCRATCH_ENTRY_DATA_ID_IH_REGISTERS:
+ ret = TASK_ARG_REG_MMIO;
+ break;
+ case SMU8_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE:
+ ret = TASK_ARG_INIT_CLK_TABLE;
+ break;
+ }
+
+ return ret;
+}
+
+static enum cgs_ucode_id smu8_convert_fw_type_to_cgs(uint32_t fw_type)
+{
+ enum cgs_ucode_id result = CGS_UCODE_ID_MAXIMUM;
+
+ switch (fw_type) {
+ case UCODE_ID_SDMA0:
+ result = CGS_UCODE_ID_SDMA0;
+ break;
+ case UCODE_ID_SDMA1:
+ result = CGS_UCODE_ID_SDMA1;
+ break;
+ case UCODE_ID_CP_CE:
+ result = CGS_UCODE_ID_CP_CE;
+ break;
+ case UCODE_ID_CP_PFP:
+ result = CGS_UCODE_ID_CP_PFP;
+ break;
+ case UCODE_ID_CP_ME:
+ result = CGS_UCODE_ID_CP_ME;
+ break;
+ case UCODE_ID_CP_MEC_JT1:
+ result = CGS_UCODE_ID_CP_MEC_JT1;
+ break;
+ case UCODE_ID_CP_MEC_JT2:
+ result = CGS_UCODE_ID_CP_MEC_JT2;
+ break;
+ case UCODE_ID_RLC_G:
+ result = CGS_UCODE_ID_RLC_G;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+static int smu8_smu_populate_single_scratch_task(
+ struct pp_hwmgr *hwmgr,
+ enum smu8_scratch_entry fw_enum,
+ uint8_t type, bool is_last)
+{
+ uint8_t i;
+ struct smu8_smumgr *smu8_smu = hwmgr->smu_backend;
+ struct TOC *toc = (struct TOC *)smu8_smu->toc_buffer.kaddr;
+ struct SMU_Task *task = &toc->tasks[smu8_smu->toc_entry_used_count++];
+
+ task->type = type;
+ task->arg = smu8_translate_firmware_enum_to_arg(hwmgr, fw_enum);
+ task->next = is_last ? END_OF_TASK_LIST : smu8_smu->toc_entry_used_count;
+
+ for (i = 0; i < smu8_smu->scratch_buffer_length; i++)
+ if (smu8_smu->scratch_buffer[i].firmware_ID == fw_enum)
+ break;
+
+ if (i >= smu8_smu->scratch_buffer_length) {
+ pr_err("Invalid Firmware Type\n");
+ return -EINVAL;
+ }
+
+ task->addr.low = lower_32_bits(smu8_smu->scratch_buffer[i].mc_addr);
+ task->addr.high = upper_32_bits(smu8_smu->scratch_buffer[i].mc_addr);
+ task->size_bytes = smu8_smu->scratch_buffer[i].data_size;
+
+ if (SMU8_SCRATCH_ENTRY_DATA_ID_IH_REGISTERS == fw_enum) {
+ struct smu8_ih_meta_data *pIHReg_restore =
+ (struct smu8_ih_meta_data *)smu8_smu->scratch_buffer[i].kaddr;
+ pIHReg_restore->command =
+ METADATA_CMD_MODE0 | METADATA_PERFORM_ON_LOAD;
+ }
+
+ return 0;
+}
+
+static int smu8_smu_populate_single_ucode_load_task(
+ struct pp_hwmgr *hwmgr,
+ enum smu8_scratch_entry fw_enum,
+ bool is_last)
+{
+ uint8_t i;
+ struct smu8_smumgr *smu8_smu = hwmgr->smu_backend;
+ struct TOC *toc = (struct TOC *)smu8_smu->toc_buffer.kaddr;
+ struct SMU_Task *task = &toc->tasks[smu8_smu->toc_entry_used_count++];
+
+ task->type = TASK_TYPE_UCODE_LOAD;
+ task->arg = smu8_translate_firmware_enum_to_arg(hwmgr, fw_enum);
+ task->next = is_last ? END_OF_TASK_LIST : smu8_smu->toc_entry_used_count;
+
+ for (i = 0; i < smu8_smu->driver_buffer_length; i++)
+ if (smu8_smu->driver_buffer[i].firmware_ID == fw_enum)
+ break;
+
+ if (i >= smu8_smu->driver_buffer_length) {
+ pr_err("Invalid Firmware Type\n");
+ return -EINVAL;
+ }
+
+ task->addr.low = lower_32_bits(smu8_smu->driver_buffer[i].mc_addr);
+ task->addr.high = upper_32_bits(smu8_smu->driver_buffer[i].mc_addr);
+ task->size_bytes = smu8_smu->driver_buffer[i].data_size;
+
+ return 0;
+}
+
+static int smu8_smu_construct_toc_for_rlc_aram_save(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_smumgr *smu8_smu = hwmgr->smu_backend;
+
+ smu8_smu->toc_entry_aram = smu8_smu->toc_entry_used_count;
+ smu8_smu_populate_single_scratch_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_ARAM,
+ TASK_TYPE_UCODE_SAVE, true);
+
+ return 0;
+}
+
+static int smu8_smu_initialize_toc_empty_job_list(struct pp_hwmgr *hwmgr)
+{
+ int i;
+ struct smu8_smumgr *smu8_smu = hwmgr->smu_backend;
+ struct TOC *toc = (struct TOC *)smu8_smu->toc_buffer.kaddr;
+
+ for (i = 0; i < NUM_JOBLIST_ENTRIES; i++)
+ toc->JobList[i] = (uint8_t)IGNORE_JOB;
+
+ return 0;
+}
+
+static int smu8_smu_construct_toc_for_vddgfx_enter(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_smumgr *smu8_smu = hwmgr->smu_backend;
+ struct TOC *toc = (struct TOC *)smu8_smu->toc_buffer.kaddr;
+
+ toc->JobList[JOB_GFX_SAVE] = (uint8_t)smu8_smu->toc_entry_used_count;
+ smu8_smu_populate_single_scratch_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_SCRATCH,
+ TASK_TYPE_UCODE_SAVE, false);
+
+ smu8_smu_populate_single_scratch_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_DRAM,
+ TASK_TYPE_UCODE_SAVE, true);
+
+ return 0;
+}
+
+
+static int smu8_smu_construct_toc_for_vddgfx_exit(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_smumgr *smu8_smu = hwmgr->smu_backend;
+ struct TOC *toc = (struct TOC *)smu8_smu->toc_buffer.kaddr;
+
+ toc->JobList[JOB_GFX_RESTORE] = (uint8_t)smu8_smu->toc_entry_used_count;
+
+ smu8_smu_populate_single_ucode_load_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_CE, false);
+ smu8_smu_populate_single_ucode_load_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_PFP, false);
+ smu8_smu_populate_single_ucode_load_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_ME, false);
+ smu8_smu_populate_single_ucode_load_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1, false);
+
+ if (hwmgr->chip_id == CHIP_STONEY)
+ smu8_smu_populate_single_ucode_load_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1, false);
+ else
+ smu8_smu_populate_single_ucode_load_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT2, false);
+
+ smu8_smu_populate_single_ucode_load_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_G, false);
+
+ /* populate scratch */
+ smu8_smu_populate_single_scratch_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_SCRATCH,
+ TASK_TYPE_UCODE_LOAD, false);
+
+ smu8_smu_populate_single_scratch_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_ARAM,
+ TASK_TYPE_UCODE_LOAD, false);
+
+ smu8_smu_populate_single_scratch_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_DRAM,
+ TASK_TYPE_UCODE_LOAD, true);
+
+ return 0;
+}
+
+static int smu8_smu_construct_toc_for_power_profiling(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_smumgr *smu8_smu = hwmgr->smu_backend;
+
+ smu8_smu->toc_entry_power_profiling_index = smu8_smu->toc_entry_used_count;
+
+ smu8_smu_populate_single_scratch_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_POWER_PROFILING,
+ TASK_TYPE_INITIALIZE, true);
+ return 0;
+}
+
+static int smu8_smu_construct_toc_for_bootup(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_smumgr *smu8_smu = hwmgr->smu_backend;
+
+ smu8_smu->toc_entry_initialize_index = smu8_smu->toc_entry_used_count;
+
+ smu8_smu_populate_single_ucode_load_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_SDMA0, false);
+ if (hwmgr->chip_id != CHIP_STONEY)
+ smu8_smu_populate_single_ucode_load_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_SDMA1, false);
+ smu8_smu_populate_single_ucode_load_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_CE, false);
+ smu8_smu_populate_single_ucode_load_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_PFP, false);
+ smu8_smu_populate_single_ucode_load_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_ME, false);
+ smu8_smu_populate_single_ucode_load_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1, false);
+ if (hwmgr->chip_id != CHIP_STONEY)
+ smu8_smu_populate_single_ucode_load_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT2, false);
+ smu8_smu_populate_single_ucode_load_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_G, true);
+
+ return 0;
+}
+
+static int smu8_smu_construct_toc_for_clock_table(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_smumgr *smu8_smu = hwmgr->smu_backend;
+
+ smu8_smu->toc_entry_clock_table = smu8_smu->toc_entry_used_count;
+
+ smu8_smu_populate_single_scratch_task(hwmgr,
+ SMU8_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE,
+ TASK_TYPE_INITIALIZE, true);
+
+ return 0;
+}
+
+static int smu8_smu_construct_toc(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_smumgr *smu8_smu = hwmgr->smu_backend;
+
+ smu8_smu->toc_entry_used_count = 0;
+ smu8_smu_initialize_toc_empty_job_list(hwmgr);
+ smu8_smu_construct_toc_for_rlc_aram_save(hwmgr);
+ smu8_smu_construct_toc_for_vddgfx_enter(hwmgr);
+ smu8_smu_construct_toc_for_vddgfx_exit(hwmgr);
+ smu8_smu_construct_toc_for_power_profiling(hwmgr);
+ smu8_smu_construct_toc_for_bootup(hwmgr);
+ smu8_smu_construct_toc_for_clock_table(hwmgr);
+
+ return 0;
+}
+
+static int smu8_smu_populate_firmware_entries(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_smumgr *smu8_smu = hwmgr->smu_backend;
+ uint32_t firmware_type;
+ uint32_t i;
+ int ret;
+ enum cgs_ucode_id ucode_id;
+ struct cgs_firmware_info info = {0};
+
+ smu8_smu->driver_buffer_length = 0;
+
+ for (i = 0; i < ARRAY_SIZE(firmware_list); i++) {
+
+ firmware_type = smu8_translate_firmware_enum_to_arg(hwmgr,
+ firmware_list[i]);
+
+ ucode_id = smu8_convert_fw_type_to_cgs(firmware_type);
+
+ ret = cgs_get_firmware_info(hwmgr->device,
+ ucode_id, &info);
+
+ if (ret == 0) {
+ smu8_smu->driver_buffer[i].mc_addr = info.mc_addr;
+
+ smu8_smu->driver_buffer[i].data_size = info.image_size;
+
+ smu8_smu->driver_buffer[i].firmware_ID = firmware_list[i];
+ smu8_smu->driver_buffer_length++;
+ }
+ }
+
+ return 0;
+}
+
+static int smu8_smu_populate_single_scratch_entry(
+ struct pp_hwmgr *hwmgr,
+ enum smu8_scratch_entry scratch_type,
+ uint32_t ulsize_byte,
+ struct smu8_buffer_entry *entry)
+{
+ struct smu8_smumgr *smu8_smu = hwmgr->smu_backend;
+ uint32_t ulsize_aligned = SIZE_ALIGN_32(ulsize_byte);
+
+ entry->data_size = ulsize_byte;
+ entry->kaddr = (char *) smu8_smu->smu_buffer.kaddr +
+ smu8_smu->smu_buffer_used_bytes;
+ entry->mc_addr = smu8_smu->smu_buffer.mc_addr + smu8_smu->smu_buffer_used_bytes;
+ entry->firmware_ID = scratch_type;
+
+ smu8_smu->smu_buffer_used_bytes += ulsize_aligned;
+
+ return 0;
+}
+
+static int smu8_download_pptable_settings(struct pp_hwmgr *hwmgr, void **table)
+{
+ struct smu8_smumgr *smu8_smu = hwmgr->smu_backend;
+ unsigned long i;
+
+ for (i = 0; i < smu8_smu->scratch_buffer_length; i++) {
+ if (smu8_smu->scratch_buffer[i].firmware_ID
+ == SMU8_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE)
+ break;
+ }
+
+ *table = (struct SMU8_Fusion_ClkTable *)smu8_smu->scratch_buffer[i].kaddr;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetClkTableAddrHi,
+ upper_32_bits(smu8_smu->scratch_buffer[i].mc_addr),
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetClkTableAddrLo,
+ lower_32_bits(smu8_smu->scratch_buffer[i].mc_addr),
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_ExecuteJob,
+ smu8_smu->toc_entry_clock_table,
+ NULL);
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ClkTableXferToDram, NULL);
+
+ return 0;
+}
+
+static int smu8_upload_pptable_settings(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_smumgr *smu8_smu = hwmgr->smu_backend;
+ unsigned long i;
+
+ for (i = 0; i < smu8_smu->scratch_buffer_length; i++) {
+ if (smu8_smu->scratch_buffer[i].firmware_ID
+ == SMU8_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE)
+ break;
+ }
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetClkTableAddrHi,
+ upper_32_bits(smu8_smu->scratch_buffer[i].mc_addr),
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetClkTableAddrLo,
+ lower_32_bits(smu8_smu->scratch_buffer[i].mc_addr),
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_ExecuteJob,
+ smu8_smu->toc_entry_clock_table,
+ NULL);
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_ClkTableXferToSmu, NULL);
+
+ return 0;
+}
+
+static int smu8_request_smu_load_fw(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_smumgr *smu8_smu = hwmgr->smu_backend;
+ uint32_t smc_address;
+ uint32_t fw_to_check = 0;
+ int ret;
+
+ amdgpu_ucode_init_bo(hwmgr->adev);
+
+ smu8_smu_populate_firmware_entries(hwmgr);
+
+ smu8_smu_construct_toc(hwmgr);
+
+ smc_address = SMU8_FIRMWARE_HEADER_LOCATION +
+ offsetof(struct SMU8_Firmware_Header, UcodeLoadStatus);
+
+ smu8_write_smc_sram_dword(hwmgr, smc_address, 0, smc_address+4);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DriverDramAddrHi,
+ upper_32_bits(smu8_smu->toc_buffer.mc_addr),
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DriverDramAddrLo,
+ lower_32_bits(smu8_smu->toc_buffer.mc_addr),
+ NULL);
+
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_InitJobs, NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_ExecuteJob,
+ smu8_smu->toc_entry_aram,
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_ExecuteJob,
+ smu8_smu->toc_entry_power_profiling_index,
+ NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_ExecuteJob,
+ smu8_smu->toc_entry_initialize_index,
+ NULL);
+
+ fw_to_check = UCODE_ID_RLC_G_MASK |
+ UCODE_ID_SDMA0_MASK |
+ UCODE_ID_SDMA1_MASK |
+ UCODE_ID_CP_CE_MASK |
+ UCODE_ID_CP_ME_MASK |
+ UCODE_ID_CP_PFP_MASK |
+ UCODE_ID_CP_MEC_JT1_MASK |
+ UCODE_ID_CP_MEC_JT2_MASK;
+
+ if (hwmgr->chip_id == CHIP_STONEY)
+ fw_to_check &= ~(UCODE_ID_SDMA1_MASK | UCODE_ID_CP_MEC_JT2_MASK);
+
+ ret = smu8_check_fw_load_finish(hwmgr, fw_to_check);
+ if (ret) {
+ pr_err("SMU firmware load failed\n");
+ return ret;
+ }
+
+ ret = smu8_load_mec_firmware(hwmgr);
+ if (ret) {
+ pr_err("Mec Firmware load failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int smu8_start_smu(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev;
+
+ uint32_t index = SMN_MP1_SRAM_START_ADDR +
+ SMU8_FIRMWARE_HEADER_LOCATION +
+ offsetof(struct SMU8_Firmware_Header, Version);
+
+ if (hwmgr == NULL || hwmgr->device == NULL)
+ return -EINVAL;
+
+ adev = hwmgr->adev;
+
+ cgs_write_register(hwmgr->device, mmMP0PUB_IND_INDEX, index);
+ hwmgr->smu_version = cgs_read_register(hwmgr->device, mmMP0PUB_IND_DATA);
+ pr_info("smu version %02d.%02d.%02d\n",
+ ((hwmgr->smu_version >> 16) & 0xFF),
+ ((hwmgr->smu_version >> 8) & 0xFF),
+ (hwmgr->smu_version & 0xFF));
+ adev->pm.fw_version = hwmgr->smu_version >> 8;
+
+ return smu8_request_smu_load_fw(hwmgr);
+}
+
+static int smu8_smu_init(struct pp_hwmgr *hwmgr)
+{
+ int ret = 0;
+ struct smu8_smumgr *smu8_smu;
+
+ smu8_smu = kzalloc(sizeof(struct smu8_smumgr), GFP_KERNEL);
+ if (smu8_smu == NULL)
+ return -ENOMEM;
+
+ hwmgr->smu_backend = smu8_smu;
+
+ smu8_smu->toc_buffer.data_size = 4096;
+ smu8_smu->smu_buffer.data_size =
+ ALIGN(UCODE_ID_RLC_SCRATCH_SIZE_BYTE, 32) +
+ ALIGN(UCODE_ID_RLC_SRM_ARAM_SIZE_BYTE, 32) +
+ ALIGN(UCODE_ID_RLC_SRM_DRAM_SIZE_BYTE, 32) +
+ ALIGN(sizeof(struct SMU8_MultimediaPowerLogData), 32) +
+ ALIGN(sizeof(struct SMU8_Fusion_ClkTable), 32);
+
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ smu8_smu->toc_buffer.data_size,
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &smu8_smu->toc_buffer.handle,
+ &smu8_smu->toc_buffer.mc_addr,
+ &smu8_smu->toc_buffer.kaddr);
+ if (ret)
+ goto err2;
+
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ smu8_smu->smu_buffer.data_size,
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &smu8_smu->smu_buffer.handle,
+ &smu8_smu->smu_buffer.mc_addr,
+ &smu8_smu->smu_buffer.kaddr);
+ if (ret)
+ goto err1;
+
+ if (0 != smu8_smu_populate_single_scratch_entry(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_SCRATCH,
+ UCODE_ID_RLC_SCRATCH_SIZE_BYTE,
+ &smu8_smu->scratch_buffer[smu8_smu->scratch_buffer_length++])) {
+ pr_err("Error when Populate Firmware Entry.\n");
+ goto err0;
+ }
+
+ if (0 != smu8_smu_populate_single_scratch_entry(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_ARAM,
+ UCODE_ID_RLC_SRM_ARAM_SIZE_BYTE,
+ &smu8_smu->scratch_buffer[smu8_smu->scratch_buffer_length++])) {
+ pr_err("Error when Populate Firmware Entry.\n");
+ goto err0;
+ }
+ if (0 != smu8_smu_populate_single_scratch_entry(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_DRAM,
+ UCODE_ID_RLC_SRM_DRAM_SIZE_BYTE,
+ &smu8_smu->scratch_buffer[smu8_smu->scratch_buffer_length++])) {
+ pr_err("Error when Populate Firmware Entry.\n");
+ goto err0;
+ }
+
+ if (0 != smu8_smu_populate_single_scratch_entry(hwmgr,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_POWER_PROFILING,
+ sizeof(struct SMU8_MultimediaPowerLogData),
+ &smu8_smu->scratch_buffer[smu8_smu->scratch_buffer_length++])) {
+ pr_err("Error when Populate Firmware Entry.\n");
+ goto err0;
+ }
+
+ if (0 != smu8_smu_populate_single_scratch_entry(hwmgr,
+ SMU8_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE,
+ sizeof(struct SMU8_Fusion_ClkTable),
+ &smu8_smu->scratch_buffer[smu8_smu->scratch_buffer_length++])) {
+ pr_err("Error when Populate Firmware Entry.\n");
+ goto err0;
+ }
+
+ return 0;
+
+err0:
+ amdgpu_bo_free_kernel(&smu8_smu->smu_buffer.handle,
+ &smu8_smu->smu_buffer.mc_addr,
+ &smu8_smu->smu_buffer.kaddr);
+err1:
+ amdgpu_bo_free_kernel(&smu8_smu->toc_buffer.handle,
+ &smu8_smu->toc_buffer.mc_addr,
+ &smu8_smu->toc_buffer.kaddr);
+err2:
+ kfree(smu8_smu);
+ return -EINVAL;
+}
+
+static int smu8_smu_fini(struct pp_hwmgr *hwmgr)
+{
+ struct smu8_smumgr *smu8_smu;
+
+ if (hwmgr == NULL || hwmgr->device == NULL)
+ return -EINVAL;
+
+ smu8_smu = hwmgr->smu_backend;
+ if (smu8_smu) {
+ amdgpu_bo_free_kernel(&smu8_smu->toc_buffer.handle,
+ &smu8_smu->toc_buffer.mc_addr,
+ &smu8_smu->toc_buffer.kaddr);
+ amdgpu_bo_free_kernel(&smu8_smu->smu_buffer.handle,
+ &smu8_smu->smu_buffer.mc_addr,
+ &smu8_smu->smu_buffer.kaddr);
+ kfree(smu8_smu);
+ }
+
+ return 0;
+}
+
+static bool smu8_dpm_check_smu_features(struct pp_hwmgr *hwmgr,
+ unsigned long check_feature)
+{
+ int result;
+ uint32_t features;
+
+ result = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_GetFeatureStatus,
+ 0,
+ &features);
+ if (result == 0) {
+ if (features & check_feature)
+ return true;
+ }
+
+ return false;
+}
+
+static bool smu8_is_dpm_running(struct pp_hwmgr *hwmgr)
+{
+ if (smu8_dpm_check_smu_features(hwmgr, SMU_EnabledFeatureScoreboard_SclkDpmOn))
+ return true;
+ return false;
+}
+
+const struct pp_smumgr_func smu8_smu_funcs = {
+ .name = "smu8_smu",
+ .smu_init = smu8_smu_init,
+ .smu_fini = smu8_smu_fini,
+ .start_smu = smu8_start_smu,
+ .check_fw_load_finish = smu8_check_fw_load_finish,
+ .request_smu_load_fw = NULL,
+ .request_smu_load_specific_fw = NULL,
+ .get_argument = smu8_get_argument,
+ .send_msg_to_smc = smu8_send_msg_to_smc,
+ .send_msg_to_smc_with_parameter = smu8_send_msg_to_smc_with_parameter,
+ .download_pptable_settings = smu8_download_pptable_settings,
+ .upload_pptable_settings = smu8_upload_pptable_settings,
+ .is_dpm_running = smu8_is_dpm_running,
+};
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu8_smumgr.h b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu8_smumgr.h
new file mode 100644
index 000000000000..c7b61222d258
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu8_smumgr.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _SMU8_SMUMGR_H_
+#define _SMU8_SMUMGR_H_
+
+
+#define MAX_NUM_FIRMWARE 8
+#define MAX_NUM_SCRATCH 11
+#define SMU8_SCRATCH_SIZE_NONGFX_CLOCKGATING 1024
+#define SMU8_SCRATCH_SIZE_NONGFX_GOLDENSETTING 2048
+#define SMU8_SCRATCH_SIZE_SDMA_METADATA 1024
+#define SMU8_SCRATCH_SIZE_IH ((2*256+1)*4)
+
+#define SMU_EnabledFeatureScoreboard_SclkDpmOn 0x00200000
+
+enum smu8_scratch_entry {
+ SMU8_SCRATCH_ENTRY_UCODE_ID_SDMA0 = 0,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_SDMA1,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_CE,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_PFP,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_ME,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT2,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_GMCON_RENG,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_G,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_SCRATCH,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_ARAM,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_DRAM,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_DMCU_ERAM,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_DMCU_IRAM,
+ SMU8_SCRATCH_ENTRY_UCODE_ID_POWER_PROFILING,
+ SMU8_SCRATCH_ENTRY_DATA_ID_SDMA_HALT,
+ SMU8_SCRATCH_ENTRY_DATA_ID_SYS_CLOCKGATING,
+ SMU8_SCRATCH_ENTRY_DATA_ID_SDMA_RING_REGS,
+ SMU8_SCRATCH_ENTRY_DATA_ID_NONGFX_REINIT,
+ SMU8_SCRATCH_ENTRY_DATA_ID_SDMA_START,
+ SMU8_SCRATCH_ENTRY_DATA_ID_IH_REGISTERS,
+ SMU8_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE
+};
+
+struct smu8_buffer_entry {
+ uint32_t data_size;
+ uint64_t mc_addr;
+ void *kaddr;
+ enum smu8_scratch_entry firmware_ID;
+ struct amdgpu_bo *handle; /* as bo handle used when release bo */
+};
+
+struct smu8_register_index_data_pair {
+ uint32_t offset;
+ uint32_t value;
+};
+
+struct smu8_ih_meta_data {
+ uint32_t command;
+ struct smu8_register_index_data_pair register_index_value_pair[1];
+};
+
+struct smu8_smumgr {
+ uint8_t driver_buffer_length;
+ uint8_t scratch_buffer_length;
+ uint16_t toc_entry_used_count;
+ uint16_t toc_entry_initialize_index;
+ uint16_t toc_entry_power_profiling_index;
+ uint16_t toc_entry_aram;
+ uint16_t toc_entry_ih_register_restore_task_index;
+ uint16_t toc_entry_clock_table;
+ uint16_t ih_register_restore_task_size;
+ uint16_t smu_buffer_used_bytes;
+
+ struct smu8_buffer_entry toc_buffer;
+ struct smu8_buffer_entry smu_buffer;
+ struct smu8_buffer_entry firmware_buffer;
+ struct smu8_buffer_entry driver_buffer[MAX_NUM_FIRMWARE];
+ struct smu8_buffer_entry meta_data_buffer[MAX_NUM_FIRMWARE];
+ struct smu8_buffer_entry scratch_buffer[MAX_NUM_SCRATCH];
+};
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu9_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu9_smumgr.c
new file mode 100644
index 000000000000..8a9aee85043e
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu9_smumgr.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "smumgr.h"
+#include "vega10_inc.h"
+#include "soc15_common.h"
+#include "pp_debug.h"
+
+
+/* MP Apertures */
+#define MP0_Public 0x03800000
+#define MP0_SRAM 0x03900000
+#define MP1_Public 0x03b00000
+#define MP1_SRAM 0x03c00004
+
+#define smnMP1_FIRMWARE_FLAGS 0x3010028
+
+bool smu9_is_smc_ram_running(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t mp1_fw_flags;
+
+ mp1_fw_flags = RREG32_PCIE(MP1_Public |
+ (smnMP1_FIRMWARE_FLAGS & 0xffffffff));
+
+ if (mp1_fw_flags & MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED_MASK)
+ return true;
+
+ return false;
+}
+
+/*
+ * Check if SMC has responded to previous message.
+ *
+ * @param smumgr the address of the powerplay hardware manager.
+ * @return TRUE SMC has responded, FALSE otherwise.
+ */
+static uint32_t smu9_wait_for_response(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t reg;
+ uint32_t ret;
+
+ if (hwmgr->pp_one_vf) {
+ reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_SMN_C2PMSG_103);
+
+ ret = phm_wait_for_register_unequal(hwmgr, reg,
+ 0, MP1_C2PMSG_103__CONTENT_MASK);
+
+ if (ret)
+ pr_err("No response from smu\n");
+
+ return RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_103);
+ } else {
+ reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_SMN_C2PMSG_90);
+
+ ret = phm_wait_for_register_unequal(hwmgr, reg,
+ 0, MP1_C2PMSG_90__CONTENT_MASK);
+
+ if (ret)
+ pr_err("No response from smu\n");
+ return RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90);
+ }
+}
+
+/*
+ * Send a message to the SMC, and do not wait for its response.
+ * @param smumgr the address of the powerplay hardware manager.
+ * @param msg the message to send.
+ * @return Always return 0.
+ */
+static int smu9_send_msg_to_smc_without_waiting(struct pp_hwmgr *hwmgr,
+ uint16_t msg)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ if (hwmgr->pp_one_vf) {
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_101, msg);
+ } else {
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_66, msg);
+ }
+
+ return 0;
+}
+
+/*
+ * Send a message to the SMC, and wait for its response.
+ * @param hwmgr the address of the powerplay hardware manager.
+ * @param msg the message to send.
+ * @return Always return 0.
+ */
+int smu9_send_msg_to_smc(struct pp_hwmgr *hwmgr, uint16_t msg)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t ret;
+
+ smu9_wait_for_response(hwmgr);
+
+ if (hwmgr->pp_one_vf)
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_103, 0);
+ else
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90, 0);
+
+ smu9_send_msg_to_smc_without_waiting(hwmgr, msg);
+
+ ret = smu9_wait_for_response(hwmgr);
+ if (ret != 1)
+ pr_err("Failed to send message: 0x%x, ret value: 0x%x\n", msg, ret);
+
+ return 0;
+}
+
+/*
+ * Send a message to the SMC with parameter
+ * @param hwmgr: the address of the powerplay hardware manager.
+ * @param msg: the message to send.
+ * @param parameter: the parameter to send
+ * @return Always return 0.
+ */
+int smu9_send_msg_to_smc_with_parameter(struct pp_hwmgr *hwmgr,
+ uint16_t msg, uint32_t parameter)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t ret;
+
+ smu9_wait_for_response(hwmgr);
+
+ if (hwmgr->pp_one_vf) {
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_103, 0);
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_102, parameter);
+ } else {
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90, 0);
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_82, parameter);
+ }
+
+ smu9_send_msg_to_smc_without_waiting(hwmgr, msg);
+
+ ret = smu9_wait_for_response(hwmgr);
+ if (ret != 1)
+ pr_err("Failed message: 0x%x, input parameter: 0x%x, error code: 0x%x\n", msg, parameter, ret);
+
+ return 0;
+}
+
+uint32_t smu9_get_argument(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ if (hwmgr->pp_one_vf)
+ return RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_102);
+ else
+ return RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_82);
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu9_smumgr.h b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu9_smumgr.h
new file mode 100644
index 000000000000..1462279ca128
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu9_smumgr.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _SMU9_SMUMANAGER_H_
+#define _SMU9_SMUMANAGER_H_
+
+bool smu9_is_smc_ram_running(struct pp_hwmgr *hwmgr);
+int smu9_send_msg_to_smc(struct pp_hwmgr *hwmgr, uint16_t msg);
+int smu9_send_msg_to_smc_with_parameter(struct pp_hwmgr *hwmgr,
+ uint16_t msg, uint32_t parameter);
+uint32_t smu9_get_argument(struct pp_hwmgr *hwmgr);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smumgr.c
new file mode 100644
index 000000000000..b6921db3c130
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smumgr.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <drm/amdgpu_drm.h>
+#include "smumgr.h"
+
+MODULE_FIRMWARE("amdgpu/bonaire_smc.bin");
+MODULE_FIRMWARE("amdgpu/bonaire_k_smc.bin");
+MODULE_FIRMWARE("amdgpu/hawaii_smc.bin");
+MODULE_FIRMWARE("amdgpu/hawaii_k_smc.bin");
+MODULE_FIRMWARE("amdgpu/topaz_smc.bin");
+MODULE_FIRMWARE("amdgpu/topaz_k_smc.bin");
+MODULE_FIRMWARE("amdgpu/tonga_smc.bin");
+MODULE_FIRMWARE("amdgpu/tonga_k_smc.bin");
+MODULE_FIRMWARE("amdgpu/fiji_smc.bin");
+MODULE_FIRMWARE("amdgpu/polaris10_smc.bin");
+MODULE_FIRMWARE("amdgpu/polaris10_smc_sk.bin");
+MODULE_FIRMWARE("amdgpu/polaris10_k_smc.bin");
+MODULE_FIRMWARE("amdgpu/polaris10_k2_smc.bin");
+MODULE_FIRMWARE("amdgpu/polaris11_smc.bin");
+MODULE_FIRMWARE("amdgpu/polaris11_smc_sk.bin");
+MODULE_FIRMWARE("amdgpu/polaris11_k_smc.bin");
+MODULE_FIRMWARE("amdgpu/polaris11_k2_smc.bin");
+MODULE_FIRMWARE("amdgpu/polaris12_smc.bin");
+MODULE_FIRMWARE("amdgpu/polaris12_k_smc.bin");
+MODULE_FIRMWARE("amdgpu/vegam_smc.bin");
+MODULE_FIRMWARE("amdgpu/vega10_smc.bin");
+MODULE_FIRMWARE("amdgpu/vega10_acg_smc.bin");
+MODULE_FIRMWARE("amdgpu/vega12_smc.bin");
+MODULE_FIRMWARE("amdgpu/vega20_smc.bin");
+
+int smum_thermal_avfs_enable(struct pp_hwmgr *hwmgr)
+{
+ if (NULL != hwmgr->smumgr_funcs->thermal_avfs_enable)
+ return hwmgr->smumgr_funcs->thermal_avfs_enable(hwmgr);
+
+ return 0;
+}
+
+int smum_thermal_setup_fan_table(struct pp_hwmgr *hwmgr)
+{
+ if (NULL != hwmgr->smumgr_funcs->thermal_setup_fan_table)
+ return hwmgr->smumgr_funcs->thermal_setup_fan_table(hwmgr);
+
+ return 0;
+}
+
+int smum_update_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+
+ if (NULL != hwmgr->smumgr_funcs->update_sclk_threshold)
+ return hwmgr->smumgr_funcs->update_sclk_threshold(hwmgr);
+
+ return 0;
+}
+
+int smum_update_smc_table(struct pp_hwmgr *hwmgr, uint32_t type)
+{
+
+ if (NULL != hwmgr->smumgr_funcs->update_smc_table)
+ return hwmgr->smumgr_funcs->update_smc_table(hwmgr, type);
+
+ return 0;
+}
+
+uint32_t smum_get_offsetof(struct pp_hwmgr *hwmgr, uint32_t type, uint32_t member)
+{
+ if (NULL != hwmgr->smumgr_funcs->get_offsetof)
+ return hwmgr->smumgr_funcs->get_offsetof(type, member);
+
+ return 0;
+}
+
+int smum_process_firmware_header(struct pp_hwmgr *hwmgr)
+{
+ if (NULL != hwmgr->smumgr_funcs->process_firmware_header)
+ return hwmgr->smumgr_funcs->process_firmware_header(hwmgr);
+ return 0;
+}
+
+uint32_t smum_get_mac_definition(struct pp_hwmgr *hwmgr, uint32_t value)
+{
+ if (NULL != hwmgr->smumgr_funcs->get_mac_definition)
+ return hwmgr->smumgr_funcs->get_mac_definition(value);
+
+ return 0;
+}
+
+int smum_download_powerplay_table(struct pp_hwmgr *hwmgr, void **table)
+{
+ if (NULL != hwmgr->smumgr_funcs->download_pptable_settings)
+ return hwmgr->smumgr_funcs->download_pptable_settings(hwmgr,
+ table);
+ return 0;
+}
+
+int smum_upload_powerplay_table(struct pp_hwmgr *hwmgr)
+{
+ if (NULL != hwmgr->smumgr_funcs->upload_pptable_settings)
+ return hwmgr->smumgr_funcs->upload_pptable_settings(hwmgr);
+
+ return 0;
+}
+
+int smum_send_msg_to_smc(struct pp_hwmgr *hwmgr, uint16_t msg, uint32_t *resp)
+{
+ int ret = 0;
+
+ if (hwmgr == NULL ||
+ hwmgr->smumgr_funcs->send_msg_to_smc == NULL ||
+ (resp && !hwmgr->smumgr_funcs->get_argument))
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->msg_lock);
+
+ ret = hwmgr->smumgr_funcs->send_msg_to_smc(hwmgr, msg);
+ if (ret) {
+ mutex_unlock(&hwmgr->msg_lock);
+ return ret;
+ }
+
+ if (resp)
+ *resp = hwmgr->smumgr_funcs->get_argument(hwmgr);
+
+ mutex_unlock(&hwmgr->msg_lock);
+
+ return ret;
+}
+
+int smum_send_msg_to_smc_with_parameter(struct pp_hwmgr *hwmgr,
+ uint16_t msg,
+ uint32_t parameter,
+ uint32_t *resp)
+{
+ int ret = 0;
+
+ if (hwmgr == NULL ||
+ hwmgr->smumgr_funcs->send_msg_to_smc_with_parameter == NULL ||
+ (resp && !hwmgr->smumgr_funcs->get_argument))
+ return -EINVAL;
+
+ mutex_lock(&hwmgr->msg_lock);
+
+ ret = hwmgr->smumgr_funcs->send_msg_to_smc_with_parameter(
+ hwmgr, msg, parameter);
+ if (ret) {
+ mutex_unlock(&hwmgr->msg_lock);
+ return ret;
+ }
+
+ if (resp)
+ *resp = hwmgr->smumgr_funcs->get_argument(hwmgr);
+
+ mutex_unlock(&hwmgr->msg_lock);
+
+ return ret;
+}
+
+int smum_init_smc_table(struct pp_hwmgr *hwmgr)
+{
+ if (NULL != hwmgr->smumgr_funcs->init_smc_table)
+ return hwmgr->smumgr_funcs->init_smc_table(hwmgr);
+
+ return 0;
+}
+
+int smum_populate_all_graphic_levels(struct pp_hwmgr *hwmgr)
+{
+ if (NULL != hwmgr->smumgr_funcs->populate_all_graphic_levels)
+ return hwmgr->smumgr_funcs->populate_all_graphic_levels(hwmgr);
+
+ return 0;
+}
+
+int smum_populate_all_memory_levels(struct pp_hwmgr *hwmgr)
+{
+ if (NULL != hwmgr->smumgr_funcs->populate_all_memory_levels)
+ return hwmgr->smumgr_funcs->populate_all_memory_levels(hwmgr);
+
+ return 0;
+}
+
+/*this interface is needed by island ci/vi */
+int smum_initialize_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+ if (NULL != hwmgr->smumgr_funcs->initialize_mc_reg_table)
+ return hwmgr->smumgr_funcs->initialize_mc_reg_table(hwmgr);
+
+ return 0;
+}
+
+bool smum_is_dpm_running(struct pp_hwmgr *hwmgr)
+{
+ if (NULL != hwmgr->smumgr_funcs->is_dpm_running)
+ return hwmgr->smumgr_funcs->is_dpm_running(hwmgr);
+
+ return true;
+}
+
+bool smum_is_hw_avfs_present(struct pp_hwmgr *hwmgr)
+{
+ if (hwmgr->smumgr_funcs->is_hw_avfs_present)
+ return hwmgr->smumgr_funcs->is_hw_avfs_present(hwmgr);
+
+ return false;
+}
+
+int smum_update_dpm_settings(struct pp_hwmgr *hwmgr, void *profile_setting)
+{
+ if (hwmgr->smumgr_funcs->update_dpm_settings)
+ return hwmgr->smumgr_funcs->update_dpm_settings(hwmgr, profile_setting);
+
+ return -EINVAL;
+}
+
+int smum_smc_table_manager(struct pp_hwmgr *hwmgr, uint8_t *table, uint16_t table_id, bool rw)
+{
+ if (hwmgr->smumgr_funcs->smc_table_manager)
+ return hwmgr->smumgr_funcs->smc_table_manager(hwmgr, table, table_id, rw);
+
+ return -EINVAL;
+}
+
+int smum_stop_smc(struct pp_hwmgr *hwmgr)
+{
+ if (hwmgr->smumgr_funcs->stop_smc)
+ return hwmgr->smumgr_funcs->stop_smc(hwmgr);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/tonga_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/tonga_smumgr.c
new file mode 100644
index 000000000000..4bfadb49521b
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/tonga_smumgr.c
@@ -0,0 +1,3268 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "pp_debug.h"
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/gfp.h>
+
+#include "smumgr.h"
+#include "tonga_smumgr.h"
+#include "smu_ucode_xfer_vi.h"
+#include "tonga_ppsmc.h"
+#include "smu/smu_7_1_2_d.h"
+#include "smu/smu_7_1_2_sh_mask.h"
+#include "cgs_common.h"
+#include "smu7_smumgr.h"
+
+#include "smu7_dyn_defaults.h"
+
+#include "smu7_hwmgr.h"
+#include "hardwaremanager.h"
+#include "ppatomctrl.h"
+
+#include "atombios.h"
+
+#include "pppcielanes.h"
+#include "pp_endian.h"
+
+#include "gmc/gmc_8_1_d.h"
+#include "gmc/gmc_8_1_sh_mask.h"
+
+#include "bif/bif_5_0_d.h"
+#include "bif/bif_5_0_sh_mask.h"
+
+#include "dce/dce_10_0_d.h"
+#include "dce/dce_10_0_sh_mask.h"
+
+#define POWERTUNE_DEFAULT_SET_MAX 1
+#define MC_CG_ARB_FREQ_F1 0x0b
+#define VDDC_VDDCI_DELTA 200
+
+
+static const struct tonga_pt_defaults tonga_power_tune_data_set_array[POWERTUNE_DEFAULT_SET_MAX] = {
+/* sviLoadLIneEn, SviLoadLineVddC, TDC_VDDC_ThrottleReleaseLimitPerc, TDC_MAWt,
+ * TdcWaterfallCtl, DTEAmbientTempBase, DisplayCac, BAPM_TEMP_GRADIENT
+ */
+ {1, 0xF, 0xFD, 0x19,
+ 5, 45, 0, 0xB0000,
+ {0x79, 0x253, 0x25D, 0xAE, 0x72, 0x80, 0x83, 0x86, 0x6F, 0xC8,
+ 0xC9, 0xC9, 0x2F, 0x4D, 0x61},
+ {0x17C, 0x172, 0x180, 0x1BC, 0x1B3, 0x1BD, 0x206, 0x200, 0x203,
+ 0x25D, 0x25A, 0x255, 0x2C3, 0x2C5, 0x2B4}
+ },
+};
+
+/* [Fmin, Fmax, LDO_REFSEL, USE_FOR_LOW_FREQ] */
+static const uint16_t tonga_clock_stretcher_lookup_table[2][4] = {
+ {600, 1050, 3, 0},
+ {600, 1050, 6, 1}
+};
+
+/* [FF, SS] type, [] 4 voltage ranges,
+ * and [Floor Freq, Boundary Freq, VID min , VID max]
+ */
+static const uint32_t tonga_clock_stretcher_ddt_table[2][4][4] = {
+ { {265, 529, 120, 128}, {325, 650, 96, 119}, {430, 860, 32, 95}, {0, 0, 0, 31} },
+ { {275, 550, 104, 112}, {319, 638, 96, 103}, {360, 720, 64, 95}, {384, 768, 32, 63} }
+};
+
+/* [Use_For_Low_freq] value, [0%, 5%, 10%, 7.14%, 14.28%, 20%] */
+static const uint8_t tonga_clock_stretch_amount_conversion[2][6] = {
+ {0, 1, 3, 2, 4, 5},
+ {0, 2, 4, 5, 6, 5}
+};
+
+static int tonga_start_in_protection_mode(struct pp_hwmgr *hwmgr)
+{
+ int result;
+
+ /* Assert reset */
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 1);
+
+ result = smu7_upload_smu_firmware_image(hwmgr);
+ if (result)
+ return result;
+
+ /* Clear status */
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixSMU_STATUS, 0);
+
+ /* Enable clock */
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
+
+ /* De-assert reset */
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 0);
+
+ /* Set SMU Auto Start */
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMU_INPUT_DATA, AUTO_START, 1);
+
+ /* Clear firmware interrupt enable flag */
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixFIRMWARE_FLAGS, 0);
+
+ PHM_WAIT_VFPF_INDIRECT_FIELD(hwmgr, SMC_IND,
+ RCU_UC_EVENTS, INTERRUPTS_ENABLED, 1);
+
+ /**
+ * Call Test SMU message with 0x20000 offset to trigger SMU start
+ */
+ smu7_send_msg_to_smc_offset(hwmgr);
+
+ /* Wait for done bit to be set */
+ PHM_WAIT_VFPF_INDIRECT_FIELD_UNEQUAL(hwmgr, SMC_IND,
+ SMU_STATUS, SMU_DONE, 0);
+
+ /* Check pass/failed indicator */
+ if (1 != PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device,
+ CGS_IND_REG__SMC, SMU_STATUS, SMU_PASS)) {
+ pr_err("SMU Firmware start failed\n");
+ return -EINVAL;
+ }
+
+ /* Wait for firmware to initialize */
+ PHM_WAIT_VFPF_INDIRECT_FIELD(hwmgr, SMC_IND,
+ FIRMWARE_FLAGS, INTERRUPTS_ENABLED, 1);
+
+ return 0;
+}
+
+static int tonga_start_in_non_protection_mode(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+
+ /* wait for smc boot up */
+ PHM_WAIT_VFPF_INDIRECT_FIELD_UNEQUAL(hwmgr, SMC_IND,
+ RCU_UC_EVENTS, boot_seq_done, 0);
+
+ /*Clear firmware interrupt enable flag*/
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixFIRMWARE_FLAGS, 0);
+
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 1);
+
+ result = smu7_upload_smu_firmware_image(hwmgr);
+
+ if (result != 0)
+ return result;
+
+ /* Set smc instruct start point at 0x0 */
+ smu7_program_jump_on_start(hwmgr);
+
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
+
+ /*De-assert reset*/
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 0);
+
+ /* Wait for firmware to initialize */
+ PHM_WAIT_VFPF_INDIRECT_FIELD(hwmgr, SMC_IND,
+ FIRMWARE_FLAGS, INTERRUPTS_ENABLED, 1);
+
+ return result;
+}
+
+static int tonga_start_smu(struct pp_hwmgr *hwmgr)
+{
+ struct tonga_smumgr *priv = hwmgr->smu_backend;
+ int result;
+
+ /* Only start SMC if SMC RAM is not running */
+ if (!smu7_is_smc_ram_running(hwmgr) && hwmgr->not_vf) {
+ /*Check if SMU is running in protected mode*/
+ if (0 == PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMU_FIRMWARE, SMU_MODE)) {
+ result = tonga_start_in_non_protection_mode(hwmgr);
+ if (result)
+ return result;
+ } else {
+ result = tonga_start_in_protection_mode(hwmgr);
+ if (result)
+ return result;
+ }
+ }
+
+ /* Setup SoftRegsStart here to visit the register UcodeLoadStatus
+ * to check fw loading state
+ */
+ smu7_read_smc_sram_dword(hwmgr,
+ SMU72_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU72_Firmware_Header, SoftRegisters),
+ &(priv->smu7_data.soft_regs_start), 0x40000);
+
+ result = smu7_request_smu_load_fw(hwmgr);
+
+ return result;
+}
+
+static int tonga_smu_init(struct pp_hwmgr *hwmgr)
+{
+ struct tonga_smumgr *tonga_priv = NULL;
+
+ tonga_priv = kzalloc(sizeof(struct tonga_smumgr), GFP_KERNEL);
+ if (tonga_priv == NULL)
+ return -ENOMEM;
+
+ hwmgr->smu_backend = tonga_priv;
+
+ if (smu7_init(hwmgr)) {
+ kfree(tonga_priv);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int tonga_get_dependency_volt_by_clk(struct pp_hwmgr *hwmgr,
+ phm_ppt_v1_clock_voltage_dependency_table *allowed_clock_voltage_table,
+ uint32_t clock, SMU_VoltageLevel *voltage, uint32_t *mvdd)
+{
+ uint32_t i = 0;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *pptable_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ /* clock - voltage dependency table is empty table */
+ if (allowed_clock_voltage_table->count == 0)
+ return -EINVAL;
+
+ for (i = 0; i < allowed_clock_voltage_table->count; i++) {
+ /* find first sclk bigger than request */
+ if (allowed_clock_voltage_table->entries[i].clk >= clock) {
+ voltage->VddGfx = phm_get_voltage_index(
+ pptable_info->vddgfx_lookup_table,
+ allowed_clock_voltage_table->entries[i].vddgfx);
+ voltage->Vddc = phm_get_voltage_index(
+ pptable_info->vddc_lookup_table,
+ allowed_clock_voltage_table->entries[i].vddc);
+
+ if (allowed_clock_voltage_table->entries[i].vddci)
+ voltage->Vddci =
+ phm_get_voltage_id(&data->vddci_voltage_table, allowed_clock_voltage_table->entries[i].vddci);
+ else
+ voltage->Vddci =
+ phm_get_voltage_id(&data->vddci_voltage_table,
+ allowed_clock_voltage_table->entries[i].vddc - VDDC_VDDCI_DELTA);
+
+
+ if (allowed_clock_voltage_table->entries[i].mvdd)
+ *mvdd = (uint32_t) allowed_clock_voltage_table->entries[i].mvdd;
+
+ voltage->Phases = 1;
+ return 0;
+ }
+ }
+
+ /* sclk is bigger than max sclk in the dependence table */
+ voltage->VddGfx = phm_get_voltage_index(pptable_info->vddgfx_lookup_table,
+ allowed_clock_voltage_table->entries[i-1].vddgfx);
+ voltage->Vddc = phm_get_voltage_index(pptable_info->vddc_lookup_table,
+ allowed_clock_voltage_table->entries[i-1].vddc);
+
+ if (allowed_clock_voltage_table->entries[i-1].vddci)
+ voltage->Vddci = phm_get_voltage_id(&data->vddci_voltage_table,
+ allowed_clock_voltage_table->entries[i-1].vddci);
+
+ if (allowed_clock_voltage_table->entries[i-1].mvdd)
+ *mvdd = (uint32_t) allowed_clock_voltage_table->entries[i-1].mvdd;
+
+ return 0;
+}
+
+static int tonga_populate_smc_vddc_table(struct pp_hwmgr *hwmgr,
+ SMU72_Discrete_DpmTable *table)
+{
+ unsigned int count;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
+ table->VddcLevelCount = data->vddc_voltage_table.count;
+ for (count = 0; count < table->VddcLevelCount; count++) {
+ table->VddcTable[count] =
+ PP_HOST_TO_SMC_US(data->vddc_voltage_table.entries[count].value * VOLTAGE_SCALE);
+ }
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VddcLevelCount);
+ }
+ return 0;
+}
+
+static int tonga_populate_smc_vdd_gfx_table(struct pp_hwmgr *hwmgr,
+ SMU72_Discrete_DpmTable *table)
+{
+ unsigned int count;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vdd_gfx_control) {
+ table->VddGfxLevelCount = data->vddgfx_voltage_table.count;
+ for (count = 0; count < data->vddgfx_voltage_table.count; count++) {
+ table->VddGfxTable[count] =
+ PP_HOST_TO_SMC_US(data->vddgfx_voltage_table.entries[count].value * VOLTAGE_SCALE);
+ }
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VddGfxLevelCount);
+ }
+ return 0;
+}
+
+static int tonga_populate_smc_vdd_ci_table(struct pp_hwmgr *hwmgr,
+ SMU72_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t count;
+
+ table->VddciLevelCount = data->vddci_voltage_table.count;
+ for (count = 0; count < table->VddciLevelCount; count++) {
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) {
+ table->VddciTable[count] =
+ PP_HOST_TO_SMC_US(data->vddci_voltage_table.entries[count].value * VOLTAGE_SCALE);
+ } else if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
+ table->SmioTable1.Pattern[count].Voltage =
+ PP_HOST_TO_SMC_US(data->vddci_voltage_table.entries[count].value * VOLTAGE_SCALE);
+ /* Index into DpmTable.Smio. Drive bits from Smio entry to get this voltage level. */
+ table->SmioTable1.Pattern[count].Smio =
+ (uint8_t) count;
+ table->Smio[count] |=
+ data->vddci_voltage_table.entries[count].smio_low;
+ table->VddciTable[count] =
+ PP_HOST_TO_SMC_US(data->vddci_voltage_table.entries[count].value * VOLTAGE_SCALE);
+ }
+ }
+
+ table->SmioMask1 = data->vddci_voltage_table.mask_low;
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VddciLevelCount);
+
+ return 0;
+}
+
+static int tonga_populate_smc_mvdd_table(struct pp_hwmgr *hwmgr,
+ SMU72_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t count;
+
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
+ table->MvddLevelCount = data->mvdd_voltage_table.count;
+ for (count = 0; count < table->MvddLevelCount; count++) {
+ table->SmioTable2.Pattern[count].Voltage =
+ PP_HOST_TO_SMC_US(data->mvdd_voltage_table.entries[count].value * VOLTAGE_SCALE);
+ /* Index into DpmTable.Smio. Drive bits from Smio entry to get this voltage level.*/
+ table->SmioTable2.Pattern[count].Smio =
+ (uint8_t) count;
+ table->Smio[count] |=
+ data->mvdd_voltage_table.entries[count].smio_low;
+ }
+ table->SmioMask2 = data->mvdd_voltage_table.mask_low;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->MvddLevelCount);
+ }
+
+ return 0;
+}
+
+static int tonga_populate_cac_tables(struct pp_hwmgr *hwmgr,
+ SMU72_Discrete_DpmTable *table)
+{
+ uint32_t count;
+ uint8_t index = 0;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *pptable_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_voltage_lookup_table *vddgfx_lookup_table =
+ pptable_info->vddgfx_lookup_table;
+ struct phm_ppt_v1_voltage_lookup_table *vddc_lookup_table =
+ pptable_info->vddc_lookup_table;
+
+ /* table is already swapped, so in order to use the value from it
+ * we need to swap it back.
+ */
+ uint32_t vddc_level_count = PP_SMC_TO_HOST_UL(table->VddcLevelCount);
+ uint32_t vddgfx_level_count = PP_SMC_TO_HOST_UL(table->VddGfxLevelCount);
+
+ for (count = 0; count < vddc_level_count; count++) {
+ /* We are populating vddc CAC data to BapmVddc table in split and merged mode */
+ index = phm_get_voltage_index(vddc_lookup_table,
+ data->vddc_voltage_table.entries[count].value);
+ table->BapmVddcVidLoSidd[count] =
+ convert_to_vid(vddc_lookup_table->entries[index].us_cac_low);
+ table->BapmVddcVidHiSidd[count] =
+ convert_to_vid(vddc_lookup_table->entries[index].us_cac_mid);
+ table->BapmVddcVidHiSidd2[count] =
+ convert_to_vid(vddc_lookup_table->entries[index].us_cac_high);
+ }
+
+ if (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) {
+ /* We are populating vddgfx CAC data to BapmVddgfx table in split mode */
+ for (count = 0; count < vddgfx_level_count; count++) {
+ index = phm_get_voltage_index(vddgfx_lookup_table,
+ convert_to_vid(vddgfx_lookup_table->entries[index].us_cac_mid));
+ table->BapmVddGfxVidHiSidd2[count] =
+ convert_to_vid(vddgfx_lookup_table->entries[index].us_cac_high);
+ }
+ } else {
+ for (count = 0; count < vddc_level_count; count++) {
+ index = phm_get_voltage_index(vddc_lookup_table,
+ data->vddc_voltage_table.entries[count].value);
+ table->BapmVddGfxVidLoSidd[count] =
+ convert_to_vid(vddc_lookup_table->entries[index].us_cac_low);
+ table->BapmVddGfxVidHiSidd[count] =
+ convert_to_vid(vddc_lookup_table->entries[index].us_cac_mid);
+ table->BapmVddGfxVidHiSidd2[count] =
+ convert_to_vid(vddc_lookup_table->entries[index].us_cac_high);
+ }
+ }
+
+ return 0;
+}
+
+static int tonga_populate_smc_voltage_tables(struct pp_hwmgr *hwmgr,
+ SMU72_Discrete_DpmTable *table)
+{
+ int result;
+
+ result = tonga_populate_smc_vddc_table(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "can not populate VDDC voltage table to SMC",
+ return -EINVAL);
+
+ result = tonga_populate_smc_vdd_ci_table(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "can not populate VDDCI voltage table to SMC",
+ return -EINVAL);
+
+ result = tonga_populate_smc_vdd_gfx_table(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "can not populate VDDGFX voltage table to SMC",
+ return -EINVAL);
+
+ result = tonga_populate_smc_mvdd_table(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "can not populate MVDD voltage table to SMC",
+ return -EINVAL);
+
+ result = tonga_populate_cac_tables(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "can not populate CAC voltage tables to SMC",
+ return -EINVAL);
+
+ return 0;
+}
+
+static int tonga_populate_ulv_level(struct pp_hwmgr *hwmgr,
+ struct SMU72_Discrete_Ulv *state)
+{
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ state->CcPwrDynRm = 0;
+ state->CcPwrDynRm1 = 0;
+
+ state->VddcOffset = (uint16_t) table_info->us_ulv_voltage_offset;
+ state->VddcOffsetVid = (uint8_t)(table_info->us_ulv_voltage_offset *
+ VOLTAGE_VID_OFFSET_SCALE2 / VOLTAGE_VID_OFFSET_SCALE1);
+
+ state->VddcPhase = 1;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm);
+ CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm1);
+ CONVERT_FROM_HOST_TO_SMC_US(state->VddcOffset);
+
+ return 0;
+}
+
+static int tonga_populate_ulv_state(struct pp_hwmgr *hwmgr,
+ struct SMU72_Discrete_DpmTable *table)
+{
+ return tonga_populate_ulv_level(hwmgr, &table->Ulv);
+}
+
+static int tonga_populate_smc_link_level(struct pp_hwmgr *hwmgr, SMU72_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct smu7_dpm_table *dpm_table = &data->dpm_table;
+ struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(hwmgr->smu_backend);
+ uint32_t i;
+
+ /* Index (dpm_table->pcie_speed_table.count) is reserved for PCIE boot level. */
+ for (i = 0; i <= dpm_table->pcie_speed_table.count; i++) {
+ table->LinkLevel[i].PcieGenSpeed =
+ (uint8_t)dpm_table->pcie_speed_table.dpm_levels[i].value;
+ table->LinkLevel[i].PcieLaneCount =
+ (uint8_t)encode_pcie_lane_width(dpm_table->pcie_speed_table.dpm_levels[i].param1);
+ table->LinkLevel[i].EnabledForActivity =
+ 1;
+ table->LinkLevel[i].SPC =
+ (uint8_t)(data->pcie_spc_cap & 0xff);
+ table->LinkLevel[i].DownThreshold =
+ PP_HOST_TO_SMC_UL(5);
+ table->LinkLevel[i].UpThreshold =
+ PP_HOST_TO_SMC_UL(30);
+ }
+
+ smu_data->smc_state_table.LinkLevelCount =
+ (uint8_t)dpm_table->pcie_speed_table.count;
+ data->dpm_level_enable_mask.pcie_dpm_enable_mask =
+ phm_get_dpm_level_enable_mask_value(&dpm_table->pcie_speed_table);
+
+ return 0;
+}
+
+static int tonga_calculate_sclk_params(struct pp_hwmgr *hwmgr,
+ uint32_t engine_clock, SMU72_Discrete_GraphicsLevel *sclk)
+{
+ const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ pp_atomctrl_clock_dividers_vi dividers;
+ uint32_t spll_func_cntl = data->clock_registers.vCG_SPLL_FUNC_CNTL;
+ uint32_t spll_func_cntl_3 = data->clock_registers.vCG_SPLL_FUNC_CNTL_3;
+ uint32_t spll_func_cntl_4 = data->clock_registers.vCG_SPLL_FUNC_CNTL_4;
+ uint32_t cg_spll_spread_spectrum = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM;
+ uint32_t cg_spll_spread_spectrum_2 = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2;
+ uint32_t reference_clock;
+ uint32_t reference_divider;
+ uint32_t fbdiv;
+ int result;
+
+ /* get the engine clock dividers for this clock value*/
+ result = atomctrl_get_engine_pll_dividers_vi(hwmgr, engine_clock, &dividers);
+
+ PP_ASSERT_WITH_CODE(result == 0,
+ "Error retrieving Engine Clock dividers from VBIOS.", return result);
+
+ /* To get FBDIV we need to multiply this by 16384 and divide it by Fref.*/
+ reference_clock = atomctrl_get_reference_clock(hwmgr);
+
+ reference_divider = 1 + dividers.uc_pll_ref_div;
+
+ /* low 14 bits is fraction and high 12 bits is divider*/
+ fbdiv = dividers.ul_fb_div.ul_fb_divider & 0x3FFFFFF;
+
+ /* SPLL_FUNC_CNTL setup*/
+ spll_func_cntl = PHM_SET_FIELD(spll_func_cntl,
+ CG_SPLL_FUNC_CNTL, SPLL_REF_DIV, dividers.uc_pll_ref_div);
+ spll_func_cntl = PHM_SET_FIELD(spll_func_cntl,
+ CG_SPLL_FUNC_CNTL, SPLL_PDIV_A, dividers.uc_pll_post_div);
+
+ /* SPLL_FUNC_CNTL_3 setup*/
+ spll_func_cntl_3 = PHM_SET_FIELD(spll_func_cntl_3,
+ CG_SPLL_FUNC_CNTL_3, SPLL_FB_DIV, fbdiv);
+
+ /* set to use fractional accumulation*/
+ spll_func_cntl_3 = PHM_SET_FIELD(spll_func_cntl_3,
+ CG_SPLL_FUNC_CNTL_3, SPLL_DITHEN, 1);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_EngineSpreadSpectrumSupport)) {
+ pp_atomctrl_internal_ss_info ss_info;
+
+ uint32_t vcoFreq = engine_clock * dividers.uc_pll_post_div;
+ if (0 == atomctrl_get_engine_clock_spread_spectrum(hwmgr, vcoFreq, &ss_info)) {
+ /*
+ * ss_info.speed_spectrum_percentage -- in unit of 0.01%
+ * ss_info.speed_spectrum_rate -- in unit of khz
+ */
+ /* clks = reference_clock * 10 / (REFDIV + 1) / speed_spectrum_rate / 2 */
+ uint32_t clkS = reference_clock * 5 / (reference_divider * ss_info.speed_spectrum_rate);
+
+ /* clkv = 2 * D * fbdiv / NS */
+ uint32_t clkV = 4 * ss_info.speed_spectrum_percentage * fbdiv / (clkS * 10000);
+
+ cg_spll_spread_spectrum =
+ PHM_SET_FIELD(cg_spll_spread_spectrum, CG_SPLL_SPREAD_SPECTRUM, CLKS, clkS);
+ cg_spll_spread_spectrum =
+ PHM_SET_FIELD(cg_spll_spread_spectrum, CG_SPLL_SPREAD_SPECTRUM, SSEN, 1);
+ cg_spll_spread_spectrum_2 =
+ PHM_SET_FIELD(cg_spll_spread_spectrum_2, CG_SPLL_SPREAD_SPECTRUM_2, CLKV, clkV);
+ }
+ }
+
+ sclk->SclkFrequency = engine_clock;
+ sclk->CgSpllFuncCntl3 = spll_func_cntl_3;
+ sclk->CgSpllFuncCntl4 = spll_func_cntl_4;
+ sclk->SpllSpreadSpectrum = cg_spll_spread_spectrum;
+ sclk->SpllSpreadSpectrum2 = cg_spll_spread_spectrum_2;
+ sclk->SclkDid = (uint8_t)dividers.pll_post_divider;
+
+ return 0;
+}
+
+static int tonga_populate_single_graphic_level(struct pp_hwmgr *hwmgr,
+ uint32_t engine_clock,
+ SMU72_Discrete_GraphicsLevel *graphic_level)
+{
+ int result;
+ uint32_t mvdd;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *pptable_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ phm_ppt_v1_clock_voltage_dependency_table *vdd_dep_table = NULL;
+
+ result = tonga_calculate_sclk_params(hwmgr, engine_clock, graphic_level);
+
+ if (hwmgr->od_enabled)
+ vdd_dep_table = (phm_ppt_v1_clock_voltage_dependency_table *)&data->odn_dpm_table.vdd_dependency_on_sclk;
+ else
+ vdd_dep_table = pptable_info->vdd_dep_on_sclk;
+
+ /* populate graphics levels*/
+ result = tonga_get_dependency_volt_by_clk(hwmgr,
+ vdd_dep_table, engine_clock,
+ &graphic_level->MinVoltage, &mvdd);
+ PP_ASSERT_WITH_CODE((!result),
+ "can not find VDDC voltage value for VDDC "
+ "engine clock dependency table", return result);
+
+ /* SCLK frequency in units of 10KHz*/
+ graphic_level->SclkFrequency = engine_clock;
+ /* Indicates maximum activity level for this performance level. 50% for now*/
+ graphic_level->ActivityLevel = data->current_profile_setting.sclk_activity;
+
+ graphic_level->CcPwrDynRm = 0;
+ graphic_level->CcPwrDynRm1 = 0;
+ /* this level can be used if activity is high enough.*/
+ graphic_level->EnabledForActivity = 0;
+ /* this level can be used for throttling.*/
+ graphic_level->EnabledForThrottle = 1;
+ graphic_level->UpHyst = data->current_profile_setting.sclk_up_hyst;
+ graphic_level->DownHyst = data->current_profile_setting.sclk_down_hyst;
+ graphic_level->VoltageDownHyst = 0;
+ graphic_level->PowerThrottle = 0;
+
+ data->display_timing.min_clock_in_sr =
+ hwmgr->display_config->min_core_set_clock_in_sr;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkDeepSleep))
+ graphic_level->DeepSleepDivId =
+ smu7_get_sleep_divider_id_from_clock(engine_clock,
+ data->display_timing.min_clock_in_sr);
+
+ /* Default to slow, highest DPM level will be set to PPSMC_DISPLAY_WATERMARK_LOW later.*/
+ graphic_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+ if (!result) {
+ /* CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->MinVoltage);*/
+ /* CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->MinVddcPhases);*/
+ CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->SclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_US(graphic_level->ActivityLevel);
+ CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CgSpllFuncCntl3);
+ CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CgSpllFuncCntl4);
+ CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->SpllSpreadSpectrum);
+ CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->SpllSpreadSpectrum2);
+ CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CcPwrDynRm);
+ CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CcPwrDynRm1);
+ }
+
+ return result;
+}
+
+static int tonga_populate_all_graphic_levels(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(hwmgr->smu_backend);
+ struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct smu7_dpm_table *dpm_table = &data->dpm_table;
+ struct phm_ppt_v1_pcie_table *pcie_table = pptable_info->pcie_table;
+ uint8_t pcie_entry_count = (uint8_t) data->dpm_table.pcie_speed_table.count;
+ uint32_t level_array_address = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU72_Discrete_DpmTable, GraphicsLevel);
+
+ uint32_t level_array_size = sizeof(SMU72_Discrete_GraphicsLevel) *
+ SMU72_MAX_LEVELS_GRAPHICS;
+
+ SMU72_Discrete_GraphicsLevel *levels = smu_data->smc_state_table.GraphicsLevel;
+
+ uint32_t i, max_entry;
+ uint8_t highest_pcie_level_enabled = 0;
+ uint8_t lowest_pcie_level_enabled = 0, mid_pcie_level_enabled = 0;
+ uint8_t count = 0;
+ int result = 0;
+
+ memset(levels, 0x00, level_array_size);
+
+ for (i = 0; i < dpm_table->sclk_table.count; i++) {
+ result = tonga_populate_single_graphic_level(hwmgr,
+ dpm_table->sclk_table.dpm_levels[i].value,
+ &(smu_data->smc_state_table.GraphicsLevel[i]));
+ if (result != 0)
+ return result;
+
+ /* Making sure only DPM level 0-1 have Deep Sleep Div ID populated. */
+ if (i > 1)
+ smu_data->smc_state_table.GraphicsLevel[i].DeepSleepDivId = 0;
+ }
+
+ /* Only enable level 0 for now. */
+ smu_data->smc_state_table.GraphicsLevel[0].EnabledForActivity = 1;
+
+ /* set highest level watermark to high */
+ if (dpm_table->sclk_table.count > 1)
+ smu_data->smc_state_table.GraphicsLevel[dpm_table->sclk_table.count-1].DisplayWatermark =
+ PPSMC_DISPLAY_WATERMARK_HIGH;
+
+ smu_data->smc_state_table.GraphicsDpmLevelCount =
+ (uint8_t)dpm_table->sclk_table.count;
+ data->dpm_level_enable_mask.sclk_dpm_enable_mask =
+ phm_get_dpm_level_enable_mask_value(&dpm_table->sclk_table);
+
+ if (pcie_table != NULL) {
+ PP_ASSERT_WITH_CODE((pcie_entry_count >= 1),
+ "There must be 1 or more PCIE levels defined in PPTable.",
+ return -EINVAL);
+ max_entry = pcie_entry_count - 1; /* for indexing, we need to decrement by 1.*/
+ for (i = 0; i < dpm_table->sclk_table.count; i++) {
+ smu_data->smc_state_table.GraphicsLevel[i].pcieDpmLevel =
+ (uint8_t) ((i < max_entry) ? i : max_entry);
+ }
+ } else {
+ if (0 == data->dpm_level_enable_mask.pcie_dpm_enable_mask)
+ pr_err("Pcie Dpm Enablemask is 0 !");
+
+ while (data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
+ ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+ (1<<(highest_pcie_level_enabled+1))) != 0)) {
+ highest_pcie_level_enabled++;
+ }
+
+ while (data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
+ ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+ (1<<lowest_pcie_level_enabled)) == 0)) {
+ lowest_pcie_level_enabled++;
+ }
+
+ while ((count < highest_pcie_level_enabled) &&
+ ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+ (1<<(lowest_pcie_level_enabled+1+count))) == 0)) {
+ count++;
+ }
+ mid_pcie_level_enabled = (lowest_pcie_level_enabled+1+count) < highest_pcie_level_enabled ?
+ (lowest_pcie_level_enabled+1+count) : highest_pcie_level_enabled;
+
+
+ /* set pcieDpmLevel to highest_pcie_level_enabled*/
+ for (i = 2; i < dpm_table->sclk_table.count; i++)
+ smu_data->smc_state_table.GraphicsLevel[i].pcieDpmLevel = highest_pcie_level_enabled;
+
+ /* set pcieDpmLevel to lowest_pcie_level_enabled*/
+ smu_data->smc_state_table.GraphicsLevel[0].pcieDpmLevel = lowest_pcie_level_enabled;
+
+ /* set pcieDpmLevel to mid_pcie_level_enabled*/
+ smu_data->smc_state_table.GraphicsLevel[1].pcieDpmLevel = mid_pcie_level_enabled;
+ }
+ /* level count will send to smc once at init smc table and never change*/
+ result = smu7_copy_bytes_to_smc(hwmgr, level_array_address,
+ (uint8_t *)levels, (uint32_t)level_array_size,
+ SMC_RAM_END);
+
+ return result;
+}
+
+static int tonga_calculate_mclk_params(
+ struct pp_hwmgr *hwmgr,
+ uint32_t memory_clock,
+ SMU72_Discrete_MemoryLevel *mclk,
+ bool strobe_mode,
+ bool dllStateOn
+ )
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ uint32_t dll_cntl = data->clock_registers.vDLL_CNTL;
+ uint32_t mclk_pwrmgt_cntl = data->clock_registers.vMCLK_PWRMGT_CNTL;
+ uint32_t mpll_ad_func_cntl = data->clock_registers.vMPLL_AD_FUNC_CNTL;
+ uint32_t mpll_dq_func_cntl = data->clock_registers.vMPLL_DQ_FUNC_CNTL;
+ uint32_t mpll_func_cntl = data->clock_registers.vMPLL_FUNC_CNTL;
+ uint32_t mpll_func_cntl_1 = data->clock_registers.vMPLL_FUNC_CNTL_1;
+ uint32_t mpll_func_cntl_2 = data->clock_registers.vMPLL_FUNC_CNTL_2;
+ uint32_t mpll_ss1 = data->clock_registers.vMPLL_SS1;
+ uint32_t mpll_ss2 = data->clock_registers.vMPLL_SS2;
+
+ pp_atomctrl_memory_clock_param mpll_param;
+ int result;
+
+ result = atomctrl_get_memory_pll_dividers_si(hwmgr,
+ memory_clock, &mpll_param, strobe_mode);
+ PP_ASSERT_WITH_CODE(
+ !result,
+ "Error retrieving Memory Clock Parameters from VBIOS.",
+ return result);
+
+ /* MPLL_FUNC_CNTL setup*/
+ mpll_func_cntl = PHM_SET_FIELD(mpll_func_cntl, MPLL_FUNC_CNTL, BWCTRL,
+ mpll_param.bw_ctrl);
+
+ /* MPLL_FUNC_CNTL_1 setup*/
+ mpll_func_cntl_1 = PHM_SET_FIELD(mpll_func_cntl_1,
+ MPLL_FUNC_CNTL_1, CLKF,
+ mpll_param.mpll_fb_divider.cl_kf);
+ mpll_func_cntl_1 = PHM_SET_FIELD(mpll_func_cntl_1,
+ MPLL_FUNC_CNTL_1, CLKFRAC,
+ mpll_param.mpll_fb_divider.clk_frac);
+ mpll_func_cntl_1 = PHM_SET_FIELD(mpll_func_cntl_1,
+ MPLL_FUNC_CNTL_1, VCO_MODE,
+ mpll_param.vco_mode);
+
+ /* MPLL_AD_FUNC_CNTL setup*/
+ mpll_ad_func_cntl = PHM_SET_FIELD(mpll_ad_func_cntl,
+ MPLL_AD_FUNC_CNTL, YCLK_POST_DIV,
+ mpll_param.mpll_post_divider);
+
+ if (data->is_memory_gddr5) {
+ /* MPLL_DQ_FUNC_CNTL setup*/
+ mpll_dq_func_cntl = PHM_SET_FIELD(mpll_dq_func_cntl,
+ MPLL_DQ_FUNC_CNTL, YCLK_SEL,
+ mpll_param.yclk_sel);
+ mpll_dq_func_cntl = PHM_SET_FIELD(mpll_dq_func_cntl,
+ MPLL_DQ_FUNC_CNTL, YCLK_POST_DIV,
+ mpll_param.mpll_post_divider);
+ }
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MemorySpreadSpectrumSupport)) {
+ /*
+ ************************************
+ Fref = Reference Frequency
+ NF = Feedback divider ratio
+ NR = Reference divider ratio
+ Fnom = Nominal VCO output frequency = Fref * NF / NR
+ Fs = Spreading Rate
+ D = Percentage down-spread / 2
+ Fint = Reference input frequency to PFD = Fref / NR
+ NS = Spreading rate divider ratio = int(Fint / (2 * Fs))
+ CLKS = NS - 1 = ISS_STEP_NUM[11:0]
+ NV = D * Fs / Fnom * 4 * ((Fnom/Fref * NR) ^ 2)
+ CLKV = 65536 * NV = ISS_STEP_SIZE[25:0]
+ *************************************
+ */
+ pp_atomctrl_internal_ss_info ss_info;
+ uint32_t freq_nom;
+ uint32_t tmp;
+ uint32_t reference_clock = atomctrl_get_mpll_reference_clock(hwmgr);
+
+ /* for GDDR5 for all modes and DDR3 */
+ if (1 == mpll_param.qdr)
+ freq_nom = memory_clock * 4 * (1 << mpll_param.mpll_post_divider);
+ else
+ freq_nom = memory_clock * 2 * (1 << mpll_param.mpll_post_divider);
+
+ /* tmp = (freq_nom / reference_clock * reference_divider) ^ 2 Note: S.I. reference_divider = 1*/
+ tmp = (freq_nom / reference_clock);
+ tmp = tmp * tmp;
+
+ if (0 == atomctrl_get_memory_clock_spread_spectrum(hwmgr, freq_nom, &ss_info)) {
+ /* ss_info.speed_spectrum_percentage -- in unit of 0.01% */
+ /* ss.Info.speed_spectrum_rate -- in unit of khz */
+ /* CLKS = reference_clock / (2 * speed_spectrum_rate * reference_divider) * 10 */
+ /* = reference_clock * 5 / speed_spectrum_rate */
+ uint32_t clks = reference_clock * 5 / ss_info.speed_spectrum_rate;
+
+ /* CLKV = 65536 * speed_spectrum_percentage / 2 * spreadSpecrumRate / freq_nom * 4 / 100000 * ((freq_nom / reference_clock) ^ 2) */
+ /* = 131 * speed_spectrum_percentage * speed_spectrum_rate / 100 * ((freq_nom / reference_clock) ^ 2) / freq_nom */
+ uint32_t clkv =
+ (uint32_t)((((131 * ss_info.speed_spectrum_percentage *
+ ss_info.speed_spectrum_rate) / 100) * tmp) / freq_nom);
+
+ mpll_ss1 = PHM_SET_FIELD(mpll_ss1, MPLL_SS1, CLKV, clkv);
+ mpll_ss2 = PHM_SET_FIELD(mpll_ss2, MPLL_SS2, CLKS, clks);
+ }
+ }
+
+ /* MCLK_PWRMGT_CNTL setup */
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, DLL_SPEED, mpll_param.dll_speed);
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, MRDCK0_PDNB, dllStateOn);
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, MRDCK1_PDNB, dllStateOn);
+
+ /* Save the result data to outpupt memory level structure */
+ mclk->MclkFrequency = memory_clock;
+ mclk->MpllFuncCntl = mpll_func_cntl;
+ mclk->MpllFuncCntl_1 = mpll_func_cntl_1;
+ mclk->MpllFuncCntl_2 = mpll_func_cntl_2;
+ mclk->MpllAdFuncCntl = mpll_ad_func_cntl;
+ mclk->MpllDqFuncCntl = mpll_dq_func_cntl;
+ mclk->MclkPwrmgtCntl = mclk_pwrmgt_cntl;
+ mclk->DllCntl = dll_cntl;
+ mclk->MpllSs1 = mpll_ss1;
+ mclk->MpllSs2 = mpll_ss2;
+
+ return 0;
+}
+
+static uint8_t tonga_get_mclk_frequency_ratio(uint32_t memory_clock,
+ bool strobe_mode)
+{
+ uint8_t mc_para_index;
+
+ if (strobe_mode) {
+ if (memory_clock < 12500)
+ mc_para_index = 0x00;
+ else if (memory_clock > 47500)
+ mc_para_index = 0x0f;
+ else
+ mc_para_index = (uint8_t)((memory_clock - 10000) / 2500);
+ } else {
+ if (memory_clock < 65000)
+ mc_para_index = 0x00;
+ else if (memory_clock > 135000)
+ mc_para_index = 0x0f;
+ else
+ mc_para_index = (uint8_t)((memory_clock - 60000) / 5000);
+ }
+
+ return mc_para_index;
+}
+
+static uint8_t tonga_get_ddr3_mclk_frequency_ratio(uint32_t memory_clock)
+{
+ uint8_t mc_para_index;
+
+ if (memory_clock < 10000)
+ mc_para_index = 0;
+ else if (memory_clock >= 80000)
+ mc_para_index = 0x0f;
+ else
+ mc_para_index = (uint8_t)((memory_clock - 10000) / 5000 + 1);
+
+ return mc_para_index;
+}
+
+
+static int tonga_populate_single_memory_level(
+ struct pp_hwmgr *hwmgr,
+ uint32_t memory_clock,
+ SMU72_Discrete_MemoryLevel *memory_level
+ )
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *pptable_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ uint32_t mclk_edc_wr_enable_threshold = 40000;
+ uint32_t mclk_stutter_mode_threshold = 30000;
+ uint32_t mclk_edc_enable_threshold = 40000;
+ uint32_t mclk_strobe_mode_threshold = 40000;
+ phm_ppt_v1_clock_voltage_dependency_table *vdd_dep_table = NULL;
+ int result = 0;
+ bool dll_state_on;
+ uint32_t mvdd = 0;
+
+ if (hwmgr->od_enabled)
+ vdd_dep_table = (phm_ppt_v1_clock_voltage_dependency_table *)&data->odn_dpm_table.vdd_dependency_on_mclk;
+ else
+ vdd_dep_table = pptable_info->vdd_dep_on_mclk;
+
+ if (NULL != vdd_dep_table) {
+ result = tonga_get_dependency_volt_by_clk(hwmgr,
+ vdd_dep_table,
+ memory_clock,
+ &memory_level->MinVoltage, &mvdd);
+ PP_ASSERT_WITH_CODE(
+ !result,
+ "can not find MinVddc voltage value from memory VDDC "
+ "voltage dependency table",
+ return result);
+ }
+
+ if (data->mvdd_control == SMU7_VOLTAGE_CONTROL_NONE)
+ memory_level->MinMvdd = data->vbios_boot_state.mvdd_bootup_value;
+ else
+ memory_level->MinMvdd = mvdd;
+
+ memory_level->EnabledForThrottle = 1;
+ memory_level->EnabledForActivity = 0;
+ memory_level->UpHyst = data->current_profile_setting.mclk_up_hyst;
+ memory_level->DownHyst = data->current_profile_setting.mclk_down_hyst;
+ memory_level->VoltageDownHyst = 0;
+
+ /* Indicates maximum activity level for this performance level.*/
+ memory_level->ActivityLevel = data->current_profile_setting.mclk_activity;
+ memory_level->StutterEnable = 0;
+ memory_level->StrobeEnable = 0;
+ memory_level->EdcReadEnable = 0;
+ memory_level->EdcWriteEnable = 0;
+ memory_level->RttEnable = 0;
+
+ /* default set to low watermark. Highest level will be set to high later.*/
+ memory_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+ data->display_timing.num_existing_displays = hwmgr->display_config->num_display;
+ data->display_timing.vrefresh = hwmgr->display_config->vrefresh;
+
+ if ((mclk_stutter_mode_threshold != 0) &&
+ (memory_clock <= mclk_stutter_mode_threshold) &&
+ (!data->is_uvd_enabled)
+ && (PHM_READ_FIELD(hwmgr->device, DPG_PIPE_STUTTER_CONTROL, STUTTER_ENABLE) & 0x1)
+ && (data->display_timing.num_existing_displays <= 2)
+ && (data->display_timing.num_existing_displays != 0))
+ memory_level->StutterEnable = 1;
+
+ /* decide strobe mode*/
+ memory_level->StrobeEnable = (mclk_strobe_mode_threshold != 0) &&
+ (memory_clock <= mclk_strobe_mode_threshold);
+
+ /* decide EDC mode and memory clock ratio*/
+ if (data->is_memory_gddr5) {
+ memory_level->StrobeRatio = tonga_get_mclk_frequency_ratio(memory_clock,
+ memory_level->StrobeEnable);
+
+ if ((mclk_edc_enable_threshold != 0) &&
+ (memory_clock > mclk_edc_enable_threshold)) {
+ memory_level->EdcReadEnable = 1;
+ }
+
+ if ((mclk_edc_wr_enable_threshold != 0) &&
+ (memory_clock > mclk_edc_wr_enable_threshold)) {
+ memory_level->EdcWriteEnable = 1;
+ }
+
+ if (memory_level->StrobeEnable) {
+ if (tonga_get_mclk_frequency_ratio(memory_clock, 1) >=
+ ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC7) >> 16) & 0xf)) {
+ dll_state_on = ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC5) >> 1) & 0x1) ? 1 : 0;
+ } else {
+ dll_state_on = ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC6) >> 1) & 0x1) ? 1 : 0;
+ }
+
+ } else {
+ dll_state_on = data->dll_default_on;
+ }
+ } else {
+ memory_level->StrobeRatio =
+ tonga_get_ddr3_mclk_frequency_ratio(memory_clock);
+ dll_state_on = ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC5) >> 1) & 0x1) ? 1 : 0;
+ }
+
+ result = tonga_calculate_mclk_params(hwmgr,
+ memory_clock, memory_level, memory_level->StrobeEnable, dll_state_on);
+
+ if (!result) {
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MinMvdd);
+ /* MCLK frequency in units of 10KHz*/
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MclkFrequency);
+ /* Indicates maximum activity level for this performance level.*/
+ CONVERT_FROM_HOST_TO_SMC_US(memory_level->ActivityLevel);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl_1);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl_2);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllAdFuncCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllDqFuncCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MclkPwrmgtCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->DllCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllSs1);
+ CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllSs2);
+ }
+
+ return result;
+}
+
+static int tonga_populate_all_memory_levels(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct tonga_smumgr *smu_data =
+ (struct tonga_smumgr *)(hwmgr->smu_backend);
+ struct smu7_dpm_table *dpm_table = &data->dpm_table;
+ int result;
+
+ /* populate MCLK dpm table to SMU7 */
+ uint32_t level_array_address =
+ smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU72_Discrete_DpmTable, MemoryLevel);
+ uint32_t level_array_size =
+ sizeof(SMU72_Discrete_MemoryLevel) *
+ SMU72_MAX_LEVELS_MEMORY;
+ SMU72_Discrete_MemoryLevel *levels =
+ smu_data->smc_state_table.MemoryLevel;
+ uint32_t i;
+
+ memset(levels, 0x00, level_array_size);
+
+ for (i = 0; i < dpm_table->mclk_table.count; i++) {
+ PP_ASSERT_WITH_CODE((0 != dpm_table->mclk_table.dpm_levels[i].value),
+ "can not populate memory level as memory clock is zero",
+ return -EINVAL);
+ result = tonga_populate_single_memory_level(
+ hwmgr,
+ dpm_table->mclk_table.dpm_levels[i].value,
+ &(smu_data->smc_state_table.MemoryLevel[i]));
+ if (result)
+ return result;
+ }
+
+ /* Only enable level 0 for now.*/
+ smu_data->smc_state_table.MemoryLevel[0].EnabledForActivity = 1;
+
+ /*
+ * in order to prevent MC activity from stutter mode to push DPM up.
+ * the UVD change complements this by putting the MCLK in a higher state
+ * by default such that we are not effected by up threshold or and MCLK DPM latency.
+ */
+ smu_data->smc_state_table.MemoryLevel[0].ActivityLevel = 0x1F;
+ CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.MemoryLevel[0].ActivityLevel);
+
+ smu_data->smc_state_table.MemoryDpmLevelCount = (uint8_t)dpm_table->mclk_table.count;
+ data->dpm_level_enable_mask.mclk_dpm_enable_mask = phm_get_dpm_level_enable_mask_value(&dpm_table->mclk_table);
+ /* set highest level watermark to high*/
+ smu_data->smc_state_table.MemoryLevel[dpm_table->mclk_table.count-1].DisplayWatermark = PPSMC_DISPLAY_WATERMARK_HIGH;
+
+ /* level count will send to smc once at init smc table and never change*/
+ result = smu7_copy_bytes_to_smc(hwmgr,
+ level_array_address, (uint8_t *)levels, (uint32_t)level_array_size,
+ SMC_RAM_END);
+
+ return result;
+}
+
+static int tonga_populate_mvdd_value(struct pp_hwmgr *hwmgr,
+ uint32_t mclk, SMIO_Pattern *smio_pattern)
+{
+ const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ uint32_t i = 0;
+
+ if (SMU7_VOLTAGE_CONTROL_NONE != data->mvdd_control) {
+ /* find mvdd value which clock is more than request */
+ for (i = 0; i < table_info->vdd_dep_on_mclk->count; i++) {
+ if (mclk <= table_info->vdd_dep_on_mclk->entries[i].clk) {
+ /* Always round to higher voltage. */
+ smio_pattern->Voltage =
+ data->mvdd_voltage_table.entries[i].value;
+ break;
+ }
+ }
+
+ PP_ASSERT_WITH_CODE(i < table_info->vdd_dep_on_mclk->count,
+ "MVDD Voltage is outside the supported range.",
+ return -EINVAL);
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int tonga_populate_smc_acpi_level(struct pp_hwmgr *hwmgr,
+ SMU72_Discrete_DpmTable *table)
+{
+ int result = 0;
+ struct tonga_smumgr *smu_data =
+ (struct tonga_smumgr *)(hwmgr->smu_backend);
+ const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct pp_atomctrl_clock_dividers_vi dividers;
+
+ SMIO_Pattern voltage_level;
+ uint32_t spll_func_cntl = data->clock_registers.vCG_SPLL_FUNC_CNTL;
+ uint32_t spll_func_cntl_2 = data->clock_registers.vCG_SPLL_FUNC_CNTL_2;
+ uint32_t dll_cntl = data->clock_registers.vDLL_CNTL;
+ uint32_t mclk_pwrmgt_cntl = data->clock_registers.vMCLK_PWRMGT_CNTL;
+
+ /* The ACPI state should not do DPM on DC (or ever).*/
+ table->ACPILevel.Flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+ table->ACPILevel.MinVoltage =
+ smu_data->smc_state_table.GraphicsLevel[0].MinVoltage;
+
+ /* assign zero for now*/
+ table->ACPILevel.SclkFrequency = atomctrl_get_reference_clock(hwmgr);
+
+ /* get the engine clock dividers for this clock value*/
+ result = atomctrl_get_engine_pll_dividers_vi(hwmgr,
+ table->ACPILevel.SclkFrequency, &dividers);
+
+ PP_ASSERT_WITH_CODE(result == 0,
+ "Error retrieving Engine Clock dividers from VBIOS.",
+ return result);
+
+ /* divider ID for required SCLK*/
+ table->ACPILevel.SclkDid = (uint8_t)dividers.pll_post_divider;
+ table->ACPILevel.DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+ table->ACPILevel.DeepSleepDivId = 0;
+
+ spll_func_cntl = PHM_SET_FIELD(spll_func_cntl, CG_SPLL_FUNC_CNTL,
+ SPLL_PWRON, 0);
+ spll_func_cntl = PHM_SET_FIELD(spll_func_cntl, CG_SPLL_FUNC_CNTL,
+ SPLL_RESET, 1);
+ spll_func_cntl_2 = PHM_SET_FIELD(spll_func_cntl_2, CG_SPLL_FUNC_CNTL_2,
+ SCLK_MUX_SEL, 4);
+
+ table->ACPILevel.CgSpllFuncCntl = spll_func_cntl;
+ table->ACPILevel.CgSpllFuncCntl2 = spll_func_cntl_2;
+ table->ACPILevel.CgSpllFuncCntl3 = data->clock_registers.vCG_SPLL_FUNC_CNTL_3;
+ table->ACPILevel.CgSpllFuncCntl4 = data->clock_registers.vCG_SPLL_FUNC_CNTL_4;
+ table->ACPILevel.SpllSpreadSpectrum = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM;
+ table->ACPILevel.SpllSpreadSpectrum2 = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2;
+ table->ACPILevel.CcPwrDynRm = 0;
+ table->ACPILevel.CcPwrDynRm1 = 0;
+
+
+ /* For various features to be enabled/disabled while this level is active.*/
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.Flags);
+ /* SCLK frequency in units of 10KHz*/
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl2);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl3);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl4);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum2);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm1);
+
+ /* table->MemoryACPILevel.MinVddcPhases = table->ACPILevel.MinVddcPhases;*/
+ table->MemoryACPILevel.MinVoltage =
+ smu_data->smc_state_table.MemoryLevel[0].MinVoltage;
+
+ /* CONVERT_FROM_HOST_TO_SMC_UL(table->MemoryACPILevel.MinVoltage);*/
+
+ if (0 == tonga_populate_mvdd_value(hwmgr, 0, &voltage_level))
+ table->MemoryACPILevel.MinMvdd =
+ PP_HOST_TO_SMC_UL(voltage_level.Voltage * VOLTAGE_SCALE);
+ else
+ table->MemoryACPILevel.MinMvdd = 0;
+
+ /* Force reset on DLL*/
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, MRDCK0_RESET, 0x1);
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, MRDCK1_RESET, 0x1);
+
+ /* Disable DLL in ACPIState*/
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, MRDCK0_PDNB, 0);
+ mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+ MCLK_PWRMGT_CNTL, MRDCK1_PDNB, 0);
+
+ /* Enable DLL bypass signal*/
+ dll_cntl = PHM_SET_FIELD(dll_cntl,
+ DLL_CNTL, MRDCK0_BYPASS, 0);
+ dll_cntl = PHM_SET_FIELD(dll_cntl,
+ DLL_CNTL, MRDCK1_BYPASS, 0);
+
+ table->MemoryACPILevel.DllCntl =
+ PP_HOST_TO_SMC_UL(dll_cntl);
+ table->MemoryACPILevel.MclkPwrmgtCntl =
+ PP_HOST_TO_SMC_UL(mclk_pwrmgt_cntl);
+ table->MemoryACPILevel.MpllAdFuncCntl =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_AD_FUNC_CNTL);
+ table->MemoryACPILevel.MpllDqFuncCntl =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_DQ_FUNC_CNTL);
+ table->MemoryACPILevel.MpllFuncCntl =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_FUNC_CNTL);
+ table->MemoryACPILevel.MpllFuncCntl_1 =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_FUNC_CNTL_1);
+ table->MemoryACPILevel.MpllFuncCntl_2 =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_FUNC_CNTL_2);
+ table->MemoryACPILevel.MpllSs1 =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_SS1);
+ table->MemoryACPILevel.MpllSs2 =
+ PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_SS2);
+
+ table->MemoryACPILevel.EnabledForThrottle = 0;
+ table->MemoryACPILevel.EnabledForActivity = 0;
+ table->MemoryACPILevel.UpHyst = 0;
+ table->MemoryACPILevel.DownHyst = 100;
+ table->MemoryACPILevel.VoltageDownHyst = 0;
+ /* Indicates maximum activity level for this performance level.*/
+ table->MemoryACPILevel.ActivityLevel =
+ PP_HOST_TO_SMC_US(data->current_profile_setting.mclk_activity);
+
+ table->MemoryACPILevel.StutterEnable = 0;
+ table->MemoryACPILevel.StrobeEnable = 0;
+ table->MemoryACPILevel.EdcReadEnable = 0;
+ table->MemoryACPILevel.EdcWriteEnable = 0;
+ table->MemoryACPILevel.RttEnable = 0;
+
+ return result;
+}
+
+static int tonga_populate_smc_uvd_level(struct pp_hwmgr *hwmgr,
+ SMU72_Discrete_DpmTable *table)
+{
+ int result = 0;
+
+ uint8_t count;
+ pp_atomctrl_clock_dividers_vi dividers;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *pptable_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+ pptable_info->mm_dep_table;
+
+ table->UvdLevelCount = (uint8_t) (mm_table->count);
+ table->UvdBootLevel = 0;
+
+ for (count = 0; count < table->UvdLevelCount; count++) {
+ table->UvdLevel[count].VclkFrequency = mm_table->entries[count].vclk;
+ table->UvdLevel[count].DclkFrequency = mm_table->entries[count].dclk;
+ table->UvdLevel[count].MinVoltage.Vddc =
+ phm_get_voltage_index(pptable_info->vddc_lookup_table,
+ mm_table->entries[count].vddc);
+ table->UvdLevel[count].MinVoltage.VddGfx =
+ (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) ?
+ phm_get_voltage_index(pptable_info->vddgfx_lookup_table,
+ mm_table->entries[count].vddgfx) : 0;
+ table->UvdLevel[count].MinVoltage.Vddci =
+ phm_get_voltage_id(&data->vddci_voltage_table,
+ mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
+ table->UvdLevel[count].MinVoltage.Phases = 1;
+
+ /* retrieve divider value for VBIOS */
+ result = atomctrl_get_dfs_pll_dividers_vi(
+ hwmgr,
+ table->UvdLevel[count].VclkFrequency,
+ &dividers);
+
+ PP_ASSERT_WITH_CODE((!result),
+ "can not find divide id for Vclk clock",
+ return result);
+
+ table->UvdLevel[count].VclkDivider = (uint8_t)dividers.pll_post_divider;
+
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+ table->UvdLevel[count].DclkFrequency, &dividers);
+ PP_ASSERT_WITH_CODE((!result),
+ "can not find divide id for Dclk clock",
+ return result);
+
+ table->UvdLevel[count].DclkDivider =
+ (uint8_t)dividers.pll_post_divider;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].VclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].DclkFrequency);
+ }
+
+ return result;
+
+}
+
+static int tonga_populate_smc_vce_level(struct pp_hwmgr *hwmgr,
+ SMU72_Discrete_DpmTable *table)
+{
+ int result = 0;
+
+ uint8_t count;
+ pp_atomctrl_clock_dividers_vi dividers;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *pptable_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+ pptable_info->mm_dep_table;
+
+ table->VceLevelCount = (uint8_t) (mm_table->count);
+ table->VceBootLevel = 0;
+
+ for (count = 0; count < table->VceLevelCount; count++) {
+ table->VceLevel[count].Frequency =
+ mm_table->entries[count].eclk;
+ table->VceLevel[count].MinVoltage.Vddc =
+ phm_get_voltage_index(pptable_info->vddc_lookup_table,
+ mm_table->entries[count].vddc);
+ table->VceLevel[count].MinVoltage.VddGfx =
+ (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) ?
+ phm_get_voltage_index(pptable_info->vddgfx_lookup_table,
+ mm_table->entries[count].vddgfx) : 0;
+ table->VceLevel[count].MinVoltage.Vddci =
+ phm_get_voltage_id(&data->vddci_voltage_table,
+ mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
+ table->VceLevel[count].MinVoltage.Phases = 1;
+
+ /* retrieve divider value for VBIOS */
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+ table->VceLevel[count].Frequency, &dividers);
+ PP_ASSERT_WITH_CODE((!result),
+ "can not find divide id for VCE engine clock",
+ return result);
+
+ table->VceLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VceLevel[count].Frequency);
+ }
+
+ return result;
+}
+
+static int tonga_populate_smc_acp_level(struct pp_hwmgr *hwmgr,
+ SMU72_Discrete_DpmTable *table)
+{
+ int result = 0;
+ uint8_t count;
+ pp_atomctrl_clock_dividers_vi dividers;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *pptable_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+ pptable_info->mm_dep_table;
+
+ table->AcpLevelCount = (uint8_t) (mm_table->count);
+ table->AcpBootLevel = 0;
+
+ for (count = 0; count < table->AcpLevelCount; count++) {
+ table->AcpLevel[count].Frequency =
+ pptable_info->mm_dep_table->entries[count].aclk;
+ table->AcpLevel[count].MinVoltage.Vddc =
+ phm_get_voltage_index(pptable_info->vddc_lookup_table,
+ mm_table->entries[count].vddc);
+ table->AcpLevel[count].MinVoltage.VddGfx =
+ (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) ?
+ phm_get_voltage_index(pptable_info->vddgfx_lookup_table,
+ mm_table->entries[count].vddgfx) : 0;
+ table->AcpLevel[count].MinVoltage.Vddci =
+ phm_get_voltage_id(&data->vddci_voltage_table,
+ mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
+ table->AcpLevel[count].MinVoltage.Phases = 1;
+
+ /* retrieve divider value for VBIOS */
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+ table->AcpLevel[count].Frequency, &dividers);
+ PP_ASSERT_WITH_CODE((!result),
+ "can not find divide id for engine clock", return result);
+
+ table->AcpLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->AcpLevel[count].Frequency);
+ }
+
+ return result;
+}
+
+static int tonga_populate_memory_timing_parameters(
+ struct pp_hwmgr *hwmgr,
+ uint32_t engine_clock,
+ uint32_t memory_clock,
+ struct SMU72_Discrete_MCArbDramTimingTableEntry *arb_regs
+ )
+{
+ uint32_t dramTiming;
+ uint32_t dramTiming2;
+ uint32_t burstTime;
+ int result;
+
+ result = atomctrl_set_engine_dram_timings_rv770(hwmgr,
+ engine_clock, memory_clock);
+
+ PP_ASSERT_WITH_CODE(result == 0,
+ "Error calling VBIOS to set DRAM_TIMING.", return result);
+
+ dramTiming = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING);
+ dramTiming2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2);
+ burstTime = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0);
+
+ arb_regs->McArbDramTiming = PP_HOST_TO_SMC_UL(dramTiming);
+ arb_regs->McArbDramTiming2 = PP_HOST_TO_SMC_UL(dramTiming2);
+ arb_regs->McArbBurstTime = (uint8_t)burstTime;
+
+ return 0;
+}
+
+static int tonga_program_memory_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct tonga_smumgr *smu_data =
+ (struct tonga_smumgr *)(hwmgr->smu_backend);
+ int result = 0;
+ SMU72_Discrete_MCArbDramTimingTable arb_regs;
+ uint32_t i, j;
+
+ memset(&arb_regs, 0x00, sizeof(SMU72_Discrete_MCArbDramTimingTable));
+
+ for (i = 0; i < data->dpm_table.sclk_table.count; i++) {
+ for (j = 0; j < data->dpm_table.mclk_table.count; j++) {
+ result = tonga_populate_memory_timing_parameters
+ (hwmgr, data->dpm_table.sclk_table.dpm_levels[i].value,
+ data->dpm_table.mclk_table.dpm_levels[j].value,
+ &arb_regs.entries[i][j]);
+
+ if (result)
+ break;
+ }
+ }
+
+ if (!result) {
+ result = smu7_copy_bytes_to_smc(
+ hwmgr,
+ smu_data->smu7_data.arb_table_start,
+ (uint8_t *)&arb_regs,
+ sizeof(SMU72_Discrete_MCArbDramTimingTable),
+ SMC_RAM_END
+ );
+ }
+
+ return result;
+}
+
+static int tonga_populate_smc_boot_level(struct pp_hwmgr *hwmgr,
+ SMU72_Discrete_DpmTable *table)
+{
+ int result = 0;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct tonga_smumgr *smu_data =
+ (struct tonga_smumgr *)(hwmgr->smu_backend);
+ table->GraphicsBootLevel = 0;
+ table->MemoryBootLevel = 0;
+
+ /* find boot level from dpm table*/
+ result = phm_find_boot_level(&(data->dpm_table.sclk_table),
+ data->vbios_boot_state.sclk_bootup_value,
+ (uint32_t *)&(smu_data->smc_state_table.GraphicsBootLevel));
+
+ if (result != 0) {
+ smu_data->smc_state_table.GraphicsBootLevel = 0;
+ pr_err("[powerplay] VBIOS did not find boot engine "
+ "clock value in dependency table. "
+ "Using Graphics DPM level 0 !");
+ result = 0;
+ }
+
+ result = phm_find_boot_level(&(data->dpm_table.mclk_table),
+ data->vbios_boot_state.mclk_bootup_value,
+ (uint32_t *)&(smu_data->smc_state_table.MemoryBootLevel));
+
+ if (result != 0) {
+ smu_data->smc_state_table.MemoryBootLevel = 0;
+ pr_err("[powerplay] VBIOS did not find boot "
+ "engine clock value in dependency table."
+ "Using Memory DPM level 0 !");
+ result = 0;
+ }
+
+ table->BootVoltage.Vddc =
+ phm_get_voltage_id(&(data->vddc_voltage_table),
+ data->vbios_boot_state.vddc_bootup_value);
+ table->BootVoltage.VddGfx =
+ phm_get_voltage_id(&(data->vddgfx_voltage_table),
+ data->vbios_boot_state.vddgfx_bootup_value);
+ table->BootVoltage.Vddci =
+ phm_get_voltage_id(&(data->vddci_voltage_table),
+ data->vbios_boot_state.vddci_bootup_value);
+ table->BootMVdd = data->vbios_boot_state.mvdd_bootup_value;
+
+ CONVERT_FROM_HOST_TO_SMC_US(table->BootMVdd);
+
+ return result;
+}
+
+static int tonga_populate_clock_stretcher_data_table(struct pp_hwmgr *hwmgr)
+{
+ uint32_t ro, efuse, efuse2, clock_freq, volt_without_cks,
+ volt_with_cks, value;
+ uint16_t clock_freq_u16;
+ struct tonga_smumgr *smu_data =
+ (struct tonga_smumgr *)(hwmgr->smu_backend);
+ uint8_t type, i, j, cks_setting, stretch_amount, stretch_amount2,
+ volt_offset = 0;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
+ table_info->vdd_dep_on_sclk;
+ uint32_t hw_revision, dev_id;
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ stretch_amount = (uint8_t)table_info->cac_dtp_table->usClockStretchAmount;
+
+ hw_revision = adev->pdev->revision;
+ dev_id = adev->pdev->device;
+
+ /* Read SMU_Eefuse to read and calculate RO and determine
+ * if the part is SS or FF. if RO >= 1660MHz, part is FF.
+ */
+ efuse = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixSMU_EFUSE_0 + (146 * 4));
+ efuse2 = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixSMU_EFUSE_0 + (148 * 4));
+ efuse &= 0xFF000000;
+ efuse = efuse >> 24;
+ efuse2 &= 0xF;
+
+ if (efuse2 == 1)
+ ro = (2300 - 1350) * efuse / 255 + 1350;
+ else
+ ro = (2500 - 1000) * efuse / 255 + 1000;
+
+ if (ro >= 1660)
+ type = 0;
+ else
+ type = 1;
+
+ /* Populate Stretch amount */
+ smu_data->smc_state_table.ClockStretcherAmount = stretch_amount;
+
+
+ /* Populate Sclk_CKS_masterEn0_7 and Sclk_voltageOffset */
+ for (i = 0; i < sclk_table->count; i++) {
+ smu_data->smc_state_table.Sclk_CKS_masterEn0_7 |=
+ sclk_table->entries[i].cks_enable << i;
+ if (ASICID_IS_TONGA_P(dev_id, hw_revision)) {
+ volt_without_cks = (uint32_t)((7732 + 60 - ro - 20838 *
+ (sclk_table->entries[i].clk/100) / 10000) * 1000 /
+ (8730 - (5301 * (sclk_table->entries[i].clk/100) / 1000)));
+ volt_with_cks = (uint32_t)((5250 + 51 - ro - 2404 *
+ (sclk_table->entries[i].clk/100) / 100000) * 1000 /
+ (6146 - (3193 * (sclk_table->entries[i].clk/100) / 1000)));
+ } else {
+ volt_without_cks = (uint32_t)((14041 *
+ (sclk_table->entries[i].clk/100) / 10000 + 3571 + 75 - ro) * 1000 /
+ (4026 - (13924 * (sclk_table->entries[i].clk/100) / 10000)));
+ volt_with_cks = (uint32_t)((13946 *
+ (sclk_table->entries[i].clk/100) / 10000 + 3320 + 45 - ro) * 1000 /
+ (3664 - (11454 * (sclk_table->entries[i].clk/100) / 10000)));
+ }
+ if (volt_without_cks >= volt_with_cks)
+ volt_offset = (uint8_t)(((volt_without_cks - volt_with_cks +
+ sclk_table->entries[i].cks_voffset) * 100 / 625) + 1);
+ smu_data->smc_state_table.Sclk_voltageOffset[i] = volt_offset;
+ }
+
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
+ STRETCH_ENABLE, 0x0);
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
+ masterReset, 0x1);
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
+ staticEnable, 0x1);
+ PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
+ masterReset, 0x0);
+
+ /* Populate CKS Lookup Table */
+ if (stretch_amount == 1 || stretch_amount == 2 || stretch_amount == 5)
+ stretch_amount2 = 0;
+ else if (stretch_amount == 3 || stretch_amount == 4)
+ stretch_amount2 = 1;
+ else {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ClockStretcher);
+ PP_ASSERT_WITH_CODE(false,
+ "Stretch Amount in PPTable not supported",
+ return -EINVAL);
+ }
+
+ value = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixPWR_CKS_CNTL);
+ value &= 0xFFC2FF87;
+ smu_data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].minFreq =
+ tonga_clock_stretcher_lookup_table[stretch_amount2][0];
+ smu_data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].maxFreq =
+ tonga_clock_stretcher_lookup_table[stretch_amount2][1];
+ clock_freq_u16 = (uint16_t)(PP_SMC_TO_HOST_UL(smu_data->smc_state_table.
+ GraphicsLevel[smu_data->smc_state_table.GraphicsDpmLevelCount - 1].
+ SclkFrequency) / 100);
+ if (tonga_clock_stretcher_lookup_table[stretch_amount2][0] <
+ clock_freq_u16 &&
+ tonga_clock_stretcher_lookup_table[stretch_amount2][1] >
+ clock_freq_u16) {
+ /* Program PWR_CKS_CNTL. CKS_USE_FOR_LOW_FREQ */
+ value |= (tonga_clock_stretcher_lookup_table[stretch_amount2][3]) << 16;
+ /* Program PWR_CKS_CNTL. CKS_LDO_REFSEL */
+ value |= (tonga_clock_stretcher_lookup_table[stretch_amount2][2]) << 18;
+ /* Program PWR_CKS_CNTL. CKS_STRETCH_AMOUNT */
+ value |= (tonga_clock_stretch_amount_conversion
+ [tonga_clock_stretcher_lookup_table[stretch_amount2][3]]
+ [stretch_amount]) << 3;
+ }
+ CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.CKS_LOOKUPTable.
+ CKS_LOOKUPTableEntry[0].minFreq);
+ CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.CKS_LOOKUPTable.
+ CKS_LOOKUPTableEntry[0].maxFreq);
+ smu_data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].setting =
+ tonga_clock_stretcher_lookup_table[stretch_amount2][2] & 0x7F;
+ smu_data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].setting |=
+ (tonga_clock_stretcher_lookup_table[stretch_amount2][3]) << 7;
+
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixPWR_CKS_CNTL, value);
+
+ /* Populate DDT Lookup Table */
+ for (i = 0; i < 4; i++) {
+ /* Assign the minimum and maximum VID stored
+ * in the last row of Clock Stretcher Voltage Table.
+ */
+ smu_data->smc_state_table.ClockStretcherDataTable.
+ ClockStretcherDataTableEntry[i].minVID =
+ (uint8_t) tonga_clock_stretcher_ddt_table[type][i][2];
+ smu_data->smc_state_table.ClockStretcherDataTable.
+ ClockStretcherDataTableEntry[i].maxVID =
+ (uint8_t) tonga_clock_stretcher_ddt_table[type][i][3];
+ /* Loop through each SCLK and check the frequency
+ * to see if it lies within the frequency for clock stretcher.
+ */
+ for (j = 0; j < smu_data->smc_state_table.GraphicsDpmLevelCount; j++) {
+ cks_setting = 0;
+ clock_freq = PP_SMC_TO_HOST_UL(
+ smu_data->smc_state_table.GraphicsLevel[j].SclkFrequency);
+ /* Check the allowed frequency against the sclk level[j].
+ * Sclk's endianness has already been converted,
+ * and it's in 10Khz unit,
+ * as opposed to Data table, which is in Mhz unit.
+ */
+ if (clock_freq >= tonga_clock_stretcher_ddt_table[type][i][0] * 100) {
+ cks_setting |= 0x2;
+ if (clock_freq < tonga_clock_stretcher_ddt_table[type][i][1] * 100)
+ cks_setting |= 0x1;
+ }
+ smu_data->smc_state_table.ClockStretcherDataTable.
+ ClockStretcherDataTableEntry[i].setting |= cks_setting << (j * 2);
+ }
+ CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.
+ ClockStretcherDataTable.
+ ClockStretcherDataTableEntry[i].setting);
+ }
+
+ value = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixPWR_CKS_CNTL);
+ value &= 0xFFFFFFFE;
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixPWR_CKS_CNTL, value);
+
+ return 0;
+}
+
+static int tonga_populate_vr_config(struct pp_hwmgr *hwmgr,
+ SMU72_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint16_t config;
+
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vdd_gfx_control) {
+ /* Splitted mode */
+ config = VR_SVI2_PLANE_1;
+ table->VRConfig |= (config<<VRCONF_VDDGFX_SHIFT);
+
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
+ config = VR_SVI2_PLANE_2;
+ table->VRConfig |= config;
+ } else {
+ pr_err("VDDC and VDDGFX should "
+ "be both on SVI2 control in splitted mode !\n");
+ }
+ } else {
+ /* Merged mode */
+ config = VR_MERGED_WITH_VDDC;
+ table->VRConfig |= (config<<VRCONF_VDDGFX_SHIFT);
+
+ /* Set Vddc Voltage Controller */
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
+ config = VR_SVI2_PLANE_1;
+ table->VRConfig |= config;
+ } else {
+ pr_err("VDDC should be on "
+ "SVI2 control in merged mode !\n");
+ }
+ }
+
+ /* Set Vddci Voltage Controller */
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) {
+ config = VR_SVI2_PLANE_2; /* only in merged mode */
+ table->VRConfig |= (config<<VRCONF_VDDCI_SHIFT);
+ } else if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
+ config = VR_SMIO_PATTERN_1;
+ table->VRConfig |= (config<<VRCONF_VDDCI_SHIFT);
+ }
+
+ /* Set Mvdd Voltage Controller */
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
+ config = VR_SMIO_PATTERN_2;
+ table->VRConfig |= (config<<VRCONF_MVDD_SHIFT);
+ }
+
+ return 0;
+}
+
+static int tonga_init_arb_table_index(struct pp_hwmgr *hwmgr)
+{
+ struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(hwmgr->smu_backend);
+ uint32_t tmp;
+ int result;
+
+ /*
+ * This is a read-modify-write on the first byte of the ARB table.
+ * The first byte in the SMU72_Discrete_MCArbDramTimingTable structure
+ * is the field 'current'.
+ * This solution is ugly, but we never write the whole table only
+ * individual fields in it.
+ * In reality this field should not be in that structure
+ * but in a soft register.
+ */
+ result = smu7_read_smc_sram_dword(hwmgr,
+ smu_data->smu7_data.arb_table_start, &tmp, SMC_RAM_END);
+
+ if (result != 0)
+ return result;
+
+ tmp &= 0x00FFFFFF;
+ tmp |= ((uint32_t)MC_CG_ARB_FREQ_F1) << 24;
+
+ return smu7_write_smc_sram_dword(hwmgr,
+ smu_data->smu7_data.arb_table_start, tmp, SMC_RAM_END);
+}
+
+
+static int tonga_populate_bapm_parameters_in_dpm_table(struct pp_hwmgr *hwmgr)
+{
+ struct tonga_smumgr *smu_data =
+ (struct tonga_smumgr *)(hwmgr->smu_backend);
+ const struct tonga_pt_defaults *defaults = smu_data->power_tune_defaults;
+ SMU72_Discrete_DpmTable *dpm_table = &(smu_data->smc_state_table);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_cac_tdp_table *cac_dtp_table = table_info->cac_dtp_table;
+ int i, j, k;
+ const uint16_t *pdef1, *pdef2;
+
+ dpm_table->DefaultTdp = PP_HOST_TO_SMC_US(
+ (uint16_t)(cac_dtp_table->usTDP * 256));
+ dpm_table->TargetTdp = PP_HOST_TO_SMC_US(
+ (uint16_t)(cac_dtp_table->usConfigurableTDP * 256));
+
+ PP_ASSERT_WITH_CODE(cac_dtp_table->usTargetOperatingTemp <= 255,
+ "Target Operating Temp is out of Range !",
+ );
+
+ dpm_table->GpuTjMax = (uint8_t)(cac_dtp_table->usTargetOperatingTemp);
+ dpm_table->GpuTjHyst = 8;
+
+ dpm_table->DTEAmbientTempBase = defaults->dte_ambient_temp_base;
+
+ dpm_table->BAPM_TEMP_GRADIENT =
+ PP_HOST_TO_SMC_UL(defaults->bapm_temp_gradient);
+ pdef1 = defaults->bapmti_r;
+ pdef2 = defaults->bapmti_rc;
+
+ for (i = 0; i < SMU72_DTE_ITERATIONS; i++) {
+ for (j = 0; j < SMU72_DTE_SOURCES; j++) {
+ for (k = 0; k < SMU72_DTE_SINKS; k++) {
+ dpm_table->BAPMTI_R[i][j][k] =
+ PP_HOST_TO_SMC_US(*pdef1);
+ dpm_table->BAPMTI_RC[i][j][k] =
+ PP_HOST_TO_SMC_US(*pdef2);
+ pdef1++;
+ pdef2++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int tonga_populate_svi_load_line(struct pp_hwmgr *hwmgr)
+{
+ struct tonga_smumgr *smu_data =
+ (struct tonga_smumgr *)(hwmgr->smu_backend);
+ const struct tonga_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+ smu_data->power_tune_table.SviLoadLineEn = defaults->svi_load_line_en;
+ smu_data->power_tune_table.SviLoadLineVddC = defaults->svi_load_line_vddC;
+ smu_data->power_tune_table.SviLoadLineTrimVddC = 3;
+ smu_data->power_tune_table.SviLoadLineOffsetVddC = 0;
+
+ return 0;
+}
+
+static int tonga_populate_tdc_limit(struct pp_hwmgr *hwmgr)
+{
+ uint16_t tdc_limit;
+ struct tonga_smumgr *smu_data =
+ (struct tonga_smumgr *)(hwmgr->smu_backend);
+ const struct tonga_pt_defaults *defaults = smu_data->power_tune_defaults;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ /* TDC number of fraction bits are changed from 8 to 7
+ * for Fiji as requested by SMC team
+ */
+ tdc_limit = (uint16_t)(table_info->cac_dtp_table->usTDC * 256);
+ smu_data->power_tune_table.TDC_VDDC_PkgLimit =
+ CONVERT_FROM_HOST_TO_SMC_US(tdc_limit);
+ smu_data->power_tune_table.TDC_VDDC_ThrottleReleaseLimitPerc =
+ defaults->tdc_vddc_throttle_release_limit_perc;
+ smu_data->power_tune_table.TDC_MAWt = defaults->tdc_mawt;
+
+ return 0;
+}
+
+static int tonga_populate_dw8(struct pp_hwmgr *hwmgr, uint32_t fuse_table_offset)
+{
+ struct tonga_smumgr *smu_data =
+ (struct tonga_smumgr *)(hwmgr->smu_backend);
+ const struct tonga_pt_defaults *defaults = smu_data->power_tune_defaults;
+ uint32_t temp;
+
+ if (smu7_read_smc_sram_dword(hwmgr,
+ fuse_table_offset +
+ offsetof(SMU72_Discrete_PmFuses, TdcWaterfallCtl),
+ (uint32_t *)&temp, SMC_RAM_END))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to read PmFuses.DW6 "
+ "(SviLoadLineEn) from SMC Failed !",
+ return -EINVAL);
+ else
+ smu_data->power_tune_table.TdcWaterfallCtl = defaults->tdc_waterfall_ctl;
+
+ return 0;
+}
+
+static int tonga_populate_temperature_scaler(struct pp_hwmgr *hwmgr)
+{
+ int i;
+ struct tonga_smumgr *smu_data =
+ (struct tonga_smumgr *)(hwmgr->smu_backend);
+
+ /* Currently not used. Set all to zero. */
+ for (i = 0; i < 16; i++)
+ smu_data->power_tune_table.LPMLTemperatureScaler[i] = 0;
+
+ return 0;
+}
+
+static int tonga_populate_fuzzy_fan(struct pp_hwmgr *hwmgr)
+{
+ struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(hwmgr->smu_backend);
+
+ if ((hwmgr->thermal_controller.advanceFanControlParameters.
+ usFanOutputSensitivity & (1 << 15)) ||
+ (hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity == 0))
+ hwmgr->thermal_controller.advanceFanControlParameters.
+ usFanOutputSensitivity = hwmgr->thermal_controller.
+ advanceFanControlParameters.usDefaultFanOutputSensitivity;
+
+ smu_data->power_tune_table.FuzzyFan_PwmSetDelta =
+ PP_HOST_TO_SMC_US(hwmgr->thermal_controller.
+ advanceFanControlParameters.usFanOutputSensitivity);
+ return 0;
+}
+
+static int tonga_populate_gnb_lpml(struct pp_hwmgr *hwmgr)
+{
+ int i;
+ struct tonga_smumgr *smu_data =
+ (struct tonga_smumgr *)(hwmgr->smu_backend);
+
+ /* Currently not used. Set all to zero. */
+ for (i = 0; i < 16; i++)
+ smu_data->power_tune_table.GnbLPML[i] = 0;
+
+ return 0;
+}
+
+static int tonga_populate_bapm_vddc_base_leakage_sidd(struct pp_hwmgr *hwmgr)
+{
+ struct tonga_smumgr *smu_data =
+ (struct tonga_smumgr *)(hwmgr->smu_backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ uint16_t hi_sidd = smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd;
+ uint16_t lo_sidd = smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd;
+ struct phm_cac_tdp_table *cac_table = table_info->cac_dtp_table;
+
+ hi_sidd = (uint16_t)(cac_table->usHighCACLeakage / 100 * 256);
+ lo_sidd = (uint16_t)(cac_table->usLowCACLeakage / 100 * 256);
+
+ smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd =
+ CONVERT_FROM_HOST_TO_SMC_US(hi_sidd);
+ smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd =
+ CONVERT_FROM_HOST_TO_SMC_US(lo_sidd);
+
+ return 0;
+}
+
+static int tonga_populate_pm_fuses(struct pp_hwmgr *hwmgr)
+{
+ struct tonga_smumgr *smu_data =
+ (struct tonga_smumgr *)(hwmgr->smu_backend);
+ uint32_t pm_fuse_table_offset;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PowerContainment)) {
+ if (smu7_read_smc_sram_dword(hwmgr,
+ SMU72_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU72_Firmware_Header, PmFuseTable),
+ &pm_fuse_table_offset, SMC_RAM_END))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to get pm_fuse_table_offset Failed !",
+ return -EINVAL);
+
+ /* DW6 */
+ if (tonga_populate_svi_load_line(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate SviLoadLine Failed !",
+ return -EINVAL);
+ /* DW7 */
+ if (tonga_populate_tdc_limit(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate TDCLimit Failed !",
+ return -EINVAL);
+ /* DW8 */
+ if (tonga_populate_dw8(hwmgr, pm_fuse_table_offset))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate TdcWaterfallCtl Failed !",
+ return -EINVAL);
+
+ /* DW9-DW12 */
+ if (tonga_populate_temperature_scaler(hwmgr) != 0)
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate LPMLTemperatureScaler Failed !",
+ return -EINVAL);
+
+ /* DW13-DW14 */
+ if (tonga_populate_fuzzy_fan(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate Fuzzy Fan "
+ "Control parameters Failed !",
+ return -EINVAL);
+
+ /* DW15-DW18 */
+ if (tonga_populate_gnb_lpml(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate GnbLPML Failed !",
+ return -EINVAL);
+
+ /* DW20 */
+ if (tonga_populate_bapm_vddc_base_leakage_sidd(hwmgr))
+ PP_ASSERT_WITH_CODE(
+ false,
+ "Attempt to populate BapmVddCBaseLeakage "
+ "Hi and Lo Sidd Failed !",
+ return -EINVAL);
+
+ if (smu7_copy_bytes_to_smc(hwmgr, pm_fuse_table_offset,
+ (uint8_t *)&smu_data->power_tune_table,
+ sizeof(struct SMU72_Discrete_PmFuses), SMC_RAM_END))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to download PmFuseTable Failed !",
+ return -EINVAL);
+ }
+ return 0;
+}
+
+static int tonga_populate_mc_reg_address(struct pp_hwmgr *hwmgr,
+ SMU72_Discrete_MCRegisters *mc_reg_table)
+{
+ const struct tonga_smumgr *smu_data = (struct tonga_smumgr *)hwmgr->smu_backend;
+
+ uint32_t i, j;
+
+ for (i = 0, j = 0; j < smu_data->mc_reg_table.last; j++) {
+ if (smu_data->mc_reg_table.validflag & 1<<j) {
+ PP_ASSERT_WITH_CODE(
+ i < SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE,
+ "Index of mc_reg_table->address[] array "
+ "out of boundary",
+ return -EINVAL);
+ mc_reg_table->address[i].s0 =
+ PP_HOST_TO_SMC_US(smu_data->mc_reg_table.mc_reg_address[j].s0);
+ mc_reg_table->address[i].s1 =
+ PP_HOST_TO_SMC_US(smu_data->mc_reg_table.mc_reg_address[j].s1);
+ i++;
+ }
+ }
+
+ mc_reg_table->last = (uint8_t)i;
+
+ return 0;
+}
+
+/*convert register values from driver to SMC format */
+static void tonga_convert_mc_registers(
+ const struct tonga_mc_reg_entry *entry,
+ SMU72_Discrete_MCRegisterSet *data,
+ uint32_t num_entries, uint32_t valid_flag)
+{
+ uint32_t i, j;
+
+ for (i = 0, j = 0; j < num_entries; j++) {
+ if (valid_flag & 1<<j) {
+ data->value[i] = PP_HOST_TO_SMC_UL(entry->mc_data[j]);
+ i++;
+ }
+ }
+}
+
+static int tonga_convert_mc_reg_table_entry_to_smc(
+ struct pp_hwmgr *hwmgr,
+ const uint32_t memory_clock,
+ SMU72_Discrete_MCRegisterSet *mc_reg_table_data
+ )
+{
+ struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(hwmgr->smu_backend);
+ uint32_t i = 0;
+
+ for (i = 0; i < smu_data->mc_reg_table.num_entries; i++) {
+ if (memory_clock <=
+ smu_data->mc_reg_table.mc_reg_table_entry[i].mclk_max) {
+ break;
+ }
+ }
+
+ if ((i == smu_data->mc_reg_table.num_entries) && (i > 0))
+ --i;
+
+ tonga_convert_mc_registers(&smu_data->mc_reg_table.mc_reg_table_entry[i],
+ mc_reg_table_data, smu_data->mc_reg_table.last,
+ smu_data->mc_reg_table.validflag);
+
+ return 0;
+}
+
+static int tonga_convert_mc_reg_table_to_smc(struct pp_hwmgr *hwmgr,
+ SMU72_Discrete_MCRegisters *mc_regs)
+{
+ int result = 0;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ int res;
+ uint32_t i;
+
+ for (i = 0; i < data->dpm_table.mclk_table.count; i++) {
+ res = tonga_convert_mc_reg_table_entry_to_smc(
+ hwmgr,
+ data->dpm_table.mclk_table.dpm_levels[i].value,
+ &mc_regs->data[i]
+ );
+
+ if (0 != res)
+ result = res;
+ }
+
+ return result;
+}
+
+static int tonga_update_and_upload_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+ struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(hwmgr->smu_backend);
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t address;
+ int32_t result;
+
+ if (0 == (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK))
+ return 0;
+
+
+ memset(&smu_data->mc_regs, 0, sizeof(SMU72_Discrete_MCRegisters));
+
+ result = tonga_convert_mc_reg_table_to_smc(hwmgr, &(smu_data->mc_regs));
+
+ if (result != 0)
+ return result;
+
+
+ address = smu_data->smu7_data.mc_reg_table_start +
+ (uint32_t)offsetof(SMU72_Discrete_MCRegisters, data[0]);
+
+ return smu7_copy_bytes_to_smc(
+ hwmgr, address,
+ (uint8_t *)&smu_data->mc_regs.data[0],
+ sizeof(SMU72_Discrete_MCRegisterSet) *
+ data->dpm_table.mclk_table.count,
+ SMC_RAM_END);
+}
+
+static int tonga_populate_initial_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(hwmgr->smu_backend);
+
+ memset(&smu_data->mc_regs, 0x00, sizeof(SMU72_Discrete_MCRegisters));
+ result = tonga_populate_mc_reg_address(hwmgr, &(smu_data->mc_regs));
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize MCRegTable for the MC register addresses !",
+ return result;);
+
+ result = tonga_convert_mc_reg_table_to_smc(hwmgr, &smu_data->mc_regs);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize MCRegTable for driver state !",
+ return result;);
+
+ return smu7_copy_bytes_to_smc(hwmgr, smu_data->smu7_data.mc_reg_table_start,
+ (uint8_t *)&smu_data->mc_regs, sizeof(SMU72_Discrete_MCRegisters), SMC_RAM_END);
+}
+
+static void tonga_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr)
+{
+ struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(hwmgr->smu_backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ if (table_info &&
+ table_info->cac_dtp_table->usPowerTuneDataSetID <= POWERTUNE_DEFAULT_SET_MAX &&
+ table_info->cac_dtp_table->usPowerTuneDataSetID)
+ smu_data->power_tune_defaults =
+ &tonga_power_tune_data_set_array
+ [table_info->cac_dtp_table->usPowerTuneDataSetID - 1];
+ else
+ smu_data->power_tune_defaults = &tonga_power_tune_data_set_array[0];
+}
+
+static int tonga_init_smc_table(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct tonga_smumgr *smu_data =
+ (struct tonga_smumgr *)(hwmgr->smu_backend);
+ SMU72_Discrete_DpmTable *table = &(smu_data->smc_state_table);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ uint8_t i;
+ pp_atomctrl_gpio_pin_assignment gpio_pin_assignment;
+
+
+ memset(&(smu_data->smc_state_table), 0x00, sizeof(smu_data->smc_state_table));
+
+ tonga_initialize_power_tune_defaults(hwmgr);
+
+ if (SMU7_VOLTAGE_CONTROL_NONE != data->voltage_control)
+ tonga_populate_smc_voltage_tables(hwmgr, table);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition))
+ table->SystemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_StepVddc))
+ table->SystemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+ if (data->is_memory_gddr5)
+ table->SystemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
+
+ i = PHM_READ_FIELD(hwmgr->device, CC_MC_MAX_CHANNEL, NOOFCHAN);
+
+ if (i == 1 || i == 0)
+ table->SystemFlags |= 0x40;
+
+ if (data->ulv_supported && table_info->us_ulv_voltage_offset) {
+ result = tonga_populate_ulv_state(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize ULV state !",
+ return result;);
+
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixCG_ULV_PARAMETER, 0x40035);
+ }
+
+ result = tonga_populate_smc_link_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize Link Level !", return result);
+
+ result = tonga_populate_all_graphic_levels(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize Graphics Level !", return result);
+
+ result = tonga_populate_all_memory_levels(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize Memory Level !", return result);
+
+ result = tonga_populate_smc_acpi_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize ACPI Level !", return result);
+
+ result = tonga_populate_smc_vce_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize VCE Level !", return result);
+
+ result = tonga_populate_smc_acp_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize ACP Level !", return result);
+
+ /* Since only the initial state is completely set up at this
+ * point (the other states are just copies of the boot state) we only
+ * need to populate the ARB settings for the initial state.
+ */
+ result = tonga_program_memory_timing_parameters(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to Write ARB settings for the initial state.",
+ return result;);
+
+ result = tonga_populate_smc_uvd_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize UVD Level !", return result);
+
+ result = tonga_populate_smc_boot_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize Boot Level !", return result);
+
+ tonga_populate_bapm_parameters_in_dpm_table(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to populate BAPM Parameters !", return result);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ClockStretcher)) {
+ result = tonga_populate_clock_stretcher_data_table(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to populate Clock Stretcher Data Table !",
+ return result;);
+ }
+ table->GraphicsVoltageChangeEnable = 1;
+ table->GraphicsThermThrottleEnable = 1;
+ table->GraphicsInterval = 1;
+ table->VoltageInterval = 1;
+ table->ThermalInterval = 1;
+ table->TemperatureLimitHigh =
+ table_info->cac_dtp_table->usTargetOperatingTemp *
+ SMU7_Q88_FORMAT_CONVERSION_UNIT;
+ table->TemperatureLimitLow =
+ (table_info->cac_dtp_table->usTargetOperatingTemp - 1) *
+ SMU7_Q88_FORMAT_CONVERSION_UNIT;
+ table->MemoryVoltageChangeEnable = 1;
+ table->MemoryInterval = 1;
+ table->VoltageResponseTime = 0;
+ table->PhaseResponseTime = 0;
+ table->MemoryThermThrottleEnable = 1;
+
+ /*
+ * Cail reads current link status and reports it as cap (we cannot
+ * change this due to some previous issues we had)
+ * SMC drops the link status to lowest level after enabling
+ * DPM by PowerPlay. After pnp or toggling CF, driver gets reloaded again
+ * but this time Cail reads current link status which was set to low by
+ * SMC and reports it as cap to powerplay
+ * To avoid it, we set PCIeBootLinkLevel to highest dpm level
+ */
+ PP_ASSERT_WITH_CODE((1 <= data->dpm_table.pcie_speed_table.count),
+ "There must be 1 or more PCIE levels defined in PPTable.",
+ return -EINVAL);
+
+ table->PCIeBootLinkLevel = (uint8_t) (data->dpm_table.pcie_speed_table.count);
+
+ table->PCIeGenInterval = 1;
+
+ result = tonga_populate_vr_config(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to populate VRConfig setting !", return result);
+ data->vr_config = table->VRConfig;
+ table->ThermGpio = 17;
+ table->SclkStepSize = 0x4000;
+
+ if (atomctrl_get_pp_assign_pin(hwmgr, VDDC_VRHOT_GPIO_PINID,
+ &gpio_pin_assignment)) {
+ table->VRHotGpio = gpio_pin_assignment.uc_gpio_pin_bit_shift;
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_RegulatorHot);
+ } else {
+ table->VRHotGpio = SMU7_UNUSED_GPIO_PIN;
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_RegulatorHot);
+ }
+
+ if (atomctrl_get_pp_assign_pin(hwmgr, PP_AC_DC_SWITCH_GPIO_PINID,
+ &gpio_pin_assignment)) {
+ table->AcDcGpio = gpio_pin_assignment.uc_gpio_pin_bit_shift;
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition);
+ } else {
+ table->AcDcGpio = SMU7_UNUSED_GPIO_PIN;
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition);
+ }
+
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_Falcon_QuickTransition);
+
+ if (0) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition);
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_Falcon_QuickTransition);
+ }
+
+ if (atomctrl_get_pp_assign_pin(hwmgr,
+ THERMAL_INT_OUTPUT_GPIO_PINID, &gpio_pin_assignment)) {
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ThermalOutGPIO);
+
+ table->ThermOutGpio = gpio_pin_assignment.uc_gpio_pin_bit_shift;
+
+ table->ThermOutPolarity =
+ (0 == (cgs_read_register(hwmgr->device, mmGPIOPAD_A) &
+ (1 << gpio_pin_assignment.uc_gpio_pin_bit_shift))) ? 1 : 0;
+
+ table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_ONLY;
+
+ /* if required, combine VRHot/PCC with thermal out GPIO*/
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_RegulatorHot) &&
+ phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_CombinePCCWithThermalSignal)){
+ table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_VRHOT;
+ }
+ } else {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ThermalOutGPIO);
+
+ table->ThermOutGpio = 17;
+ table->ThermOutPolarity = 1;
+ table->ThermOutMode = SMU7_THERM_OUT_MODE_DISABLE;
+ }
+
+ for (i = 0; i < SMU72_MAX_ENTRIES_SMIO; i++)
+ table->Smio[i] = PP_HOST_TO_SMC_UL(table->Smio[i]);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SystemFlags);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VRConfig);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask1);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask2);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SclkStepSize);
+ CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitHigh);
+ CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitLow);
+ CONVERT_FROM_HOST_TO_SMC_US(table->VoltageResponseTime);
+ CONVERT_FROM_HOST_TO_SMC_US(table->PhaseResponseTime);
+
+ /* Upload all dpm data to SMC memory.(dpm level, dpm level count etc) */
+ result = smu7_copy_bytes_to_smc(
+ hwmgr,
+ smu_data->smu7_data.dpm_table_start + offsetof(SMU72_Discrete_DpmTable, SystemFlags),
+ (uint8_t *)&(table->SystemFlags),
+ sizeof(SMU72_Discrete_DpmTable) - 3 * sizeof(SMU72_PIDController),
+ SMC_RAM_END);
+
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to upload dpm data to SMC memory !", return result;);
+
+ result = tonga_init_arb_table_index(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to upload arb data to SMC memory !", return result);
+
+ tonga_populate_pm_fuses(hwmgr);
+ PP_ASSERT_WITH_CODE((!result),
+ "Failed to populate initialize pm fuses !", return result);
+
+ result = tonga_populate_initial_mc_reg_table(hwmgr);
+ PP_ASSERT_WITH_CODE((!result),
+ "Failed to populate initialize MC Reg table !", return result);
+
+ return 0;
+}
+
+static int tonga_thermal_setup_fan_table(struct pp_hwmgr *hwmgr)
+{
+ struct tonga_smumgr *smu_data =
+ (struct tonga_smumgr *)(hwmgr->smu_backend);
+ SMU72_Discrete_FanTable fan_table = { FDO_MODE_HARDWARE };
+ uint32_t duty100;
+ uint32_t t_diff1, t_diff2, pwm_diff1, pwm_diff2;
+ uint16_t fdo_min, slope1, slope2;
+ uint32_t reference_clock;
+ int res;
+ uint64_t tmp64;
+
+ if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl))
+ return 0;
+
+ if (hwmgr->thermal_controller.fanInfo.bNoFan) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
+ if (0 == smu_data->smu7_data.fan_table_start) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
+ duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device,
+ CGS_IND_REG__SMC,
+ CG_FDO_CTRL1, FMAX_DUTY100);
+
+ if (0 == duty100) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
+ tmp64 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin * duty100;
+ do_div(tmp64, 10000);
+ fdo_min = (uint16_t)tmp64;
+
+ t_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usTMed -
+ hwmgr->thermal_controller.advanceFanControlParameters.usTMin;
+ t_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usTHigh -
+ hwmgr->thermal_controller.advanceFanControlParameters.usTMed;
+
+ pwm_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed -
+ hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin;
+ pwm_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh -
+ hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed;
+
+ slope1 = (uint16_t)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100);
+ slope2 = (uint16_t)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100);
+
+ fan_table.TempMin = cpu_to_be16((50 + hwmgr->thermal_controller.advanceFanControlParameters.usTMin) / 100);
+ fan_table.TempMed = cpu_to_be16((50 + hwmgr->thermal_controller.advanceFanControlParameters.usTMed) / 100);
+ fan_table.TempMax = cpu_to_be16((50 + hwmgr->thermal_controller.advanceFanControlParameters.usTMax) / 100);
+
+ fan_table.Slope1 = cpu_to_be16(slope1);
+ fan_table.Slope2 = cpu_to_be16(slope2);
+
+ fan_table.FdoMin = cpu_to_be16(fdo_min);
+
+ fan_table.HystDown = cpu_to_be16(hwmgr->thermal_controller.advanceFanControlParameters.ucTHyst);
+
+ fan_table.HystUp = cpu_to_be16(1);
+
+ fan_table.HystSlope = cpu_to_be16(1);
+
+ fan_table.TempRespLim = cpu_to_be16(5);
+
+ reference_clock = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev);
+
+ fan_table.RefreshPeriod = cpu_to_be32((hwmgr->thermal_controller.advanceFanControlParameters.ulCycleDelay * reference_clock) / 1600);
+
+ fan_table.FdoMax = cpu_to_be16((uint16_t)duty100);
+
+ fan_table.TempSrc = (uint8_t)PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_MULT_THERMAL_CTRL, TEMP_SEL);
+
+ fan_table.FanControl_GL_Flag = 1;
+
+ res = smu7_copy_bytes_to_smc(hwmgr,
+ smu_data->smu7_data.fan_table_start,
+ (uint8_t *)&fan_table,
+ (uint32_t)sizeof(fan_table),
+ SMC_RAM_END);
+
+ return 0;
+}
+
+
+static int tonga_program_mem_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (data->need_update_smu7_dpm_table &
+ (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_OD_UPDATE_MCLK))
+ return tonga_program_memory_timing_parameters(hwmgr);
+
+ return 0;
+}
+
+static int tonga_update_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct tonga_smumgr *smu_data =
+ (struct tonga_smumgr *)(hwmgr->smu_backend);
+
+ int result = 0;
+ uint32_t low_sclk_interrupt_threshold = 0;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkThrottleLowNotification)
+ && (data->low_sclk_interrupt_threshold != 0)) {
+ low_sclk_interrupt_threshold =
+ data->low_sclk_interrupt_threshold;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(low_sclk_interrupt_threshold);
+
+ result = smu7_copy_bytes_to_smc(
+ hwmgr,
+ smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU72_Discrete_DpmTable,
+ LowSclkInterruptThreshold),
+ (uint8_t *)&low_sclk_interrupt_threshold,
+ sizeof(uint32_t),
+ SMC_RAM_END);
+ }
+
+ result = tonga_update_and_upload_mc_reg_table(hwmgr);
+
+ PP_ASSERT_WITH_CODE((!result),
+ "Failed to upload MC reg table !",
+ return result);
+
+ result = tonga_program_mem_timing_parameters(hwmgr);
+ PP_ASSERT_WITH_CODE((result == 0),
+ "Failed to program memory timing parameters !",
+ );
+
+ return result;
+}
+
+static uint32_t tonga_get_offsetof(uint32_t type, uint32_t member)
+{
+ switch (type) {
+ case SMU_SoftRegisters:
+ switch (member) {
+ case HandshakeDisables:
+ return offsetof(SMU72_SoftRegisters, HandshakeDisables);
+ case VoltageChangeTimeout:
+ return offsetof(SMU72_SoftRegisters, VoltageChangeTimeout);
+ case AverageGraphicsActivity:
+ return offsetof(SMU72_SoftRegisters, AverageGraphicsActivity);
+ case AverageMemoryActivity:
+ return offsetof(SMU72_SoftRegisters, AverageMemoryActivity);
+ case PreVBlankGap:
+ return offsetof(SMU72_SoftRegisters, PreVBlankGap);
+ case VBlankTimeout:
+ return offsetof(SMU72_SoftRegisters, VBlankTimeout);
+ case UcodeLoadStatus:
+ return offsetof(SMU72_SoftRegisters, UcodeLoadStatus);
+ case DRAM_LOG_ADDR_H:
+ return offsetof(SMU72_SoftRegisters, DRAM_LOG_ADDR_H);
+ case DRAM_LOG_ADDR_L:
+ return offsetof(SMU72_SoftRegisters, DRAM_LOG_ADDR_L);
+ case DRAM_LOG_PHY_ADDR_H:
+ return offsetof(SMU72_SoftRegisters, DRAM_LOG_PHY_ADDR_H);
+ case DRAM_LOG_PHY_ADDR_L:
+ return offsetof(SMU72_SoftRegisters, DRAM_LOG_PHY_ADDR_L);
+ case DRAM_LOG_BUFF_SIZE:
+ return offsetof(SMU72_SoftRegisters, DRAM_LOG_BUFF_SIZE);
+ }
+ break;
+ case SMU_Discrete_DpmTable:
+ switch (member) {
+ case UvdBootLevel:
+ return offsetof(SMU72_Discrete_DpmTable, UvdBootLevel);
+ case VceBootLevel:
+ return offsetof(SMU72_Discrete_DpmTable, VceBootLevel);
+ case LowSclkInterruptThreshold:
+ return offsetof(SMU72_Discrete_DpmTable, LowSclkInterruptThreshold);
+ }
+ break;
+ }
+ pr_warn("can't get the offset of type %x member %x\n", type, member);
+ return 0;
+}
+
+static uint32_t tonga_get_mac_definition(uint32_t value)
+{
+ switch (value) {
+ case SMU_MAX_LEVELS_GRAPHICS:
+ return SMU72_MAX_LEVELS_GRAPHICS;
+ case SMU_MAX_LEVELS_MEMORY:
+ return SMU72_MAX_LEVELS_MEMORY;
+ case SMU_MAX_LEVELS_LINK:
+ return SMU72_MAX_LEVELS_LINK;
+ case SMU_MAX_ENTRIES_SMIO:
+ return SMU72_MAX_ENTRIES_SMIO;
+ case SMU_MAX_LEVELS_VDDC:
+ return SMU72_MAX_LEVELS_VDDC;
+ case SMU_MAX_LEVELS_VDDGFX:
+ return SMU72_MAX_LEVELS_VDDGFX;
+ case SMU_MAX_LEVELS_VDDCI:
+ return SMU72_MAX_LEVELS_VDDCI;
+ case SMU_MAX_LEVELS_MVDD:
+ return SMU72_MAX_LEVELS_MVDD;
+ }
+ pr_warn("can't get the mac value %x\n", value);
+
+ return 0;
+}
+
+static int tonga_update_uvd_smc_table(struct pp_hwmgr *hwmgr)
+{
+ struct tonga_smumgr *smu_data =
+ (struct tonga_smumgr *)(hwmgr->smu_backend);
+ uint32_t mm_boot_level_offset, mm_boot_level_value;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ smu_data->smc_state_table.UvdBootLevel = 0;
+ if (table_info->mm_dep_table->count > 0)
+ smu_data->smc_state_table.UvdBootLevel =
+ (uint8_t) (table_info->mm_dep_table->count - 1);
+ mm_boot_level_offset = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU72_Discrete_DpmTable, UvdBootLevel);
+ mm_boot_level_offset /= 4;
+ mm_boot_level_offset *= 4;
+ mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, mm_boot_level_offset);
+ mm_boot_level_value &= 0x00FFFFFF;
+ mm_boot_level_value |= smu_data->smc_state_table.UvdBootLevel << 24;
+ cgs_write_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC,
+ mm_boot_level_offset, mm_boot_level_value);
+
+ if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UVDDPM) ||
+ phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_StablePState))
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_UVDDPM_SetEnabledMask,
+ (uint32_t)(1 << smu_data->smc_state_table.UvdBootLevel),
+ NULL);
+ return 0;
+}
+
+static int tonga_update_vce_smc_table(struct pp_hwmgr *hwmgr)
+{
+ struct tonga_smumgr *smu_data =
+ (struct tonga_smumgr *)(hwmgr->smu_backend);
+ uint32_t mm_boot_level_offset, mm_boot_level_value;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+
+ smu_data->smc_state_table.VceBootLevel =
+ (uint8_t) (table_info->mm_dep_table->count - 1);
+
+ mm_boot_level_offset = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU72_Discrete_DpmTable, VceBootLevel);
+ mm_boot_level_offset /= 4;
+ mm_boot_level_offset *= 4;
+ mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, mm_boot_level_offset);
+ mm_boot_level_value &= 0xFF00FFFF;
+ mm_boot_level_value |= smu_data->smc_state_table.VceBootLevel << 16;
+ cgs_write_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_StablePState))
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_VCEDPM_SetEnabledMask,
+ (uint32_t)1 << smu_data->smc_state_table.VceBootLevel,
+ NULL);
+ return 0;
+}
+
+static int tonga_update_smc_table(struct pp_hwmgr *hwmgr, uint32_t type)
+{
+ switch (type) {
+ case SMU_UVD_TABLE:
+ tonga_update_uvd_smc_table(hwmgr);
+ break;
+ case SMU_VCE_TABLE:
+ tonga_update_vce_smc_table(hwmgr);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int tonga_process_firmware_header(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(hwmgr->smu_backend);
+
+ uint32_t tmp;
+ int result;
+ bool error = false;
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU72_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU72_Firmware_Header, DpmTable),
+ &tmp, SMC_RAM_END);
+
+ if (!result)
+ smu_data->smu7_data.dpm_table_start = tmp;
+
+ error |= (result != 0);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU72_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU72_Firmware_Header, SoftRegisters),
+ &tmp, SMC_RAM_END);
+
+ if (!result) {
+ data->soft_regs_start = tmp;
+ smu_data->smu7_data.soft_regs_start = tmp;
+ }
+
+ error |= (result != 0);
+
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU72_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU72_Firmware_Header, mcRegisterTable),
+ &tmp, SMC_RAM_END);
+
+ if (!result)
+ smu_data->smu7_data.mc_reg_table_start = tmp;
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU72_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU72_Firmware_Header, FanTable),
+ &tmp, SMC_RAM_END);
+
+ if (!result)
+ smu_data->smu7_data.fan_table_start = tmp;
+
+ error |= (result != 0);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU72_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU72_Firmware_Header, mcArbDramTimingTable),
+ &tmp, SMC_RAM_END);
+
+ if (!result)
+ smu_data->smu7_data.arb_table_start = tmp;
+
+ error |= (result != 0);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU72_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU72_Firmware_Header, Version),
+ &tmp, SMC_RAM_END);
+
+ if (!result)
+ hwmgr->microcode_version_info.SMC = tmp;
+
+ error |= (result != 0);
+
+ return error ? 1 : 0;
+}
+
+/*---------------------------MC----------------------------*/
+
+static uint8_t tonga_get_memory_modile_index(struct pp_hwmgr *hwmgr)
+{
+ return (uint8_t) (0xFF & (cgs_read_register(hwmgr->device, mmBIOS_SCRATCH_4) >> 16));
+}
+
+static bool tonga_check_s0_mc_reg_index(uint16_t in_reg, uint16_t *out_reg)
+{
+ bool result = true;
+
+ switch (in_reg) {
+ case mmMC_SEQ_RAS_TIMING:
+ *out_reg = mmMC_SEQ_RAS_TIMING_LP;
+ break;
+
+ case mmMC_SEQ_DLL_STBY:
+ *out_reg = mmMC_SEQ_DLL_STBY_LP;
+ break;
+
+ case mmMC_SEQ_G5PDX_CMD0:
+ *out_reg = mmMC_SEQ_G5PDX_CMD0_LP;
+ break;
+
+ case mmMC_SEQ_G5PDX_CMD1:
+ *out_reg = mmMC_SEQ_G5PDX_CMD1_LP;
+ break;
+
+ case mmMC_SEQ_G5PDX_CTRL:
+ *out_reg = mmMC_SEQ_G5PDX_CTRL_LP;
+ break;
+
+ case mmMC_SEQ_CAS_TIMING:
+ *out_reg = mmMC_SEQ_CAS_TIMING_LP;
+ break;
+
+ case mmMC_SEQ_MISC_TIMING:
+ *out_reg = mmMC_SEQ_MISC_TIMING_LP;
+ break;
+
+ case mmMC_SEQ_MISC_TIMING2:
+ *out_reg = mmMC_SEQ_MISC_TIMING2_LP;
+ break;
+
+ case mmMC_SEQ_PMG_DVS_CMD:
+ *out_reg = mmMC_SEQ_PMG_DVS_CMD_LP;
+ break;
+
+ case mmMC_SEQ_PMG_DVS_CTL:
+ *out_reg = mmMC_SEQ_PMG_DVS_CTL_LP;
+ break;
+
+ case mmMC_SEQ_RD_CTL_D0:
+ *out_reg = mmMC_SEQ_RD_CTL_D0_LP;
+ break;
+
+ case mmMC_SEQ_RD_CTL_D1:
+ *out_reg = mmMC_SEQ_RD_CTL_D1_LP;
+ break;
+
+ case mmMC_SEQ_WR_CTL_D0:
+ *out_reg = mmMC_SEQ_WR_CTL_D0_LP;
+ break;
+
+ case mmMC_SEQ_WR_CTL_D1:
+ *out_reg = mmMC_SEQ_WR_CTL_D1_LP;
+ break;
+
+ case mmMC_PMG_CMD_EMRS:
+ *out_reg = mmMC_SEQ_PMG_CMD_EMRS_LP;
+ break;
+
+ case mmMC_PMG_CMD_MRS:
+ *out_reg = mmMC_SEQ_PMG_CMD_MRS_LP;
+ break;
+
+ case mmMC_PMG_CMD_MRS1:
+ *out_reg = mmMC_SEQ_PMG_CMD_MRS1_LP;
+ break;
+
+ case mmMC_SEQ_PMG_TIMING:
+ *out_reg = mmMC_SEQ_PMG_TIMING_LP;
+ break;
+
+ case mmMC_PMG_CMD_MRS2:
+ *out_reg = mmMC_SEQ_PMG_CMD_MRS2_LP;
+ break;
+
+ case mmMC_SEQ_WR_CTL_2:
+ *out_reg = mmMC_SEQ_WR_CTL_2_LP;
+ break;
+
+ default:
+ result = false;
+ break;
+ }
+
+ return result;
+}
+
+static int tonga_set_s0_mc_reg_index(struct tonga_mc_reg_table *table)
+{
+ uint32_t i;
+ uint16_t address;
+
+ for (i = 0; i < table->last; i++) {
+ table->mc_reg_address[i].s0 =
+ tonga_check_s0_mc_reg_index(table->mc_reg_address[i].s1,
+ &address) ?
+ address :
+ table->mc_reg_address[i].s1;
+ }
+ return 0;
+}
+
+static int tonga_copy_vbios_smc_reg_table(const pp_atomctrl_mc_reg_table *table,
+ struct tonga_mc_reg_table *ni_table)
+{
+ uint8_t i, j;
+
+ PP_ASSERT_WITH_CODE((table->last <= SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+ "Invalid VramInfo table.", return -EINVAL);
+ PP_ASSERT_WITH_CODE((table->num_entries <= MAX_AC_TIMING_ENTRIES),
+ "Invalid VramInfo table.", return -EINVAL);
+
+ for (i = 0; i < table->last; i++)
+ ni_table->mc_reg_address[i].s1 = table->mc_reg_address[i].s1;
+
+ ni_table->last = table->last;
+
+ for (i = 0; i < table->num_entries; i++) {
+ ni_table->mc_reg_table_entry[i].mclk_max =
+ table->mc_reg_table_entry[i].mclk_max;
+ for (j = 0; j < table->last; j++) {
+ ni_table->mc_reg_table_entry[i].mc_data[j] =
+ table->mc_reg_table_entry[i].mc_data[j];
+ }
+ }
+
+ ni_table->num_entries = table->num_entries;
+
+ return 0;
+}
+
+static int tonga_set_mc_special_registers(struct pp_hwmgr *hwmgr,
+ struct tonga_mc_reg_table *table)
+{
+ uint8_t i, j, k;
+ uint32_t temp_reg;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ for (i = 0, j = table->last; i < table->last; i++) {
+ PP_ASSERT_WITH_CODE((j < SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+ "Invalid VramInfo table.", return -EINVAL);
+
+ switch (table->mc_reg_address[i].s1) {
+
+ case mmMC_SEQ_MISC1:
+ temp_reg = cgs_read_register(hwmgr->device,
+ mmMC_PMG_CMD_EMRS);
+ table->mc_reg_address[j].s1 = mmMC_PMG_CMD_EMRS;
+ table->mc_reg_address[j].s0 = mmMC_SEQ_PMG_CMD_EMRS_LP;
+ for (k = 0; k < table->num_entries; k++) {
+ table->mc_reg_table_entry[k].mc_data[j] =
+ ((temp_reg & 0xffff0000)) |
+ ((table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16);
+ }
+ j++;
+
+ PP_ASSERT_WITH_CODE((j < SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+ "Invalid VramInfo table.", return -EINVAL);
+ temp_reg = cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS);
+ table->mc_reg_address[j].s1 = mmMC_PMG_CMD_MRS;
+ table->mc_reg_address[j].s0 = mmMC_SEQ_PMG_CMD_MRS_LP;
+ for (k = 0; k < table->num_entries; k++) {
+ table->mc_reg_table_entry[k].mc_data[j] =
+ (temp_reg & 0xffff0000) |
+ (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
+
+ if (!data->is_memory_gddr5)
+ table->mc_reg_table_entry[k].mc_data[j] |= 0x100;
+ }
+ j++;
+
+ if (!data->is_memory_gddr5) {
+ PP_ASSERT_WITH_CODE((j < SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+ "Invalid VramInfo table.", return -EINVAL);
+ table->mc_reg_address[j].s1 = mmMC_PMG_AUTO_CMD;
+ table->mc_reg_address[j].s0 = mmMC_PMG_AUTO_CMD;
+ for (k = 0; k < table->num_entries; k++)
+ table->mc_reg_table_entry[k].mc_data[j] =
+ (table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16;
+ j++;
+ }
+
+ break;
+
+ case mmMC_SEQ_RESERVE_M:
+ temp_reg = cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS1);
+ table->mc_reg_address[j].s1 = mmMC_PMG_CMD_MRS1;
+ table->mc_reg_address[j].s0 = mmMC_SEQ_PMG_CMD_MRS1_LP;
+ for (k = 0; k < table->num_entries; k++) {
+ table->mc_reg_table_entry[k].mc_data[j] =
+ (temp_reg & 0xffff0000) |
+ (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
+ }
+ j++;
+ break;
+
+ default:
+ break;
+ }
+
+ }
+
+ table->last = j;
+
+ return 0;
+}
+
+static int tonga_set_valid_flag(struct tonga_mc_reg_table *table)
+{
+ uint8_t i, j;
+
+ for (i = 0; i < table->last; i++) {
+ for (j = 1; j < table->num_entries; j++) {
+ if (table->mc_reg_table_entry[j-1].mc_data[i] !=
+ table->mc_reg_table_entry[j].mc_data[i]) {
+ table->validflag |= (1<<i);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int tonga_initialize_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(hwmgr->smu_backend);
+ pp_atomctrl_mc_reg_table *table;
+ struct tonga_mc_reg_table *ni_table = &smu_data->mc_reg_table;
+ uint8_t module_index = tonga_get_memory_modile_index(hwmgr);
+
+ table = kzalloc(sizeof(pp_atomctrl_mc_reg_table), GFP_KERNEL);
+
+ if (table == NULL)
+ return -ENOMEM;
+
+ /* Program additional LP registers that are no longer programmed by VBIOS */
+ cgs_write_register(hwmgr->device, mmMC_SEQ_RAS_TIMING_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_RAS_TIMING));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_CAS_TIMING_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_CAS_TIMING));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_DLL_STBY_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_DLL_STBY));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD0_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD0));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD1_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD1));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_G5PDX_CTRL_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_G5PDX_CTRL));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CMD_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CMD));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CTL_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CTL));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_MISC_TIMING_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_MISC_TIMING));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_MISC_TIMING2_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_MISC_TIMING2));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_EMRS_LP,
+ cgs_read_register(hwmgr->device, mmMC_PMG_CMD_EMRS));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_MRS_LP,
+ cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_MRS1_LP,
+ cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS1));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_D0_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_D0));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_D1_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_D1));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_RD_CTL_D0_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_RD_CTL_D0));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_RD_CTL_D1_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_RD_CTL_D1));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_TIMING_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_TIMING));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_MRS2_LP,
+ cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS2));
+ cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_2_LP,
+ cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_2));
+
+ result = atomctrl_initialize_mc_reg_table(hwmgr, module_index, table);
+
+ if (!result)
+ result = tonga_copy_vbios_smc_reg_table(table, ni_table);
+
+ if (!result) {
+ tonga_set_s0_mc_reg_index(ni_table);
+ result = tonga_set_mc_special_registers(hwmgr, ni_table);
+ }
+
+ if (!result)
+ tonga_set_valid_flag(ni_table);
+
+ kfree(table);
+
+ return result;
+}
+
+static bool tonga_is_dpm_running(struct pp_hwmgr *hwmgr)
+{
+ return (1 == PHM_READ_INDIRECT_FIELD(hwmgr->device,
+ CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON))
+ ? true : false;
+}
+
+static int tonga_update_dpm_settings(struct pp_hwmgr *hwmgr,
+ void *profile_setting)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct tonga_smumgr *smu_data = (struct tonga_smumgr *)
+ (hwmgr->smu_backend);
+ struct profile_mode_setting *setting;
+ struct SMU72_Discrete_GraphicsLevel *levels =
+ smu_data->smc_state_table.GraphicsLevel;
+ uint32_t array = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU72_Discrete_DpmTable, GraphicsLevel);
+
+ uint32_t mclk_array = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU72_Discrete_DpmTable, MemoryLevel);
+ struct SMU72_Discrete_MemoryLevel *mclk_levels =
+ smu_data->smc_state_table.MemoryLevel;
+ uint32_t i;
+ uint32_t offset, up_hyst_offset, down_hyst_offset, clk_activity_offset, tmp;
+
+ if (profile_setting == NULL)
+ return -EINVAL;
+
+ setting = (struct profile_mode_setting *)profile_setting;
+
+ if (setting->bupdate_sclk) {
+ if (!data->sclk_dpm_key_disabled)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_SCLKDPM_FreezeLevel, NULL);
+ for (i = 0; i < smu_data->smc_state_table.GraphicsDpmLevelCount; i++) {
+ if (levels[i].ActivityLevel !=
+ cpu_to_be16(setting->sclk_activity)) {
+ levels[i].ActivityLevel = cpu_to_be16(setting->sclk_activity);
+
+ clk_activity_offset = array + (sizeof(SMU72_Discrete_GraphicsLevel) * i)
+ + offsetof(SMU72_Discrete_GraphicsLevel, ActivityLevel);
+ offset = clk_activity_offset & ~0x3;
+ tmp = PP_HOST_TO_SMC_UL(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset));
+ tmp = phm_set_field_to_u32(clk_activity_offset, tmp, levels[i].ActivityLevel, sizeof(uint16_t));
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset, PP_HOST_TO_SMC_UL(tmp));
+
+ }
+ if (levels[i].UpHyst != setting->sclk_up_hyst ||
+ levels[i].DownHyst != setting->sclk_down_hyst) {
+ levels[i].UpHyst = setting->sclk_up_hyst;
+ levels[i].DownHyst = setting->sclk_down_hyst;
+ up_hyst_offset = array + (sizeof(SMU72_Discrete_GraphicsLevel) * i)
+ + offsetof(SMU72_Discrete_GraphicsLevel, UpHyst);
+ down_hyst_offset = array + (sizeof(SMU72_Discrete_GraphicsLevel) * i)
+ + offsetof(SMU72_Discrete_GraphicsLevel, DownHyst);
+ offset = up_hyst_offset & ~0x3;
+ tmp = PP_HOST_TO_SMC_UL(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset));
+ tmp = phm_set_field_to_u32(up_hyst_offset, tmp, levels[i].UpHyst, sizeof(uint8_t));
+ tmp = phm_set_field_to_u32(down_hyst_offset, tmp, levels[i].DownHyst, sizeof(uint8_t));
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset, PP_HOST_TO_SMC_UL(tmp));
+ }
+ }
+ if (!data->sclk_dpm_key_disabled)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_SCLKDPM_UnfreezeLevel, NULL);
+ }
+
+ if (setting->bupdate_mclk) {
+ if (!data->mclk_dpm_key_disabled)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_MCLKDPM_FreezeLevel, NULL);
+ for (i = 0; i < smu_data->smc_state_table.MemoryDpmLevelCount; i++) {
+ if (mclk_levels[i].ActivityLevel !=
+ cpu_to_be16(setting->mclk_activity)) {
+ mclk_levels[i].ActivityLevel = cpu_to_be16(setting->mclk_activity);
+
+ clk_activity_offset = mclk_array + (sizeof(SMU72_Discrete_MemoryLevel) * i)
+ + offsetof(SMU72_Discrete_MemoryLevel, ActivityLevel);
+ offset = clk_activity_offset & ~0x3;
+ tmp = PP_HOST_TO_SMC_UL(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset));
+ tmp = phm_set_field_to_u32(clk_activity_offset, tmp, mclk_levels[i].ActivityLevel, sizeof(uint16_t));
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset, PP_HOST_TO_SMC_UL(tmp));
+
+ }
+ if (mclk_levels[i].UpHyst != setting->mclk_up_hyst ||
+ mclk_levels[i].DownHyst != setting->mclk_down_hyst) {
+ mclk_levels[i].UpHyst = setting->mclk_up_hyst;
+ mclk_levels[i].DownHyst = setting->mclk_down_hyst;
+ up_hyst_offset = mclk_array + (sizeof(SMU72_Discrete_MemoryLevel) * i)
+ + offsetof(SMU72_Discrete_MemoryLevel, UpHyst);
+ down_hyst_offset = mclk_array + (sizeof(SMU72_Discrete_MemoryLevel) * i)
+ + offsetof(SMU72_Discrete_MemoryLevel, DownHyst);
+ offset = up_hyst_offset & ~0x3;
+ tmp = PP_HOST_TO_SMC_UL(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset));
+ tmp = phm_set_field_to_u32(up_hyst_offset, tmp, mclk_levels[i].UpHyst, sizeof(uint8_t));
+ tmp = phm_set_field_to_u32(down_hyst_offset, tmp, mclk_levels[i].DownHyst, sizeof(uint8_t));
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset, PP_HOST_TO_SMC_UL(tmp));
+ }
+ }
+ if (!data->mclk_dpm_key_disabled)
+ smum_send_msg_to_smc(hwmgr, PPSMC_MSG_MCLKDPM_UnfreezeLevel, NULL);
+ }
+ return 0;
+}
+
+const struct pp_smumgr_func tonga_smu_funcs = {
+ .name = "tonga_smu",
+ .smu_init = &tonga_smu_init,
+ .smu_fini = &smu7_smu_fini,
+ .start_smu = &tonga_start_smu,
+ .check_fw_load_finish = &smu7_check_fw_load_finish,
+ .request_smu_load_fw = &smu7_request_smu_load_fw,
+ .request_smu_load_specific_fw = NULL,
+ .send_msg_to_smc = &smu7_send_msg_to_smc,
+ .send_msg_to_smc_with_parameter = &smu7_send_msg_to_smc_with_parameter,
+ .get_argument = smu7_get_argument,
+ .download_pptable_settings = NULL,
+ .upload_pptable_settings = NULL,
+ .update_smc_table = tonga_update_smc_table,
+ .get_offsetof = tonga_get_offsetof,
+ .process_firmware_header = tonga_process_firmware_header,
+ .init_smc_table = tonga_init_smc_table,
+ .update_sclk_threshold = tonga_update_sclk_threshold,
+ .thermal_setup_fan_table = tonga_thermal_setup_fan_table,
+ .populate_all_graphic_levels = tonga_populate_all_graphic_levels,
+ .populate_all_memory_levels = tonga_populate_all_memory_levels,
+ .get_mac_definition = tonga_get_mac_definition,
+ .initialize_mc_reg_table = tonga_initialize_mc_reg_table,
+ .is_dpm_running = tonga_is_dpm_running,
+ .update_dpm_settings = tonga_update_dpm_settings,
+};
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/tonga_smumgr.h b/drivers/gpu/drm/amd/pm/powerplay/smumgr/tonga_smumgr.h
new file mode 100644
index 000000000000..d664fedd3d85
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/tonga_smumgr.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _TONGA_SMUMGR_H_
+#define _TONGA_SMUMGR_H_
+
+#include "smu72_discrete.h"
+#include "smu7_smumgr.h"
+#include "smu72.h"
+
+
+#define ASICID_IS_TONGA_P(wDID, bRID) \
+ (((wDID == 0x6930) && ((bRID == 0xF0) || (bRID == 0xF1) || (bRID == 0xFF))) \
+ || ((wDID == 0x6920) && ((bRID == 0) || (bRID == 1))))
+
+struct tonga_pt_defaults {
+ uint8_t svi_load_line_en;
+ uint8_t svi_load_line_vddC;
+ uint8_t tdc_vddc_throttle_release_limit_perc;
+ uint8_t tdc_mawt;
+ uint8_t tdc_waterfall_ctl;
+ uint8_t dte_ambient_temp_base;
+ uint32_t display_cac;
+ uint32_t bapm_temp_gradient;
+ uint16_t bapmti_r[SMU72_DTE_ITERATIONS * SMU72_DTE_SOURCES * SMU72_DTE_SINKS];
+ uint16_t bapmti_rc[SMU72_DTE_ITERATIONS * SMU72_DTE_SOURCES * SMU72_DTE_SINKS];
+};
+
+struct tonga_mc_reg_entry {
+ uint32_t mclk_max;
+ uint32_t mc_data[SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct tonga_mc_reg_table {
+ uint8_t last; /* number of registers*/
+ uint8_t num_entries; /* number of entries in mc_reg_table_entry used*/
+ uint16_t validflag; /* indicate the corresponding register is valid or not. 1: valid, 0: invalid. bit0->address[0], bit1->address[1], etc.*/
+ struct tonga_mc_reg_entry mc_reg_table_entry[MAX_AC_TIMING_ENTRIES];
+ SMU72_Discrete_MCRegisterAddress mc_reg_address[SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE];
+};
+
+
+struct tonga_smumgr {
+
+ struct smu7_smumgr smu7_data;
+ struct SMU72_Discrete_DpmTable smc_state_table;
+ struct SMU72_Discrete_Ulv ulv_setting;
+ struct SMU72_Discrete_PmFuses power_tune_table;
+ const struct tonga_pt_defaults *power_tune_defaults;
+ SMU72_Discrete_MCRegisters mc_regs;
+ struct tonga_mc_reg_table mc_reg_table;
+};
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega10_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega10_smumgr.c
new file mode 100644
index 000000000000..daf122f24f23
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega10_smumgr.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <linux/pci.h>
+
+#include "smumgr.h"
+#include "vega10_inc.h"
+#include "soc15_common.h"
+#include "vega10_smumgr.h"
+#include "vega10_hwmgr.h"
+#include "vega10_ppsmc.h"
+#include "smu9_driver_if.h"
+#include "smu9_smumgr.h"
+#include "ppatomctrl.h"
+#include "pp_debug.h"
+
+
+static int vega10_copy_table_from_smc(struct pp_hwmgr *hwmgr,
+ uint8_t *table, int16_t table_id)
+{
+ struct vega10_smumgr *priv = hwmgr->smu_backend;
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ PP_ASSERT_WITH_CODE(table_id < MAX_SMU_TABLE,
+ "Invalid SMU Table ID!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(priv->smu_tables.entry[table_id].version != 0,
+ "Invalid SMU Table version!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(priv->smu_tables.entry[table_id].size != 0,
+ "Invalid SMU Table Length!", return -EINVAL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrHigh,
+ upper_32_bits(priv->smu_tables.entry[table_id].mc_addr),
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrLow,
+ lower_32_bits(priv->smu_tables.entry[table_id].mc_addr),
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_TransferTableSmu2Dram,
+ priv->smu_tables.entry[table_id].table_id,
+ NULL);
+
+ /* flush hdp cache */
+ amdgpu_asic_flush_hdp(adev, NULL);
+
+ memcpy(table, priv->smu_tables.entry[table_id].table,
+ priv->smu_tables.entry[table_id].size);
+
+ return 0;
+}
+
+static int vega10_copy_table_to_smc(struct pp_hwmgr *hwmgr,
+ uint8_t *table, int16_t table_id)
+{
+ struct vega10_smumgr *priv = hwmgr->smu_backend;
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ /* under sriov, vbios or hypervisor driver
+ * has already copy table to smc so here only skip it
+ */
+ if (!hwmgr->not_vf)
+ return 0;
+
+ PP_ASSERT_WITH_CODE(table_id < MAX_SMU_TABLE,
+ "Invalid SMU Table ID!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(priv->smu_tables.entry[table_id].version != 0,
+ "Invalid SMU Table version!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(priv->smu_tables.entry[table_id].size != 0,
+ "Invalid SMU Table Length!", return -EINVAL);
+
+ memcpy(priv->smu_tables.entry[table_id].table, table,
+ priv->smu_tables.entry[table_id].size);
+
+ amdgpu_asic_flush_hdp(adev, NULL);
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrHigh,
+ upper_32_bits(priv->smu_tables.entry[table_id].mc_addr),
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrLow,
+ lower_32_bits(priv->smu_tables.entry[table_id].mc_addr),
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_TransferTableDram2Smu,
+ priv->smu_tables.entry[table_id].table_id,
+ NULL);
+
+ return 0;
+}
+
+int vega10_enable_smc_features(struct pp_hwmgr *hwmgr,
+ bool enable, uint32_t feature_mask)
+{
+ int msg = enable ? PPSMC_MSG_EnableSmuFeatures :
+ PPSMC_MSG_DisableSmuFeatures;
+
+ /* VF has no permission to change smu feature due
+ * to security concern even under pp one vf mode
+ * it still can't do it. For vega10, the smu in
+ * vbios will enable the appropriate features.
+ * */
+ if (!hwmgr->not_vf)
+ return 0;
+
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ msg, feature_mask, NULL);
+}
+
+int vega10_get_enabled_smc_features(struct pp_hwmgr *hwmgr,
+ uint64_t *features_enabled)
+{
+ uint32_t enabled_features;
+
+ if (features_enabled == NULL)
+ return -EINVAL;
+
+ smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_GetEnabledSmuFeatures,
+ &enabled_features);
+ *features_enabled = enabled_features;
+
+ return 0;
+}
+
+static bool vega10_is_dpm_running(struct pp_hwmgr *hwmgr)
+{
+ uint64_t features_enabled = 0;
+
+ vega10_get_enabled_smc_features(hwmgr, &features_enabled);
+
+ if (features_enabled & SMC_DPM_FEATURES)
+ return true;
+ else
+ return false;
+}
+
+static int vega10_set_tools_address(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_smumgr *priv = hwmgr->smu_backend;
+
+ if (priv->smu_tables.entry[TOOLSTABLE].mc_addr) {
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetToolsDramAddrHigh,
+ upper_32_bits(priv->smu_tables.entry[TOOLSTABLE].mc_addr),
+ NULL);
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetToolsDramAddrLow,
+ lower_32_bits(priv->smu_tables.entry[TOOLSTABLE].mc_addr),
+ NULL);
+ }
+ return 0;
+}
+
+static int vega10_verify_smc_interface(struct pp_hwmgr *hwmgr)
+{
+ uint32_t smc_driver_if_version;
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t dev_id;
+ uint32_t rev_id;
+
+ PP_ASSERT_WITH_CODE(!smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_GetDriverIfVersion,
+ &smc_driver_if_version),
+ "Attempt to get SMC IF Version Number Failed!",
+ return -EINVAL);
+
+ dev_id = adev->pdev->device;
+ rev_id = adev->pdev->revision;
+
+ if (!((dev_id == 0x687f) &&
+ ((rev_id == 0xc0) ||
+ (rev_id == 0xc1) ||
+ (rev_id == 0xc3)))) {
+ if (smc_driver_if_version != SMU9_DRIVER_IF_VERSION) {
+ pr_err("Your firmware(0x%x) doesn't match SMU9_DRIVER_IF_VERSION(0x%x). Please update your firmware!\n",
+ smc_driver_if_version, SMU9_DRIVER_IF_VERSION);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int vega10_smu_init(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_smumgr *priv;
+ unsigned long tools_size;
+ int ret;
+ struct cgs_firmware_info info = {0};
+
+ if (!amdgpu_sriov_vf((struct amdgpu_device *)hwmgr->adev)) {
+ ret = cgs_get_firmware_info(hwmgr->device,
+ CGS_UCODE_ID_SMU,
+ &info);
+ if (ret || !info.kptr)
+ return -EINVAL;
+ }
+
+ priv = kzalloc(sizeof(struct vega10_smumgr), GFP_KERNEL);
+
+ if (!priv)
+ return -ENOMEM;
+
+ hwmgr->smu_backend = priv;
+
+ /* allocate space for pptable */
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ sizeof(PPTable_t),
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[PPTABLE].handle,
+ &priv->smu_tables.entry[PPTABLE].mc_addr,
+ &priv->smu_tables.entry[PPTABLE].table);
+ if (ret)
+ goto free_backend;
+
+ priv->smu_tables.entry[PPTABLE].version = 0x01;
+ priv->smu_tables.entry[PPTABLE].size = sizeof(PPTable_t);
+ priv->smu_tables.entry[PPTABLE].table_id = TABLE_PPTABLE;
+
+ /* allocate space for watermarks table */
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ sizeof(Watermarks_t),
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[WMTABLE].handle,
+ &priv->smu_tables.entry[WMTABLE].mc_addr,
+ &priv->smu_tables.entry[WMTABLE].table);
+
+ if (ret)
+ goto err0;
+
+ priv->smu_tables.entry[WMTABLE].version = 0x01;
+ priv->smu_tables.entry[WMTABLE].size = sizeof(Watermarks_t);
+ priv->smu_tables.entry[WMTABLE].table_id = TABLE_WATERMARKS;
+
+ /* allocate space for AVFS table */
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ sizeof(AvfsTable_t),
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[AVFSTABLE].handle,
+ &priv->smu_tables.entry[AVFSTABLE].mc_addr,
+ &priv->smu_tables.entry[AVFSTABLE].table);
+
+ if (ret)
+ goto err1;
+
+ priv->smu_tables.entry[AVFSTABLE].version = 0x01;
+ priv->smu_tables.entry[AVFSTABLE].size = sizeof(AvfsTable_t);
+ priv->smu_tables.entry[AVFSTABLE].table_id = TABLE_AVFS;
+
+ tools_size = 0x19000;
+ if (tools_size) {
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ tools_size,
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[TOOLSTABLE].handle,
+ &priv->smu_tables.entry[TOOLSTABLE].mc_addr,
+ &priv->smu_tables.entry[TOOLSTABLE].table);
+ if (ret)
+ goto err2;
+ priv->smu_tables.entry[TOOLSTABLE].version = 0x01;
+ priv->smu_tables.entry[TOOLSTABLE].size = tools_size;
+ priv->smu_tables.entry[TOOLSTABLE].table_id = TABLE_PMSTATUSLOG;
+ }
+
+ /* allocate space for AVFS Fuse table */
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ sizeof(AvfsFuseOverride_t),
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[AVFSFUSETABLE].handle,
+ &priv->smu_tables.entry[AVFSFUSETABLE].mc_addr,
+ &priv->smu_tables.entry[AVFSFUSETABLE].table);
+ if (ret)
+ goto err3;
+
+ priv->smu_tables.entry[AVFSFUSETABLE].version = 0x01;
+ priv->smu_tables.entry[AVFSFUSETABLE].size = sizeof(AvfsFuseOverride_t);
+ priv->smu_tables.entry[AVFSFUSETABLE].table_id = TABLE_AVFS_FUSE_OVERRIDE;
+
+
+ return 0;
+
+err3:
+ if (priv->smu_tables.entry[TOOLSTABLE].table)
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TOOLSTABLE].handle,
+ &priv->smu_tables.entry[TOOLSTABLE].mc_addr,
+ &priv->smu_tables.entry[TOOLSTABLE].table);
+err2:
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[AVFSTABLE].handle,
+ &priv->smu_tables.entry[AVFSTABLE].mc_addr,
+ &priv->smu_tables.entry[AVFSTABLE].table);
+err1:
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[WMTABLE].handle,
+ &priv->smu_tables.entry[WMTABLE].mc_addr,
+ &priv->smu_tables.entry[WMTABLE].table);
+err0:
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[PPTABLE].handle,
+ &priv->smu_tables.entry[PPTABLE].mc_addr,
+ &priv->smu_tables.entry[PPTABLE].table);
+free_backend:
+ kfree(hwmgr->smu_backend);
+
+ return -EINVAL;
+}
+
+static int vega10_smu_fini(struct pp_hwmgr *hwmgr)
+{
+ struct vega10_smumgr *priv = hwmgr->smu_backend;
+
+ if (priv) {
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[PPTABLE].handle,
+ &priv->smu_tables.entry[PPTABLE].mc_addr,
+ &priv->smu_tables.entry[PPTABLE].table);
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[WMTABLE].handle,
+ &priv->smu_tables.entry[WMTABLE].mc_addr,
+ &priv->smu_tables.entry[WMTABLE].table);
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[AVFSTABLE].handle,
+ &priv->smu_tables.entry[AVFSTABLE].mc_addr,
+ &priv->smu_tables.entry[AVFSTABLE].table);
+ if (priv->smu_tables.entry[TOOLSTABLE].table)
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TOOLSTABLE].handle,
+ &priv->smu_tables.entry[TOOLSTABLE].mc_addr,
+ &priv->smu_tables.entry[TOOLSTABLE].table);
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[AVFSFUSETABLE].handle,
+ &priv->smu_tables.entry[AVFSFUSETABLE].mc_addr,
+ &priv->smu_tables.entry[AVFSFUSETABLE].table);
+ kfree(hwmgr->smu_backend);
+ hwmgr->smu_backend = NULL;
+ }
+ return 0;
+}
+
+static int vega10_start_smu(struct pp_hwmgr *hwmgr)
+{
+ if (!smu9_is_smc_ram_running(hwmgr))
+ return -EINVAL;
+
+ PP_ASSERT_WITH_CODE(!vega10_verify_smc_interface(hwmgr),
+ "Failed to verify SMC interface!",
+ return -EINVAL);
+
+ vega10_set_tools_address(hwmgr);
+
+ return 0;
+}
+
+static int vega10_smc_table_manager(struct pp_hwmgr *hwmgr, uint8_t *table,
+ uint16_t table_id, bool rw)
+{
+ int ret;
+
+ if (rw)
+ ret = vega10_copy_table_from_smc(hwmgr, table, table_id);
+ else
+ ret = vega10_copy_table_to_smc(hwmgr, table, table_id);
+
+ return ret;
+}
+
+const struct pp_smumgr_func vega10_smu_funcs = {
+ .name = "vega10_smu",
+ .smu_init = &vega10_smu_init,
+ .smu_fini = &vega10_smu_fini,
+ .start_smu = &vega10_start_smu,
+ .request_smu_load_specific_fw = NULL,
+ .send_msg_to_smc = &smu9_send_msg_to_smc,
+ .send_msg_to_smc_with_parameter = &smu9_send_msg_to_smc_with_parameter,
+ .download_pptable_settings = NULL,
+ .upload_pptable_settings = NULL,
+ .is_dpm_running = vega10_is_dpm_running,
+ .get_argument = smu9_get_argument,
+ .smc_table_manager = vega10_smc_table_manager,
+};
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega10_smumgr.h b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega10_smumgr.h
new file mode 100644
index 000000000000..bad760f22624
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega10_smumgr.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _VEGA10_SMUMANAGER_H_
+#define _VEGA10_SMUMANAGER_H_
+
+#define MAX_SMU_TABLE 5
+
+struct smu_table_entry {
+ uint32_t version;
+ uint32_t size;
+ uint32_t table_id;
+ uint64_t mc_addr;
+ void *table;
+ struct amdgpu_bo *handle;
+};
+
+struct smu_table_array {
+ struct smu_table_entry entry[MAX_SMU_TABLE];
+};
+
+struct vega10_smumgr {
+ struct smu_table_array smu_tables;
+};
+
+int vega10_enable_smc_features(struct pp_hwmgr *hwmgr,
+ bool enable, uint32_t feature_mask);
+int vega10_get_enabled_smc_features(struct pp_hwmgr *hwmgr,
+ uint64_t *features_enabled);
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega12_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega12_smumgr.c
new file mode 100644
index 000000000000..f54df76537e4
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega12_smumgr.c
@@ -0,0 +1,416 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "smumgr.h"
+#include "vega12_inc.h"
+#include "soc15_common.h"
+#include "smu9_smumgr.h"
+#include "vega12_smumgr.h"
+#include "vega12_ppsmc.h"
+#include "vega12/smu9_driver_if.h"
+#include "ppatomctrl.h"
+#include "pp_debug.h"
+
+
+/*
+ * Copy table from SMC into driver FB
+ * @param hwmgr the address of the HW manager
+ * @param table_id the driver's table ID to copy from
+ */
+static int vega12_copy_table_from_smc(struct pp_hwmgr *hwmgr,
+ uint8_t *table, int16_t table_id)
+{
+ struct vega12_smumgr *priv =
+ (struct vega12_smumgr *)(hwmgr->smu_backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ PP_ASSERT_WITH_CODE(table_id < TABLE_COUNT,
+ "Invalid SMU Table ID!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(priv->smu_tables.entry[table_id].version != 0,
+ "Invalid SMU Table version!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(priv->smu_tables.entry[table_id].size != 0,
+ "Invalid SMU Table Length!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrHigh,
+ upper_32_bits(priv->smu_tables.entry[table_id].mc_addr),
+ NULL) == 0,
+ "[CopyTableFromSMC] Attempt to Set Dram Addr High Failed!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrLow,
+ lower_32_bits(priv->smu_tables.entry[table_id].mc_addr),
+ NULL) == 0,
+ "[CopyTableFromSMC] Attempt to Set Dram Addr Low Failed!",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_TransferTableSmu2Dram,
+ table_id,
+ NULL) == 0,
+ "[CopyTableFromSMC] Attempt to Transfer Table From SMU Failed!",
+ return -EINVAL);
+
+ /* flush hdp cache */
+ amdgpu_asic_flush_hdp(adev, NULL);
+
+ memcpy(table, priv->smu_tables.entry[table_id].table,
+ priv->smu_tables.entry[table_id].size);
+
+ return 0;
+}
+
+/*
+ * Copy table from Driver FB into SMC
+ * @param hwmgr the address of the HW manager
+ * @param table_id the table to copy from
+ */
+static int vega12_copy_table_to_smc(struct pp_hwmgr *hwmgr,
+ uint8_t *table, int16_t table_id)
+{
+ struct vega12_smumgr *priv =
+ (struct vega12_smumgr *)(hwmgr->smu_backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ PP_ASSERT_WITH_CODE(table_id < TABLE_COUNT,
+ "Invalid SMU Table ID!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(priv->smu_tables.entry[table_id].version != 0,
+ "Invalid SMU Table version!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(priv->smu_tables.entry[table_id].size != 0,
+ "Invalid SMU Table Length!", return -EINVAL);
+
+ memcpy(priv->smu_tables.entry[table_id].table, table,
+ priv->smu_tables.entry[table_id].size);
+
+ amdgpu_asic_flush_hdp(adev, NULL);
+
+ PP_ASSERT_WITH_CODE(smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrHigh,
+ upper_32_bits(priv->smu_tables.entry[table_id].mc_addr),
+ NULL) == 0,
+ "[CopyTableToSMC] Attempt to Set Dram Addr High Failed!",
+ return -EINVAL;);
+ PP_ASSERT_WITH_CODE(smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrLow,
+ lower_32_bits(priv->smu_tables.entry[table_id].mc_addr),
+ NULL) == 0,
+ "[CopyTableToSMC] Attempt to Set Dram Addr Low Failed!",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_TransferTableDram2Smu,
+ table_id,
+ NULL) == 0,
+ "[CopyTableToSMC] Attempt to Transfer Table To SMU Failed!",
+ return -EINVAL);
+
+ return 0;
+}
+
+int vega12_enable_smc_features(struct pp_hwmgr *hwmgr,
+ bool enable, uint64_t feature_mask)
+{
+ uint32_t smu_features_low, smu_features_high;
+
+ smu_features_low = (uint32_t)((feature_mask & SMU_FEATURES_LOW_MASK) >> SMU_FEATURES_LOW_SHIFT);
+ smu_features_high = (uint32_t)((feature_mask & SMU_FEATURES_HIGH_MASK) >> SMU_FEATURES_HIGH_SHIFT);
+
+ if (enable) {
+ PP_ASSERT_WITH_CODE(smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_EnableSmuFeaturesLow, smu_features_low, NULL) == 0,
+ "[EnableDisableSMCFeatures] Attempt to enable SMU features Low failed!",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_EnableSmuFeaturesHigh, smu_features_high, NULL) == 0,
+ "[EnableDisableSMCFeatures] Attempt to enable SMU features High failed!",
+ return -EINVAL);
+ } else {
+ PP_ASSERT_WITH_CODE(smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DisableSmuFeaturesLow, smu_features_low, NULL) == 0,
+ "[EnableDisableSMCFeatures] Attempt to disable SMU features Low failed!",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DisableSmuFeaturesHigh, smu_features_high, NULL) == 0,
+ "[EnableDisableSMCFeatures] Attempt to disable SMU features High failed!",
+ return -EINVAL);
+ }
+
+ return 0;
+}
+
+int vega12_get_enabled_smc_features(struct pp_hwmgr *hwmgr,
+ uint64_t *features_enabled)
+{
+ uint32_t smc_features_low, smc_features_high;
+
+ if (features_enabled == NULL)
+ return -EINVAL;
+
+ PP_ASSERT_WITH_CODE(smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_GetEnabledSmuFeaturesLow,
+ &smc_features_low) == 0,
+ "[GetEnabledSMCFeatures] Attempt to get SMU features Low failed!",
+ return -EINVAL);
+
+ PP_ASSERT_WITH_CODE(smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_GetEnabledSmuFeaturesHigh,
+ &smc_features_high) == 0,
+ "[GetEnabledSMCFeatures] Attempt to get SMU features High failed!",
+ return -EINVAL);
+
+ *features_enabled = ((((uint64_t)smc_features_low << SMU_FEATURES_LOW_SHIFT) & SMU_FEATURES_LOW_MASK) |
+ (((uint64_t)smc_features_high << SMU_FEATURES_HIGH_SHIFT) & SMU_FEATURES_HIGH_MASK));
+
+ return 0;
+}
+
+static bool vega12_is_dpm_running(struct pp_hwmgr *hwmgr)
+{
+ uint64_t features_enabled = 0;
+
+ vega12_get_enabled_smc_features(hwmgr, &features_enabled);
+
+ if (features_enabled & SMC_DPM_FEATURES)
+ return true;
+ else
+ return false;
+}
+
+static int vega12_set_tools_address(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_smumgr *priv =
+ (struct vega12_smumgr *)(hwmgr->smu_backend);
+
+ if (priv->smu_tables.entry[TABLE_PMSTATUSLOG].mc_addr) {
+ if (!smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetToolsDramAddrHigh,
+ upper_32_bits(priv->smu_tables.entry[TABLE_PMSTATUSLOG].mc_addr),
+ NULL))
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetToolsDramAddrLow,
+ lower_32_bits(priv->smu_tables.entry[TABLE_PMSTATUSLOG].mc_addr),
+ NULL);
+ }
+ return 0;
+}
+
+static int vega12_smu_init(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_smumgr *priv;
+ unsigned long tools_size;
+ struct cgs_firmware_info info = {0};
+ int ret;
+
+ ret = cgs_get_firmware_info(hwmgr->device, CGS_UCODE_ID_SMU,
+ &info);
+ if (ret || !info.kptr)
+ return -EINVAL;
+
+ priv = kzalloc(sizeof(struct vega12_smumgr), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ hwmgr->smu_backend = priv;
+
+ /* allocate space for pptable */
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ sizeof(PPTable_t),
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[TABLE_PPTABLE].handle,
+ &priv->smu_tables.entry[TABLE_PPTABLE].mc_addr,
+ &priv->smu_tables.entry[TABLE_PPTABLE].table);
+ if (ret)
+ goto free_backend;
+
+ priv->smu_tables.entry[TABLE_PPTABLE].version = 0x01;
+ priv->smu_tables.entry[TABLE_PPTABLE].size = sizeof(PPTable_t);
+
+ /* allocate space for watermarks table */
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ sizeof(Watermarks_t),
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[TABLE_WATERMARKS].handle,
+ &priv->smu_tables.entry[TABLE_WATERMARKS].mc_addr,
+ &priv->smu_tables.entry[TABLE_WATERMARKS].table);
+
+ if (ret)
+ goto err0;
+
+ priv->smu_tables.entry[TABLE_WATERMARKS].version = 0x01;
+ priv->smu_tables.entry[TABLE_WATERMARKS].size = sizeof(Watermarks_t);
+
+ tools_size = 0x19000;
+ if (tools_size) {
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ tools_size,
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[TABLE_PMSTATUSLOG].handle,
+ &priv->smu_tables.entry[TABLE_PMSTATUSLOG].mc_addr,
+ &priv->smu_tables.entry[TABLE_PMSTATUSLOG].table);
+ if (ret)
+ goto err1;
+
+ priv->smu_tables.entry[TABLE_PMSTATUSLOG].version = 0x01;
+ priv->smu_tables.entry[TABLE_PMSTATUSLOG].size = tools_size;
+ }
+
+ /* allocate space for AVFS Fuse table */
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ sizeof(AvfsFuseOverride_t),
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[TABLE_AVFS_FUSE_OVERRIDE].handle,
+ &priv->smu_tables.entry[TABLE_AVFS_FUSE_OVERRIDE].mc_addr,
+ &priv->smu_tables.entry[TABLE_AVFS_FUSE_OVERRIDE].table);
+
+ if (ret)
+ goto err2;
+
+ priv->smu_tables.entry[TABLE_AVFS_FUSE_OVERRIDE].version = 0x01;
+ priv->smu_tables.entry[TABLE_AVFS_FUSE_OVERRIDE].size = sizeof(AvfsFuseOverride_t);
+
+ /* allocate space for OverDrive table */
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ sizeof(OverDriveTable_t),
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[TABLE_OVERDRIVE].handle,
+ &priv->smu_tables.entry[TABLE_OVERDRIVE].mc_addr,
+ &priv->smu_tables.entry[TABLE_OVERDRIVE].table);
+ if (ret)
+ goto err3;
+
+ priv->smu_tables.entry[TABLE_OVERDRIVE].version = 0x01;
+ priv->smu_tables.entry[TABLE_OVERDRIVE].size = sizeof(OverDriveTable_t);
+
+ /* allocate space for SMU_METRICS table */
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ sizeof(SmuMetrics_t),
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[TABLE_SMU_METRICS].handle,
+ &priv->smu_tables.entry[TABLE_SMU_METRICS].mc_addr,
+ &priv->smu_tables.entry[TABLE_SMU_METRICS].table);
+ if (ret)
+ goto err4;
+
+ priv->smu_tables.entry[TABLE_SMU_METRICS].version = 0x01;
+ priv->smu_tables.entry[TABLE_SMU_METRICS].size = sizeof(SmuMetrics_t);
+
+ return 0;
+
+err4:
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_OVERDRIVE].handle,
+ &priv->smu_tables.entry[TABLE_OVERDRIVE].mc_addr,
+ &priv->smu_tables.entry[TABLE_OVERDRIVE].table);
+err3:
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_AVFS_FUSE_OVERRIDE].handle,
+ &priv->smu_tables.entry[TABLE_AVFS_FUSE_OVERRIDE].mc_addr,
+ &priv->smu_tables.entry[TABLE_AVFS_FUSE_OVERRIDE].table);
+err2:
+ if (priv->smu_tables.entry[TABLE_PMSTATUSLOG].table)
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_PMSTATUSLOG].handle,
+ &priv->smu_tables.entry[TABLE_PMSTATUSLOG].mc_addr,
+ &priv->smu_tables.entry[TABLE_PMSTATUSLOG].table);
+err1:
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_WATERMARKS].handle,
+ &priv->smu_tables.entry[TABLE_WATERMARKS].mc_addr,
+ &priv->smu_tables.entry[TABLE_WATERMARKS].table);
+err0:
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_PPTABLE].handle,
+ &priv->smu_tables.entry[TABLE_PPTABLE].mc_addr,
+ &priv->smu_tables.entry[TABLE_PPTABLE].table);
+free_backend:
+ kfree(hwmgr->smu_backend);
+
+ return -EINVAL;
+}
+
+static int vega12_smu_fini(struct pp_hwmgr *hwmgr)
+{
+ struct vega12_smumgr *priv =
+ (struct vega12_smumgr *)(hwmgr->smu_backend);
+
+ if (priv) {
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_PPTABLE].handle,
+ &priv->smu_tables.entry[TABLE_PPTABLE].mc_addr,
+ &priv->smu_tables.entry[TABLE_PPTABLE].table);
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_WATERMARKS].handle,
+ &priv->smu_tables.entry[TABLE_WATERMARKS].mc_addr,
+ &priv->smu_tables.entry[TABLE_WATERMARKS].table);
+ if (priv->smu_tables.entry[TABLE_PMSTATUSLOG].table)
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_PMSTATUSLOG].handle,
+ &priv->smu_tables.entry[TABLE_PMSTATUSLOG].mc_addr,
+ &priv->smu_tables.entry[TABLE_PMSTATUSLOG].table);
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_AVFS_FUSE_OVERRIDE].handle,
+ &priv->smu_tables.entry[TABLE_AVFS_FUSE_OVERRIDE].mc_addr,
+ &priv->smu_tables.entry[TABLE_AVFS_FUSE_OVERRIDE].table);
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_OVERDRIVE].handle,
+ &priv->smu_tables.entry[TABLE_OVERDRIVE].mc_addr,
+ &priv->smu_tables.entry[TABLE_OVERDRIVE].table);
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_SMU_METRICS].handle,
+ &priv->smu_tables.entry[TABLE_SMU_METRICS].mc_addr,
+ &priv->smu_tables.entry[TABLE_SMU_METRICS].table);
+ kfree(hwmgr->smu_backend);
+ hwmgr->smu_backend = NULL;
+ }
+ return 0;
+}
+
+static int vega12_start_smu(struct pp_hwmgr *hwmgr)
+{
+ PP_ASSERT_WITH_CODE(smu9_is_smc_ram_running(hwmgr),
+ "SMC is not running!",
+ return -EINVAL);
+
+ vega12_set_tools_address(hwmgr);
+
+ return 0;
+}
+
+static int vega12_smc_table_manager(struct pp_hwmgr *hwmgr, uint8_t *table,
+ uint16_t table_id, bool rw)
+{
+ int ret;
+
+ if (rw)
+ ret = vega12_copy_table_from_smc(hwmgr, table, table_id);
+ else
+ ret = vega12_copy_table_to_smc(hwmgr, table, table_id);
+
+ return ret;
+}
+
+const struct pp_smumgr_func vega12_smu_funcs = {
+ .name = "vega12_smu",
+ .smu_init = &vega12_smu_init,
+ .smu_fini = &vega12_smu_fini,
+ .start_smu = &vega12_start_smu,
+ .request_smu_load_specific_fw = NULL,
+ .send_msg_to_smc = &smu9_send_msg_to_smc,
+ .send_msg_to_smc_with_parameter = &smu9_send_msg_to_smc_with_parameter,
+ .download_pptable_settings = NULL,
+ .upload_pptable_settings = NULL,
+ .is_dpm_running = vega12_is_dpm_running,
+ .get_argument = smu9_get_argument,
+ .smc_table_manager = vega12_smc_table_manager,
+};
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega12_smumgr.h b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega12_smumgr.h
new file mode 100644
index 000000000000..aeec965ce81f
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega12_smumgr.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _VEGA12_SMUMANAGER_H_
+#define _VEGA12_SMUMANAGER_H_
+
+#include "hwmgr.h"
+#include "vega12/smu9_driver_if.h"
+#include "vega12_hwmgr.h"
+
+struct smu_table_entry {
+ uint32_t version;
+ uint32_t size;
+ uint64_t mc_addr;
+ void *table;
+ struct amdgpu_bo *handle;
+};
+
+struct smu_table_array {
+ struct smu_table_entry entry[TABLE_COUNT];
+};
+
+struct vega12_smumgr {
+ struct smu_table_array smu_tables;
+};
+
+#define SMU_FEATURES_LOW_MASK 0x00000000FFFFFFFF
+#define SMU_FEATURES_LOW_SHIFT 0
+#define SMU_FEATURES_HIGH_MASK 0xFFFFFFFF00000000
+#define SMU_FEATURES_HIGH_SHIFT 32
+
+int vega12_enable_smc_features(struct pp_hwmgr *hwmgr,
+ bool enable, uint64_t feature_mask);
+int vega12_get_enabled_smc_features(struct pp_hwmgr *hwmgr,
+ uint64_t *features_enabled);
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega20_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega20_smumgr.c
new file mode 100644
index 000000000000..cf43629d29d2
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega20_smumgr.c
@@ -0,0 +1,646 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "smumgr.h"
+#include "vega20_inc.h"
+#include "soc15_common.h"
+#include "vega20_smumgr.h"
+#include "vega20_ppsmc.h"
+#include "smu11_driver_if.h"
+#include "ppatomctrl.h"
+#include "pp_debug.h"
+#include "smu_ucode_xfer_vi.h"
+#include "smu7_smumgr.h"
+#include "vega20_hwmgr.h"
+
+#include "smu_v11_0_i2c.h"
+
+/* MP Apertures */
+#define MP0_Public 0x03800000
+#define MP0_SRAM 0x03900000
+#define MP1_Public 0x03b00000
+#define MP1_SRAM 0x03c00004
+
+/* address block */
+#define smnMP1_FIRMWARE_FLAGS 0x3010024
+#define smnMP0_FW_INTF 0x30101c0
+#define smnMP1_PUB_CTRL 0x3010b14
+
+bool vega20_is_smc_ram_running(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t mp1_fw_flags;
+
+ mp1_fw_flags = RREG32_PCIE(MP1_Public |
+ (smnMP1_FIRMWARE_FLAGS & 0xffffffff));
+
+ if ((mp1_fw_flags & MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED_MASK) >>
+ MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED__SHIFT)
+ return true;
+
+ return false;
+}
+
+/*
+ * Check if SMC has responded to previous message.
+ *
+ * @param smumgr the address of the powerplay hardware manager.
+ * @return TRUE SMC has responded, FALSE otherwise.
+ */
+static uint32_t vega20_wait_for_response(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ uint32_t reg;
+
+ reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_SMN_C2PMSG_90);
+
+ phm_wait_for_register_unequal(hwmgr, reg,
+ 0, MP1_C2PMSG_90__CONTENT_MASK);
+
+ return RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90);
+}
+
+/*
+ * Send a message to the SMC, and do not wait for its response.
+ * @param smumgr the address of the powerplay hardware manager.
+ * @param msg the message to send.
+ * @return Always return 0.
+ */
+static int vega20_send_msg_to_smc_without_waiting(struct pp_hwmgr *hwmgr,
+ uint16_t msg)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_66, msg);
+
+ return 0;
+}
+
+/*
+ * Send a message to the SMC, and wait for its response.
+ * @param hwmgr the address of the powerplay hardware manager.
+ * @param msg the message to send.
+ * @return Always return 0.
+ */
+static int vega20_send_msg_to_smc(struct pp_hwmgr *hwmgr, uint16_t msg)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ int ret = 0;
+
+ vega20_wait_for_response(hwmgr);
+
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90, 0);
+
+ vega20_send_msg_to_smc_without_waiting(hwmgr, msg);
+
+ ret = vega20_wait_for_response(hwmgr);
+ if (ret != PPSMC_Result_OK)
+ pr_err("Failed to send message 0x%x, response 0x%x\n", msg, ret);
+
+ return (ret == PPSMC_Result_OK) ? 0 : -EIO;
+}
+
+/*
+ * Send a message to the SMC with parameter
+ * @param hwmgr: the address of the powerplay hardware manager.
+ * @param msg: the message to send.
+ * @param parameter: the parameter to send
+ * @return Always return 0.
+ */
+static int vega20_send_msg_to_smc_with_parameter(struct pp_hwmgr *hwmgr,
+ uint16_t msg, uint32_t parameter)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+ int ret = 0;
+
+ vega20_wait_for_response(hwmgr);
+
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90, 0);
+
+ WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_82, parameter);
+
+ vega20_send_msg_to_smc_without_waiting(hwmgr, msg);
+
+ ret = vega20_wait_for_response(hwmgr);
+ if (ret != PPSMC_Result_OK)
+ pr_err("Failed to send message 0x%x, response 0x%x\n", msg, ret);
+
+ return (ret == PPSMC_Result_OK) ? 0 : -EIO;
+}
+
+static uint32_t vega20_get_argument(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ return RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_82);
+}
+
+/*
+ * Copy table from SMC into driver FB
+ * @param hwmgr the address of the HW manager
+ * @param table_id the driver's table ID to copy from
+ */
+static int vega20_copy_table_from_smc(struct pp_hwmgr *hwmgr,
+ uint8_t *table, int16_t table_id)
+{
+ struct vega20_smumgr *priv =
+ (struct vega20_smumgr *)(hwmgr->smu_backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+ int ret = 0;
+
+ PP_ASSERT_WITH_CODE(table_id < TABLE_COUNT,
+ "Invalid SMU Table ID!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(priv->smu_tables.entry[table_id].version != 0,
+ "Invalid SMU Table version!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(priv->smu_tables.entry[table_id].size != 0,
+ "Invalid SMU Table Length!", return -EINVAL);
+
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrHigh,
+ upper_32_bits(priv->smu_tables.entry[table_id].mc_addr),
+ NULL)) == 0,
+ "[CopyTableFromSMC] Attempt to Set Dram Addr High Failed!",
+ return ret);
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrLow,
+ lower_32_bits(priv->smu_tables.entry[table_id].mc_addr),
+ NULL)) == 0,
+ "[CopyTableFromSMC] Attempt to Set Dram Addr Low Failed!",
+ return ret);
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_TransferTableSmu2Dram, table_id, NULL)) == 0,
+ "[CopyTableFromSMC] Attempt to Transfer Table From SMU Failed!",
+ return ret);
+
+ /* flush hdp cache */
+ amdgpu_asic_flush_hdp(adev, NULL);
+
+ memcpy(table, priv->smu_tables.entry[table_id].table,
+ priv->smu_tables.entry[table_id].size);
+
+ return 0;
+}
+
+/*
+ * Copy table from Driver FB into SMC
+ * @param hwmgr the address of the HW manager
+ * @param table_id the table to copy from
+ */
+static int vega20_copy_table_to_smc(struct pp_hwmgr *hwmgr,
+ uint8_t *table, int16_t table_id)
+{
+ struct vega20_smumgr *priv =
+ (struct vega20_smumgr *)(hwmgr->smu_backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+ int ret = 0;
+
+ PP_ASSERT_WITH_CODE(table_id < TABLE_COUNT,
+ "Invalid SMU Table ID!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(priv->smu_tables.entry[table_id].version != 0,
+ "Invalid SMU Table version!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(priv->smu_tables.entry[table_id].size != 0,
+ "Invalid SMU Table Length!", return -EINVAL);
+
+ memcpy(priv->smu_tables.entry[table_id].table, table,
+ priv->smu_tables.entry[table_id].size);
+
+ amdgpu_asic_flush_hdp(adev, NULL);
+
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrHigh,
+ upper_32_bits(priv->smu_tables.entry[table_id].mc_addr),
+ NULL)) == 0,
+ "[CopyTableToSMC] Attempt to Set Dram Addr High Failed!",
+ return ret);
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrLow,
+ lower_32_bits(priv->smu_tables.entry[table_id].mc_addr),
+ NULL)) == 0,
+ "[CopyTableToSMC] Attempt to Set Dram Addr Low Failed!",
+ return ret);
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_TransferTableDram2Smu, table_id, NULL)) == 0,
+ "[CopyTableToSMC] Attempt to Transfer Table To SMU Failed!",
+ return ret);
+
+ return 0;
+}
+
+int vega20_set_activity_monitor_coeff(struct pp_hwmgr *hwmgr,
+ uint8_t *table, uint16_t workload_type)
+{
+ struct vega20_smumgr *priv =
+ (struct vega20_smumgr *)(hwmgr->smu_backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+ int ret = 0;
+
+ memcpy(priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].table, table,
+ priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].size);
+
+ amdgpu_asic_flush_hdp(adev, NULL);
+
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrHigh,
+ upper_32_bits(priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].mc_addr),
+ NULL)) == 0,
+ "[SetActivityMonitor] Attempt to Set Dram Addr High Failed!",
+ return ret);
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrLow,
+ lower_32_bits(priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].mc_addr),
+ NULL)) == 0,
+ "[SetActivityMonitor] Attempt to Set Dram Addr Low Failed!",
+ return ret);
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_TransferTableDram2Smu,
+ TABLE_ACTIVITY_MONITOR_COEFF | (workload_type << 16),
+ NULL)) == 0,
+ "[SetActivityMonitor] Attempt to Transfer Table To SMU Failed!",
+ return ret);
+
+ return 0;
+}
+
+int vega20_get_activity_monitor_coeff(struct pp_hwmgr *hwmgr,
+ uint8_t *table, uint16_t workload_type)
+{
+ struct vega20_smumgr *priv =
+ (struct vega20_smumgr *)(hwmgr->smu_backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+ int ret = 0;
+
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrHigh,
+ upper_32_bits(priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].mc_addr),
+ NULL)) == 0,
+ "[GetActivityMonitor] Attempt to Set Dram Addr High Failed!",
+ return ret);
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrLow,
+ lower_32_bits(priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].mc_addr),
+ NULL)) == 0,
+ "[GetActivityMonitor] Attempt to Set Dram Addr Low Failed!",
+ return ret);
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_TransferTableSmu2Dram,
+ TABLE_ACTIVITY_MONITOR_COEFF | (workload_type << 16), NULL)) == 0,
+ "[GetActivityMonitor] Attempt to Transfer Table From SMU Failed!",
+ return ret);
+
+ /* flush hdp cache */
+ amdgpu_asic_flush_hdp(adev, NULL);
+
+ memcpy(table, priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].table,
+ priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].size);
+
+ return 0;
+}
+
+int vega20_enable_smc_features(struct pp_hwmgr *hwmgr,
+ bool enable, uint64_t feature_mask)
+{
+ uint32_t smu_features_low, smu_features_high;
+ int ret = 0;
+
+ smu_features_low = (uint32_t)((feature_mask & SMU_FEATURES_LOW_MASK) >> SMU_FEATURES_LOW_SHIFT);
+ smu_features_high = (uint32_t)((feature_mask & SMU_FEATURES_HIGH_MASK) >> SMU_FEATURES_HIGH_SHIFT);
+
+ if (enable) {
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_EnableSmuFeaturesLow, smu_features_low, NULL)) == 0,
+ "[EnableDisableSMCFeatures] Attempt to enable SMU features Low failed!",
+ return ret);
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_EnableSmuFeaturesHigh, smu_features_high, NULL)) == 0,
+ "[EnableDisableSMCFeatures] Attempt to enable SMU features High failed!",
+ return ret);
+ } else {
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DisableSmuFeaturesLow, smu_features_low, NULL)) == 0,
+ "[EnableDisableSMCFeatures] Attempt to disable SMU features Low failed!",
+ return ret);
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_DisableSmuFeaturesHigh, smu_features_high, NULL)) == 0,
+ "[EnableDisableSMCFeatures] Attempt to disable SMU features High failed!",
+ return ret);
+ }
+
+ return 0;
+}
+
+int vega20_get_enabled_smc_features(struct pp_hwmgr *hwmgr,
+ uint64_t *features_enabled)
+{
+ uint32_t smc_features_low, smc_features_high;
+ int ret = 0;
+
+ if (features_enabled == NULL)
+ return -EINVAL;
+
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_GetEnabledSmuFeaturesLow,
+ &smc_features_low)) == 0,
+ "[GetEnabledSMCFeatures] Attempt to get SMU features Low failed!",
+ return ret);
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_GetEnabledSmuFeaturesHigh,
+ &smc_features_high)) == 0,
+ "[GetEnabledSMCFeatures] Attempt to get SMU features High failed!",
+ return ret);
+
+ *features_enabled = ((((uint64_t)smc_features_low << SMU_FEATURES_LOW_SHIFT) & SMU_FEATURES_LOW_MASK) |
+ (((uint64_t)smc_features_high << SMU_FEATURES_HIGH_SHIFT) & SMU_FEATURES_HIGH_MASK));
+
+ return 0;
+}
+
+static int vega20_set_tools_address(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_smumgr *priv =
+ (struct vega20_smumgr *)(hwmgr->smu_backend);
+ int ret = 0;
+
+ if (priv->smu_tables.entry[TABLE_PMSTATUSLOG].mc_addr) {
+ ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetToolsDramAddrHigh,
+ upper_32_bits(priv->smu_tables.entry[TABLE_PMSTATUSLOG].mc_addr),
+ NULL);
+ if (!ret)
+ ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetToolsDramAddrLow,
+ lower_32_bits(priv->smu_tables.entry[TABLE_PMSTATUSLOG].mc_addr),
+ NULL);
+ }
+
+ return ret;
+}
+
+int vega20_set_pptable_driver_address(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_smumgr *priv =
+ (struct vega20_smumgr *)(hwmgr->smu_backend);
+ int ret = 0;
+
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrHigh,
+ upper_32_bits(priv->smu_tables.entry[TABLE_PPTABLE].mc_addr),
+ NULL)) == 0,
+ "[SetPPtabeDriverAddress] Attempt to Set Dram Addr High Failed!",
+ return ret);
+ PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetDriverDramAddrLow,
+ lower_32_bits(priv->smu_tables.entry[TABLE_PPTABLE].mc_addr),
+ NULL)) == 0,
+ "[SetPPtabeDriverAddress] Attempt to Set Dram Addr Low Failed!",
+ return ret);
+
+ return ret;
+}
+
+static int vega20_smu_init(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_smumgr *priv;
+ unsigned long tools_size = 0x19000;
+ int ret = 0;
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ struct cgs_firmware_info info = {0};
+
+ ret = cgs_get_firmware_info(hwmgr->device,
+ smu7_convert_fw_type_to_cgs(UCODE_ID_SMU),
+ &info);
+ if (ret || !info.kptr)
+ return -EINVAL;
+
+ priv = kzalloc(sizeof(struct vega20_smumgr), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ hwmgr->smu_backend = priv;
+
+ /* allocate space for pptable */
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ sizeof(PPTable_t),
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[TABLE_PPTABLE].handle,
+ &priv->smu_tables.entry[TABLE_PPTABLE].mc_addr,
+ &priv->smu_tables.entry[TABLE_PPTABLE].table);
+ if (ret)
+ goto free_backend;
+
+ priv->smu_tables.entry[TABLE_PPTABLE].version = 0x01;
+ priv->smu_tables.entry[TABLE_PPTABLE].size = sizeof(PPTable_t);
+
+ /* allocate space for watermarks table */
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ sizeof(Watermarks_t),
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[TABLE_WATERMARKS].handle,
+ &priv->smu_tables.entry[TABLE_WATERMARKS].mc_addr,
+ &priv->smu_tables.entry[TABLE_WATERMARKS].table);
+ if (ret)
+ goto err0;
+
+ priv->smu_tables.entry[TABLE_WATERMARKS].version = 0x01;
+ priv->smu_tables.entry[TABLE_WATERMARKS].size = sizeof(Watermarks_t);
+
+ /* allocate space for pmstatuslog table */
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ tools_size,
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[TABLE_PMSTATUSLOG].handle,
+ &priv->smu_tables.entry[TABLE_PMSTATUSLOG].mc_addr,
+ &priv->smu_tables.entry[TABLE_PMSTATUSLOG].table);
+ if (ret)
+ goto err1;
+
+ priv->smu_tables.entry[TABLE_PMSTATUSLOG].version = 0x01;
+ priv->smu_tables.entry[TABLE_PMSTATUSLOG].size = tools_size;
+
+ /* allocate space for OverDrive table */
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ sizeof(OverDriveTable_t),
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[TABLE_OVERDRIVE].handle,
+ &priv->smu_tables.entry[TABLE_OVERDRIVE].mc_addr,
+ &priv->smu_tables.entry[TABLE_OVERDRIVE].table);
+ if (ret)
+ goto err2;
+
+ priv->smu_tables.entry[TABLE_OVERDRIVE].version = 0x01;
+ priv->smu_tables.entry[TABLE_OVERDRIVE].size = sizeof(OverDriveTable_t);
+
+ /* allocate space for SmuMetrics table */
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ sizeof(SmuMetrics_t),
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[TABLE_SMU_METRICS].handle,
+ &priv->smu_tables.entry[TABLE_SMU_METRICS].mc_addr,
+ &priv->smu_tables.entry[TABLE_SMU_METRICS].table);
+ if (ret)
+ goto err3;
+
+ priv->smu_tables.entry[TABLE_SMU_METRICS].version = 0x01;
+ priv->smu_tables.entry[TABLE_SMU_METRICS].size = sizeof(SmuMetrics_t);
+
+ /* allocate space for ActivityMonitor table */
+ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev,
+ sizeof(DpmActivityMonitorCoeffInt_t),
+ PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM,
+ &priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].handle,
+ &priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].mc_addr,
+ &priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].table);
+ if (ret)
+ goto err4;
+
+ priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].version = 0x01;
+ priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].size = sizeof(DpmActivityMonitorCoeffInt_t);
+
+ ret = smu_v11_0_i2c_control_init(&adev->pm.smu_i2c);
+ if (ret)
+ goto err4;
+
+ return 0;
+
+err4:
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_SMU_METRICS].handle,
+ &priv->smu_tables.entry[TABLE_SMU_METRICS].mc_addr,
+ &priv->smu_tables.entry[TABLE_SMU_METRICS].table);
+err3:
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_OVERDRIVE].handle,
+ &priv->smu_tables.entry[TABLE_OVERDRIVE].mc_addr,
+ &priv->smu_tables.entry[TABLE_OVERDRIVE].table);
+err2:
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_PMSTATUSLOG].handle,
+ &priv->smu_tables.entry[TABLE_PMSTATUSLOG].mc_addr,
+ &priv->smu_tables.entry[TABLE_PMSTATUSLOG].table);
+err1:
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_WATERMARKS].handle,
+ &priv->smu_tables.entry[TABLE_WATERMARKS].mc_addr,
+ &priv->smu_tables.entry[TABLE_WATERMARKS].table);
+err0:
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_PPTABLE].handle,
+ &priv->smu_tables.entry[TABLE_PPTABLE].mc_addr,
+ &priv->smu_tables.entry[TABLE_PPTABLE].table);
+free_backend:
+ kfree(hwmgr->smu_backend);
+
+ return -EINVAL;
+}
+
+static int vega20_smu_fini(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_smumgr *priv =
+ (struct vega20_smumgr *)(hwmgr->smu_backend);
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ smu_v11_0_i2c_control_fini(&adev->pm.smu_i2c);
+
+ if (priv) {
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_PPTABLE].handle,
+ &priv->smu_tables.entry[TABLE_PPTABLE].mc_addr,
+ &priv->smu_tables.entry[TABLE_PPTABLE].table);
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_WATERMARKS].handle,
+ &priv->smu_tables.entry[TABLE_WATERMARKS].mc_addr,
+ &priv->smu_tables.entry[TABLE_WATERMARKS].table);
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_PMSTATUSLOG].handle,
+ &priv->smu_tables.entry[TABLE_PMSTATUSLOG].mc_addr,
+ &priv->smu_tables.entry[TABLE_PMSTATUSLOG].table);
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_OVERDRIVE].handle,
+ &priv->smu_tables.entry[TABLE_OVERDRIVE].mc_addr,
+ &priv->smu_tables.entry[TABLE_OVERDRIVE].table);
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_SMU_METRICS].handle,
+ &priv->smu_tables.entry[TABLE_SMU_METRICS].mc_addr,
+ &priv->smu_tables.entry[TABLE_SMU_METRICS].table);
+ amdgpu_bo_free_kernel(&priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].handle,
+ &priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].mc_addr,
+ &priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].table);
+ kfree(hwmgr->smu_backend);
+ hwmgr->smu_backend = NULL;
+ }
+
+ return 0;
+}
+
+static int vega20_start_smu(struct pp_hwmgr *hwmgr)
+{
+ int ret;
+
+ ret = vega20_is_smc_ram_running(hwmgr);
+ PP_ASSERT_WITH_CODE(ret,
+ "[Vega20StartSmu] SMC is not running!",
+ return -EINVAL);
+
+ ret = vega20_set_tools_address(hwmgr);
+ PP_ASSERT_WITH_CODE(!ret,
+ "[Vega20StartSmu] Failed to set tools address!",
+ return ret);
+
+ return 0;
+}
+
+static bool vega20_is_dpm_running(struct pp_hwmgr *hwmgr)
+{
+ uint64_t features_enabled = 0;
+
+ vega20_get_enabled_smc_features(hwmgr, &features_enabled);
+
+ if (features_enabled & SMC_DPM_FEATURES)
+ return true;
+ else
+ return false;
+}
+
+static int vega20_smc_table_manager(struct pp_hwmgr *hwmgr, uint8_t *table,
+ uint16_t table_id, bool rw)
+{
+ int ret;
+
+ if (rw)
+ ret = vega20_copy_table_from_smc(hwmgr, table, table_id);
+ else
+ ret = vega20_copy_table_to_smc(hwmgr, table, table_id);
+
+ return ret;
+}
+
+const struct pp_smumgr_func vega20_smu_funcs = {
+ .name = "vega20_smu",
+ .smu_init = &vega20_smu_init,
+ .smu_fini = &vega20_smu_fini,
+ .start_smu = &vega20_start_smu,
+ .request_smu_load_specific_fw = NULL,
+ .send_msg_to_smc = &vega20_send_msg_to_smc,
+ .send_msg_to_smc_with_parameter = &vega20_send_msg_to_smc_with_parameter,
+ .download_pptable_settings = NULL,
+ .upload_pptable_settings = NULL,
+ .is_dpm_running = vega20_is_dpm_running,
+ .get_argument = vega20_get_argument,
+ .smc_table_manager = vega20_smc_table_manager,
+};
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega20_smumgr.h b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega20_smumgr.h
new file mode 100644
index 000000000000..62ebbfd6068f
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega20_smumgr.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _VEGA20_SMUMANAGER_H_
+#define _VEGA20_SMUMANAGER_H_
+
+#include "hwmgr.h"
+#include "smu11_driver_if.h"
+
+struct smu_table_entry {
+ uint32_t version;
+ uint32_t size;
+ uint64_t mc_addr;
+ void *table;
+ struct amdgpu_bo *handle;
+};
+
+struct smu_table_array {
+ struct smu_table_entry entry[TABLE_COUNT];
+};
+
+struct vega20_smumgr {
+ struct smu_table_array smu_tables;
+};
+
+#define SMU_FEATURES_LOW_MASK 0x00000000FFFFFFFF
+#define SMU_FEATURES_LOW_SHIFT 0
+#define SMU_FEATURES_HIGH_MASK 0xFFFFFFFF00000000
+#define SMU_FEATURES_HIGH_SHIFT 32
+
+int vega20_enable_smc_features(struct pp_hwmgr *hwmgr,
+ bool enable, uint64_t feature_mask);
+int vega20_get_enabled_smc_features(struct pp_hwmgr *hwmgr,
+ uint64_t *features_enabled);
+int vega20_set_activity_monitor_coeff(struct pp_hwmgr *hwmgr,
+ uint8_t *table, uint16_t workload_type);
+int vega20_get_activity_monitor_coeff(struct pp_hwmgr *hwmgr,
+ uint8_t *table, uint16_t workload_type);
+int vega20_set_pptable_driver_address(struct pp_hwmgr *hwmgr);
+
+bool vega20_is_smc_ram_running(struct pp_hwmgr *hwmgr);
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/vegam_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vegam_smumgr.c
new file mode 100644
index 000000000000..0ecc18b55ffb
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vegam_smumgr.c
@@ -0,0 +1,2302 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "pp_debug.h"
+#include "smumgr.h"
+#include "smu_ucode_xfer_vi.h"
+#include "vegam_smumgr.h"
+#include "smu/smu_7_1_3_d.h"
+#include "smu/smu_7_1_3_sh_mask.h"
+#include "gmc/gmc_8_1_d.h"
+#include "gmc/gmc_8_1_sh_mask.h"
+#include "oss/oss_3_0_d.h"
+#include "gca/gfx_8_0_d.h"
+#include "bif/bif_5_0_d.h"
+#include "bif/bif_5_0_sh_mask.h"
+#include "ppatomctrl.h"
+#include "cgs_common.h"
+#include "smu7_ppsmc.h"
+
+#include "smu7_dyn_defaults.h"
+
+#include "smu7_hwmgr.h"
+#include "hardwaremanager.h"
+#include "atombios.h"
+#include "pppcielanes.h"
+
+#include "dce/dce_11_2_d.h"
+#include "dce/dce_11_2_sh_mask.h"
+
+#define PPVEGAM_TARGETACTIVITY_DFLT 50
+
+#define VOLTAGE_VID_OFFSET_SCALE1 625
+#define VOLTAGE_VID_OFFSET_SCALE2 100
+#define POWERTUNE_DEFAULT_SET_MAX 1
+#define VDDC_VDDCI_DELTA 200
+#define MC_CG_ARB_FREQ_F1 0x0b
+
+#define STRAP_ASIC_RO_LSB 2168
+#define STRAP_ASIC_RO_MSB 2175
+
+#define PPSMC_MSG_ApplyAvfsCksOffVoltage ((uint16_t) 0x415)
+#define PPSMC_MSG_EnableModeSwitchRLCNotification ((uint16_t) 0x305)
+
+static const struct vegam_pt_defaults
+vegam_power_tune_data_set_array[POWERTUNE_DEFAULT_SET_MAX] = {
+ /* sviLoadLIneEn, SviLoadLineVddC, TDC_VDDC_ThrottleReleaseLimitPerc, TDC_MAWt,
+ * TdcWaterfallCtl, DTEAmbientTempBase, DisplayCac, BAPM_TEMP_GRADIENT */
+ { 1, 0xF, 0xFD, 0x19, 5, 45, 0, 0xB0000,
+ { 0x79, 0x253, 0x25D, 0xAE, 0x72, 0x80, 0x83, 0x86, 0x6F, 0xC8, 0xC9, 0xC9, 0x2F, 0x4D, 0x61},
+ { 0x17C, 0x172, 0x180, 0x1BC, 0x1B3, 0x1BD, 0x206, 0x200, 0x203, 0x25D, 0x25A, 0x255, 0x2C3, 0x2C5, 0x2B4 } },
+};
+
+static const sclkFcwRange_t Range_Table[NUM_SCLK_RANGE] = {
+ {VCO_2_4, POSTDIV_DIV_BY_16, 75, 160, 112},
+ {VCO_3_6, POSTDIV_DIV_BY_16, 112, 224, 160},
+ {VCO_2_4, POSTDIV_DIV_BY_8, 75, 160, 112},
+ {VCO_3_6, POSTDIV_DIV_BY_8, 112, 224, 160},
+ {VCO_2_4, POSTDIV_DIV_BY_4, 75, 160, 112},
+ {VCO_3_6, POSTDIV_DIV_BY_4, 112, 216, 160},
+ {VCO_2_4, POSTDIV_DIV_BY_2, 75, 160, 108},
+ {VCO_3_6, POSTDIV_DIV_BY_2, 112, 216, 160} };
+
+static int vegam_smu_init(struct pp_hwmgr *hwmgr)
+{
+ struct vegam_smumgr *smu_data;
+
+ smu_data = kzalloc(sizeof(struct vegam_smumgr), GFP_KERNEL);
+ if (smu_data == NULL)
+ return -ENOMEM;
+
+ hwmgr->smu_backend = smu_data;
+
+ if (smu7_init(hwmgr)) {
+ kfree(smu_data);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vegam_start_smu_in_protection_mode(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+
+ /* Wait for smc boot up */
+ /* PHM_WAIT_VFPF_INDIRECT_FIELD_UNEQUAL(smumgr, SMC_IND, RCU_UC_EVENTS, boot_seq_done, 0) */
+
+ /* Assert reset */
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 1);
+
+ result = smu7_upload_smu_firmware_image(hwmgr);
+ if (result != 0)
+ return result;
+
+ /* Clear status */
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixSMU_STATUS, 0);
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
+
+ /* De-assert reset */
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 0);
+
+
+ PHM_WAIT_VFPF_INDIRECT_FIELD(hwmgr, SMC_IND, RCU_UC_EVENTS, INTERRUPTS_ENABLED, 1);
+
+
+ /* Call Test SMU message with 0x20000 offset to trigger SMU start */
+ smu7_send_msg_to_smc_offset(hwmgr);
+
+ /* Wait done bit to be set */
+ /* Check pass/failed indicator */
+
+ PHM_WAIT_VFPF_INDIRECT_FIELD_UNEQUAL(hwmgr, SMC_IND, SMU_STATUS, SMU_DONE, 0);
+
+ if (1 != PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMU_STATUS, SMU_PASS))
+ PP_ASSERT_WITH_CODE(false, "SMU Firmware start failed!", return -1);
+
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixFIRMWARE_FLAGS, 0);
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 1);
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 0);
+
+ /* Wait for firmware to initialize */
+ PHM_WAIT_VFPF_INDIRECT_FIELD(hwmgr, SMC_IND, FIRMWARE_FLAGS, INTERRUPTS_ENABLED, 1);
+
+ return result;
+}
+
+static int vegam_start_smu_in_non_protection_mode(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+
+ /* wait for smc boot up */
+ PHM_WAIT_VFPF_INDIRECT_FIELD_UNEQUAL(hwmgr, SMC_IND, RCU_UC_EVENTS, boot_seq_done, 0);
+
+ /* Clear firmware interrupt enable flag */
+ /* PHM_WRITE_VFPF_INDIRECT_FIELD(pSmuMgr, SMC_IND, SMC_SYSCON_MISC_CNTL, pre_fetcher_en, 1); */
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixFIRMWARE_FLAGS, 0);
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL,
+ rst_reg, 1);
+
+ result = smu7_upload_smu_firmware_image(hwmgr);
+ if (result != 0)
+ return result;
+
+ /* Set smc instruct start point at 0x0 */
+ smu7_program_jump_on_start(hwmgr);
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
+
+ PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+ SMC_SYSCON_RESET_CNTL, rst_reg, 0);
+
+ /* Wait for firmware to initialize */
+
+ PHM_WAIT_VFPF_INDIRECT_FIELD(hwmgr, SMC_IND,
+ FIRMWARE_FLAGS, INTERRUPTS_ENABLED, 1);
+
+ return result;
+}
+
+static int vegam_start_smu(struct pp_hwmgr *hwmgr)
+{
+ int result = 0;
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+
+ /* Only start SMC if SMC RAM is not running */
+ if (!smu7_is_smc_ram_running(hwmgr) && hwmgr->not_vf) {
+ smu_data->protected_mode = (uint8_t)(PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device,
+ CGS_IND_REG__SMC, SMU_FIRMWARE, SMU_MODE));
+ smu_data->smu7_data.security_hard_key = (uint8_t)(PHM_READ_VFPF_INDIRECT_FIELD(
+ hwmgr->device, CGS_IND_REG__SMC, SMU_FIRMWARE, SMU_SEL));
+
+ /* Check if SMU is running in protected mode */
+ if (smu_data->protected_mode == 0)
+ result = vegam_start_smu_in_non_protection_mode(hwmgr);
+ else
+ result = vegam_start_smu_in_protection_mode(hwmgr);
+
+ if (result != 0)
+ PP_ASSERT_WITH_CODE(0, "Failed to load SMU ucode.", return result);
+ }
+
+ /* Setup SoftRegsStart here for register lookup in case DummyBackEnd is used and ProcessFirmwareHeader is not executed */
+ smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION + offsetof(SMU75_Firmware_Header, SoftRegisters),
+ &(smu_data->smu7_data.soft_regs_start),
+ 0x40000);
+
+ result = smu7_request_smu_load_fw(hwmgr);
+
+ return result;
+}
+
+static int vegam_process_firmware_header(struct pp_hwmgr *hwmgr)
+{
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t tmp;
+ int result;
+ bool error = false;
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU75_Firmware_Header, DpmTable),
+ &tmp, SMC_RAM_END);
+
+ if (0 == result)
+ smu_data->smu7_data.dpm_table_start = tmp;
+
+ error |= (0 != result);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU75_Firmware_Header, SoftRegisters),
+ &tmp, SMC_RAM_END);
+
+ if (!result) {
+ data->soft_regs_start = tmp;
+ smu_data->smu7_data.soft_regs_start = tmp;
+ }
+
+ error |= (0 != result);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU75_Firmware_Header, mcRegisterTable),
+ &tmp, SMC_RAM_END);
+
+ if (!result)
+ smu_data->smu7_data.mc_reg_table_start = tmp;
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU75_Firmware_Header, FanTable),
+ &tmp, SMC_RAM_END);
+
+ if (!result)
+ smu_data->smu7_data.fan_table_start = tmp;
+
+ error |= (0 != result);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU75_Firmware_Header, mcArbDramTimingTable),
+ &tmp, SMC_RAM_END);
+
+ if (!result)
+ smu_data->smu7_data.arb_table_start = tmp;
+
+ error |= (0 != result);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU75_Firmware_Header, Version),
+ &tmp, SMC_RAM_END);
+
+ if (!result)
+ hwmgr->microcode_version_info.SMC = tmp;
+
+ error |= (0 != result);
+
+ return error ? -1 : 0;
+}
+
+static bool vegam_is_dpm_running(struct pp_hwmgr *hwmgr)
+{
+ return (1 == PHM_READ_INDIRECT_FIELD(hwmgr->device,
+ CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON))
+ ? true : false;
+}
+
+static uint32_t vegam_get_mac_definition(uint32_t value)
+{
+ switch (value) {
+ case SMU_MAX_LEVELS_GRAPHICS:
+ return SMU75_MAX_LEVELS_GRAPHICS;
+ case SMU_MAX_LEVELS_MEMORY:
+ return SMU75_MAX_LEVELS_MEMORY;
+ case SMU_MAX_LEVELS_LINK:
+ return SMU75_MAX_LEVELS_LINK;
+ case SMU_MAX_ENTRIES_SMIO:
+ return SMU75_MAX_ENTRIES_SMIO;
+ case SMU_MAX_LEVELS_VDDC:
+ return SMU75_MAX_LEVELS_VDDC;
+ case SMU_MAX_LEVELS_VDDGFX:
+ return SMU75_MAX_LEVELS_VDDGFX;
+ case SMU_MAX_LEVELS_VDDCI:
+ return SMU75_MAX_LEVELS_VDDCI;
+ case SMU_MAX_LEVELS_MVDD:
+ return SMU75_MAX_LEVELS_MVDD;
+ case SMU_UVD_MCLK_HANDSHAKE_DISABLE:
+ return SMU7_UVD_MCLK_HANDSHAKE_DISABLE |
+ SMU7_VCE_MCLK_HANDSHAKE_DISABLE;
+ }
+
+ pr_warn("can't get the mac of %x\n", value);
+ return 0;
+}
+
+static int vegam_update_uvd_smc_table(struct pp_hwmgr *hwmgr)
+{
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+ uint32_t mm_boot_level_offset, mm_boot_level_value;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ smu_data->smc_state_table.UvdBootLevel = 0;
+ if (table_info->mm_dep_table->count > 0)
+ smu_data->smc_state_table.UvdBootLevel =
+ (uint8_t) (table_info->mm_dep_table->count - 1);
+ mm_boot_level_offset = smu_data->smu7_data.dpm_table_start + offsetof(SMU75_Discrete_DpmTable,
+ UvdBootLevel);
+ mm_boot_level_offset /= 4;
+ mm_boot_level_offset *= 4;
+ mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, mm_boot_level_offset);
+ mm_boot_level_value &= 0x00FFFFFF;
+ mm_boot_level_value |= smu_data->smc_state_table.UvdBootLevel << 24;
+ cgs_write_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
+
+ if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_UVDDPM) ||
+ phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_StablePState))
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_UVDDPM_SetEnabledMask,
+ (uint32_t)(1 << smu_data->smc_state_table.UvdBootLevel),
+ NULL);
+ return 0;
+}
+
+static int vegam_update_vce_smc_table(struct pp_hwmgr *hwmgr)
+{
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+ uint32_t mm_boot_level_offset, mm_boot_level_value;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_StablePState))
+ smu_data->smc_state_table.VceBootLevel =
+ (uint8_t) (table_info->mm_dep_table->count - 1);
+ else
+ smu_data->smc_state_table.VceBootLevel = 0;
+
+ mm_boot_level_offset = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU75_Discrete_DpmTable, VceBootLevel);
+ mm_boot_level_offset /= 4;
+ mm_boot_level_offset *= 4;
+ mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, mm_boot_level_offset);
+ mm_boot_level_value &= 0xFF00FFFF;
+ mm_boot_level_value |= smu_data->smc_state_table.VceBootLevel << 16;
+ cgs_write_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_StablePState))
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_VCEDPM_SetEnabledMask,
+ (uint32_t)1 << smu_data->smc_state_table.VceBootLevel,
+ NULL);
+ return 0;
+}
+
+static int vegam_update_bif_smc_table(struct pp_hwmgr *hwmgr)
+{
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_pcie_table *pcie_table = table_info->pcie_table;
+ int max_entry, i;
+
+ max_entry = (SMU75_MAX_LEVELS_LINK < pcie_table->count) ?
+ SMU75_MAX_LEVELS_LINK :
+ pcie_table->count;
+ /* Setup BIF_SCLK levels */
+ for (i = 0; i < max_entry; i++)
+ smu_data->bif_sclk_table[i] = pcie_table->entries[i].pcie_sclk;
+ return 0;
+}
+
+static int vegam_update_smc_table(struct pp_hwmgr *hwmgr, uint32_t type)
+{
+ switch (type) {
+ case SMU_UVD_TABLE:
+ vegam_update_uvd_smc_table(hwmgr);
+ break;
+ case SMU_VCE_TABLE:
+ vegam_update_vce_smc_table(hwmgr);
+ break;
+ case SMU_BIF_TABLE:
+ vegam_update_bif_smc_table(hwmgr);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void vegam_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr)
+{
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ if (table_info &&
+ table_info->cac_dtp_table->usPowerTuneDataSetID <= POWERTUNE_DEFAULT_SET_MAX &&
+ table_info->cac_dtp_table->usPowerTuneDataSetID)
+ smu_data->power_tune_defaults =
+ &vegam_power_tune_data_set_array
+ [table_info->cac_dtp_table->usPowerTuneDataSetID - 1];
+ else
+ smu_data->power_tune_defaults = &vegam_power_tune_data_set_array[0];
+
+}
+
+static int vegam_populate_smc_mvdd_table(struct pp_hwmgr *hwmgr,
+ SMU75_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t count, level;
+
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
+ count = data->mvdd_voltage_table.count;
+ if (count > SMU_MAX_SMIO_LEVELS)
+ count = SMU_MAX_SMIO_LEVELS;
+ for (level = 0; level < count; level++) {
+ table->SmioTable2.Pattern[level].Voltage = PP_HOST_TO_SMC_US(
+ data->mvdd_voltage_table.entries[level].value * VOLTAGE_SCALE);
+ /* Index into DpmTable.Smio. Drive bits from Smio entry to get this voltage level.*/
+ table->SmioTable2.Pattern[level].Smio =
+ (uint8_t) level;
+ table->Smio[level] |=
+ data->mvdd_voltage_table.entries[level].smio_low;
+ }
+ table->SmioMask2 = data->mvdd_voltage_table.mask_low;
+
+ table->MvddLevelCount = (uint32_t) PP_HOST_TO_SMC_UL(count);
+ }
+
+ return 0;
+}
+
+static int vegam_populate_smc_vddci_table(struct pp_hwmgr *hwmgr,
+ struct SMU75_Discrete_DpmTable *table)
+{
+ uint32_t count, level;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ count = data->vddci_voltage_table.count;
+
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
+ if (count > SMU_MAX_SMIO_LEVELS)
+ count = SMU_MAX_SMIO_LEVELS;
+ for (level = 0; level < count; ++level) {
+ table->SmioTable1.Pattern[level].Voltage = PP_HOST_TO_SMC_US(
+ data->vddci_voltage_table.entries[level].value * VOLTAGE_SCALE);
+ table->SmioTable1.Pattern[level].Smio = (uint8_t) level;
+
+ table->Smio[level] |= data->vddci_voltage_table.entries[level].smio_low;
+ }
+ }
+
+ table->SmioMask1 = data->vddci_voltage_table.mask_low;
+
+ return 0;
+}
+
+static int vegam_populate_cac_table(struct pp_hwmgr *hwmgr,
+ struct SMU75_Discrete_DpmTable *table)
+{
+ uint32_t count;
+ uint8_t index;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_voltage_lookup_table *lookup_table =
+ table_info->vddc_lookup_table;
+ /* tables is already swapped, so in order to use the value from it,
+ * we need to swap it back.
+ * We are populating vddc CAC data to BapmVddc table
+ * in split and merged mode
+ */
+ for (count = 0; count < lookup_table->count; count++) {
+ index = phm_get_voltage_index(lookup_table,
+ data->vddc_voltage_table.entries[count].value);
+ table->BapmVddcVidLoSidd[count] =
+ convert_to_vid(lookup_table->entries[index].us_cac_low);
+ table->BapmVddcVidHiSidd[count] =
+ convert_to_vid(lookup_table->entries[index].us_cac_mid);
+ table->BapmVddcVidHiSidd2[count] =
+ convert_to_vid(lookup_table->entries[index].us_cac_high);
+ }
+
+ return 0;
+}
+
+static int vegam_populate_smc_voltage_tables(struct pp_hwmgr *hwmgr,
+ struct SMU75_Discrete_DpmTable *table)
+{
+ vegam_populate_smc_vddci_table(hwmgr, table);
+ vegam_populate_smc_mvdd_table(hwmgr, table);
+ vegam_populate_cac_table(hwmgr, table);
+
+ return 0;
+}
+
+static int vegam_populate_ulv_level(struct pp_hwmgr *hwmgr,
+ struct SMU75_Discrete_Ulv *state)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+ state->CcPwrDynRm = 0;
+ state->CcPwrDynRm1 = 0;
+
+ state->VddcOffset = (uint16_t) table_info->us_ulv_voltage_offset;
+ state->VddcOffsetVid = (uint8_t)(table_info->us_ulv_voltage_offset *
+ VOLTAGE_VID_OFFSET_SCALE2 / VOLTAGE_VID_OFFSET_SCALE1);
+
+ state->VddcPhase = data->vddc_phase_shed_control ^ 0x3;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm);
+ CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm1);
+ CONVERT_FROM_HOST_TO_SMC_US(state->VddcOffset);
+
+ return 0;
+}
+
+static int vegam_populate_ulv_state(struct pp_hwmgr *hwmgr,
+ struct SMU75_Discrete_DpmTable *table)
+{
+ return vegam_populate_ulv_level(hwmgr, &table->Ulv);
+}
+
+static int vegam_populate_smc_link_level(struct pp_hwmgr *hwmgr,
+ struct SMU75_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct vegam_smumgr *smu_data =
+ (struct vegam_smumgr *)(hwmgr->smu_backend);
+ struct smu7_dpm_table *dpm_table = &data->dpm_table;
+ int i;
+
+ /* Index (dpm_table->pcie_speed_table.count)
+ * is reserved for PCIE boot level. */
+ for (i = 0; i <= dpm_table->pcie_speed_table.count; i++) {
+ table->LinkLevel[i].PcieGenSpeed =
+ (uint8_t)dpm_table->pcie_speed_table.dpm_levels[i].value;
+ table->LinkLevel[i].PcieLaneCount = (uint8_t)encode_pcie_lane_width(
+ dpm_table->pcie_speed_table.dpm_levels[i].param1);
+ table->LinkLevel[i].EnabledForActivity = 1;
+ table->LinkLevel[i].SPC = (uint8_t)(data->pcie_spc_cap & 0xff);
+ table->LinkLevel[i].DownThreshold = PP_HOST_TO_SMC_UL(5);
+ table->LinkLevel[i].UpThreshold = PP_HOST_TO_SMC_UL(30);
+ }
+
+ smu_data->smc_state_table.LinkLevelCount =
+ (uint8_t)dpm_table->pcie_speed_table.count;
+
+/* To Do move to hwmgr */
+ data->dpm_level_enable_mask.pcie_dpm_enable_mask =
+ phm_get_dpm_level_enable_mask_value(&dpm_table->pcie_speed_table);
+
+ return 0;
+}
+
+static int vegam_get_dependency_volt_by_clk(struct pp_hwmgr *hwmgr,
+ struct phm_ppt_v1_clock_voltage_dependency_table *dep_table,
+ uint32_t clock, SMU_VoltageLevel *voltage, uint32_t *mvdd)
+{
+ uint32_t i;
+ uint16_t vddci;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ *voltage = *mvdd = 0;
+
+ /* clock - voltage dependency table is empty table */
+ if (dep_table->count == 0)
+ return -EINVAL;
+
+ for (i = 0; i < dep_table->count; i++) {
+ /* find first sclk bigger than request */
+ if (dep_table->entries[i].clk >= clock) {
+ *voltage |= (dep_table->entries[i].vddc *
+ VOLTAGE_SCALE) << VDDC_SHIFT;
+ if (SMU7_VOLTAGE_CONTROL_NONE == data->vddci_control)
+ *voltage |= (data->vbios_boot_state.vddci_bootup_value *
+ VOLTAGE_SCALE) << VDDCI_SHIFT;
+ else if (dep_table->entries[i].vddci)
+ *voltage |= (dep_table->entries[i].vddci *
+ VOLTAGE_SCALE) << VDDCI_SHIFT;
+ else {
+ vddci = phm_find_closest_vddci(&(data->vddci_voltage_table),
+ (dep_table->entries[i].vddc -
+ (uint16_t)VDDC_VDDCI_DELTA));
+ *voltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
+ }
+
+ if (SMU7_VOLTAGE_CONTROL_NONE == data->mvdd_control)
+ *mvdd = data->vbios_boot_state.mvdd_bootup_value *
+ VOLTAGE_SCALE;
+ else if (dep_table->entries[i].mvdd)
+ *mvdd = (uint32_t) dep_table->entries[i].mvdd *
+ VOLTAGE_SCALE;
+
+ *voltage |= 1 << PHASES_SHIFT;
+ return 0;
+ }
+ }
+
+ /* sclk is bigger than max sclk in the dependence table */
+ *voltage |= (dep_table->entries[i - 1].vddc * VOLTAGE_SCALE) << VDDC_SHIFT;
+
+ if (SMU7_VOLTAGE_CONTROL_NONE == data->vddci_control)
+ *voltage |= (data->vbios_boot_state.vddci_bootup_value *
+ VOLTAGE_SCALE) << VDDCI_SHIFT;
+ else if (dep_table->entries[i - 1].vddci)
+ *voltage |= (dep_table->entries[i - 1].vddci *
+ VOLTAGE_SCALE) << VDDC_SHIFT;
+ else {
+ vddci = phm_find_closest_vddci(&(data->vddci_voltage_table),
+ (dep_table->entries[i - 1].vddc -
+ (uint16_t)VDDC_VDDCI_DELTA));
+
+ *voltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
+ }
+
+ if (SMU7_VOLTAGE_CONTROL_NONE == data->mvdd_control)
+ *mvdd = data->vbios_boot_state.mvdd_bootup_value * VOLTAGE_SCALE;
+ else if (dep_table->entries[i].mvdd)
+ *mvdd = (uint32_t) dep_table->entries[i - 1].mvdd * VOLTAGE_SCALE;
+
+ return 0;
+}
+
+static void vegam_get_sclk_range_table(struct pp_hwmgr *hwmgr,
+ SMU75_Discrete_DpmTable *table)
+{
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+ uint32_t i, ref_clk;
+
+ struct pp_atom_ctrl_sclk_range_table range_table_from_vbios = { { {0} } };
+
+ ref_clk = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev);
+
+ if (0 == atomctrl_get_smc_sclk_range_table(hwmgr, &range_table_from_vbios)) {
+ for (i = 0; i < NUM_SCLK_RANGE; i++) {
+ table->SclkFcwRangeTable[i].vco_setting =
+ range_table_from_vbios.entry[i].ucVco_setting;
+ table->SclkFcwRangeTable[i].postdiv =
+ range_table_from_vbios.entry[i].ucPostdiv;
+ table->SclkFcwRangeTable[i].fcw_pcc =
+ range_table_from_vbios.entry[i].usFcw_pcc;
+
+ table->SclkFcwRangeTable[i].fcw_trans_upper =
+ range_table_from_vbios.entry[i].usFcw_trans_upper;
+ table->SclkFcwRangeTable[i].fcw_trans_lower =
+ range_table_from_vbios.entry[i].usRcw_trans_lower;
+
+ CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_pcc);
+ CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_trans_upper);
+ CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_trans_lower);
+ }
+ return;
+ }
+
+ for (i = 0; i < NUM_SCLK_RANGE; i++) {
+ smu_data->range_table[i].trans_lower_frequency =
+ (ref_clk * Range_Table[i].fcw_trans_lower) >> Range_Table[i].postdiv;
+ smu_data->range_table[i].trans_upper_frequency =
+ (ref_clk * Range_Table[i].fcw_trans_upper) >> Range_Table[i].postdiv;
+
+ table->SclkFcwRangeTable[i].vco_setting = Range_Table[i].vco_setting;
+ table->SclkFcwRangeTable[i].postdiv = Range_Table[i].postdiv;
+ table->SclkFcwRangeTable[i].fcw_pcc = Range_Table[i].fcw_pcc;
+
+ table->SclkFcwRangeTable[i].fcw_trans_upper = Range_Table[i].fcw_trans_upper;
+ table->SclkFcwRangeTable[i].fcw_trans_lower = Range_Table[i].fcw_trans_lower;
+
+ CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_pcc);
+ CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_trans_upper);
+ CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_trans_lower);
+ }
+}
+
+static int vegam_calculate_sclk_params(struct pp_hwmgr *hwmgr,
+ uint32_t clock, SMU_SclkSetting *sclk_setting)
+{
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+ const SMU75_Discrete_DpmTable *table = &(smu_data->smc_state_table);
+ struct pp_atomctrl_clock_dividers_ai dividers;
+ uint32_t ref_clock;
+ uint32_t pcc_target_percent, pcc_target_freq, ss_target_percent, ss_target_freq;
+ uint8_t i;
+ int result;
+ uint64_t temp;
+
+ sclk_setting->SclkFrequency = clock;
+ /* get the engine clock dividers for this clock value */
+ result = atomctrl_get_engine_pll_dividers_ai(hwmgr, clock, &dividers);
+ if (result == 0) {
+ sclk_setting->Fcw_int = dividers.usSclk_fcw_int;
+ sclk_setting->Fcw_frac = dividers.usSclk_fcw_frac;
+ sclk_setting->Pcc_fcw_int = dividers.usPcc_fcw_int;
+ sclk_setting->PllRange = dividers.ucSclkPllRange;
+ sclk_setting->Sclk_slew_rate = 0x400;
+ sclk_setting->Pcc_up_slew_rate = dividers.usPcc_fcw_slew_frac;
+ sclk_setting->Pcc_down_slew_rate = 0xffff;
+ sclk_setting->SSc_En = dividers.ucSscEnable;
+ sclk_setting->Fcw1_int = dividers.usSsc_fcw1_int;
+ sclk_setting->Fcw1_frac = dividers.usSsc_fcw1_frac;
+ sclk_setting->Sclk_ss_slew_rate = dividers.usSsc_fcw_slew_frac;
+ return result;
+ }
+
+ ref_clock = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev);
+
+ for (i = 0; i < NUM_SCLK_RANGE; i++) {
+ if (clock > smu_data->range_table[i].trans_lower_frequency
+ && clock <= smu_data->range_table[i].trans_upper_frequency) {
+ sclk_setting->PllRange = i;
+ break;
+ }
+ }
+
+ sclk_setting->Fcw_int = (uint16_t)
+ ((clock << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv) /
+ ref_clock);
+ temp = clock << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv;
+ temp <<= 0x10;
+ do_div(temp, ref_clock);
+ sclk_setting->Fcw_frac = temp & 0xffff;
+
+ pcc_target_percent = 10; /* Hardcode 10% for now. */
+ pcc_target_freq = clock - (clock * pcc_target_percent / 100);
+ sclk_setting->Pcc_fcw_int = (uint16_t)
+ ((pcc_target_freq << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv) /
+ ref_clock);
+
+ ss_target_percent = 2; /* Hardcode 2% for now. */
+ sclk_setting->SSc_En = 0;
+ if (ss_target_percent) {
+ sclk_setting->SSc_En = 1;
+ ss_target_freq = clock - (clock * ss_target_percent / 100);
+ sclk_setting->Fcw1_int = (uint16_t)
+ ((ss_target_freq << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv) /
+ ref_clock);
+ temp = ss_target_freq << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv;
+ temp <<= 0x10;
+ do_div(temp, ref_clock);
+ sclk_setting->Fcw1_frac = temp & 0xffff;
+ }
+
+ return 0;
+}
+
+static uint8_t vegam_get_sleep_divider_id_from_clock(uint32_t clock,
+ uint32_t clock_insr)
+{
+ uint8_t i;
+ uint32_t temp;
+ uint32_t min = max(clock_insr, (uint32_t)SMU7_MINIMUM_ENGINE_CLOCK);
+
+ PP_ASSERT_WITH_CODE((clock >= min),
+ "Engine clock can't satisfy stutter requirement!",
+ return 0);
+ for (i = 31; ; i--) {
+ temp = clock / (i + 1);
+
+ if (temp >= min || i == 0)
+ break;
+ }
+ return i;
+}
+
+static int vegam_populate_single_graphic_level(struct pp_hwmgr *hwmgr,
+ uint32_t clock, struct SMU75_Discrete_GraphicsLevel *level)
+{
+ int result;
+ /* PP_Clocks minClocks; */
+ uint32_t mvdd;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ SMU_SclkSetting curr_sclk_setting = { 0 };
+
+ result = vegam_calculate_sclk_params(hwmgr, clock, &curr_sclk_setting);
+
+ /* populate graphics levels */
+ result = vegam_get_dependency_volt_by_clk(hwmgr,
+ table_info->vdd_dep_on_sclk, clock,
+ &level->MinVoltage, &mvdd);
+
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find VDDC voltage value for "
+ "VDDC engine clock dependency table",
+ return result);
+ level->ActivityLevel = (uint16_t)(SclkDPMTuning_VEGAM >> DPMTuning_Activity_Shift);
+
+ level->CcPwrDynRm = 0;
+ level->CcPwrDynRm1 = 0;
+ level->EnabledForActivity = 0;
+ level->EnabledForThrottle = 1;
+ level->VoltageDownHyst = 0;
+ level->PowerThrottle = 0;
+ data->display_timing.min_clock_in_sr = hwmgr->display_config->min_core_set_clock_in_sr;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SclkDeepSleep))
+ level->DeepSleepDivId = vegam_get_sleep_divider_id_from_clock(clock,
+ hwmgr->display_config->min_core_set_clock_in_sr);
+
+ level->SclkSetting = curr_sclk_setting;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(level->MinVoltage);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->CcPwrDynRm);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->CcPwrDynRm1);
+ CONVERT_FROM_HOST_TO_SMC_US(level->ActivityLevel);
+ CONVERT_FROM_HOST_TO_SMC_UL(level->SclkSetting.SclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Fcw_int);
+ CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Fcw_frac);
+ CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Pcc_fcw_int);
+ CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Sclk_slew_rate);
+ CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Pcc_up_slew_rate);
+ CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Pcc_down_slew_rate);
+ CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Fcw1_int);
+ CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Fcw1_frac);
+ CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Sclk_ss_slew_rate);
+ return 0;
+}
+
+static int vegam_populate_all_graphic_levels(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *hw_data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+ struct smu7_dpm_table *dpm_table = &hw_data->dpm_table;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_pcie_table *pcie_table = table_info->pcie_table;
+ uint8_t pcie_entry_cnt = (uint8_t) hw_data->dpm_table.pcie_speed_table.count;
+ int result = 0;
+ uint32_t array = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU75_Discrete_DpmTable, GraphicsLevel);
+ uint32_t array_size = sizeof(struct SMU75_Discrete_GraphicsLevel) *
+ SMU75_MAX_LEVELS_GRAPHICS;
+ struct SMU75_Discrete_GraphicsLevel *levels =
+ smu_data->smc_state_table.GraphicsLevel;
+ uint32_t i, max_entry;
+ uint8_t hightest_pcie_level_enabled = 0,
+ lowest_pcie_level_enabled = 0,
+ mid_pcie_level_enabled = 0,
+ count = 0;
+
+ vegam_get_sclk_range_table(hwmgr, &(smu_data->smc_state_table));
+
+ for (i = 0; i < dpm_table->sclk_table.count; i++) {
+
+ result = vegam_populate_single_graphic_level(hwmgr,
+ dpm_table->sclk_table.dpm_levels[i].value,
+ &(smu_data->smc_state_table.GraphicsLevel[i]));
+ if (result)
+ return result;
+
+ levels[i].UpHyst = (uint8_t)
+ (SclkDPMTuning_VEGAM >> DPMTuning_Uphyst_Shift);
+ levels[i].DownHyst = (uint8_t)
+ (SclkDPMTuning_VEGAM >> DPMTuning_Downhyst_Shift);
+ /* Making sure only DPM level 0-1 have Deep Sleep Div ID populated. */
+ if (i > 1)
+ levels[i].DeepSleepDivId = 0;
+ }
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SPLLShutdownSupport))
+ smu_data->smc_state_table.GraphicsLevel[0].SclkSetting.SSc_En = 0;
+
+ smu_data->smc_state_table.GraphicsDpmLevelCount =
+ (uint8_t)dpm_table->sclk_table.count;
+ hw_data->dpm_level_enable_mask.sclk_dpm_enable_mask =
+ phm_get_dpm_level_enable_mask_value(&dpm_table->sclk_table);
+
+ for (i = 0; i < dpm_table->sclk_table.count; i++)
+ levels[i].EnabledForActivity =
+ (hw_data->dpm_level_enable_mask.sclk_dpm_enable_mask >> i) & 0x1;
+
+ if (pcie_table != NULL) {
+ PP_ASSERT_WITH_CODE((1 <= pcie_entry_cnt),
+ "There must be 1 or more PCIE levels defined in PPTable.",
+ return -EINVAL);
+ max_entry = pcie_entry_cnt - 1;
+ for (i = 0; i < dpm_table->sclk_table.count; i++)
+ levels[i].pcieDpmLevel =
+ (uint8_t) ((i < max_entry) ? i : max_entry);
+ } else {
+ while (hw_data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
+ ((hw_data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+ (1 << (hightest_pcie_level_enabled + 1))) != 0))
+ hightest_pcie_level_enabled++;
+
+ while (hw_data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
+ ((hw_data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+ (1 << lowest_pcie_level_enabled)) == 0))
+ lowest_pcie_level_enabled++;
+
+ while ((count < hightest_pcie_level_enabled) &&
+ ((hw_data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+ (1 << (lowest_pcie_level_enabled + 1 + count))) == 0))
+ count++;
+
+ mid_pcie_level_enabled = (lowest_pcie_level_enabled + 1 + count) <
+ hightest_pcie_level_enabled ?
+ (lowest_pcie_level_enabled + 1 + count) :
+ hightest_pcie_level_enabled;
+
+ /* set pcieDpmLevel to hightest_pcie_level_enabled */
+ for (i = 2; i < dpm_table->sclk_table.count; i++)
+ levels[i].pcieDpmLevel = hightest_pcie_level_enabled;
+
+ /* set pcieDpmLevel to lowest_pcie_level_enabled */
+ levels[0].pcieDpmLevel = lowest_pcie_level_enabled;
+
+ /* set pcieDpmLevel to mid_pcie_level_enabled */
+ levels[1].pcieDpmLevel = mid_pcie_level_enabled;
+ }
+ /* level count will send to smc once at init smc table and never change */
+ result = smu7_copy_bytes_to_smc(hwmgr, array, (uint8_t *)levels,
+ (uint32_t)array_size, SMC_RAM_END);
+
+ return result;
+}
+
+static int vegam_calculate_mclk_params(struct pp_hwmgr *hwmgr,
+ uint32_t clock, struct SMU75_Discrete_MemoryLevel *mem_level)
+{
+ struct pp_atomctrl_memory_clock_param_ai mpll_param;
+
+ PP_ASSERT_WITH_CODE(!atomctrl_get_memory_pll_dividers_ai(hwmgr,
+ clock, &mpll_param),
+ "Failed to retrieve memory pll parameter.",
+ return -EINVAL);
+
+ mem_level->MclkFrequency = (uint32_t)mpll_param.ulClock;
+ mem_level->Fcw_int = (uint16_t)mpll_param.ulMclk_fcw_int;
+ mem_level->Fcw_frac = (uint16_t)mpll_param.ulMclk_fcw_frac;
+ mem_level->Postdiv = (uint8_t)mpll_param.ulPostDiv;
+
+ return 0;
+}
+
+static int vegam_populate_single_memory_level(struct pp_hwmgr *hwmgr,
+ uint32_t clock, struct SMU75_Discrete_MemoryLevel *mem_level)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ int result = 0;
+ uint32_t mclk_stutter_mode_threshold = 60000;
+
+
+ if (table_info->vdd_dep_on_mclk) {
+ result = vegam_get_dependency_volt_by_clk(hwmgr,
+ table_info->vdd_dep_on_mclk, clock,
+ &mem_level->MinVoltage, &mem_level->MinMvdd);
+ PP_ASSERT_WITH_CODE(!result,
+ "can not find MinVddc voltage value from memory "
+ "VDDC voltage dependency table", return result);
+ }
+
+ result = vegam_calculate_mclk_params(hwmgr, clock, mem_level);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to calculate mclk params.",
+ return -EINVAL);
+
+ mem_level->EnabledForThrottle = 1;
+ mem_level->EnabledForActivity = 0;
+ mem_level->VoltageDownHyst = 0;
+ mem_level->ActivityLevel = (uint16_t)
+ (MemoryDPMTuning_VEGAM >> DPMTuning_Activity_Shift);
+ mem_level->StutterEnable = false;
+ mem_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+ data->display_timing.num_existing_displays = hwmgr->display_config->num_display;
+ data->display_timing.vrefresh = hwmgr->display_config->vrefresh;
+
+ if (mclk_stutter_mode_threshold &&
+ (clock <= mclk_stutter_mode_threshold) &&
+ (PHM_READ_FIELD(hwmgr->device, DPG_PIPE_STUTTER_CONTROL,
+ STUTTER_ENABLE) & 0x1))
+ mem_level->StutterEnable = true;
+
+ if (!result) {
+ CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MinMvdd);
+ CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_US(mem_level->Fcw_int);
+ CONVERT_FROM_HOST_TO_SMC_US(mem_level->Fcw_frac);
+ CONVERT_FROM_HOST_TO_SMC_US(mem_level->ActivityLevel);
+ CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MinVoltage);
+ }
+
+ return result;
+}
+
+static int vegam_populate_all_memory_levels(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *hw_data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+ struct smu7_dpm_table *dpm_table = &hw_data->dpm_table;
+ int result;
+ /* populate MCLK dpm table to SMU7 */
+ uint32_t array = smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU75_Discrete_DpmTable, MemoryLevel);
+ uint32_t array_size = sizeof(SMU75_Discrete_MemoryLevel) *
+ SMU75_MAX_LEVELS_MEMORY;
+ struct SMU75_Discrete_MemoryLevel *levels =
+ smu_data->smc_state_table.MemoryLevel;
+ uint32_t i;
+
+ for (i = 0; i < dpm_table->mclk_table.count; i++) {
+ PP_ASSERT_WITH_CODE((0 != dpm_table->mclk_table.dpm_levels[i].value),
+ "can not populate memory level as memory clock is zero",
+ return -EINVAL);
+ result = vegam_populate_single_memory_level(hwmgr,
+ dpm_table->mclk_table.dpm_levels[i].value,
+ &levels[i]);
+
+ if (result)
+ return result;
+
+ levels[i].UpHyst = (uint8_t)
+ (MemoryDPMTuning_VEGAM >> DPMTuning_Uphyst_Shift);
+ levels[i].DownHyst = (uint8_t)
+ (MemoryDPMTuning_VEGAM >> DPMTuning_Downhyst_Shift);
+ }
+
+ smu_data->smc_state_table.MemoryDpmLevelCount =
+ (uint8_t)dpm_table->mclk_table.count;
+ hw_data->dpm_level_enable_mask.mclk_dpm_enable_mask =
+ phm_get_dpm_level_enable_mask_value(&dpm_table->mclk_table);
+
+ for (i = 0; i < dpm_table->mclk_table.count; i++)
+ levels[i].EnabledForActivity =
+ (hw_data->dpm_level_enable_mask.mclk_dpm_enable_mask >> i) & 0x1;
+
+ levels[dpm_table->mclk_table.count - 1].DisplayWatermark =
+ PPSMC_DISPLAY_WATERMARK_HIGH;
+
+ /* level count will send to smc once at init smc table and never change */
+ result = smu7_copy_bytes_to_smc(hwmgr, array, (uint8_t *)levels,
+ (uint32_t)array_size, SMC_RAM_END);
+
+ return result;
+}
+
+static int vegam_populate_mvdd_value(struct pp_hwmgr *hwmgr,
+ uint32_t mclk, SMIO_Pattern *smio_pat)
+{
+ const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ uint32_t i = 0;
+
+ if (SMU7_VOLTAGE_CONTROL_NONE != data->mvdd_control) {
+ /* find mvdd value which clock is more than request */
+ for (i = 0; i < table_info->vdd_dep_on_mclk->count; i++) {
+ if (mclk <= table_info->vdd_dep_on_mclk->entries[i].clk) {
+ smio_pat->Voltage = data->mvdd_voltage_table.entries[i].value;
+ break;
+ }
+ }
+ PP_ASSERT_WITH_CODE(i < table_info->vdd_dep_on_mclk->count,
+ "MVDD Voltage is outside the supported range.",
+ return -EINVAL);
+ } else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vegam_populate_smc_acpi_level(struct pp_hwmgr *hwmgr,
+ SMU75_Discrete_DpmTable *table)
+{
+ int result = 0;
+ uint32_t sclk_frequency;
+ const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ SMIO_Pattern vol_level;
+ uint32_t mvdd;
+
+ table->ACPILevel.Flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+ /* Get MinVoltage and Frequency from DPM0,
+ * already converted to SMC_UL */
+ sclk_frequency = data->vbios_boot_state.sclk_bootup_value;
+ result = vegam_get_dependency_volt_by_clk(hwmgr,
+ table_info->vdd_dep_on_sclk,
+ sclk_frequency,
+ &table->ACPILevel.MinVoltage, &mvdd);
+ PP_ASSERT_WITH_CODE(!result,
+ "Cannot find ACPI VDDC voltage value "
+ "in Clock Dependency Table",
+ );
+
+ result = vegam_calculate_sclk_params(hwmgr, sclk_frequency,
+ &(table->ACPILevel.SclkSetting));
+ PP_ASSERT_WITH_CODE(!result,
+ "Error retrieving Engine Clock dividers from VBIOS.",
+ return result);
+
+ table->ACPILevel.DeepSleepDivId = 0;
+ table->ACPILevel.CcPwrDynRm = 0;
+ table->ACPILevel.CcPwrDynRm1 = 0;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.Flags);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.MinVoltage);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm1);
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SclkSetting.SclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Fcw_int);
+ CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Fcw_frac);
+ CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Pcc_fcw_int);
+ CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Sclk_slew_rate);
+ CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Pcc_up_slew_rate);
+ CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Pcc_down_slew_rate);
+ CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Fcw1_int);
+ CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Fcw1_frac);
+ CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Sclk_ss_slew_rate);
+
+
+ /* Get MinVoltage and Frequency from DPM0, already converted to SMC_UL */
+ table->MemoryACPILevel.MclkFrequency = data->vbios_boot_state.mclk_bootup_value;
+ result = vegam_get_dependency_volt_by_clk(hwmgr,
+ table_info->vdd_dep_on_mclk,
+ table->MemoryACPILevel.MclkFrequency,
+ &table->MemoryACPILevel.MinVoltage, &mvdd);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "Cannot find ACPI VDDCI voltage value "
+ "in Clock Dependency Table",
+ );
+
+ if (!vegam_populate_mvdd_value(hwmgr, 0, &vol_level))
+ table->MemoryACPILevel.MinMvdd = PP_HOST_TO_SMC_UL(vol_level.Voltage);
+ else
+ table->MemoryACPILevel.MinMvdd = 0;
+
+ table->MemoryACPILevel.StutterEnable = false;
+
+ table->MemoryACPILevel.EnabledForThrottle = 0;
+ table->MemoryACPILevel.EnabledForActivity = 0;
+ table->MemoryACPILevel.UpHyst = 0;
+ table->MemoryACPILevel.DownHyst = 100;
+ table->MemoryACPILevel.VoltageDownHyst = 0;
+ table->MemoryACPILevel.ActivityLevel =
+ PP_HOST_TO_SMC_US(data->current_profile_setting.mclk_activity);
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->MemoryACPILevel.MclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->MemoryACPILevel.MinVoltage);
+
+ return result;
+}
+
+static int vegam_populate_smc_vce_level(struct pp_hwmgr *hwmgr,
+ SMU75_Discrete_DpmTable *table)
+{
+ int result = -EINVAL;
+ uint8_t count;
+ struct pp_atomctrl_clock_dividers_vi dividers;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+ table_info->mm_dep_table;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t vddci;
+
+ table->VceLevelCount = (uint8_t)(mm_table->count);
+ table->VceBootLevel = 0;
+
+ for (count = 0; count < table->VceLevelCount; count++) {
+ table->VceLevel[count].Frequency = mm_table->entries[count].eclk;
+ table->VceLevel[count].MinVoltage = 0;
+ table->VceLevel[count].MinVoltage |=
+ (mm_table->entries[count].vddc * VOLTAGE_SCALE) << VDDC_SHIFT;
+
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control)
+ vddci = (uint32_t)phm_find_closest_vddci(&(data->vddci_voltage_table),
+ mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
+ else if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control)
+ vddci = mm_table->entries[count].vddc - VDDC_VDDCI_DELTA;
+ else
+ vddci = (data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE) << VDDCI_SHIFT;
+
+
+ table->VceLevel[count].MinVoltage |=
+ (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
+ table->VceLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
+
+ /*retrieve divider value for VBIOS */
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+ table->VceLevel[count].Frequency, &dividers);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find divide id for VCE engine clock",
+ return result);
+
+ table->VceLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VceLevel[count].Frequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VceLevel[count].MinVoltage);
+ }
+ return result;
+}
+
+static int vegam_populate_memory_timing_parameters(struct pp_hwmgr *hwmgr,
+ int32_t eng_clock, int32_t mem_clock,
+ SMU75_Discrete_MCArbDramTimingTableEntry *arb_regs)
+{
+ uint32_t dram_timing;
+ uint32_t dram_timing2;
+ uint32_t burst_time;
+ uint32_t rfsh_rate;
+ uint32_t misc3;
+
+ int result;
+
+ result = atomctrl_set_engine_dram_timings_rv770(hwmgr,
+ eng_clock, mem_clock);
+ PP_ASSERT_WITH_CODE(result == 0,
+ "Error calling VBIOS to set DRAM_TIMING.",
+ return result);
+
+ dram_timing = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING);
+ dram_timing2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2);
+ burst_time = cgs_read_register(hwmgr->device, mmMC_ARB_BURST_TIME);
+ rfsh_rate = cgs_read_register(hwmgr->device, mmMC_ARB_RFSH_RATE);
+ misc3 = cgs_read_register(hwmgr->device, mmMC_ARB_MISC3);
+
+ arb_regs->McArbDramTiming = PP_HOST_TO_SMC_UL(dram_timing);
+ arb_regs->McArbDramTiming2 = PP_HOST_TO_SMC_UL(dram_timing2);
+ arb_regs->McArbBurstTime = PP_HOST_TO_SMC_UL(burst_time);
+ arb_regs->McArbRfshRate = PP_HOST_TO_SMC_UL(rfsh_rate);
+ arb_regs->McArbMisc3 = PP_HOST_TO_SMC_UL(misc3);
+
+ return 0;
+}
+
+static int vegam_program_memory_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *hw_data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+ struct SMU75_Discrete_MCArbDramTimingTable arb_regs;
+ uint32_t i, j;
+ int result = 0;
+
+ memset(&arb_regs, 0, sizeof(SMU75_Discrete_MCArbDramTimingTable));
+
+ for (i = 0; i < hw_data->dpm_table.sclk_table.count; i++) {
+ for (j = 0; j < hw_data->dpm_table.mclk_table.count; j++) {
+ result = vegam_populate_memory_timing_parameters(hwmgr,
+ hw_data->dpm_table.sclk_table.dpm_levels[i].value,
+ hw_data->dpm_table.mclk_table.dpm_levels[j].value,
+ &arb_regs.entries[i][j]);
+ if (result)
+ return result;
+ }
+ }
+
+ result = smu7_copy_bytes_to_smc(
+ hwmgr,
+ smu_data->smu7_data.arb_table_start,
+ (uint8_t *)&arb_regs,
+ sizeof(SMU75_Discrete_MCArbDramTimingTable),
+ SMC_RAM_END);
+ return result;
+}
+
+static int vegam_populate_smc_uvd_level(struct pp_hwmgr *hwmgr,
+ struct SMU75_Discrete_DpmTable *table)
+{
+ int result = -EINVAL;
+ uint8_t count;
+ struct pp_atomctrl_clock_dividers_vi dividers;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+ table_info->mm_dep_table;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ uint32_t vddci;
+
+ table->UvdLevelCount = (uint8_t)(mm_table->count);
+ table->UvdBootLevel = 0;
+
+ for (count = 0; count < table->UvdLevelCount; count++) {
+ table->UvdLevel[count].MinVoltage = 0;
+ table->UvdLevel[count].VclkFrequency = mm_table->entries[count].vclk;
+ table->UvdLevel[count].DclkFrequency = mm_table->entries[count].dclk;
+ table->UvdLevel[count].MinVoltage |=
+ (mm_table->entries[count].vddc * VOLTAGE_SCALE) << VDDC_SHIFT;
+
+ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control)
+ vddci = (uint32_t)phm_find_closest_vddci(&(data->vddci_voltage_table),
+ mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
+ else if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control)
+ vddci = mm_table->entries[count].vddc - VDDC_VDDCI_DELTA;
+ else
+ vddci = (data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE) << VDDCI_SHIFT;
+
+ table->UvdLevel[count].MinVoltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
+ table->UvdLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
+
+ /* retrieve divider value for VBIOS */
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+ table->UvdLevel[count].VclkFrequency, &dividers);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find divide id for Vclk clock", return result);
+
+ table->UvdLevel[count].VclkDivider = (uint8_t)dividers.pll_post_divider;
+
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+ table->UvdLevel[count].DclkFrequency, &dividers);
+ PP_ASSERT_WITH_CODE((0 == result),
+ "can not find divide id for Dclk clock", return result);
+
+ table->UvdLevel[count].DclkDivider = (uint8_t)dividers.pll_post_divider;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].VclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].DclkFrequency);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].MinVoltage);
+ }
+
+ return result;
+}
+
+static int vegam_populate_smc_boot_level(struct pp_hwmgr *hwmgr,
+ struct SMU75_Discrete_DpmTable *table)
+{
+ int result = 0;
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ table->GraphicsBootLevel = 0;
+ table->MemoryBootLevel = 0;
+
+ /* find boot level from dpm table */
+ result = phm_find_boot_level(&(data->dpm_table.sclk_table),
+ data->vbios_boot_state.sclk_bootup_value,
+ (uint32_t *)&(table->GraphicsBootLevel));
+ if (result)
+ return result;
+
+ result = phm_find_boot_level(&(data->dpm_table.mclk_table),
+ data->vbios_boot_state.mclk_bootup_value,
+ (uint32_t *)&(table->MemoryBootLevel));
+
+ if (result)
+ return result;
+
+ table->BootVddc = data->vbios_boot_state.vddc_bootup_value *
+ VOLTAGE_SCALE;
+ table->BootVddci = data->vbios_boot_state.vddci_bootup_value *
+ VOLTAGE_SCALE;
+ table->BootMVdd = data->vbios_boot_state.mvdd_bootup_value *
+ VOLTAGE_SCALE;
+
+ CONVERT_FROM_HOST_TO_SMC_US(table->BootVddc);
+ CONVERT_FROM_HOST_TO_SMC_US(table->BootVddci);
+ CONVERT_FROM_HOST_TO_SMC_US(table->BootMVdd);
+
+ return 0;
+}
+
+static int vegam_populate_smc_initial_state(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *hw_data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ uint8_t count, level;
+
+ count = (uint8_t)(table_info->vdd_dep_on_sclk->count);
+
+ for (level = 0; level < count; level++) {
+ if (table_info->vdd_dep_on_sclk->entries[level].clk >=
+ hw_data->vbios_boot_state.sclk_bootup_value) {
+ smu_data->smc_state_table.GraphicsBootLevel = level;
+ break;
+ }
+ }
+
+ count = (uint8_t)(table_info->vdd_dep_on_mclk->count);
+ for (level = 0; level < count; level++) {
+ if (table_info->vdd_dep_on_mclk->entries[level].clk >=
+ hw_data->vbios_boot_state.mclk_bootup_value) {
+ smu_data->smc_state_table.MemoryBootLevel = level;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static uint16_t scale_fan_gain_settings(uint16_t raw_setting)
+{
+ uint32_t tmp;
+ tmp = raw_setting * 4096 / 100;
+ return (uint16_t)tmp;
+}
+
+static int vegam_populate_bapm_parameters_in_dpm_table(struct pp_hwmgr *hwmgr)
+{
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+
+ const struct vegam_pt_defaults *defaults = smu_data->power_tune_defaults;
+ SMU75_Discrete_DpmTable *table = &(smu_data->smc_state_table);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_cac_tdp_table *cac_dtp_table = table_info->cac_dtp_table;
+ struct pp_advance_fan_control_parameters *fan_table =
+ &hwmgr->thermal_controller.advanceFanControlParameters;
+ int i, j, k;
+ const uint16_t *pdef1;
+ const uint16_t *pdef2;
+
+ table->DefaultTdp = PP_HOST_TO_SMC_US((uint16_t)(cac_dtp_table->usTDP * 128));
+ table->TargetTdp = PP_HOST_TO_SMC_US((uint16_t)(cac_dtp_table->usTDP * 128));
+
+ PP_ASSERT_WITH_CODE(cac_dtp_table->usTargetOperatingTemp <= 255,
+ "Target Operating Temp is out of Range!",
+ );
+
+ table->TemperatureLimitEdge = PP_HOST_TO_SMC_US(
+ cac_dtp_table->usTargetOperatingTemp * 256);
+ table->TemperatureLimitHotspot = PP_HOST_TO_SMC_US(
+ cac_dtp_table->usTemperatureLimitHotspot * 256);
+ table->FanGainEdge = PP_HOST_TO_SMC_US(
+ scale_fan_gain_settings(fan_table->usFanGainEdge));
+ table->FanGainHotspot = PP_HOST_TO_SMC_US(
+ scale_fan_gain_settings(fan_table->usFanGainHotspot));
+
+ pdef1 = defaults->BAPMTI_R;
+ pdef2 = defaults->BAPMTI_RC;
+
+ for (i = 0; i < SMU75_DTE_ITERATIONS; i++) {
+ for (j = 0; j < SMU75_DTE_SOURCES; j++) {
+ for (k = 0; k < SMU75_DTE_SINKS; k++) {
+ table->BAPMTI_R[i][j][k] = PP_HOST_TO_SMC_US(*pdef1);
+ table->BAPMTI_RC[i][j][k] = PP_HOST_TO_SMC_US(*pdef2);
+ pdef1++;
+ pdef2++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int vegam_populate_clock_stretcher_data_table(struct pp_hwmgr *hwmgr)
+{
+ uint32_t ro, efuse, volt_without_cks, volt_with_cks, value, max, min;
+ struct vegam_smumgr *smu_data =
+ (struct vegam_smumgr *)(hwmgr->smu_backend);
+
+ uint8_t i, stretch_amount, volt_offset = 0;
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
+ table_info->vdd_dep_on_sclk;
+ uint32_t mask = (1 << ((STRAP_ASIC_RO_MSB - STRAP_ASIC_RO_LSB) + 1)) - 1;
+
+ stretch_amount = (uint8_t)table_info->cac_dtp_table->usClockStretchAmount;
+
+ atomctrl_read_efuse(hwmgr, STRAP_ASIC_RO_LSB, STRAP_ASIC_RO_MSB,
+ mask, &efuse);
+
+ min = 1200;
+ max = 2500;
+
+ ro = efuse * (max - min) / 255 + min;
+
+ /* Populate Sclk_CKS_masterEn0_7 and Sclk_voltageOffset */
+ for (i = 0; i < sclk_table->count; i++) {
+ smu_data->smc_state_table.Sclk_CKS_masterEn0_7 |=
+ sclk_table->entries[i].cks_enable << i;
+ volt_without_cks = (uint32_t)((2753594000U + (sclk_table->entries[i].clk/100) *
+ 136418 - (ro - 70) * 1000000) /
+ (2424180 - (sclk_table->entries[i].clk/100) * 1132925/1000));
+ volt_with_cks = (uint32_t)((2797202000U + sclk_table->entries[i].clk/100 *
+ 3232 - (ro - 65) * 1000000) /
+ (2522480 - sclk_table->entries[i].clk/100 * 115764/100));
+
+ if (volt_without_cks >= volt_with_cks)
+ volt_offset = (uint8_t)(((volt_without_cks - volt_with_cks +
+ sclk_table->entries[i].cks_voffset) * 100 + 624) / 625);
+
+ smu_data->smc_state_table.Sclk_voltageOffset[i] = volt_offset;
+ }
+
+ smu_data->smc_state_table.LdoRefSel =
+ (table_info->cac_dtp_table->ucCKS_LDO_REFSEL != 0) ?
+ table_info->cac_dtp_table->ucCKS_LDO_REFSEL : 5;
+ /* Populate CKS Lookup Table */
+ if (!(stretch_amount == 1 || stretch_amount == 2 ||
+ stretch_amount == 5 || stretch_amount == 3 ||
+ stretch_amount == 4)) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ClockStretcher);
+ PP_ASSERT_WITH_CODE(false,
+ "Stretch Amount in PPTable not supported\n",
+ return -EINVAL);
+ }
+
+ value = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixPWR_CKS_CNTL);
+ value &= 0xFFFFFFFE;
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixPWR_CKS_CNTL, value);
+
+ return 0;
+}
+
+static bool vegam_is_hw_avfs_present(struct pp_hwmgr *hwmgr)
+{
+ uint32_t efuse;
+
+ efuse = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixSMU_EFUSE_0 + (49 * 4));
+ efuse &= 0x00000001;
+
+ if (efuse)
+ return true;
+
+ return false;
+}
+
+static int vegam_populate_avfs_parameters(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+
+ SMU75_Discrete_DpmTable *table = &(smu_data->smc_state_table);
+ int result = 0;
+ struct pp_atom_ctrl__avfs_parameters avfs_params = {0};
+ AVFS_meanNsigma_t AVFS_meanNsigma = { {0} };
+ AVFS_Sclk_Offset_t AVFS_SclkOffset = { {0} };
+ uint32_t tmp, i;
+
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)hwmgr->pptable;
+ struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
+ table_info->vdd_dep_on_sclk;
+
+ if (!hwmgr->avfs_supported)
+ return 0;
+
+ result = atomctrl_get_avfs_information(hwmgr, &avfs_params);
+
+ if (0 == result) {
+ table->BTCGB_VDROOP_TABLE[0].a0 =
+ PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSON_a0);
+ table->BTCGB_VDROOP_TABLE[0].a1 =
+ PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSON_a1);
+ table->BTCGB_VDROOP_TABLE[0].a2 =
+ PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSON_a2);
+ table->BTCGB_VDROOP_TABLE[1].a0 =
+ PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a0);
+ table->BTCGB_VDROOP_TABLE[1].a1 =
+ PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a1);
+ table->BTCGB_VDROOP_TABLE[1].a2 =
+ PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a2);
+ table->AVFSGB_FUSE_TABLE[0].m1 =
+ PP_HOST_TO_SMC_UL(avfs_params.ulAVFSGB_FUSE_TABLE_CKSON_m1);
+ table->AVFSGB_FUSE_TABLE[0].m2 =
+ PP_HOST_TO_SMC_US(avfs_params.usAVFSGB_FUSE_TABLE_CKSON_m2);
+ table->AVFSGB_FUSE_TABLE[0].b =
+ PP_HOST_TO_SMC_UL(avfs_params.ulAVFSGB_FUSE_TABLE_CKSON_b);
+ table->AVFSGB_FUSE_TABLE[0].m1_shift = 24;
+ table->AVFSGB_FUSE_TABLE[0].m2_shift = 12;
+ table->AVFSGB_FUSE_TABLE[1].m1 =
+ PP_HOST_TO_SMC_UL(avfs_params.ulAVFSGB_FUSE_TABLE_CKSOFF_m1);
+ table->AVFSGB_FUSE_TABLE[1].m2 =
+ PP_HOST_TO_SMC_US(avfs_params.usAVFSGB_FUSE_TABLE_CKSOFF_m2);
+ table->AVFSGB_FUSE_TABLE[1].b =
+ PP_HOST_TO_SMC_UL(avfs_params.ulAVFSGB_FUSE_TABLE_CKSOFF_b);
+ table->AVFSGB_FUSE_TABLE[1].m1_shift = 24;
+ table->AVFSGB_FUSE_TABLE[1].m2_shift = 12;
+ table->MaxVoltage = PP_HOST_TO_SMC_US(avfs_params.usMaxVoltage_0_25mv);
+ AVFS_meanNsigma.Aconstant[0] =
+ PP_HOST_TO_SMC_UL(avfs_params.ulAVFS_meanNsigma_Acontant0);
+ AVFS_meanNsigma.Aconstant[1] =
+ PP_HOST_TO_SMC_UL(avfs_params.ulAVFS_meanNsigma_Acontant1);
+ AVFS_meanNsigma.Aconstant[2] =
+ PP_HOST_TO_SMC_UL(avfs_params.ulAVFS_meanNsigma_Acontant2);
+ AVFS_meanNsigma.DC_tol_sigma =
+ PP_HOST_TO_SMC_US(avfs_params.usAVFS_meanNsigma_DC_tol_sigma);
+ AVFS_meanNsigma.Platform_mean =
+ PP_HOST_TO_SMC_US(avfs_params.usAVFS_meanNsigma_Platform_mean);
+ AVFS_meanNsigma.PSM_Age_CompFactor =
+ PP_HOST_TO_SMC_US(avfs_params.usPSM_Age_ComFactor);
+ AVFS_meanNsigma.Platform_sigma =
+ PP_HOST_TO_SMC_US(avfs_params.usAVFS_meanNsigma_Platform_sigma);
+
+ for (i = 0; i < sclk_table->count; i++) {
+ AVFS_meanNsigma.Static_Voltage_Offset[i] =
+ (uint8_t)(sclk_table->entries[i].cks_voffset * 100 / 625);
+ AVFS_SclkOffset.Sclk_Offset[i] =
+ PP_HOST_TO_SMC_US((uint16_t)
+ (sclk_table->entries[i].sclk_offset) / 100);
+ }
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU75_Firmware_Header, AvfsMeanNSigma),
+ &tmp, SMC_RAM_END);
+ smu7_copy_bytes_to_smc(hwmgr,
+ tmp,
+ (uint8_t *)&AVFS_meanNsigma,
+ sizeof(AVFS_meanNsigma_t),
+ SMC_RAM_END);
+
+ result = smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU75_Firmware_Header, AvfsSclkOffsetTable),
+ &tmp, SMC_RAM_END);
+ smu7_copy_bytes_to_smc(hwmgr,
+ tmp,
+ (uint8_t *)&AVFS_SclkOffset,
+ sizeof(AVFS_Sclk_Offset_t),
+ SMC_RAM_END);
+
+ data->avfs_vdroop_override_setting =
+ (avfs_params.ucEnableGB_VDROOP_TABLE_CKSON << BTCGB0_Vdroop_Enable_SHIFT) |
+ (avfs_params.ucEnableGB_VDROOP_TABLE_CKSOFF << BTCGB1_Vdroop_Enable_SHIFT) |
+ (avfs_params.ucEnableGB_FUSE_TABLE_CKSON << AVFSGB0_Vdroop_Enable_SHIFT) |
+ (avfs_params.ucEnableGB_FUSE_TABLE_CKSOFF << AVFSGB1_Vdroop_Enable_SHIFT);
+ data->apply_avfs_cks_off_voltage =
+ (avfs_params.ucEnableApplyAVFS_CKS_OFF_Voltage == 1) ? true : false;
+ }
+ return result;
+}
+
+static int vegam_populate_vr_config(struct pp_hwmgr *hwmgr,
+ struct SMU75_Discrete_DpmTable *table)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct vegam_smumgr *smu_data =
+ (struct vegam_smumgr *)(hwmgr->smu_backend);
+ uint16_t config;
+
+ config = VR_MERGED_WITH_VDDC;
+ table->VRConfig |= (config << VRCONF_VDDGFX_SHIFT);
+
+ /* Set Vddc Voltage Controller */
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
+ config = VR_SVI2_PLANE_1;
+ table->VRConfig |= config;
+ } else {
+ PP_ASSERT_WITH_CODE(false,
+ "VDDC should be on SVI2 control in merged mode!",
+ );
+ }
+ /* Set Vddci Voltage Controller */
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) {
+ config = VR_SVI2_PLANE_2; /* only in merged mode */
+ table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
+ } else if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
+ config = VR_SMIO_PATTERN_1;
+ table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
+ } else {
+ config = VR_STATIC_VOLTAGE;
+ table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
+ }
+ /* Set Mvdd Voltage Controller */
+ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->mvdd_control) {
+ if (config != VR_SVI2_PLANE_2) {
+ config = VR_SVI2_PLANE_2;
+ table->VRConfig |= (config << VRCONF_MVDD_SHIFT);
+ cgs_write_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC,
+ smu_data->smu7_data.soft_regs_start +
+ offsetof(SMU75_SoftRegisters, AllowMvddSwitch),
+ 0x1);
+ } else {
+ PP_ASSERT_WITH_CODE(false,
+ "SVI2 Plane 2 is already taken, set MVDD as Static",);
+ config = VR_STATIC_VOLTAGE;
+ table->VRConfig = (config << VRCONF_MVDD_SHIFT);
+ }
+ } else if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
+ config = VR_SMIO_PATTERN_2;
+ table->VRConfig = (config << VRCONF_MVDD_SHIFT);
+ cgs_write_ind_register(hwmgr->device,
+ CGS_IND_REG__SMC,
+ smu_data->smu7_data.soft_regs_start +
+ offsetof(SMU75_SoftRegisters, AllowMvddSwitch),
+ 0x1);
+ } else {
+ config = VR_STATIC_VOLTAGE;
+ table->VRConfig |= (config << VRCONF_MVDD_SHIFT);
+ }
+
+ return 0;
+}
+
+static int vegam_populate_svi_load_line(struct pp_hwmgr *hwmgr)
+{
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+ const struct vegam_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+ smu_data->power_tune_table.SviLoadLineEn = defaults->SviLoadLineEn;
+ smu_data->power_tune_table.SviLoadLineVddC = defaults->SviLoadLineVddC;
+ smu_data->power_tune_table.SviLoadLineTrimVddC = 3;
+ smu_data->power_tune_table.SviLoadLineOffsetVddC = 0;
+
+ return 0;
+}
+
+static int vegam_populate_tdc_limit(struct pp_hwmgr *hwmgr)
+{
+ uint16_t tdc_limit;
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ const struct vegam_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+ tdc_limit = (uint16_t)(table_info->cac_dtp_table->usTDC * 128);
+ smu_data->power_tune_table.TDC_VDDC_PkgLimit =
+ CONVERT_FROM_HOST_TO_SMC_US(tdc_limit);
+ smu_data->power_tune_table.TDC_VDDC_ThrottleReleaseLimitPerc =
+ defaults->TDC_VDDC_ThrottleReleaseLimitPerc;
+ smu_data->power_tune_table.TDC_MAWt = defaults->TDC_MAWt;
+
+ return 0;
+}
+
+static int vegam_populate_dw8(struct pp_hwmgr *hwmgr, uint32_t fuse_table_offset)
+{
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+ const struct vegam_pt_defaults *defaults = smu_data->power_tune_defaults;
+ uint32_t temp;
+
+ if (smu7_read_smc_sram_dword(hwmgr,
+ fuse_table_offset +
+ offsetof(SMU75_Discrete_PmFuses, TdcWaterfallCtl),
+ (uint32_t *)&temp, SMC_RAM_END))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to read PmFuses.DW6 (SviLoadLineEn) from SMC Failed!",
+ return -EINVAL);
+ else {
+ smu_data->power_tune_table.TdcWaterfallCtl = defaults->TdcWaterfallCtl;
+ smu_data->power_tune_table.LPMLTemperatureMin =
+ (uint8_t)((temp >> 16) & 0xff);
+ smu_data->power_tune_table.LPMLTemperatureMax =
+ (uint8_t)((temp >> 8) & 0xff);
+ smu_data->power_tune_table.Reserved = (uint8_t)(temp & 0xff);
+ }
+ return 0;
+}
+
+static int vegam_populate_temperature_scaler(struct pp_hwmgr *hwmgr)
+{
+ int i;
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+
+ /* Currently not used. Set all to zero. */
+ for (i = 0; i < 16; i++)
+ smu_data->power_tune_table.LPMLTemperatureScaler[i] = 0;
+
+ return 0;
+}
+
+static int vegam_populate_fuzzy_fan(struct pp_hwmgr *hwmgr)
+{
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+
+/* TO DO move to hwmgr */
+ if ((hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity & (1 << 15))
+ || 0 == hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity)
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity =
+ hwmgr->thermal_controller.advanceFanControlParameters.usDefaultFanOutputSensitivity;
+
+ smu_data->power_tune_table.FuzzyFan_PwmSetDelta = PP_HOST_TO_SMC_US(
+ hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity);
+ return 0;
+}
+
+static int vegam_populate_gnb_lpml(struct pp_hwmgr *hwmgr)
+{
+ int i;
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+
+ /* Currently not used. Set all to zero. */
+ for (i = 0; i < 16; i++)
+ smu_data->power_tune_table.GnbLPML[i] = 0;
+
+ return 0;
+}
+
+static int vegam_populate_bapm_vddc_base_leakage_sidd(struct pp_hwmgr *hwmgr)
+{
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ uint16_t hi_sidd = smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd;
+ uint16_t lo_sidd = smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd;
+ struct phm_cac_tdp_table *cac_table = table_info->cac_dtp_table;
+
+ hi_sidd = (uint16_t)(cac_table->usHighCACLeakage / 100 * 256);
+ lo_sidd = (uint16_t)(cac_table->usLowCACLeakage / 100 * 256);
+
+ smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd =
+ CONVERT_FROM_HOST_TO_SMC_US(hi_sidd);
+ smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd =
+ CONVERT_FROM_HOST_TO_SMC_US(lo_sidd);
+
+ return 0;
+}
+
+static int vegam_populate_pm_fuses(struct pp_hwmgr *hwmgr)
+{
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+ uint32_t pm_fuse_table_offset;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_PowerContainment)) {
+ if (smu7_read_smc_sram_dword(hwmgr,
+ SMU7_FIRMWARE_HEADER_LOCATION +
+ offsetof(SMU75_Firmware_Header, PmFuseTable),
+ &pm_fuse_table_offset, SMC_RAM_END))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to get pm_fuse_table_offset Failed!",
+ return -EINVAL);
+
+ if (vegam_populate_svi_load_line(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate SviLoadLine Failed!",
+ return -EINVAL);
+
+ if (vegam_populate_tdc_limit(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate TDCLimit Failed!", return -EINVAL);
+
+ if (vegam_populate_dw8(hwmgr, pm_fuse_table_offset))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate TdcWaterfallCtl, "
+ "LPMLTemperature Min and Max Failed!",
+ return -EINVAL);
+
+ if (0 != vegam_populate_temperature_scaler(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate LPMLTemperatureScaler Failed!",
+ return -EINVAL);
+
+ if (vegam_populate_fuzzy_fan(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate Fuzzy Fan Control parameters Failed!",
+ return -EINVAL);
+
+ if (vegam_populate_gnb_lpml(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate GnbLPML Failed!",
+ return -EINVAL);
+
+ if (vegam_populate_bapm_vddc_base_leakage_sidd(hwmgr))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to populate BapmVddCBaseLeakage Hi and Lo "
+ "Sidd Failed!", return -EINVAL);
+
+ if (smu7_copy_bytes_to_smc(hwmgr, pm_fuse_table_offset,
+ (uint8_t *)&smu_data->power_tune_table,
+ (sizeof(struct SMU75_Discrete_PmFuses) - PMFUSES_AVFSSIZE),
+ SMC_RAM_END))
+ PP_ASSERT_WITH_CODE(false,
+ "Attempt to download PmFuseTable Failed!",
+ return -EINVAL);
+ }
+ return 0;
+}
+
+static int vegam_enable_reconfig_cus(struct pp_hwmgr *hwmgr)
+{
+ struct amdgpu_device *adev = hwmgr->adev;
+
+ smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_EnableModeSwitchRLCNotification,
+ adev->gfx.cu_info.number,
+ NULL);
+
+ return 0;
+}
+
+static int vegam_init_smc_table(struct pp_hwmgr *hwmgr)
+{
+ int result;
+ struct smu7_hwmgr *hw_data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct vegam_smumgr *smu_data = (struct vegam_smumgr *)(hwmgr->smu_backend);
+
+ struct phm_ppt_v1_information *table_info =
+ (struct phm_ppt_v1_information *)(hwmgr->pptable);
+ struct SMU75_Discrete_DpmTable *table = &(smu_data->smc_state_table);
+ uint8_t i;
+ struct pp_atomctrl_gpio_pin_assignment gpio_pin;
+ struct phm_ppt_v1_gpio_table *gpio_table =
+ (struct phm_ppt_v1_gpio_table *)table_info->gpio_table;
+ pp_atomctrl_clock_dividers_vi dividers;
+
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition);
+
+ vegam_initialize_power_tune_defaults(hwmgr);
+
+ if (SMU7_VOLTAGE_CONTROL_NONE != hw_data->voltage_control)
+ vegam_populate_smc_voltage_tables(hwmgr, table);
+
+ table->SystemFlags = 0;
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition))
+ table->SystemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_StepVddc))
+ table->SystemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+ if (hw_data->is_memory_gddr5)
+ table->SystemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
+
+ if (hw_data->ulv_supported && table_info->us_ulv_voltage_offset) {
+ result = vegam_populate_ulv_state(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize ULV state!", return result);
+ cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+ ixCG_ULV_PARAMETER, SMU7_CGULVPARAMETER_DFLT);
+ }
+
+ result = vegam_populate_smc_link_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize Link Level!", return result);
+
+ result = vegam_populate_all_graphic_levels(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize Graphics Level!", return result);
+
+ result = vegam_populate_all_memory_levels(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize Memory Level!", return result);
+
+ result = vegam_populate_smc_acpi_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize ACPI Level!", return result);
+
+ result = vegam_populate_smc_vce_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize VCE Level!", return result);
+
+ /* Since only the initial state is completely set up at this point
+ * (the other states are just copies of the boot state) we only
+ * need to populate the ARB settings for the initial state.
+ */
+ result = vegam_program_memory_timing_parameters(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to Write ARB settings for the initial state.", return result);
+
+ result = vegam_populate_smc_uvd_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize UVD Level!", return result);
+
+ result = vegam_populate_smc_boot_level(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize Boot Level!", return result);
+
+ result = vegam_populate_smc_initial_state(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to initialize Boot State!", return result);
+
+ result = vegam_populate_bapm_parameters_in_dpm_table(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to populate BAPM Parameters!", return result);
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_ClockStretcher)) {
+ result = vegam_populate_clock_stretcher_data_table(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to populate Clock Stretcher Data Table!",
+ return result);
+ }
+
+ result = vegam_populate_avfs_parameters(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to populate AVFS Parameters!", return result;);
+
+ table->CurrSclkPllRange = 0xff;
+ table->GraphicsVoltageChangeEnable = 1;
+ table->GraphicsThermThrottleEnable = 1;
+ table->GraphicsInterval = 1;
+ table->VoltageInterval = 1;
+ table->ThermalInterval = 1;
+ table->TemperatureLimitHigh =
+ table_info->cac_dtp_table->usTargetOperatingTemp *
+ SMU7_Q88_FORMAT_CONVERSION_UNIT;
+ table->TemperatureLimitLow =
+ (table_info->cac_dtp_table->usTargetOperatingTemp - 1) *
+ SMU7_Q88_FORMAT_CONVERSION_UNIT;
+ table->MemoryVoltageChangeEnable = 1;
+ table->MemoryInterval = 1;
+ table->VoltageResponseTime = 0;
+ table->PhaseResponseTime = 0;
+ table->MemoryThermThrottleEnable = 1;
+
+ PP_ASSERT_WITH_CODE(hw_data->dpm_table.pcie_speed_table.count >= 1,
+ "There must be 1 or more PCIE levels defined in PPTable.",
+ return -EINVAL);
+ table->PCIeBootLinkLevel =
+ hw_data->dpm_table.pcie_speed_table.count;
+ table->PCIeGenInterval = 1;
+ table->VRConfig = 0;
+
+ result = vegam_populate_vr_config(hwmgr, table);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to populate VRConfig setting!", return result);
+
+ table->ThermGpio = 17;
+ table->SclkStepSize = 0x4000;
+
+ if (atomctrl_get_pp_assign_pin(hwmgr,
+ VDDC_VRHOT_GPIO_PINID, &gpio_pin)) {
+ table->VRHotGpio = gpio_pin.uc_gpio_pin_bit_shift;
+ if (gpio_table)
+ table->VRHotLevel =
+ table_info->gpio_table->vrhot_triggered_sclk_dpm_index;
+ } else {
+ table->VRHotGpio = SMU7_UNUSED_GPIO_PIN;
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_RegulatorHot);
+ }
+
+ if (atomctrl_get_pp_assign_pin(hwmgr,
+ PP_AC_DC_SWITCH_GPIO_PINID, &gpio_pin)) {
+ table->AcDcGpio = gpio_pin.uc_gpio_pin_bit_shift;
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition) &&
+ !smum_send_msg_to_smc(hwmgr, PPSMC_MSG_UseNewGPIOScheme, NULL))
+ phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SMCtoPPLIBAcdcGpioScheme);
+ } else {
+ table->AcDcGpio = SMU7_UNUSED_GPIO_PIN;
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_AutomaticDCTransition);
+ }
+
+ /* Thermal Output GPIO */
+ if (atomctrl_get_pp_assign_pin(hwmgr,
+ THERMAL_INT_OUTPUT_GPIO_PINID, &gpio_pin)) {
+ table->ThermOutGpio = gpio_pin.uc_gpio_pin_bit_shift;
+
+ /* For porlarity read GPIOPAD_A with assigned Gpio pin
+ * since VBIOS will program this register to set 'inactive state',
+ * driver can then determine 'active state' from this and
+ * program SMU with correct polarity
+ */
+ table->ThermOutPolarity =
+ (0 == (cgs_read_register(hwmgr->device, mmGPIOPAD_A) &
+ (1 << gpio_pin.uc_gpio_pin_bit_shift))) ? 1:0;
+ table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_ONLY;
+
+ /* if required, combine VRHot/PCC with thermal out GPIO */
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_RegulatorHot) &&
+ phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_CombinePCCWithThermalSignal))
+ table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_VRHOT;
+ } else {
+ table->ThermOutGpio = 17;
+ table->ThermOutPolarity = 1;
+ table->ThermOutMode = SMU7_THERM_OUT_MODE_DISABLE;
+ }
+
+ /* Populate BIF_SCLK levels into SMC DPM table */
+ for (i = 0; i <= hw_data->dpm_table.pcie_speed_table.count; i++) {
+ result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+ smu_data->bif_sclk_table[i], &dividers);
+ PP_ASSERT_WITH_CODE(!result,
+ "Can not find DFS divide id for Sclk",
+ return result);
+
+ if (i == 0)
+ table->Ulv.BifSclkDfs =
+ PP_HOST_TO_SMC_US((uint16_t)(dividers.pll_post_divider));
+ else
+ table->LinkLevel[i - 1].BifSclkDfs =
+ PP_HOST_TO_SMC_US((uint16_t)(dividers.pll_post_divider));
+ }
+
+ for (i = 0; i < SMU75_MAX_ENTRIES_SMIO; i++)
+ table->Smio[i] = PP_HOST_TO_SMC_UL(table->Smio[i]);
+
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SystemFlags);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->VRConfig);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask1);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask2);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->SclkStepSize);
+ CONVERT_FROM_HOST_TO_SMC_UL(table->CurrSclkPllRange);
+ CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitHigh);
+ CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitLow);
+ CONVERT_FROM_HOST_TO_SMC_US(table->VoltageResponseTime);
+ CONVERT_FROM_HOST_TO_SMC_US(table->PhaseResponseTime);
+
+ /* Upload all dpm data to SMC memory.(dpm level, dpm level count etc) */
+ result = smu7_copy_bytes_to_smc(hwmgr,
+ smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU75_Discrete_DpmTable, SystemFlags),
+ (uint8_t *)&(table->SystemFlags),
+ sizeof(SMU75_Discrete_DpmTable) - 3 * sizeof(SMU75_PIDController),
+ SMC_RAM_END);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to upload dpm data to SMC memory!", return result);
+
+ result = vegam_populate_pm_fuses(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to populate PM fuses to SMC memory!", return result);
+
+ result = vegam_enable_reconfig_cus(hwmgr);
+ PP_ASSERT_WITH_CODE(!result,
+ "Failed to enable reconfigurable CUs!", return result);
+
+ return 0;
+}
+
+static uint32_t vegam_get_offsetof(uint32_t type, uint32_t member)
+{
+ switch (type) {
+ case SMU_SoftRegisters:
+ switch (member) {
+ case HandshakeDisables:
+ return offsetof(SMU75_SoftRegisters, HandshakeDisables);
+ case VoltageChangeTimeout:
+ return offsetof(SMU75_SoftRegisters, VoltageChangeTimeout);
+ case AverageGraphicsActivity:
+ return offsetof(SMU75_SoftRegisters, AverageGraphicsActivity);
+ case AverageMemoryActivity:
+ return offsetof(SMU75_SoftRegisters, AverageMemoryActivity);
+ case PreVBlankGap:
+ return offsetof(SMU75_SoftRegisters, PreVBlankGap);
+ case VBlankTimeout:
+ return offsetof(SMU75_SoftRegisters, VBlankTimeout);
+ case UcodeLoadStatus:
+ return offsetof(SMU75_SoftRegisters, UcodeLoadStatus);
+ case DRAM_LOG_ADDR_H:
+ return offsetof(SMU75_SoftRegisters, DRAM_LOG_ADDR_H);
+ case DRAM_LOG_ADDR_L:
+ return offsetof(SMU75_SoftRegisters, DRAM_LOG_ADDR_L);
+ case DRAM_LOG_PHY_ADDR_H:
+ return offsetof(SMU75_SoftRegisters, DRAM_LOG_PHY_ADDR_H);
+ case DRAM_LOG_PHY_ADDR_L:
+ return offsetof(SMU75_SoftRegisters, DRAM_LOG_PHY_ADDR_L);
+ case DRAM_LOG_BUFF_SIZE:
+ return offsetof(SMU75_SoftRegisters, DRAM_LOG_BUFF_SIZE);
+ }
+ break;
+ case SMU_Discrete_DpmTable:
+ switch (member) {
+ case UvdBootLevel:
+ return offsetof(SMU75_Discrete_DpmTable, UvdBootLevel);
+ case VceBootLevel:
+ return offsetof(SMU75_Discrete_DpmTable, VceBootLevel);
+ case LowSclkInterruptThreshold:
+ return offsetof(SMU75_Discrete_DpmTable, LowSclkInterruptThreshold);
+ }
+ break;
+ }
+ pr_warn("can't get the offset of type %x member %x\n", type, member);
+ return 0;
+}
+
+static int vegam_program_mem_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+ if (data->need_update_smu7_dpm_table &
+ (DPMTABLE_OD_UPDATE_SCLK +
+ DPMTABLE_UPDATE_SCLK +
+ DPMTABLE_UPDATE_MCLK))
+ return vegam_program_memory_timing_parameters(hwmgr);
+
+ return 0;
+}
+
+static int vegam_update_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ struct vegam_smumgr *smu_data =
+ (struct vegam_smumgr *)(hwmgr->smu_backend);
+ int result = 0;
+ uint32_t low_sclk_interrupt_threshold = 0;
+
+ if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_SclkThrottleLowNotification)
+ && (data->low_sclk_interrupt_threshold != 0)) {
+ low_sclk_interrupt_threshold =
+ data->low_sclk_interrupt_threshold;
+
+ CONVERT_FROM_HOST_TO_SMC_UL(low_sclk_interrupt_threshold);
+
+ result = smu7_copy_bytes_to_smc(
+ hwmgr,
+ smu_data->smu7_data.dpm_table_start +
+ offsetof(SMU75_Discrete_DpmTable,
+ LowSclkInterruptThreshold),
+ (uint8_t *)&low_sclk_interrupt_threshold,
+ sizeof(uint32_t),
+ SMC_RAM_END);
+ }
+ PP_ASSERT_WITH_CODE((result == 0),
+ "Failed to update SCLK threshold!", return result);
+
+ result = vegam_program_mem_timing_parameters(hwmgr);
+ PP_ASSERT_WITH_CODE((result == 0),
+ "Failed to program memory timing parameters!",
+ );
+
+ return result;
+}
+
+int vegam_thermal_avfs_enable(struct pp_hwmgr *hwmgr)
+{
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+ int ret;
+
+ if (!hwmgr->avfs_supported)
+ return 0;
+
+ ret = smum_send_msg_to_smc(hwmgr, PPSMC_MSG_EnableAvfs, NULL);
+ if (!ret) {
+ if (data->apply_avfs_cks_off_voltage)
+ ret = smum_send_msg_to_smc(hwmgr,
+ PPSMC_MSG_ApplyAvfsCksOffVoltage,
+ NULL);
+ }
+
+ return ret;
+}
+
+static int vegam_thermal_setup_fan_table(struct pp_hwmgr *hwmgr)
+{
+ PP_ASSERT_WITH_CODE(hwmgr->thermal_controller.fanInfo.bNoFan,
+ "VBIOS fan info is not correct!",
+ );
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+}
+
+const struct pp_smumgr_func vegam_smu_funcs = {
+ .name = "vegam_smu",
+ .smu_init = vegam_smu_init,
+ .smu_fini = smu7_smu_fini,
+ .start_smu = vegam_start_smu,
+ .check_fw_load_finish = smu7_check_fw_load_finish,
+ .request_smu_load_fw = smu7_reload_firmware,
+ .request_smu_load_specific_fw = NULL,
+ .send_msg_to_smc = smu7_send_msg_to_smc,
+ .send_msg_to_smc_with_parameter = smu7_send_msg_to_smc_with_parameter,
+ .get_argument = smu7_get_argument,
+ .process_firmware_header = vegam_process_firmware_header,
+ .is_dpm_running = vegam_is_dpm_running,
+ .get_mac_definition = vegam_get_mac_definition,
+ .update_smc_table = vegam_update_smc_table,
+ .init_smc_table = vegam_init_smc_table,
+ .get_offsetof = vegam_get_offsetof,
+ .populate_all_graphic_levels = vegam_populate_all_graphic_levels,
+ .populate_all_memory_levels = vegam_populate_all_memory_levels,
+ .update_sclk_threshold = vegam_update_sclk_threshold,
+ .is_hw_avfs_present = vegam_is_hw_avfs_present,
+ .thermal_avfs_enable = vegam_thermal_avfs_enable,
+ .thermal_setup_fan_table = vegam_thermal_setup_fan_table,
+};
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/vegam_smumgr.h b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vegam_smumgr.h
new file mode 100644
index 000000000000..2b6558238500
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vegam_smumgr.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _VEGAM_SMUMANAGER_H
+#define _VEGAM_SMUMANAGER_H
+
+
+#include <pp_endian.h>
+#include "smu75_discrete.h"
+#include "smu7_smumgr.h"
+
+#define SMC_RAM_END 0x40000
+
+#define DPMTuning_Uphyst_Shift 0
+#define DPMTuning_Downhyst_Shift 8
+#define DPMTuning_Activity_Shift 16
+
+#define GraphicsDPMTuning_VEGAM 0x001e6400
+#define MemoryDPMTuning_VEGAM 0x000f3c0a
+#define SclkDPMTuning_VEGAM 0x002d000a
+#define MclkDPMTuning_VEGAM 0x001f100a
+
+
+struct vegam_pt_defaults {
+ uint8_t SviLoadLineEn;
+ uint8_t SviLoadLineVddC;
+ uint8_t TDC_VDDC_ThrottleReleaseLimitPerc;
+ uint8_t TDC_MAWt;
+ uint8_t TdcWaterfallCtl;
+ uint8_t DTEAmbientTempBase;
+
+ uint32_t DisplayCac;
+ uint32_t BAPM_TEMP_GRADIENT;
+ uint16_t BAPMTI_R[SMU75_DTE_ITERATIONS * SMU75_DTE_SOURCES * SMU75_DTE_SINKS];
+ uint16_t BAPMTI_RC[SMU75_DTE_ITERATIONS * SMU75_DTE_SOURCES * SMU75_DTE_SINKS];
+};
+
+struct vegam_range_table {
+ uint32_t trans_lower_frequency; /* in 10khz */
+ uint32_t trans_upper_frequency;
+};
+
+struct vegam_smumgr {
+ struct smu7_smumgr smu7_data;
+ uint8_t protected_mode;
+ SMU75_Discrete_DpmTable smc_state_table;
+ struct SMU75_Discrete_Ulv ulv_setting;
+ struct SMU75_Discrete_PmFuses power_tune_table;
+ struct vegam_range_table range_table[NUM_SCLK_RANGE];
+ const struct vegam_pt_defaults *power_tune_defaults;
+ uint32_t bif_sclk_table[SMU75_MAX_LEVELS_LINK];
+};
+
+
+#endif