Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/gpu/drm/amd/pm/swsmu/smu14/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 93 kB image not shown  

Quelle  smu_v14_0_2_ppt.c   Sprache: C

 
/*
 * Copyright 2023 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.
 *
 */


#define SWSMU_CODE_LAYER_L2

#include <linux/firmware.h>
#include <linux/pci.h>
#include <linux/i2c.h>
#include "amdgpu.h"
#include "amdgpu_smu.h"
#include "atomfirmware.h"
#include "amdgpu_atomfirmware.h"
#include "amdgpu_atombios.h"
#include "smu_v14_0.h"
#include "smu14_driver_if_v14_0.h"
#include "soc15_common.h"
#include "atom.h"
#include "smu_v14_0_2_ppt.h"
#include "smu_v14_0_2_pptable.h"
#include "smu_v14_0_2_ppsmc.h"
#include "mp/mp_14_0_2_offset.h"
#include "mp/mp_14_0_2_sh_mask.h"

#include "smu_cmn.h"
#include "amdgpu_ras.h"

/*
 * DO NOT use these for err/warn/info/debug messages.
 * Use dev_err, dev_warn, dev_info and dev_dbg instead.
 * They are more MGPU friendly.
 */

#undef pr_err
#undef pr_warn
#undef pr_info
#undef pr_debug

#define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c))

#define FEATURE_MASK(feature) (1ULL << feature)
#define SMC_DPM_FEATURE ( \
 FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT)     | \
 FEATURE_MASK(FEATURE_DPM_UCLK_BIT)  | \
 FEATURE_MASK(FEATURE_DPM_LINK_BIT)       | \
 FEATURE_MASK(FEATURE_DPM_SOCCLK_BIT)     | \
 FEATURE_MASK(FEATURE_DPM_FCLK_BIT))

#define MP0_MP1_DATA_REGION_SIZE_COMBOPPTABLE 0x4000
#define DEBUGSMC_MSG_Mode1Reset        2
#define LINK_SPEED_MAX     3

#define PP_OD_FEATURE_GFXCLK_FMIN   0
#define PP_OD_FEATURE_GFXCLK_FMAX   1
#define PP_OD_FEATURE_UCLK_FMIN    2
#define PP_OD_FEATURE_UCLK_FMAX    3
#define PP_OD_FEATURE_GFX_VF_CURVE   4
#define PP_OD_FEATURE_FAN_CURVE_TEMP   5
#define PP_OD_FEATURE_FAN_CURVE_PWM   6
#define PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT  7
#define PP_OD_FEATURE_FAN_ACOUSTIC_TARGET  8
#define PP_OD_FEATURE_FAN_TARGET_TEMPERATURE  9
#define PP_OD_FEATURE_FAN_MINIMUM_PWM   10
#define PP_OD_FEATURE_FAN_ZERO_RPM_ENABLE  11

static struct cmn2asic_msg_mapping smu_v14_0_2_message_map[SMU_MSG_MAX_COUNT] = {
 MSG_MAP(TestMessage,   PPSMC_MSG_TestMessage,                 1),
 MSG_MAP(GetSmuVersion,   PPSMC_MSG_GetSmuVersion,               1),
 MSG_MAP(GetDriverIfVersion,  PPSMC_MSG_GetDriverIfVersion,          1),
 MSG_MAP(SetAllowedFeaturesMaskLow, PPSMC_MSG_SetAllowedFeaturesMaskLow,   0),
 MSG_MAP(SetAllowedFeaturesMaskHigh, PPSMC_MSG_SetAllowedFeaturesMaskHigh,  0),
 MSG_MAP(EnableAllSmuFeatures,  PPSMC_MSG_EnableAllSmuFeatures,        0),
 MSG_MAP(DisableAllSmuFeatures,  PPSMC_MSG_DisableAllSmuFeatures,       0),
 MSG_MAP(EnableSmuFeaturesLow,  PPSMC_MSG_EnableSmuFeaturesLow,        1),
 MSG_MAP(EnableSmuFeaturesHigh,  PPSMC_MSG_EnableSmuFeaturesHigh,       1),
 MSG_MAP(DisableSmuFeaturesLow,  PPSMC_MSG_DisableSmuFeaturesLow,       1),
 MSG_MAP(DisableSmuFeaturesHigh,  PPSMC_MSG_DisableSmuFeaturesHigh,      1),
 MSG_MAP(GetEnabledSmuFeaturesLow,       PPSMC_MSG_GetRunningSmuFeaturesLow,    1),
 MSG_MAP(GetEnabledSmuFeaturesHigh, PPSMC_MSG_GetRunningSmuFeaturesHigh,   1),
 MSG_MAP(SetWorkloadMask,  PPSMC_MSG_SetWorkloadMask,             1),
 MSG_MAP(SetPptLimit,   PPSMC_MSG_SetPptLimit,                 0),
 MSG_MAP(SetDriverDramAddrHigh,  PPSMC_MSG_SetDriverDramAddrHigh,       1),
 MSG_MAP(SetDriverDramAddrLow,  PPSMC_MSG_SetDriverDramAddrLow,        1),
 MSG_MAP(SetToolsDramAddrHigh,  PPSMC_MSG_SetToolsDramAddrHigh,        0),
 MSG_MAP(SetToolsDramAddrLow,  PPSMC_MSG_SetToolsDramAddrLow,         0),
 MSG_MAP(TransferTableSmu2Dram,  PPSMC_MSG_TransferTableSmu2Dram,       1),
 MSG_MAP(TransferTableDram2Smu,  PPSMC_MSG_TransferTableDram2Smu,       0),
 MSG_MAP(UseDefaultPPTable,  PPSMC_MSG_UseDefaultPPTable,           0),
 MSG_MAP(RunDcBtc,   PPSMC_MSG_RunDcBtc,                    0),
 MSG_MAP(EnterBaco,   PPSMC_MSG_EnterBaco,                   0),
 MSG_MAP(ExitBaco,   PPSMC_MSG_ExitBaco,                    0),
 MSG_MAP(SetSoftMinByFreq,  PPSMC_MSG_SetSoftMinByFreq,            1),
 MSG_MAP(SetSoftMaxByFreq,  PPSMC_MSG_SetSoftMaxByFreq,            1),
 MSG_MAP(SetHardMinByFreq,  PPSMC_MSG_SetHardMinByFreq,            1),
 MSG_MAP(SetHardMaxByFreq,  PPSMC_MSG_SetHardMaxByFreq,            0),
 MSG_MAP(GetMinDpmFreq,   PPSMC_MSG_GetMinDpmFreq,               1),
 MSG_MAP(GetMaxDpmFreq,   PPSMC_MSG_GetMaxDpmFreq,               1),
 MSG_MAP(GetDpmFreqByIndex,  PPSMC_MSG_GetDpmFreqByIndex,           1),
 MSG_MAP(PowerUpVcn,   PPSMC_MSG_PowerUpVcn,                  0),
 MSG_MAP(PowerDownVcn,   PPSMC_MSG_PowerDownVcn,                0),
 MSG_MAP(PowerUpJpeg,   PPSMC_MSG_PowerUpJpeg,                 0),
 MSG_MAP(PowerDownJpeg,   PPSMC_MSG_PowerDownJpeg,               0),
 MSG_MAP(GetDcModeMaxDpmFreq,  PPSMC_MSG_GetDcModeMaxDpmFreq,         1),
 MSG_MAP(OverridePcieParameters,  PPSMC_MSG_OverridePcieParameters,      0),
 MSG_MAP(DramLogSetDramAddrHigh,  PPSMC_MSG_DramLogSetDramAddrHigh,      0),
 MSG_MAP(DramLogSetDramAddrLow,  PPSMC_MSG_DramLogSetDramAddrLow,       0),
 MSG_MAP(DramLogSetDramSize,  PPSMC_MSG_DramLogSetDramSize,          0),
 MSG_MAP(AllowGfxOff,   PPSMC_MSG_AllowGfxOff,                 0),
 MSG_MAP(DisallowGfxOff,   PPSMC_MSG_DisallowGfxOff,              0),
 MSG_MAP(SetMGpuFanBoostLimitRpm, PPSMC_MSG_SetMGpuFanBoostLimitRpm,     0),
 MSG_MAP(GetPptLimit,   PPSMC_MSG_GetPptLimit,                 0),
 MSG_MAP(NotifyPowerSource,  PPSMC_MSG_NotifyPowerSource,           0),
 MSG_MAP(PrepareMp1ForUnload,  PPSMC_MSG_PrepareMp1ForUnload,         0),
 MSG_MAP(DFCstateControl,  PPSMC_MSG_SetExternalClientDfCstateAllow, 0),
 MSG_MAP(ArmD3,    PPSMC_MSG_ArmD3,                       0),
 MSG_MAP(SetNumBadMemoryPagesRetired, PPSMC_MSG_SetNumBadMemoryPagesRetired,   0),
 MSG_MAP(SetBadMemoryPagesRetiredFlagsPerChannel,
       PPSMC_MSG_SetBadMemoryPagesRetiredFlagsPerChannel,   0),
 MSG_MAP(AllowIHHostInterrupt,  PPSMC_MSG_AllowIHHostInterrupt,       0),
 MSG_MAP(ReenableAcDcInterrupt,  PPSMC_MSG_ReenableAcDcInterrupt,       0),
};

static struct cmn2asic_mapping smu_v14_0_2_clk_map[SMU_CLK_COUNT] = {
 CLK_MAP(GFXCLK,  PPCLK_GFXCLK),
 CLK_MAP(SCLK,  PPCLK_GFXCLK),
 CLK_MAP(SOCCLK,  PPCLK_SOCCLK),
 CLK_MAP(FCLK,  PPCLK_FCLK),
 CLK_MAP(UCLK,  PPCLK_UCLK),
 CLK_MAP(MCLK,  PPCLK_UCLK),
 CLK_MAP(VCLK,  PPCLK_VCLK_0),
 CLK_MAP(DCLK,  PPCLK_DCLK_0),
 CLK_MAP(DCEFCLK, PPCLK_DCFCLK),
};

