/* * 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/module.h> #include <linux/pci.h> #include <linux/slab.h> #include <asm/div64.h> #if IS_ENABLED(CONFIG_X86_64) #include <asm/intel-family.h> #endif #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"
/** * smu7_get_mc_microcode_version - Find the MC microcode version and store it in the HwMgr struct * * @hwmgr: the address of the powerplay hardware manager. * Return: always 0
*/ staticint smu7_get_mc_microcode_version(struct pp_hwmgr *hwmgr)
{
cgs_write_register(hwmgr->device, mmMC_SEQ_IO_DEBUG_INDEX, 0x9F);
PP_ASSERT_WITH_CODE((7 >= link_width), "Invalid PCIe lane width!", return 0);
return decode_pcie_lane_width(link_width);
}
/** * smu7_enable_smc_voltage_controller - Enable voltage control * * @hwmgr: the address of the powerplay hardware manager. * Return: always PP_Result_OK
*/ staticint smu7_enable_smc_voltage_controller(struct pp_hwmgr *hwmgr)
{ if (hwmgr->chip_id >= CHIP_POLARIS10 &&
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;
}
/** * smu7_voltage_control - Checks if we want to support voltage control * * @hwmgr: the address of the powerplay hardware manager.
*/ staticbool smu7_voltage_control(conststruct pp_hwmgr *hwmgr)
{ conststruct smu7_hwmgr *data =
(conststruct smu7_hwmgr *)(hwmgr->backend);
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;
}
/** * smu7_construct_voltage_tables - Create Voltage Tables. * * @hwmgr: the address of the powerplay hardware manager. * Return: always 0
*/ staticint 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);
} elseif (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); elseif (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);
} elseif (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); elseif (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;);
} elseif (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); elseif (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)));
/** * smu7_program_voting_clients - Programs activity state transition voting clients * * @hwmgr: the address of the powerplay hardware manager. * Return: always 0
*/ staticint 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;
}
staticint smu7_clear_voting_clients(struct pp_hwmgr *hwmgr)
{ int i;
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.
*/ staticint 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;
/** * smu7_initial_switch_from_arbf0_to_f1 - Initial switch from ARB F0->F1 * * @hwmgr: the address of the powerplay hardware manager. * Return: always 0 * This function is to be called from the SetPowerState table.
*/ staticint 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);
}
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));
if (data->pcie_dpm_key_disabled)
phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table,
data->dpm_table.pcie_speed_table.count,
smu7_override_pcie_speed(hwmgr), smu7_override_pcie_width(hwmgr));
} return 0;
}
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.
*/
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;
}
/* 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) {
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;
}
}
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);
} else {
PP_ASSERT_WITH_CODE(
(0 == smum_send_msg_to_smc(hwmgr,
PPSMC_MSG_PCIeDPM_Disable,
NULL)), "Failed to disable 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!",
);
}
/* make sure the output is in Mhz */
hwmgr->pstate_sclk /= 100;
hwmgr->pstate_mclk /= 100;
hwmgr->pstate_sclk_peak /= 100;
hwmgr->pstate_mclk_peak /= 100;
}
staticint 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);
if (hwmgr->chip_id >= CHIP_POLARIS10 &&
hwmgr->chip_id <= CHIP_VEGAM) {
tmp_result = smu7_notify_has_display(hwmgr);
PP_ASSERT_WITH_CODE((0 == tmp_result), "Failed to enable display setting!", result = tmp_result);
} else {
smum_send_msg_to_smc(hwmgr, (PPSMC_Msg)PPSMC_NoDisplay, NULL);
}
if (hwmgr->chip_id >= CHIP_POLARIS10 &&
hwmgr->chip_id <= CHIP_VEGAM) {
tmp_result = smu7_populate_edc_leakage_registers(hwmgr);
PP_ASSERT_WITH_CODE((0 == tmp_result), "Failed to populate edc leakage registers!", result = tmp_result);
}
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);
staticint 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);
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);
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;
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;
}
/** * smu7_patch_ppt_v1_with_vdd_leakage - Change virtual leakage voltage to actual value. * * @hwmgr: the address of the powerplay hardware manager. * @voltage: pointer to changing voltage * @leakage_table: pointer to leakage table
*/ staticvoid 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_info("Voltage value looks like a Leakage ID but it's not patched\n");
}
/** * smu7_patch_lookup_table_with_leakage - Patch voltage lookup table by EVV leakages. * * @hwmgr: the address of the powerplay hardware manager. * @lookup_table: pointer to voltage lookup table * @leakage_table: pointer to leakage table * Return: always 0
*/ staticint 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);
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++;
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);
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;
}
/** * smu7_patch_ppt_v0_with_vdd_leakage - Change virtual leakage voltage to actual value. * * @hwmgr: the address of the powerplay hardware manager. * @voltage: pointer to changing voltage * @leakage_table: pointer to leakage table
*/ staticvoid 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_info("Voltage value looks like a Leakage ID but it's not patched\n");
}
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);
/* 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");
kfree(hwmgr->backend);
hwmgr->backend = NULL; return -EINVAL;
}
} else {
smu7_get_elb_voltages(hwmgr);
}
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);
result = smu7_update_edc_leakage_table(hwmgr); if (result) goto fail;
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);
}
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;
}
/* 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;
}
}
if (disable_mclk_switching) { if (mclk < smu7_ps->performance_levels[1].memory_clock)
mclk = smu7_ps->performance_levels[1].memory_clock;
if (hwmgr->chip_id >= CHIP_POLARIS10 && hwmgr->chip_id <= CHIP_VEGAM) { if (disable_mclk_switching_for_display) { /* 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) {
latency_allowed = true;
/* 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;
/* 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 */
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;
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;
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;
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;
/* 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;
}
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
}
staticint smu7_notify_smc_display(struct pp_hwmgr *hwmgr)
{ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend); int result = 0;
if (data->mclk_ignore_signal)
result = smu7_notify_no_display(hwmgr); else
result = smu7_notify_has_display(hwmgr);
return result;
}
staticint smu7_set_power_state_tasks(struct pp_hwmgr *hwmgr, constvoid *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_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);
tmp_result = smu7_notify_smc_display(hwmgr);
PP_ASSERT_WITH_CODE((0 == tmp_result), "Failed to notify smc display settings!",
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;
}
/** * smu7_set_max_fan_rpm_output - Set maximum target operating fan output RPM * * @hwmgr: the address of the powerplay hardware manager. * @us_max_fan_rpm: max operating fan RPM value. * Return: The response that came from the SMC.
*/ staticint 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;
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));
staticint 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);
switch (type) { case PP_SCLK:
ret = smum_send_msg_to_smc(hwmgr, PPSMC_MSG_API_GetSclkFrequency, &clock); if (ret) return ret; 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:
ret = smum_send_msg_to_smc(hwmgr, PPSMC_MSG_API_GetMclkFrequency, &clock); if (ret) return ret; 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 + size, "%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 + size, "%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 + size, "%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;
}
staticvoid 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_pwm(hwmgr, 255); 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;
}
}
if (!(hwmgr->chip_id >= CHIP_POLARIS10 &&
hwmgr->chip_id <= CHIP_VEGAM)) return -EINVAL;
for (i = 0; i < dep_mclk_table->count; i++) { for (j = 0; j < dep_sclk_table->count; j++) {
valid_entry = false; for (k = 0; k < watermarks->num_wm_sets; k++) { if (dep_sclk_table->entries[i].clk >= watermarks->wm_clk_ranges[k].wm_min_eng_clk_in_khz / 10 &&
dep_sclk_table->entries[i].clk < watermarks->wm_clk_ranges[k].wm_max_eng_clk_in_khz / 10 &&
dep_mclk_table->entries[i].clk >= watermarks->wm_clk_ranges[k].wm_min_mem_clk_in_khz / 10 &&
dep_mclk_table->entries[i].clk < watermarks->wm_clk_ranges[k].wm_max_mem_clk_in_khz / 10) {
valid_entry = true;
table->DisplayWatermark[i][j] = watermarks->wm_clk_ranges[k].wm_set_id; break;
}
}
PP_ASSERT_WITH_CODE(valid_entry, "Clock is not in range of specified clock range for watermark from DAL! Using highest water mark set.",
table->DisplayWatermark[i][j] = watermarks->wm_clk_ranges[k - 1].wm_set_id);
}
}
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); returnfalse;
}
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); returnfalse;
}
} elseif (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); returnfalse;
}
} else { returnfalse;
}
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;
¤ 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.0.101Bemerkung:
(vorverarbeitet am 2026-04-29)
¤
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.