static struct cmn2asic_mapping smu_v14_0_2_feature_mask_map[SMU_FEATURE_COUNT] = {
 FEA_MAP(FW_DATA_READ),
 FEA_MAP(DPM_GFXCLK),
 FEA_MAP(DPM_GFX_POWER_OPTIMIZER),
 FEA_MAP(DPM_UCLK),
 FEA_MAP(DPM_FCLK),
 FEA_MAP(DPM_SOCCLK),
 FEA_MAP(DPM_LINK),
 FEA_MAP(DPM_DCN),
 FEA_MAP(VMEMP_SCALING),
 FEA_MAP(VDDIO_MEM_SCALING),
 FEA_MAP(DS_GFXCLK),
 FEA_MAP(DS_SOCCLK),
 FEA_MAP(DS_FCLK),
 FEA_MAP(DS_LCLK),
 FEA_MAP(DS_DCFCLK),
 FEA_MAP(DS_UCLK),
 FEA_MAP(GFX_ULV),
 FEA_MAP(FW_DSTATE),
 FEA_MAP(GFXOFF),
 FEA_MAP(BACO),
 FEA_MAP(MM_DPM),
 FEA_MAP(SOC_MPCLK_DS),
 FEA_MAP(BACO_MPCLK_DS),
 FEA_MAP(THROTTLERS),
 FEA_MAP(SMARTSHIFT),
 FEA_MAP(GTHR),
 FEA_MAP(ACDC),
 FEA_MAP(VR0HOT),
 FEA_MAP(FW_CTF),
 FEA_MAP(FAN_CONTROL),
 FEA_MAP(GFX_DCS),
 FEA_MAP(GFX_READ_MARGIN),
 FEA_MAP(LED_DISPLAY),
 FEA_MAP(GFXCLK_SPREAD_SPECTRUM),
 FEA_MAP(OUT_OF_BAND_MONITOR),
 FEA_MAP(OPTIMIZED_VMIN),
 FEA_MAP(GFX_IMU),
 FEA_MAP(BOOT_TIME_CAL),
 FEA_MAP(GFX_PCC_DFLL),
 FEA_MAP(SOC_CG),
 FEA_MAP(DF_CSTATE),
 FEA_MAP(GFX_EDC),
 FEA_MAP(BOOT_POWER_OPT),
 FEA_MAP(CLOCK_POWER_DOWN_BYPASS),
 FEA_MAP(DS_VCN),
 FEA_MAP(BACO_CG),
 FEA_MAP(MEM_TEMP_READ),
 FEA_MAP(ATHUB_MMHUB_PG),
 FEA_MAP(SOC_PCC),
 FEA_MAP(EDC_PWRBRK),
 FEA_MAP(SOC_EDC_XVMIN),
 FEA_MAP(GFX_PSM_DIDT),
 FEA_MAP(APT_ALL_ENABLE),
 FEA_MAP(APT_SQ_THROTTLE),
 FEA_MAP(APT_PF_DCS),
 FEA_MAP(GFX_EDC_XVMIN),
 FEA_MAP(GFX_DIDT_XVMIN),
 FEA_MAP(FAN_ABNORMAL),
 [SMU_FEATURE_DPM_VCLK_BIT] = {1, FEATURE_MM_DPM_BIT},
 [SMU_FEATURE_DPM_DCLK_BIT] = {1, FEATURE_MM_DPM_BIT},
 [SMU_FEATURE_PPT_BIT] = {1, FEATURE_THROTTLERS_BIT},
};

static struct cmn2asic_mapping smu_v14_0_2_table_map[SMU_TABLE_COUNT] = {
 TAB_MAP(PPTABLE),
 TAB_MAP(WATERMARKS),
 TAB_MAP(AVFS_PSM_DEBUG),
 TAB_MAP(PMSTATUSLOG),
 TAB_MAP(SMU_METRICS),
 TAB_MAP(DRIVER_SMU_CONFIG),
 TAB_MAP(ACTIVITY_MONITOR_COEFF),
 [SMU_TABLE_COMBO_PPTABLE] = {1, TABLE_COMBO_PPTABLE},
 TAB_MAP(I2C_COMMANDS),
 TAB_MAP(ECCINFO),
 TAB_MAP(OVERDRIVE),
};

static struct cmn2asic_mapping smu_v14_0_2_pwr_src_map[SMU_POWER_SOURCE_COUNT] = {
 PWR_MAP(AC),
 PWR_MAP(DC),
};

static struct cmn2asic_mapping smu_v14_0_2_workload_map[PP_SMC_POWER_PROFILE_COUNT] = {
 WORKLOAD_MAP(PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT, WORKLOAD_PPLIB_DEFAULT_BIT),
 WORKLOAD_MAP(PP_SMC_POWER_PROFILE_FULLSCREEN3D,  WORKLOAD_PPLIB_FULL_SCREEN_3D_BIT),
 WORKLOAD_MAP(PP_SMC_POWER_PROFILE_POWERSAVING,  WORKLOAD_PPLIB_POWER_SAVING_BIT),
 WORKLOAD_MAP(PP_SMC_POWER_PROFILE_VIDEO,  WORKLOAD_PPLIB_VIDEO_BIT),
 WORKLOAD_MAP(PP_SMC_POWER_PROFILE_VR,   WORKLOAD_PPLIB_VR_BIT),
 WORKLOAD_MAP(PP_SMC_POWER_PROFILE_COMPUTE,  WORKLOAD_PPLIB_COMPUTE_BIT),
 WORKLOAD_MAP(PP_SMC_POWER_PROFILE_CUSTOM,  WORKLOAD_PPLIB_CUSTOM_BIT),
 WORKLOAD_MAP(PP_SMC_POWER_PROFILE_WINDOW3D,  WORKLOAD_PPLIB_WINDOW_3D_BIT),
};

static const uint8_t smu_v14_0_2_throttler_map[] = {
 [THROTTLER_PPT0_BIT]  = (SMU_THROTTLER_PPT0_BIT),
 [THROTTLER_PPT1_BIT]  = (SMU_THROTTLER_PPT1_BIT),
 [THROTTLER_PPT2_BIT]  = (SMU_THROTTLER_PPT2_BIT),
 [THROTTLER_PPT3_BIT]  = (SMU_THROTTLER_PPT3_BIT),
 [THROTTLER_TDC_GFX_BIT]  = (SMU_THROTTLER_TDC_GFX_BIT),
 [THROTTLER_TDC_SOC_BIT]  = (SMU_THROTTLER_TDC_SOC_BIT),
 [THROTTLER_TEMP_EDGE_BIT] = (SMU_THROTTLER_TEMP_EDGE_BIT),
 [THROTTLER_TEMP_HOTSPOT_BIT] = (SMU_THROTTLER_TEMP_HOTSPOT_BIT),
 [THROTTLER_TEMP_MEM_BIT] = (SMU_THROTTLER_TEMP_MEM_BIT),
 [THROTTLER_TEMP_VR_GFX_BIT] = (SMU_THROTTLER_TEMP_VR_GFX_BIT),
 [THROTTLER_TEMP_VR_SOC_BIT] = (SMU_THROTTLER_TEMP_VR_SOC_BIT),
 [THROTTLER_TEMP_VR_MEM0_BIT] = (SMU_THROTTLER_TEMP_VR_MEM0_BIT),
 [THROTTLER_TEMP_VR_MEM1_BIT] = (SMU_THROTTLER_TEMP_VR_MEM1_BIT),
 [THROTTLER_TEMP_LIQUID0_BIT] = (SMU_THROTTLER_TEMP_LIQUID0_BIT),
 [THROTTLER_TEMP_LIQUID1_BIT] = (SMU_THROTTLER_TEMP_LIQUID1_BIT),
 [THROTTLER_GFX_APCC_PLUS_BIT] = (SMU_THROTTLER_APCC_BIT),
 [THROTTLER_FIT_BIT]  = (SMU_THROTTLER_FIT_BIT),
};

static int
smu_v14_0_2_get_allowed_feature_mask(struct smu_context *smu,
      uint32_t *feature_mask, uint32_t num)
{
 struct amdgpu_device *adev = smu->adev;
 /*u32 smu_version;*/

 if (num > 2)
  return -EINVAL;

 memset(feature_mask, 0xff, sizeof(uint32_t) * num);

 if (adev->pm.pp_feature & PP_SCLK_DPM_MASK) {
  *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT);
  *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_GFX_IMU_BIT);
 }
#if 0
 if (!(adev->pg_flags & AMD_PG_SUPPORT_ATHUB) ||
     !(adev->pg_flags & AMD_PG_SUPPORT_MMHUB))
  *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_ATHUB_MMHUB_PG_BIT);

 if (!(adev->pm.pp_feature & PP_SOCCLK_DPM_MASK))
  *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_SOCCLK_BIT);

 /* PMFW 78.58 contains a critical fix for gfxoff feature */
 smu_cmn_get_smc_version(smu, NULL, &smu_version);
 if ((smu_version < 0x004e3a00) ||
      !(adev->pm.pp_feature & PP_GFXOFF_MASK))
  *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_GFXOFF_BIT);

 if (!(adev->pm.pp_feature & PP_MCLK_DPM_MASK)) {
  *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_UCLK_BIT);
  *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_VMEMP_SCALING_BIT);
  *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_VDDIO_MEM_SCALING_BIT);
 }

 if (!(adev->pm.pp_feature & PP_SCLK_DEEP_SLEEP_MASK))
  *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DS_GFXCLK_BIT);

 if (!(adev->pm.pp_feature & PP_PCIE_DPM_MASK)) {
  *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_LINK_BIT);
  *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DS_LCLK_BIT);
 }

 if (!(adev->pm.pp_feature & PP_ULV_MASK))
  *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_GFX_ULV_BIT);
#endif

 return 0;
}

static int smu_v14_0_2_check_powerplay_table(struct smu_context *smu)
{
 struct smu_table_context *table_context = &smu->smu_table;
 struct smu_14_0_2_powerplay_table *powerplay_table =
  table_context->power_play_table;
 struct smu_baco_context *smu_baco = &smu->smu_baco;
 PPTable_t *pptable = smu->smu_table.driver_pptable;
 const OverDriveLimits_t * const overdrive_upperlimits =
    &pptable->SkuTable.OverDriveLimitsBasicMax;
 const OverDriveLimits_t * const overdrive_lowerlimits =
    &pptable->SkuTable.OverDriveLimitsBasicMin;

 if (powerplay_table->platform_caps & SMU_14_0_2_PP_PLATFORM_CAP_HARDWAREDC)
  smu->dc_controlled_by_gpio = true;

 if (powerplay_table->platform_caps & SMU_14_0_2_PP_PLATFORM_CAP_BACO) {
  smu_baco->platform_support = true;

  if (powerplay_table->platform_caps & SMU_14_0_2_PP_PLATFORM_CAP_MACO)
   smu_baco->maco_support = true;
 }

 if (!overdrive_lowerlimits->FeatureCtrlMask ||
     !overdrive_upperlimits->FeatureCtrlMask)
  smu->od_enabled = false;

 table_context->thermal_controller_type =
  powerplay_table->thermal_controller_type;

 /*
 * Instead of having its own buffer space and get overdrive_table copied,
 * smu->od_settings just points to the actual overdrive_table
 */

 smu->od_settings = &powerplay_table->overdrive_table;

 smu->adev->pm.no_fan =
  !(pptable->PFE_Settings.FeaturesToRun[0] & (1 << FEATURE_FAN_CONTROL_BIT));

 return 0;
}

static int smu_v14_0_2_store_powerplay_table(struct smu_context *smu)
{
 struct smu_table_context *table_context = &smu->smu_table;
 struct smu_14_0_2_powerplay_table *powerplay_table =
  table_context->power_play_table;

 memcpy(table_context->driver_pptable, &powerplay_table->smc_pptable,
        sizeof(PPTable_t));

 return 0;
}

static int smu_v14_0_2_get_pptable_from_pmfw(struct smu_context *smu,
          void **table,
          uint32_t *size)
{
 struct smu_table_context *smu_table = &smu->smu_table;
 void *combo_pptable = smu_table->combo_pptable;
 int ret = 0;

 ret = smu_cmn_get_combo_pptable(smu);
 if (ret)
  return ret;

 *table = combo_pptable;
 *size = sizeof(struct smu_14_0_2_powerplay_table);

 return 0;
}

static int smu_v14_0_2_setup_pptable(struct smu_context *smu)
{
 struct smu_table_context *smu_table = &smu->smu_table;
 int ret = 0;

 if (amdgpu_sriov_vf(smu->adev))
  return 0;

 ret = smu_v14_0_2_get_pptable_from_pmfw(smu,
       &smu_table->power_play_table,
       &smu_table->power_play_table_size);
 if (ret)
  return ret;

 ret = smu_v14_0_2_store_powerplay_table(smu);
 if (ret)
  return ret;

 ret = smu_v14_0_2_check_powerplay_table(smu);
 if (ret)
  return ret;

 return ret;
}

static int smu_v14_0_2_tables_init(struct smu_context *smu)
{
 struct smu_table_context *smu_table = &smu->smu_table;
 struct smu_table *tables = smu_table->tables;

 SMU_TABLE_INIT(tables, SMU_TABLE_PPTABLE, sizeof(PPTable_t),
         PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
 SMU_TABLE_INIT(tables, SMU_TABLE_WATERMARKS, sizeof(Watermarks_t),
         PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
 SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetricsExternal_t),
         PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
 SMU_TABLE_INIT(tables, SMU_TABLE_I2C_COMMANDS, sizeof(SwI2cRequest_t),
         PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
 SMU_TABLE_INIT(tables, SMU_TABLE_OVERDRIVE, sizeof(OverDriveTable_t),
         PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
 SMU_TABLE_INIT(tables, SMU_TABLE_PMSTATUSLOG, SMU14_TOOL_SIZE,
         PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
 SMU_TABLE_INIT(tables, SMU_TABLE_ACTIVITY_MONITOR_COEFF,
         sizeof(DpmActivityMonitorCoeffIntExternal_t), PAGE_SIZE,
         AMDGPU_GEM_DOMAIN_VRAM);
 SMU_TABLE_INIT(tables, SMU_TABLE_COMBO_PPTABLE, MP0_MP1_DATA_REGION_SIZE_COMBOPPTABLE,
   PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
 SMU_TABLE_INIT(tables, SMU_TABLE_ECCINFO, sizeof(EccInfoTable_t),
   PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);

 smu_table->metrics_table = kzalloc(sizeof(SmuMetricsExternal_t), GFP_KERNEL);
 if (!smu_table->metrics_table)
  goto err0_out;
 smu_table->metrics_time = 0;

 smu_table->gpu_metrics_table_size = sizeof(struct gpu_metrics_v1_3);
 smu_table->gpu_metrics_table = kzalloc(smu_table->gpu_metrics_table_size, GFP_KERNEL);
 if (!smu_table->gpu_metrics_table)
  goto err1_out;

 smu_table->watermarks_table = kzalloc(sizeof(Watermarks_t), GFP_KERNEL);
 if (!smu_table->watermarks_table)
  goto err2_out;

 smu_table->ecc_table = kzalloc(tables[SMU_TABLE_ECCINFO].size, GFP_KERNEL);
 if (!smu_table->ecc_table)
  goto err3_out;

 return 0;

err3_out:
 kfree(smu_table->watermarks_table);
err2_out:
 kfree(smu_table->gpu_metrics_table);
err1_out:
 kfree(smu_table->metrics_table);
err0_out:
 return -ENOMEM;
}

static int smu_v14_0_2_allocate_dpm_context(struct smu_context *smu)
{
 struct smu_dpm_context *smu_dpm = &smu->smu_dpm;

 smu_dpm->dpm_context = kzalloc(sizeof(struct smu_14_0_dpm_context),
           GFP_KERNEL);
 if (!smu_dpm->dpm_context)
  return -ENOMEM;

 smu_dpm->dpm_context_size = sizeof(struct smu_14_0_dpm_context);

 return 0;
}

static int smu_v14_0_2_init_smc_tables(struct smu_context *smu)
{
 int ret = 0;

 ret = smu_v14_0_2_tables_init(smu);
 if (ret)
  return ret;

 ret = smu_v14_0_2_allocate_dpm_context(smu);
 if (ret)
  return ret;

 return smu_v14_0_init_smc_tables(smu);
}

static int smu_v14_0_2_set_default_dpm_table(struct smu_context *smu)
{
 struct smu_14_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
 struct smu_table_context *table_context = &smu->smu_table;
 PPTable_t *pptable = table_context->driver_pptable;
 SkuTable_t *skutable = &pptable->SkuTable;
 struct smu_14_0_dpm_table *dpm_table;
 int ret = 0;

 /* socclk dpm table setup */
 dpm_table = &dpm_context->dpm_tables.soc_table;
 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_SOCCLK_BIT)) {
  ret = smu_v14_0_set_single_dpm_table(smu,
           SMU_SOCCLK,
           dpm_table);
  if (ret)
   return ret;
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.socclk / 100;
  dpm_table->dpm_levels[0].enabled = true;
  dpm_table->min = dpm_table->dpm_levels[0].value;
  dpm_table->max = dpm_table->dpm_levels[0].value;
 }

 /* gfxclk dpm table setup */
 dpm_table = &dpm_context->dpm_tables.gfx_table;
 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_GFXCLK_BIT)) {
  ret = smu_v14_0_set_single_dpm_table(smu,
           SMU_GFXCLK,
           dpm_table);
  if (ret)
   return ret;

  /*
 * Update the reported maximum shader clock to the value
 * which can be guarded to be achieved on all cards. This
 * is aligned with Window setting. And considering that value
 * might be not the peak frequency the card can achieve, it
 * is normal some real-time clock frequency can overtake this
 * labelled maximum clock frequency(for example in pp_dpm_sclk
 * sysfs output).
 */

  if (skutable->DriverReportedClocks.GameClockAc &&
      (dpm_table->dpm_levels[dpm_table->count - 1].value >
      skutable->DriverReportedClocks.GameClockAc)) {
   dpm_table->dpm_levels[dpm_table->count - 1].value =
    skutable->DriverReportedClocks.GameClockAc;
   dpm_table->max = skutable->DriverReportedClocks.GameClockAc;
  }
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.gfxclk / 100;
  dpm_table->dpm_levels[0].enabled = true;
  dpm_table->min = dpm_table->dpm_levels[0].value;
  dpm_table->max = dpm_table->dpm_levels[0].value;
 }

 /* uclk dpm table setup */
 dpm_table = &dpm_context->dpm_tables.uclk_table;
 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_UCLK_BIT)) {
  ret = smu_v14_0_set_single_dpm_table(smu,
           SMU_UCLK,
           dpm_table);
  if (ret)
   return ret;
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.uclk / 100;
  dpm_table->dpm_levels[0].enabled = true;
  dpm_table->min = dpm_table->dpm_levels[0].value;
  dpm_table->max = dpm_table->dpm_levels[0].value;
 }

 /* fclk dpm table setup */
 dpm_table = &dpm_context->dpm_tables.fclk_table;
 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_FCLK_BIT)) {
  ret = smu_v14_0_set_single_dpm_table(smu,
           SMU_FCLK,
           dpm_table);
  if (ret)
   return ret;
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.fclk / 100;
  dpm_table->dpm_levels[0].enabled = true;
  dpm_table->min = dpm_table->dpm_levels[0].value;
  dpm_table->max = dpm_table->dpm_levels[0].value;
 }

 /* vclk dpm table setup */
 dpm_table = &dpm_context->dpm_tables.vclk_table;
 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_VCLK_BIT)) {
  ret = smu_v14_0_set_single_dpm_table(smu,
           SMU_VCLK,
           dpm_table);
  if (ret)
   return ret;
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.vclk / 100;
  dpm_table->dpm_levels[0].enabled = true;
  dpm_table->min = dpm_table->dpm_levels[0].value;
  dpm_table->max = dpm_table->dpm_levels[0].value;
 }

 /* dclk dpm table setup */
 dpm_table = &dpm_context->dpm_tables.dclk_table;
 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCLK_BIT)) {
  ret = smu_v14_0_set_single_dpm_table(smu,
           SMU_DCLK,
           dpm_table);
  if (ret)
   return ret;
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dclk / 100;
  dpm_table->dpm_levels[0].enabled = true;
  dpm_table->min = dpm_table->dpm_levels[0].value;
  dpm_table->max = dpm_table->dpm_levels[0].value;
 }

 /* dcefclk dpm table setup */
 dpm_table = &dpm_context->dpm_tables.dcef_table;
 if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCN_BIT)) {
  ret = smu_v14_0_set_single_dpm_table(smu,
           SMU_DCEFCLK,
           dpm_table);
  if (ret)
   return ret;
 } else {
  dpm_table->count = 1;
  dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dcefclk / 100;
  dpm_table->dpm_levels[0].enabled = true;
  dpm_table->min = dpm_table->dpm_levels[0].value;
  dpm_table->max = dpm_table->dpm_levels[0].value;
 }

 return 0;
}

static bool smu_v14_0_2_is_dpm_running(struct smu_context *smu)
{
 int ret = 0;
 uint64_t feature_enabled;

 ret = smu_cmn_get_enabled_mask(smu, &feature_enabled);
 if (ret)
  return false;

 return !!(feature_enabled & SMC_DPM_FEATURE);
}

static uint32_t smu_v14_0_2_get_throttler_status(SmuMetrics_t *metrics)
{
 uint32_t throttler_status = 0;
 int i;

 for (i = 0; i < THROTTLER_COUNT; i++)
  throttler_status |=
   (metrics->ThrottlingPercentage[i] ? 1U << i : 0);

 return throttler_status;
}

#define SMU_14_0_2_BUSY_THRESHOLD 5
static int smu_v14_0_2_get_smu_metrics_data(struct smu_context *smu,
         MetricsMember_t member,
         uint32_t *value)
{
 struct smu_table_context *smu_table = &smu->smu_table;
 SmuMetrics_t *metrics =
  &(((SmuMetricsExternal_t *)(smu_table->metrics_table))->SmuMetrics);
 int ret = 0;

 ret = smu_cmn_get_metrics_table(smu,
     NULL,
     false);
 if (ret)
  return ret;

 switch (member) {
 case METRICS_CURR_GFXCLK:
  *value = metrics->CurrClock[PPCLK_GFXCLK];
  break;
 case METRICS_CURR_SOCCLK:
  *value = metrics->CurrClock[PPCLK_SOCCLK];
  break;
 case METRICS_CURR_UCLK:
  *value = metrics->CurrClock[PPCLK_UCLK];
  break;
 case METRICS_CURR_VCLK:
  *value = metrics->CurrClock[PPCLK_VCLK_0];
  break;
 case METRICS_CURR_DCLK:
  *value = metrics->CurrClock[PPCLK_DCLK_0];
  break;
 case METRICS_CURR_FCLK:
  *value = metrics->CurrClock[PPCLK_FCLK];
  break;
 case METRICS_CURR_DCEFCLK:
  *value = metrics->CurrClock[PPCLK_DCFCLK];
  break;
 case METRICS_AVERAGE_GFXCLK:
  if (metrics->AverageGfxActivity <= SMU_14_0_2_BUSY_THRESHOLD)
   *value = metrics->AverageGfxclkFrequencyPostDs;
  else
   *value = metrics->AverageGfxclkFrequencyPreDs;
  break;
 case METRICS_AVERAGE_FCLK:
  if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD)
   *value = metrics->AverageFclkFrequencyPostDs;
  else
   *value = metrics->AverageFclkFrequencyPreDs;
  break;
 case METRICS_AVERAGE_UCLK:
  if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD)
   *value = metrics->AverageMemclkFrequencyPostDs;
  else
   *value = metrics->AverageMemclkFrequencyPreDs;
  break;
 case METRICS_AVERAGE_VCLK:
  *value = metrics->AverageVclk0Frequency;
  break;
 case METRICS_AVERAGE_DCLK:
  *value = metrics->AverageDclk0Frequency;
  break;
 case METRICS_AVERAGE_VCLK1:
  *value = metrics->AverageVclk1Frequency;
  break;
 case METRICS_AVERAGE_DCLK1:
  *value = metrics->AverageDclk1Frequency;
  break;
 case METRICS_AVERAGE_GFXACTIVITY:
  *value = metrics->AverageGfxActivity;
  break;
 case METRICS_AVERAGE_MEMACTIVITY:
  *value = metrics->AverageUclkActivity;
  break;
 case METRICS_AVERAGE_VCNACTIVITY:
  *value = max(metrics->AverageVcn0ActivityPercentage,
        metrics->Vcn1ActivityPercentage);
  break;
 case METRICS_AVERAGE_SOCKETPOWER:
  *value = metrics->AverageSocketPower << 8;
  break;
 case METRICS_TEMPERATURE_EDGE:
  *value = metrics->AvgTemperature[TEMP_EDGE] *
   SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
  break;
 case METRICS_TEMPERATURE_HOTSPOT:
  *value = metrics->AvgTemperature[TEMP_HOTSPOT] *
   SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
  break;
 case METRICS_TEMPERATURE_MEM:
  *value = metrics->AvgTemperature[TEMP_MEM] *
   SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
  break;
 case METRICS_TEMPERATURE_VRGFX:
  *value = metrics->AvgTemperature[TEMP_VR_GFX] *
   SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
  break;
 case METRICS_TEMPERATURE_VRSOC:
  *value = metrics->AvgTemperature[TEMP_VR_SOC] *
   SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
  break;
 case METRICS_THROTTLER_STATUS:
  *value = smu_v14_0_2_get_throttler_status(metrics);
  break;
 case METRICS_CURR_FANSPEED:
  *value = metrics->AvgFanRpm;
  break;
 case METRICS_CURR_FANPWM:
  *value = metrics->AvgFanPwm;
  break;
 case METRICS_VOLTAGE_VDDGFX:
  *value = metrics->AvgVoltage[SVI_PLANE_VDD_GFX];
  break;
 case METRICS_PCIE_RATE:
  *value = metrics->PcieRate;
  break;
 case METRICS_PCIE_WIDTH:
  *value = metrics->PcieWidth;
  break;
 default:
  *value = UINT_MAX;
  break;
 }

 return ret;
}

static int smu_v14_0_2_get_dpm_ultimate_freq(struct smu_context *smu,
          enum smu_clk_type clk_type,
          uint32_t *min,
          uint32_t *max)
{
 struct smu_14_0_dpm_context *dpm_context =
  smu->smu_dpm.dpm_context;
 struct smu_14_0_dpm_table *dpm_table;

 switch (clk_type) {
 case SMU_MCLK:
 case SMU_UCLK:
  /* uclk dpm table */
  dpm_table = &dpm_context->dpm_tables.uclk_table;
  break;
 case SMU_GFXCLK:
 case SMU_SCLK:
  /* gfxclk dpm table */
  dpm_table = &dpm_context->dpm_tables.gfx_table;
  break;
 case SMU_SOCCLK:
  /* socclk dpm table */
  dpm_table = &dpm_context->dpm_tables.soc_table;
  break;
 case SMU_FCLK:
  /* fclk dpm table */
  dpm_table = &dpm_context->dpm_tables.fclk_table;
  break;
 case SMU_VCLK:
 case SMU_VCLK1:
  /* vclk dpm table */
  dpm_table = &dpm_context->dpm_tables.vclk_table;
  break;
 case SMU_DCLK:
 case SMU_DCLK1:
  /* dclk dpm table */
  dpm_table = &dpm_context->dpm_tables.dclk_table;
  break;
 default:
  dev_err(smu->adev->dev, "Unsupported clock type!\n");
  return -EINVAL;
 }

 if (min)
  *min = dpm_table->min;
 if (max)
  *max = dpm_table->max;

 return 0;
}

static int smu_v14_0_2_read_sensor(struct smu_context *smu,
       enum amd_pp_sensors sensor,
       void *data,
       uint32_t *size)
{
 struct smu_table_context *table_context = &smu->smu_table;
 PPTable_t *smc_pptable = table_context->driver_pptable;
 int ret = 0;

 switch (sensor) {
 case AMDGPU_PP_SENSOR_MAX_FAN_RPM:
  *(uint16_t *)data = smc_pptable->CustomSkuTable.FanMaximumRpm;
  *size = 4;
  break;
 case AMDGPU_PP_SENSOR_MEM_LOAD:
  ret = smu_v14_0_2_get_smu_metrics_data(smu,
             METRICS_AVERAGE_MEMACTIVITY,
             (uint32_t *)data);
  *size = 4;
  break;
 case AMDGPU_PP_SENSOR_GPU_LOAD:
  ret = smu_v14_0_2_get_smu_metrics_data(smu,
             METRICS_AVERAGE_GFXACTIVITY,
             (uint32_t *)data);
  *size = 4;
  break;
 case AMDGPU_PP_SENSOR_VCN_LOAD:
  ret = smu_v14_0_2_get_smu_metrics_data(smu,
             METRICS_AVERAGE_VCNACTIVITY,
             (uint32_t *)data);
  *size = 4;
  break;
 case AMDGPU_PP_SENSOR_GPU_AVG_POWER:
  ret = smu_v14_0_2_get_smu_metrics_data(smu,
             METRICS_AVERAGE_SOCKETPOWER,
             (uint32_t *)data);
  *size = 4;
  break;
 case AMDGPU_PP_SENSOR_HOTSPOT_TEMP:
  ret = smu_v14_0_2_get_smu_metrics_data(smu,
             METRICS_TEMPERATURE_HOTSPOT,
             (uint32_t *)data);
  *size = 4;
  break;
 case AMDGPU_PP_SENSOR_EDGE_TEMP:
  ret = smu_v14_0_2_get_smu_metrics_data(smu,
             METRICS_TEMPERATURE_EDGE,
             (uint32_t *)data);
  *size = 4;
  break;
 case AMDGPU_PP_SENSOR_MEM_TEMP:
  ret = smu_v14_0_2_get_smu_metrics_data(smu,
             METRICS_TEMPERATURE_MEM,
             (uint32_t *)data);
  *size = 4;
  break;
 case AMDGPU_PP_SENSOR_GFX_MCLK:
  ret = smu_v14_0_2_get_smu_metrics_data(smu,
             METRICS_CURR_UCLK,
             (uint32_t *)data);
  *(uint32_t *)data *= 100;
  *size = 4;
  break;
 case AMDGPU_PP_SENSOR_GFX_SCLK:
  ret = smu_v14_0_2_get_smu_metrics_data(smu,
             METRICS_AVERAGE_GFXCLK,
             (uint32_t *)data);
  *(uint32_t *)data *= 100;
  *size = 4;
  break;
 case AMDGPU_PP_SENSOR_VDDGFX:
  ret = smu_v14_0_2_get_smu_metrics_data(smu,
             METRICS_VOLTAGE_VDDGFX,
             (uint32_t *)data);
  *size = 4;
  break;
 default:
  ret = -EOPNOTSUPP;
  break;
 }

 return ret;
}

static int smu_v14_0_2_get_current_clk_freq_by_table(struct smu_context *smu,
           enum smu_clk_type clk_type,
           uint32_t *value)
{
 MetricsMember_t member_type;
 int clk_id = 0;

 clk_id = smu_cmn_to_asic_specific_index(smu,
      CMN2ASIC_MAPPING_CLK,
      clk_type);
 if (clk_id < 0)
  return -EINVAL;

 switch (clk_id) {
 case PPCLK_GFXCLK:
  member_type = METRICS_AVERAGE_GFXCLK;
  break;
 case PPCLK_UCLK:
  member_type = METRICS_CURR_UCLK;
  break;
 case PPCLK_FCLK:
  member_type = METRICS_CURR_FCLK;
  break;
 case PPCLK_SOCCLK:
  member_type = METRICS_CURR_SOCCLK;
  break;
 case PPCLK_VCLK_0:
  member_type = METRICS_AVERAGE_VCLK;
  break;
 case PPCLK_DCLK_0:
  member_type = METRICS_AVERAGE_DCLK;
  break;
 case PPCLK_DCFCLK:
  member_type = METRICS_CURR_DCEFCLK;
  break;
 default:
  return -EINVAL;
 }

 return smu_v14_0_2_get_smu_metrics_data(smu,
      member_type,
      value);
}

static bool smu_v14_0_2_is_od_feature_supported(struct smu_context *smu,
      int od_feature_bit)
{
 PPTable_t *pptable = smu->smu_table.driver_pptable;
 const OverDriveLimits_t * const overdrive_upperlimits =
    &pptable->SkuTable.OverDriveLimitsBasicMax;

 return overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit);
}

static void smu_v14_0_2_get_od_setting_limits(struct smu_context *smu,
           int od_feature_bit,
           int32_t *min,
           int32_t *max)
{
 PPTable_t *pptable = smu->smu_table.driver_pptable;
 const OverDriveLimits_t * const overdrive_upperlimits =
    &pptable->SkuTable.OverDriveLimitsBasicMax;
 const OverDriveLimits_t * const overdrive_lowerlimits =
    &pptable->SkuTable.OverDriveLimitsBasicMin;
 int32_t od_min_setting, od_max_setting;

 switch (od_feature_bit) {
 case PP_OD_FEATURE_GFXCLK_FMIN:
 case PP_OD_FEATURE_GFXCLK_FMAX:
  od_min_setting = overdrive_lowerlimits->GfxclkFoffset;
  od_max_setting = overdrive_upperlimits->GfxclkFoffset;
  break;
 case PP_OD_FEATURE_UCLK_FMIN:
  od_min_setting = overdrive_lowerlimits->UclkFmin;
  od_max_setting = overdrive_upperlimits->UclkFmin;
  break;
 case PP_OD_FEATURE_UCLK_FMAX:
  od_min_setting = overdrive_lowerlimits->UclkFmax;
  od_max_setting = overdrive_upperlimits->UclkFmax;
  break;
 case PP_OD_FEATURE_GFX_VF_CURVE:
  od_min_setting = overdrive_lowerlimits->VoltageOffsetPerZoneBoundary[0];
  od_max_setting = overdrive_upperlimits->VoltageOffsetPerZoneBoundary[0];
  break;
 case PP_OD_FEATURE_FAN_CURVE_TEMP:
  od_min_setting = overdrive_lowerlimits->FanLinearTempPoints[0];
  od_max_setting = overdrive_upperlimits->FanLinearTempPoints[0];
  break;
 case PP_OD_FEATURE_FAN_CURVE_PWM:
  od_min_setting = overdrive_lowerlimits->FanLinearPwmPoints[0];
  od_max_setting = overdrive_upperlimits->FanLinearPwmPoints[0];
  break;
 case PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT:
  od_min_setting = overdrive_lowerlimits->AcousticLimitRpmThreshold;
  od_max_setting = overdrive_upperlimits->AcousticLimitRpmThreshold;
  break;
 case PP_OD_FEATURE_FAN_ACOUSTIC_TARGET:
  od_min_setting = overdrive_lowerlimits->AcousticTargetRpmThreshold;
  od_max_setting = overdrive_upperlimits->AcousticTargetRpmThreshold;
  break;
 case PP_OD_FEATURE_FAN_TARGET_TEMPERATURE:
  od_min_setting = overdrive_lowerlimits->FanTargetTemperature;
  od_max_setting = overdrive_upperlimits->FanTargetTemperature;
  break;
 case PP_OD_FEATURE_FAN_MINIMUM_PWM:
  od_min_setting = overdrive_lowerlimits->FanMinimumPwm;
  od_max_setting = overdrive_upperlimits->FanMinimumPwm;
  break;
 case PP_OD_FEATURE_FAN_ZERO_RPM_ENABLE:
  od_min_setting = overdrive_lowerlimits->FanZeroRpmEnable;
  od_max_setting = overdrive_upperlimits->FanZeroRpmEnable;
  break;
 default:
  od_min_setting = od_max_setting = INT_MAX;
  break;
 }

 if (min)
  *min = od_min_setting;
 if (max)
  *max = od_max_setting;
}

static int smu_v14_0_2_print_clk_levels(struct smu_context *smu,
     enum smu_clk_type clk_type,
     char *buf)
{
 struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
 struct smu_14_0_dpm_context *dpm_context = smu_dpm->dpm_context;
 OverDriveTableExternal_t *od_table =
  (OverDriveTableExternal_t *)smu->smu_table.overdrive_table;
 struct smu_14_0_dpm_table *single_dpm_table;
 struct smu_14_0_pcie_table *pcie_table;
 uint32_t gen_speed, lane_width;
 int i, curr_freq, size = 0;
 int32_t min_value, max_value;
 int ret = 0;

 smu_cmn_get_sysfs_buf(&buf, &size);

 if (amdgpu_ras_intr_triggered()) {
  size += sysfs_emit_at(buf, size, "unavailable\n");
  return size;
 }

 switch (clk_type) {
 case SMU_SCLK:
  single_dpm_table = &(dpm_context->dpm_tables.gfx_table);
  break;
 case SMU_MCLK:
  single_dpm_table = &(dpm_context->dpm_tables.uclk_table);
  break;
 case SMU_SOCCLK:
  single_dpm_table = &(dpm_context->dpm_tables.soc_table);
  break;
 case SMU_FCLK:
  single_dpm_table = &(dpm_context->dpm_tables.fclk_table);
  break;
 case SMU_VCLK:
 case SMU_VCLK1:
  single_dpm_table = &(dpm_context->dpm_tables.vclk_table);
  break;
 case SMU_DCLK:
 case SMU_DCLK1:
  single_dpm_table = &(dpm_context->dpm_tables.dclk_table);
  break;
 case SMU_DCEFCLK:
  single_dpm_table = &(dpm_context->dpm_tables.dcef_table);
  break;
 default:
  break;
 }

 switch (clk_type) {
 case SMU_SCLK:
 case SMU_MCLK:
 case SMU_SOCCLK:
 case SMU_FCLK:
 case SMU_VCLK:
 case SMU_VCLK1:
 case SMU_DCLK:
 case SMU_DCLK1:
 case SMU_DCEFCLK:
  ret = smu_v14_0_2_get_current_clk_freq_by_table(smu, clk_type, &curr_freq);
  if (ret) {
   dev_err(smu->adev->dev, "Failed to get current clock freq!");
   return ret;
  }

  if (single_dpm_table->is_fine_grained) {
   /*
 * For fine grained dpms, there are only two dpm levels:
 *   - level 0 -> min clock freq
 *   - level 1 -> max clock freq
 * And the current clock frequency can be any value between them.
 * So, if the current clock frequency is not at level 0 or level 1,
 * we will fake it as three dpm levels:
 *   - level 0 -> min clock freq
 *   - level 1 -> current actual clock freq
 *   - level 2 -> max clock freq
 */

   if ((single_dpm_table->dpm_levels[0].value != curr_freq) &&
        (single_dpm_table->dpm_levels[1].value != curr_freq)) {
    size += sysfs_emit_at(buf, size, "0: %uMhz\n",
      single_dpm_table->dpm_levels[0].value);
    size += sysfs_emit_at(buf, size, "1: %uMhz *\n",
      curr_freq);
    size += sysfs_emit_at(buf, size, "2: %uMhz\n",
      single_dpm_table->dpm_levels[1].value);
   } else {
    size += sysfs_emit_at(buf, size, "0: %uMhz %s\n",
      single_dpm_table->dpm_levels[0].value,
      single_dpm_table->dpm_levels[0].value == curr_freq ? "*" : "");
    size += sysfs_emit_at(buf, size, "1: %uMhz %s\n",
      single_dpm_table->dpm_levels[1].value,
      single_dpm_table->dpm_levels[1].value == curr_freq ? "*" : "");
   }
  } else {
   for (i = 0; i < single_dpm_table->count; i++)
    size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n",
      i, single_dpm_table->dpm_levels[i].value,
      single_dpm_table->dpm_levels[i].value == curr_freq ? "*" : "");
  }
  break;
 case SMU_PCIE:
  ret = smu_v14_0_2_get_smu_metrics_data(smu,
             METRICS_PCIE_RATE,
             &gen_speed);
  if (ret)
   return ret;

  ret = smu_v14_0_2_get_smu_metrics_data(smu,
             METRICS_PCIE_WIDTH,
             &lane_width);
  if (ret)
   return ret;

  pcie_table = &(dpm_context->dpm_tables.pcie_table);
  for (i = 0; i < pcie_table->num_of_link_levels; i++)
   size += sysfs_emit_at(buf, size, "%d: %s %s %dMhz %s\n", i,
     (pcie_table->pcie_gen[i] == 0) ? "2.5GT/s," :
     (pcie_table->pcie_gen[i] == 1) ? "5.0GT/s," :
     (pcie_table->pcie_gen[i] == 2) ? "8.0GT/s," :
     (pcie_table->pcie_gen[i] == 3) ? "16.0GT/s," :
     (pcie_table->pcie_gen[i] == 4) ? "32.0GT/s," : "",
     (pcie_table->pcie_lane[i] == 1) ? "x1" :
     (pcie_table->pcie_lane[i] == 2) ? "x2" :
     (pcie_table->pcie_lane[i] == 3) ? "x4" :
     (pcie_table->pcie_lane[i] == 4) ? "x8" :
     (pcie_table->pcie_lane[i] == 5) ? "x12" :
     (pcie_table->pcie_lane[i] == 6) ? "x16" :
     (pcie_table->pcie_lane[i] == 7) ? "x32" : "",
     pcie_table->clk_freq[i],
     (gen_speed == DECODE_GEN_SPEED(pcie_table->pcie_gen[i])) &&
     (lane_width == DECODE_LANE_WIDTH(pcie_table->pcie_lane[i])) ?
     "*" : "");
  break;

 case SMU_OD_SCLK:
  if (!smu_v14_0_2_is_od_feature_supported(smu,
        PP_OD_FEATURE_GFXCLK_BIT))
   break;

  size += sysfs_emit_at(buf, size, "OD_SCLK_OFFSET:\n");
  size += sysfs_emit_at(buf, size, "%dMhz\n",
     od_table->OverDriveTable.GfxclkFoffset);
  break;

 case SMU_OD_MCLK:
  if (!smu_v14_0_2_is_od_feature_supported(smu,
        PP_OD_FEATURE_UCLK_BIT))
   break;

  size += sysfs_emit_at(buf, size, "OD_MCLK:\n");
  size += sysfs_emit_at(buf, size, "0: %uMhz\n1: %uMHz\n",
     od_table->OverDriveTable.UclkFmin,
     od_table->OverDriveTable.UclkFmax);
  break;

 case SMU_OD_VDDGFX_OFFSET:
  if (!smu_v14_0_2_is_od_feature_supported(smu,
        PP_OD_FEATURE_GFX_VF_CURVE_BIT))
   break;

  size += sysfs_emit_at(buf, size, "OD_VDDGFX_OFFSET:\n");
  size += sysfs_emit_at(buf, size, "%dmV\n",
          od_table->OverDriveTable.VoltageOffsetPerZoneBoundary[0]);
  break;

 case SMU_OD_FAN_CURVE:
  if (!smu_v14_0_2_is_od_feature_supported(smu,
        PP_OD_FEATURE_FAN_CURVE_BIT))
   break;

  size += sysfs_emit_at(buf, size, "OD_FAN_CURVE:\n");
  for (i = 0; i < NUM_OD_FAN_MAX_POINTS - 1; i++)
   size += sysfs_emit_at(buf, size, "%d: %dC %d%%\n",
      i,
      (int)od_table->OverDriveTable.FanLinearTempPoints[i],
      (int)od_table->OverDriveTable.FanLinearPwmPoints[i]);

  size += sysfs_emit_at(buf, size, "%s:\n""OD_RANGE");
  smu_v14_0_2_get_od_setting_limits(smu,
        PP_OD_FEATURE_FAN_CURVE_TEMP,
        &min_value,
        &max_value);
  size += sysfs_emit_at(buf, size, "FAN_CURVE(hotspot temp): %uC %uC\n",
          min_value, max_value);

  smu_v14_0_2_get_od_setting_limits(smu,
        PP_OD_FEATURE_FAN_CURVE_PWM,
        &min_value,
        &max_value);
  size += sysfs_emit_at(buf, size, "FAN_CURVE(fan speed): %u%% %u%%\n",
          min_value, max_value);

  break;

 case SMU_OD_ACOUSTIC_LIMIT:
  if (!smu_v14_0_2_is_od_feature_supported(smu,
        PP_OD_FEATURE_FAN_CURVE_BIT))
   break;

  size += sysfs_emit_at(buf, size, "OD_ACOUSTIC_LIMIT:\n");
  size += sysfs_emit_at(buf, size, "%d\n",
     (int)od_table->OverDriveTable.AcousticLimitRpmThreshold);

  size += sysfs_emit_at(buf, size, "%s:\n""OD_RANGE");
  smu_v14_0_2_get_od_setting_limits(smu,
        PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT,
        &min_value,
        &max_value);
  size += sysfs_emit_at(buf, size, "ACOUSTIC_LIMIT: %u %u\n",
          min_value, max_value);
  break;

 case SMU_OD_ACOUSTIC_TARGET:
  if (!smu_v14_0_2_is_od_feature_supported(smu,
        PP_OD_FEATURE_FAN_CURVE_BIT))
   break;

  size += sysfs_emit_at(buf, size, "OD_ACOUSTIC_TARGET:\n");
  size += sysfs_emit_at(buf, size, "%d\n",
     (int)od_table->OverDriveTable.AcousticTargetRpmThreshold);

  size += sysfs_emit_at(buf, size, "%s:\n""OD_RANGE");
  smu_v14_0_2_get_od_setting_limits(smu,
        PP_OD_FEATURE_FAN_ACOUSTIC_TARGET,
        &min_value,
        &max_value);
  size += sysfs_emit_at(buf, size, "ACOUSTIC_TARGET: %u %u\n",
          min_value, max_value);
  break;

 case SMU_OD_FAN_TARGET_TEMPERATURE:
  if (!smu_v14_0_2_is_od_feature_supported(smu,
        PP_OD_FEATURE_FAN_CURVE_BIT))
   break;

  size += sysfs_emit_at(buf, size, "FAN_TARGET_TEMPERATURE:\n");
  size += sysfs_emit_at(buf, size, "%d\n",
     (int)od_table->OverDriveTable.FanTargetTemperature);

  size += sysfs_emit_at(buf, size, "%s:\n""OD_RANGE");
  smu_v14_0_2_get_od_setting_limits(smu,
        PP_OD_FEATURE_FAN_TARGET_TEMPERATURE,
        &min_value,
        &max_value);
  size += sysfs_emit_at(buf, size, "TARGET_TEMPERATURE: %u %u\n",
          min_value, max_value);
  break;

 case SMU_OD_FAN_MINIMUM_PWM:
  if (!smu_v14_0_2_is_od_feature_supported(smu,
        PP_OD_FEATURE_FAN_CURVE_BIT))
   break;

  size += sysfs_emit_at(buf, size, "FAN_MINIMUM_PWM:\n");
  size += sysfs_emit_at(buf, size, "%d\n",
     (int)od_table->OverDriveTable.FanMinimumPwm);

  size += sysfs_emit_at(buf, size, "%s:\n""OD_RANGE");
  smu_v14_0_2_get_od_setting_limits(smu,
        PP_OD_FEATURE_FAN_MINIMUM_PWM,
        &min_value,
        &max_value);
  size += sysfs_emit_at(buf, size, "MINIMUM_PWM: %u %u\n",
          min_value, max_value);
  break;

 case SMU_OD_FAN_ZERO_RPM_ENABLE:
  if (!smu_v14_0_2_is_od_feature_supported(smu,
        PP_OD_FEATURE_ZERO_FAN_BIT))
   break;

  size += sysfs_emit_at(buf, size, "FAN_ZERO_RPM_ENABLE:\n");
  size += sysfs_emit_at(buf, size, "%d\n",
    (int)od_table->OverDriveTable.FanZeroRpmEnable);

  size += sysfs_emit_at(buf, size, "%s:\n""OD_RANGE");
  smu_v14_0_2_get_od_setting_limits(smu,
        PP_OD_FEATURE_FAN_ZERO_RPM_ENABLE,
        &min_value,
        &max_value);
  size += sysfs_emit_at(buf, size, "ZERO_RPM_ENABLE: %u %u\n",
          min_value, max_value);
  break;

 case SMU_OD_RANGE:
  if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFXCLK_BIT) &&
      !smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_UCLK_BIT) &&
      !smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFX_VF_CURVE_BIT))
   break;

  size += sysfs_emit_at(buf, size, "%s:\n""OD_RANGE");

  if (smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFXCLK_BIT)) {
   smu_v14_0_2_get_od_setting_limits(smu,
         PP_OD_FEATURE_GFXCLK_FMAX,
         &min_value,
         &max_value);
   size += sysfs_emit_at(buf, size, "SCLK_OFFSET: %7dMhz %10uMhz\n",
           min_value, max_value);
  }

  if (smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_UCLK_BIT)) {
   smu_v14_0_2_get_od_setting_limits(smu,
         PP_OD_FEATURE_UCLK_FMIN,
         &min_value,
         NULL);
   smu_v14_0_2_get_od_setting_limits(smu,
         PP_OD_FEATURE_UCLK_FMAX,
         NULL,
         &max_value);
   size += sysfs_emit_at(buf, size, "MCLK: %7uMhz %10uMhz\n",
           min_value, max_value);
  }

  if (smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFX_VF_CURVE_BIT)) {
   smu_v14_0_2_get_od_setting_limits(smu,
         PP_OD_FEATURE_GFX_VF_CURVE,
         &min_value,
         &max_value);
   size += sysfs_emit_at(buf, size, "VDDGFX_OFFSET: %7dmv %10dmv\n",
           min_value, max_value);
  }
  break;

 default:
  break;
 }

 return size;
}

static int smu_v14_0_2_force_clk_levels(struct smu_context *smu,
     enum smu_clk_type clk_type,
     uint32_t mask)
{
 struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
 struct smu_14_0_dpm_context *dpm_context = smu_dpm->dpm_context;
 struct smu_14_0_dpm_table *single_dpm_table;
 uint32_t soft_min_level, soft_max_level;
 uint32_t min_freq, max_freq;
 int ret = 0;

 soft_min_level = mask ? (ffs(mask) - 1) : 0;
 soft_max_level = mask ? (fls(mask) - 1) : 0;

 switch (clk_type) {
 case SMU_GFXCLK:
 case SMU_SCLK:
  single_dpm_table = &(dpm_context->dpm_tables.gfx_table);
  break;
 case SMU_MCLK:
 case SMU_UCLK:
  single_dpm_table = &(dpm_context->dpm_tables.uclk_table);
  break;
 case SMU_SOCCLK:
  single_dpm_table = &(dpm_context->dpm_tables.soc_table);
  break;
 case SMU_FCLK:
  single_dpm_table = &(dpm_context->dpm_tables.fclk_table);
  break;
 case SMU_VCLK:
 case SMU_VCLK1:
  single_dpm_table = &(dpm_context->dpm_tables.vclk_table);
  break;
 case SMU_DCLK:
 case SMU_DCLK1:
  single_dpm_table = &(dpm_context->dpm_tables.dclk_table);
  break;
 default:
  break;
 }

 switch (clk_type) {
 case SMU_GFXCLK:
 case SMU_SCLK:
 case SMU_MCLK:
 case SMU_UCLK:
 case SMU_SOCCLK:
 case SMU_FCLK:
 case SMU_VCLK:
 case SMU_VCLK1:
 case SMU_DCLK:
 case SMU_DCLK1:
  if (single_dpm_table->is_fine_grained) {
   /* There is only 2 levels for fine grained DPM */
   soft_max_level = (soft_max_level >= 1 ? 1 : 0);
   soft_min_level = (soft_min_level >= 1 ? 1 : 0);
  } else {
   if ((soft_max_level >= single_dpm_table->count) ||
       (soft_min_level >= single_dpm_table->count))
    return -EINVAL;
  }

  min_freq = single_dpm_table->dpm_levels[soft_min_level].value;
  max_freq = single_dpm_table->dpm_levels[soft_max_level].value;

  ret = smu_v14_0_set_soft_freq_limited_range(smu,
           clk_type,
           min_freq,
           max_freq,
           false);
  break;
 case SMU_DCEFCLK:
 case SMU_PCIE:
 default:
  break;
 }

 return ret;
}

static int smu_v14_0_2_update_pcie_parameters(struct smu_context *smu,
           uint8_t pcie_gen_cap,
           uint8_t pcie_width_cap)
{
 struct smu_14_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
 struct smu_14_0_pcie_table *pcie_table =
    &dpm_context->dpm_tables.pcie_table;
 int num_of_levels;
 uint32_t smu_pcie_arg;
 uint32_t link_level;
 struct smu_table_context *table_context = &smu->smu_table;
 PPTable_t *pptable = table_context->driver_pptable;
 SkuTable_t *skutable = &pptable->SkuTable;
 int ret = 0;
 int i;

 pcie_table->num_of_link_levels = 0;
 for (link_level = 0; link_level < NUM_LINK_LEVELS; link_level++) {
  if (!skutable->PcieGenSpeed[link_level] &&
      !skutable->PcieLaneCount[link_level] &&
      !skutable->LclkFreq[link_level])
   continue;

  pcie_table->pcie_gen[pcie_table->num_of_link_levels] =
     skutable->PcieGenSpeed[link_level];
  pcie_table->pcie_lane[pcie_table->num_of_link_levels] =
     skutable->PcieLaneCount[link_level];
  pcie_table->clk_freq[pcie_table->num_of_link_levels] =
     skutable->LclkFreq[link_level];
  pcie_table->num_of_link_levels++;
 }
 num_of_levels = pcie_table->num_of_link_levels;
 if (!num_of_levels)
  return 0;

 if (!(smu->adev->pm.pp_feature & PP_PCIE_DPM_MASK)) {
  if (pcie_table->pcie_gen[num_of_levels - 1] < pcie_gen_cap)
   pcie_gen_cap = pcie_table->pcie_gen[num_of_levels - 1];

  if (pcie_table->pcie_lane[num_of_levels - 1] < pcie_width_cap)
   pcie_width_cap = pcie_table->pcie_lane[num_of_levels - 1];

  /* Force all levels to use the same settings */
  for (i = 0; i < num_of_levels; i++) {
   pcie_table->pcie_gen[i] = pcie_gen_cap;
   pcie_table->pcie_lane[i] = pcie_width_cap;
   smu_pcie_arg = i << 16;
   smu_pcie_arg |= pcie_table->pcie_gen[i] << 8;
   smu_pcie_arg |= pcie_table->pcie_lane[i];

   ret = smu_cmn_send_smc_msg_with_param(smu,
            SMU_MSG_OverridePcieParameters,
            smu_pcie_arg,
            NULL);
   if (ret)
    break;
  }
 } else {
  for (i = 0; i < num_of_levels; i++) {
   if (pcie_table->pcie_gen[i] > pcie_gen_cap ||
    pcie_table->pcie_lane[i] > pcie_width_cap) {
    pcie_table->pcie_gen[i] = pcie_table->pcie_gen[i] > pcie_gen_cap ?
            pcie_gen_cap : pcie_table->pcie_gen[i];
    pcie_table->pcie_lane[i] = pcie_table->pcie_lane[i] > pcie_width_cap ?
             pcie_width_cap : pcie_table->pcie_lane[i];
    smu_pcie_arg = i << 16;
    smu_pcie_arg |= pcie_table->pcie_gen[i] << 8;
    smu_pcie_arg |= pcie_table->pcie_lane[i];

    ret = smu_cmn_send_smc_msg_with_param(smu,
            SMU_MSG_OverridePcieParameters,
            smu_pcie_arg,
            NULL);
    if (ret)
     break;
   }
  }
 }

 return ret;
}

static const struct smu_temperature_range smu14_thermal_policy[] = {
 {-273150,  99000, 99000, -273150, 99000, 99000, -273150, 99000, 99000},
 { 120000, 120000, 120000, 120000, 120000, 120000, 120000, 120000, 120000},
};

static int smu_v14_0_2_get_thermal_temperature_range(struct smu_context *smu,
           struct smu_temperature_range *range)
{
 struct smu_table_context *table_context = &smu->smu_table;
 struct smu_14_0_2_powerplay_table *powerplay_table =
  table_context->power_play_table;
 PPTable_t *pptable = smu->smu_table.driver_pptable;

 if (amdgpu_sriov_vf(smu->adev))
  return 0;

 if (!range)
  return -EINVAL;

 memcpy(range, &smu14_thermal_policy[0], sizeof(struct smu_temperature_range));

 range->max = pptable->CustomSkuTable.TemperatureLimit[TEMP_EDGE] *
  SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
 range->edge_emergency_max = (pptable->CustomSkuTable.TemperatureLimit[TEMP_EDGE] + CTF_OFFSET_EDGE) *
  SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
 range->hotspot_crit_max = pptable->CustomSkuTable.TemperatureLimit[TEMP_HOTSPOT] *
  SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
 range->hotspot_emergency_max = (pptable->CustomSkuTable.TemperatureLimit[TEMP_HOTSPOT] + CTF_OFFSET_HOTSPOT) *
  SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
 range->mem_crit_max = pptable->CustomSkuTable.TemperatureLimit[TEMP_MEM] *
  SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
 range->mem_emergency_max = (pptable->CustomSkuTable.TemperatureLimit[TEMP_MEM] + CTF_OFFSET_MEM)*
  SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
 range->software_shutdown_temp = powerplay_table->software_shutdown_temp;
 range->software_shutdown_temp_offset = pptable->CustomSkuTable.FanAbnormalTempLimitOffset;

 return 0;
}

static int smu_v14_0_2_populate_umd_state_clk(struct smu_context *smu)
{
 struct smu_14_0_dpm_context *dpm_context =
  smu->smu_dpm.dpm_context;
 struct smu_14_0_dpm_table *gfx_table =
  &dpm_context->dpm_tables.gfx_table;
 struct smu_14_0_dpm_table *mem_table =
  &dpm_context->dpm_tables.uclk_table;
 struct smu_14_0_dpm_table *soc_table =
  &dpm_context->dpm_tables.soc_table;
 struct smu_14_0_dpm_table *vclk_table =
  &dpm_context->dpm_tables.vclk_table;
 struct smu_14_0_dpm_table *dclk_table =
  &dpm_context->dpm_tables.dclk_table;
 struct smu_14_0_dpm_table *fclk_table =
  &dpm_context->dpm_tables.fclk_table;
 struct smu_umd_pstate_table *pstate_table =
  &smu->pstate_table;
 struct smu_table_context *table_context = &smu->smu_table;
 PPTable_t *pptable = table_context->driver_pptable;
 DriverReportedClocks_t driver_clocks =
   pptable->SkuTable.DriverReportedClocks;

 pstate_table->gfxclk_pstate.min = gfx_table->min;
 if (driver_clocks.GameClockAc &&
     (driver_clocks.GameClockAc < gfx_table->max))
  pstate_table->gfxclk_pstate.peak = driver_clocks.GameClockAc;
 else
  pstate_table->gfxclk_pstate.peak = gfx_table->max;

 pstate_table->uclk_pstate.min = mem_table->min;
 pstate_table->uclk_pstate.peak = mem_table->max;

 pstate_table->socclk_pstate.min = soc_table->min;
 pstate_table->socclk_pstate.peak = soc_table->max;

 pstate_table->vclk_pstate.min = vclk_table->min;
 pstate_table->vclk_pstate.peak = vclk_table->max;

 pstate_table->dclk_pstate.min = dclk_table->min;
 pstate_table->dclk_pstate.peak = dclk_table->max;

 pstate_table->fclk_pstate.min = fclk_table->min;
 pstate_table->fclk_pstate.peak = fclk_table->max;

 if (driver_clocks.BaseClockAc &&
     driver_clocks.BaseClockAc < gfx_table->max)
  pstate_table->gfxclk_pstate.standard = driver_clocks.BaseClockAc;
 else
  pstate_table->gfxclk_pstate.standard = gfx_table->max;
 pstate_table->uclk_pstate.standard = mem_table->max;
 pstate_table->socclk_pstate.standard = soc_table->min;
 pstate_table->vclk_pstate.standard = vclk_table->min;
 pstate_table->dclk_pstate.standard = dclk_table->min;
 pstate_table->fclk_pstate.standard = fclk_table->min;

 return 0;
}

static void smu_v14_0_2_get_unique_id(struct smu_context *smu)
{
 struct smu_table_context *smu_table = &smu->smu_table;
 SmuMetrics_t *metrics =
  &(((SmuMetricsExternal_t *)(smu_table->metrics_table))->SmuMetrics);
 struct amdgpu_device *adev = smu->adev;
 uint32_t upper32 = 0, lower32 = 0;
 int ret;

 ret = smu_cmn_get_metrics_table(smu, NULL, false);
 if (ret)
  goto out;

 upper32 = metrics->PublicSerialNumberUpper;
 lower32 = metrics->PublicSerialNumberLower;

out:
 adev->unique_id = ((uint64_t)upper32 << 32) | lower32;
}

static int smu_v14_0_2_get_fan_speed_pwm(struct smu_context *smu,
      uint32_t *speed)
{
 int ret;

 if (!speed)
  return -EINVAL;

 ret = smu_v14_0_2_get_smu_metrics_data(smu,
            METRICS_CURR_FANPWM,
            speed);
 if (ret) {
  dev_err(smu->adev->dev, "Failed to get fan speed(PWM)!");
  return ret;
 }

 /* Convert the PMFW output which is in percent to pwm(255) based */
 *speed = min(*speed * 255 / 100, (uint32_t)255);

 return 0;
}

static int smu_v14_0_2_get_fan_speed_rpm(struct smu_context *smu,
      uint32_t *speed)
{
 if (!speed)
  return -EINVAL;

 return smu_v14_0_2_get_smu_metrics_data(smu,
      METRICS_CURR_FANSPEED,
      speed);
}

static int smu_v14_0_2_get_power_limit(struct smu_context *smu,
           uint32_t *current_power_limit,
           uint32_t *default_power_limit,
           uint32_t *max_power_limit,
           uint32_t *min_power_limit)
{
 struct smu_table_context *table_context = &smu->smu_table;
 struct smu_14_0_2_powerplay_table *powerplay_table =
  table_context->power_play_table;
 PPTable_t *pptable = table_context->driver_pptable;
 CustomSkuTable_t *skutable = &pptable->CustomSkuTable;
 uint32_t power_limit, od_percent_upper = 0, od_percent_lower = 0;
 uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC];

 if (smu_v14_0_get_current_power_limit(smu, &power_limit))
  power_limit = smu->adev->pm.ac_power ?
         skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] :
         skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0];

 if (current_power_limit)
  *current_power_limit = power_limit;
 if (default_power_limit)
  *default_power_limit = power_limit;

 if (powerplay_table) {
  if (smu->od_enabled &&
      smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_PPT_BIT)) {
   od_percent_upper = pptable->SkuTable.OverDriveLimitsBasicMax.Ppt;
   od_percent_lower = pptable->SkuTable.OverDriveLimitsBasicMin.Ppt;
  } else if (smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_PPT_BIT)) {
   od_percent_upper = 0;
   od_percent_lower = pptable->SkuTable.OverDriveLimitsBasicMin.Ppt;
  }
 }

 dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n",
     od_percent_upper, od_percent_lower, power_limit);

 if (max_power_limit) {
  *max_power_limit = msg_limit * (100 + od_percent_upper);
  *max_power_limit /= 100;
 }

 if (min_power_limit) {
  *min_power_limit = power_limit * (100 + od_percent_lower);
  *min_power_limit /= 100;
 }

 return 0;
}

static int smu_v14_0_2_get_power_profile_mode(struct smu_context *smu,
           char *buf)
{
 DpmActivityMonitorCoeffIntExternal_t activity_monitor_external;
 DpmActivityMonitorCoeffInt_t *activity_monitor =
  &(activity_monitor_external.DpmActivityMonitorCoeffInt);
 static const char *title[] = {
   "PROFILE_INDEX(NAME)",
   "CLOCK_TYPE(NAME)",
   "FPS",
   "MinActiveFreqType",
   "MinActiveFreq",
   "BoosterFreqType",
   "BoosterFreq",
   "PD_Data_limit_c",
   "PD_Data_error_coeff",
   "PD_Data_error_rate_coeff"};
 int16_t workload_type = 0;
 uint32_t i, size = 0;
 int result = 0;

 if (!buf)
  return -EINVAL;

 size += sysfs_emit_at(buf, size, "%16s %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]);

 for (i = 0; i < PP_SMC_POWER_PROFILE_COUNT; i++) {
  /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
  workload_type = smu_cmn_to_asic_specific_index(smu,
              CMN2ASIC_MAPPING_WORKLOAD,
              i);
  if (workload_type == -ENOTSUPP)
   continue;
  else if (workload_type < 0)
   return -EINVAL;

  result = smu_cmn_update_table(smu,
           SMU_TABLE_ACTIVITY_MONITOR_COEFF,
           workload_type,
           (void *)(&activity_monitor_external),
           false);
  if (result) {
   dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__);
   return result;
  }

  size += sysfs_emit_at(buf, size, "%2d %14s%s:\n",
   i, amdgpu_pp_profile_name[i], (i == smu->power_profile_mode) ? "*" : " ");

  size += sysfs_emit_at(buf, size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d\n",
   " ",
   0,
   "GFXCLK",
   activity_monitor->Gfx_FPS,
   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 += sysfs_emit_at(buf, size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d\n",
   " ",
   1,
   "FCLK",
   activity_monitor->Fclk_FPS,
   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;
}

#define SMU_14_0_2_CUSTOM_PARAMS_COUNT 9
#define SMU_14_0_2_CUSTOM_PARAMS_CLOCK_COUNT 2
#define SMU_14_0_2_CUSTOM_PARAMS_SIZE (SMU_14_0_2_CUSTOM_PARAMS_CLOCK_COUNT * SMU_14_0_2_CUSTOM_PARAMS_COUNT * sizeof(long))

static int smu_v14_0_2_set_power_profile_mode_coeff(struct smu_context *smu,
          long *input)
{
 DpmActivityMonitorCoeffIntExternal_t activity_monitor_external;
 DpmActivityMonitorCoeffInt_t *activity_monitor =
  &(activity_monitor_external.DpmActivityMonitorCoeffInt);
 int ret, idx;

 ret = smu_cmn_update_table(smu,
       SMU_TABLE_ACTIVITY_MONITOR_COEFF,
       WORKLOAD_PPLIB_CUSTOM_BIT,
       (void *)(&activity_monitor_external),
       false);
 if (ret) {
  dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__);
  return ret;
 }

 idx = 0 * SMU_14_0_2_CUSTOM_PARAMS_COUNT;
 if (input[idx]) {
  /* Gfxclk */
  activity_monitor->Gfx_FPS = input[idx + 1];
  activity_monitor->Gfx_MinActiveFreqType = input[idx + 2];
  activity_monitor->Gfx_MinActiveFreq = input[idx + 3];
  activity_monitor->Gfx_BoosterFreqType = input[idx + 4];
  activity_monitor->Gfx_BoosterFreq = input[idx + 5];
  activity_monitor->Gfx_PD_Data_limit_c = input[idx + 6];
  activity_monitor->Gfx_PD_Data_error_coeff = input[idx + 7];
  activity_monitor->Gfx_PD_Data_error_rate_coeff = input[idx + 8];
 }
 idx = 1 * SMU_14_0_2_CUSTOM_PARAMS_COUNT;
 if (input[idx]) {
  /* Fclk */
  activity_monitor->Fclk_FPS = input[idx + 1];
  activity_monitor->Fclk_MinActiveFreqType = input[idx + 2];
  activity_monitor->Fclk_MinActiveFreq = input[idx + 3];
  activity_monitor->Fclk_BoosterFreqType = input[idx + 4];
  activity_monitor->Fclk_BoosterFreq = input[idx + 5];
  activity_monitor->Fclk_PD_Data_limit_c = input[idx + 6];
  activity_monitor->Fclk_PD_Data_error_coeff = input[idx + 7];
  activity_monitor->Fclk_PD_Data_error_rate_coeff = input[idx + 8];
 }

 ret = smu_cmn_update_table(smu,
       SMU_TABLE_ACTIVITY_MONITOR_COEFF,
       WORKLOAD_PPLIB_CUSTOM_BIT,
       (void *)(&activity_monitor_external),
       true);
 if (ret) {
  dev_err(smu->adev->dev, "[%s] Failed to set activity monitor!", __func__);
  return ret;
 }

 return ret;
}

static int smu_v14_0_2_set_power_profile_mode(struct smu_context *smu,
           u32 workload_mask,
           long *custom_params,
           u32 custom_params_max_idx)
{
 u32 backend_workload_mask = 0;
 int ret, idx = -1, i;

 smu_cmn_get_backend_workload_mask(smu, workload_mask,
       &backend_workload_mask);

 /* disable deep sleep if compute is enabled */
 if (workload_mask & (1 << PP_SMC_POWER_PROFILE_COMPUTE))
  smu_v14_0_deep_sleep_control(smu, false);
 else
  smu_v14_0_deep_sleep_control(smu, true);

 if (workload_mask & (1 << PP_SMC_POWER_PROFILE_CUSTOM)) {
  if (!smu->custom_profile_params) {
   smu->custom_profile_params =
    kzalloc(SMU_14_0_2_CUSTOM_PARAMS_SIZE, GFP_KERNEL);
   if (!smu->custom_profile_params)
    return -ENOMEM;
  }
  if (custom_params && custom_params_max_idx) {
   if (custom_params_max_idx != SMU_14_0_2_CUSTOM_PARAMS_COUNT)
    return -EINVAL;
   if (custom_params[0] >= SMU_14_0_2_CUSTOM_PARAMS_CLOCK_COUNT)
    return -EINVAL;
   idx = custom_params[0] * SMU_14_0_2_CUSTOM_PARAMS_COUNT;
   smu->custom_profile_params[idx] = 1;
   for (i = 1; i < custom_params_max_idx; i++)
    smu->custom_profile_params[idx + i] = custom_params[i];
  }
  ret = smu_v14_0_2_set_power_profile_mode_coeff(smu,
              smu->custom_profile_params);
  if (ret) {
   if (idx != -1)
    smu->custom_profile_params[idx] = 0;
   return ret;
  }
 } else if (smu->custom_profile_params) {
  memset(smu->custom_profile_params, 0, SMU_14_0_2_CUSTOM_PARAMS_SIZE);
 }

 ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetWorkloadMask,
           backend_workload_mask, NULL);
 if (ret) {
  dev_err(smu->adev->dev, "Failed to set workload mask 0x%08x\n",
   workload_mask);
  if (idx != -1)
   smu->custom_profile_params[idx] = 0;
  return ret;
 }

 return ret;
}

static int smu_v14_0_2_baco_enter(struct smu_context *smu)
{
 struct smu_baco_context *smu_baco = &smu->smu_baco;
 struct amdgpu_device *adev = smu->adev;

 if (adev->in_runpm && smu_cmn_is_audio_func_enabled(adev))
  return smu_v14_0_baco_set_armd3_sequence(smu,
    smu_baco->maco_support ? BACO_SEQ_BAMACO : BACO_SEQ_BACO);
 else
  return smu_v14_0_baco_enter(smu);
}

static int smu_v14_0_2_baco_exit(struct smu_context *smu)
{
 struct amdgpu_device *adev = smu->adev;

 if (adev->in_runpm && smu_cmn_is_audio_func_enabled(adev)) {
  /* Wait for PMFW handling for the Dstate change */
  usleep_range(10000, 11000);
  return smu_v14_0_baco_set_armd3_sequence(smu, BACO_SEQ_ULPS);
 } else {
  return smu_v14_0_baco_exit(smu);
 }
}

static bool smu_v14_0_2_is_mode1_reset_supported(struct smu_context *smu)
{
 // TODO

 return true;
}

static int smu_v14_0_2_i2c_xfer(struct i2c_adapter *i2c_adap,
       struct i2c_msg *msg, int num_msgs)
{
 struct amdgpu_smu_i2c_bus *smu_i2c = i2c_get_adapdata(i2c_adap);
 struct amdgpu_device *adev = smu_i2c->adev;
 struct smu_context *smu = adev->powerplay.pp_handle;
 struct smu_table_context *smu_table = &smu->smu_table;
 struct smu_table *table = &smu_table->driver_table;
 SwI2cRequest_t *req, *res = (SwI2cRequest_t *)table->cpu_addr;
 int i, j, r, c;
 u16 dir;

 if (!adev->pm.dpm_enabled)
  return -EBUSY;

 req = kzalloc(sizeof(*req), GFP_KERNEL);
 if (!req)
  return -ENOMEM;

 req->I2CcontrollerPort = smu_i2c->port;
 req->I2CSpeed = I2C_SPEED_FAST_400K;
 req->SlaveAddress = msg[0].addr << 1; /* wants an 8-bit address */
 dir = msg[0].flags & I2C_M_RD;

 for (c = i = 0; i < num_msgs; i++) {
  for (j = 0; j < msg[i].len; j++, c++) {
   SwI2cCmd_t *cmd = &req->SwI2cCmds[c];

   if (!(msg[i].flags & I2C_M_RD)) {
    /* write */
    cmd->CmdConfig |= CMDCONFIG_READWRITE_MASK;
    cmd->ReadWriteData = msg[i].buf[j];
   }

   if ((dir ^ msg[i].flags) & I2C_M_RD) {
    /* The direction changes.
 */

    dir = msg[i].flags & I2C_M_RD;
    cmd->CmdConfig |= CMDCONFIG_RESTART_MASK;
   }

   req->NumCmds++;

   /*
 * Insert STOP if we are at the last byte of either last
 * message for the transaction or the client explicitly
 * requires a STOP at this particular message.
 */

   if ((j == msg[i].len - 1) &&
       ((i == num_msgs - 1) || (msg[i].flags & I2C_M_STOP))) {
    cmd->CmdConfig &= ~CMDCONFIG_RESTART_MASK;
    cmd->CmdConfig |= CMDCONFIG_STOP_MASK;
   }
  }
 }
 mutex_lock(&adev->pm.mutex);
 r = smu_cmn_update_table(smu, SMU_TABLE_I2C_COMMANDS, 0, req, true);
 mutex_unlock(&adev->pm.mutex);
 if (r)
  goto fail;

 for (c = i = 0; i < num_msgs; i++) {
  if (!(msg[i].flags & I2C_M_RD)) {
   c += msg[i].len;
   continue;
  }
  for (j = 0; j < msg[i].len; j++, c++) {
   SwI2cCmd_t *cmd = &res->SwI2cCmds[c];

   msg[i].buf[j] = cmd->ReadWriteData;
  }
 }
 r = num_msgs;
fail:
 kfree(req);
 return r;
}

static u32 smu_v14_0_2_i2c_func(struct i2c_adapter *adap)
{
 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}

static const struct i2c_algorithm smu_v14_0_2_i2c_algo = {
 .master_xfer = smu_v14_0_2_i2c_xfer,
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=100 H=97 G=98

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.