/* * Copyright 2015 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. *
*/ #include"pp_debug.h" #include <linux/types.h> #include <linux/kernel.h> #include <linux/pci.h> #include <linux/slab.h> #include <linux/gfp.h>
/* Only start SMC if SMC RAM is not running */ if (!smu7_is_smc_ram_running(hwmgr) && hwmgr->not_vf) { /*Check if SMU is running in protected mode*/ if (0 == PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
SMU_FIRMWARE, SMU_MODE)) {
result = tonga_start_in_non_protection_mode(hwmgr); if (result) return result;
} else {
result = tonga_start_in_protection_mode(hwmgr); if (result) return result;
}
}
/* Setup SoftRegsStart here to visit the register UcodeLoadStatus * to check fw loading state
*/
smu7_read_smc_sram_dword(hwmgr,
SMU72_FIRMWARE_HEADER_LOCATION +
offsetof(SMU72_Firmware_Header, SoftRegisters),
&(priv->smu7_data.soft_regs_start), 0x40000);
if (allowed_clock_voltage_table->entries[i].mvdd)
*mvdd = (uint32_t) allowed_clock_voltage_table->entries[i].mvdd;
voltage->Phases = 1; return 0;
}
}
/* sclk is bigger than max sclk in the dependence table */
voltage->VddGfx = phm_get_voltage_index(pptable_info->vddgfx_lookup_table,
allowed_clock_voltage_table->entries[i-1].vddgfx);
voltage->Vddc = phm_get_voltage_index(pptable_info->vddc_lookup_table,
allowed_clock_voltage_table->entries[i-1].vddc);
if (allowed_clock_voltage_table->entries[i-1].vddci)
voltage->Vddci = phm_get_voltage_id(&data->vddci_voltage_table,
allowed_clock_voltage_table->entries[i-1].vddci);
if (allowed_clock_voltage_table->entries[i-1].mvdd)
*mvdd = (uint32_t) allowed_clock_voltage_table->entries[i-1].mvdd;
/* table is already swapped, so in order to use the value from it * we need to swap it back.
*/
uint32_t vddc_level_count = PP_SMC_TO_HOST_UL(table->VddcLevelCount);
uint32_t vddgfx_level_count = PP_SMC_TO_HOST_UL(table->VddGfxLevelCount);
for (count = 0; count < vddc_level_count; count++) { /* We are populating vddc CAC data to BapmVddc table in split and merged mode */
index = phm_get_voltage_index(vddc_lookup_table,
data->vddc_voltage_table.entries[count].value);
table->BapmVddcVidLoSidd[count] =
convert_to_vid(vddc_lookup_table->entries[index].us_cac_low);
table->BapmVddcVidHiSidd[count] =
convert_to_vid(vddc_lookup_table->entries[index].us_cac_mid);
table->BapmVddcVidHiSidd2[count] =
convert_to_vid(vddc_lookup_table->entries[index].us_cac_high);
}
if (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) { /* We are populating vddgfx CAC data to BapmVddgfx table in split mode */ for (count = 0; count < vddgfx_level_count; count++) {
index = phm_get_voltage_index(vddgfx_lookup_table,
convert_to_vid(vddgfx_lookup_table->entries[index].us_cac_mid));
table->BapmVddGfxVidHiSidd2[count] =
convert_to_vid(vddgfx_lookup_table->entries[index].us_cac_high);
}
} else { for (count = 0; count < vddc_level_count; count++) {
index = phm_get_voltage_index(vddc_lookup_table,
data->vddc_voltage_table.entries[count].value);
table->BapmVddGfxVidLoSidd[count] =
convert_to_vid(vddc_lookup_table->entries[index].us_cac_low);
table->BapmVddGfxVidHiSidd[count] =
convert_to_vid(vddc_lookup_table->entries[index].us_cac_mid);
table->BapmVddGfxVidHiSidd2[count] =
convert_to_vid(vddc_lookup_table->entries[index].us_cac_high);
}
}
return 0;
}
staticint tonga_populate_smc_voltage_tables(struct pp_hwmgr *hwmgr,
SMU72_Discrete_DpmTable *table)
{ int result;
result = tonga_populate_smc_vddc_table(hwmgr, table);
PP_ASSERT_WITH_CODE(!result, "can not populate VDDC voltage table to SMC", return -EINVAL);
result = tonga_populate_smc_vdd_ci_table(hwmgr, table);
PP_ASSERT_WITH_CODE(!result, "can not populate VDDCI voltage table to SMC", return -EINVAL);
result = tonga_populate_smc_vdd_gfx_table(hwmgr, table);
PP_ASSERT_WITH_CODE(!result, "can not populate VDDGFX voltage table to SMC", return -EINVAL);
result = tonga_populate_smc_mvdd_table(hwmgr, table);
PP_ASSERT_WITH_CODE(!result, "can not populate MVDD voltage table to SMC", return -EINVAL);
result = tonga_populate_cac_tables(hwmgr, table);
PP_ASSERT_WITH_CODE(!result, "can not populate CAC voltage tables to SMC", return -EINVAL);
result = tonga_calculate_sclk_params(hwmgr, engine_clock, graphic_level);
if (hwmgr->od_enabled)
vdd_dep_table = (phm_ppt_v1_clock_voltage_dependency_table *)&data->odn_dpm_table.vdd_dependency_on_sclk; else
vdd_dep_table = pptable_info->vdd_dep_on_sclk;
/* populate graphics levels*/
result = tonga_get_dependency_volt_by_clk(hwmgr,
vdd_dep_table, engine_clock,
&graphic_level->MinVoltage, &mvdd);
PP_ASSERT_WITH_CODE((!result), "can not find VDDC voltage value for VDDC " "engine clock dependency table", return result);
/* SCLK frequency in units of 10KHz*/
graphic_level->SclkFrequency = engine_clock; /* Indicates maximum activity level for this performance level. 50% for now*/
graphic_level->ActivityLevel = data->current_profile_setting.sclk_activity;
graphic_level->CcPwrDynRm = 0;
graphic_level->CcPwrDynRm1 = 0; /* this level can be used if activity is high enough.*/
graphic_level->EnabledForActivity = 0; /* this level can be used for throttling.*/
graphic_level->EnabledForThrottle = 1;
graphic_level->UpHyst = data->current_profile_setting.sclk_up_hyst;
graphic_level->DownHyst = data->current_profile_setting.sclk_down_hyst;
graphic_level->VoltageDownHyst = 0;
graphic_level->PowerThrottle = 0;
if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
PHM_PlatformCaps_SclkDeepSleep))
graphic_level->DeepSleepDivId =
smu7_get_sleep_divider_id_from_clock(engine_clock,
data->display_timing.min_clock_in_sr);
/* Default to slow, highest DPM level will be set to PPSMC_DISPLAY_WATERMARK_LOW later.*/
graphic_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
uint32_t i, max_entry;
uint8_t highest_pcie_level_enabled = 0;
uint8_t lowest_pcie_level_enabled = 0, mid_pcie_level_enabled = 0;
uint8_t count = 0; int result = 0;
memset(levels, 0x00, level_array_size);
for (i = 0; i < dpm_table->sclk_table.count; i++) {
result = tonga_populate_single_graphic_level(hwmgr,
dpm_table->sclk_table.dpm_levels[i].value,
&(smu_data->smc_state_table.GraphicsLevel[i])); if (result != 0) return result;
/* Making sure only DPM level 0-1 have Deep Sleep Div ID populated. */ if (i > 1)
smu_data->smc_state_table.GraphicsLevel[i].DeepSleepDivId = 0;
}
/* Only enable level 0 for now. */
smu_data->smc_state_table.GraphicsLevel[0].EnabledForActivity = 1;
/* set highest level watermark to high */ if (dpm_table->sclk_table.count > 1)
smu_data->smc_state_table.GraphicsLevel[dpm_table->sclk_table.count-1].DisplayWatermark =
PPSMC_DISPLAY_WATERMARK_HIGH;
if (pcie_table != NULL) {
PP_ASSERT_WITH_CODE((pcie_entry_count >= 1), "There must be 1 or more PCIE levels defined in PPTable.", return -EINVAL);
max_entry = pcie_entry_count - 1; /* for indexing, we need to decrement by 1.*/ for (i = 0; i < dpm_table->sclk_table.count; i++) {
smu_data->smc_state_table.GraphicsLevel[i].pcieDpmLevel =
(uint8_t) ((i < max_entry) ? i : max_entry);
}
} else { if (0 == data->dpm_level_enable_mask.pcie_dpm_enable_mask)
pr_err("Pcie Dpm Enablemask is 0 !");
/* set pcieDpmLevel to highest_pcie_level_enabled*/ for (i = 2; i < dpm_table->sclk_table.count; i++)
smu_data->smc_state_table.GraphicsLevel[i].pcieDpmLevel = highest_pcie_level_enabled;
/* set pcieDpmLevel to lowest_pcie_level_enabled*/
smu_data->smc_state_table.GraphicsLevel[0].pcieDpmLevel = lowest_pcie_level_enabled;
/* set pcieDpmLevel to mid_pcie_level_enabled*/
smu_data->smc_state_table.GraphicsLevel[1].pcieDpmLevel = mid_pcie_level_enabled;
} /* level count will send to smc once at init smc table and never change*/
result = smu7_copy_bytes_to_smc(hwmgr, level_array_address,
(uint8_t *)levels, (uint32_t)level_array_size,
SMC_RAM_END);
if (hwmgr->od_enabled)
vdd_dep_table = (phm_ppt_v1_clock_voltage_dependency_table *)&data->odn_dpm_table.vdd_dependency_on_mclk; else
vdd_dep_table = pptable_info->vdd_dep_on_mclk;
if (NULL != vdd_dep_table) {
result = tonga_get_dependency_volt_by_clk(hwmgr,
vdd_dep_table,
memory_clock,
&memory_level->MinVoltage, &mvdd);
PP_ASSERT_WITH_CODE(
!result, "can not find MinVddc voltage value from memory VDDC " "voltage dependency table", return result);
}
/* decide EDC mode and memory clock ratio*/ if (data->is_memory_gddr5) {
memory_level->StrobeRatio = tonga_get_mclk_frequency_ratio(memory_clock,
memory_level->StrobeEnable);
result = tonga_calculate_mclk_params(hwmgr,
memory_clock, memory_level, memory_level->StrobeEnable, dll_state_on);
if (!result) {
CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MinMvdd); /* MCLK frequency in units of 10KHz*/
CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MclkFrequency); /* Indicates maximum activity level for this performance level.*/
CONVERT_FROM_HOST_TO_SMC_US(memory_level->ActivityLevel);
CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl);
CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl_1);
CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl_2);
CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllAdFuncCntl);
CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllDqFuncCntl);
CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MclkPwrmgtCntl);
CONVERT_FROM_HOST_TO_SMC_UL(memory_level->DllCntl);
CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllSs1);
CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllSs2);
}
for (i = 0; i < dpm_table->mclk_table.count; i++) {
PP_ASSERT_WITH_CODE((0 != dpm_table->mclk_table.dpm_levels[i].value), "can not populate memory level as memory clock is zero", return -EINVAL);
result = tonga_populate_single_memory_level(
hwmgr,
dpm_table->mclk_table.dpm_levels[i].value,
&(smu_data->smc_state_table.MemoryLevel[i])); if (result) return result;
}
/* Only enable level 0 for now.*/
smu_data->smc_state_table.MemoryLevel[0].EnabledForActivity = 1;
/* * in order to prevent MC activity from stutter mode to push DPM up. * the UVD change complements this by putting the MCLK in a higher state * by default such that we are not effected by up threshold or and MCLK DPM latency.
*/
smu_data->smc_state_table.MemoryLevel[0].ActivityLevel = 0x1F;
CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.MemoryLevel[0].ActivityLevel);
smu_data->smc_state_table.MemoryDpmLevelCount = (uint8_t)dpm_table->mclk_table.count;
data->dpm_level_enable_mask.mclk_dpm_enable_mask = phm_get_dpm_level_enable_mask_value(&dpm_table->mclk_table); /* set highest level watermark to high*/
smu_data->smc_state_table.MemoryLevel[dpm_table->mclk_table.count-1].DisplayWatermark = PPSMC_DISPLAY_WATERMARK_HIGH;
/* level count will send to smc once at init smc table and never change*/
result = smu7_copy_bytes_to_smc(hwmgr,
level_array_address, (uint8_t *)levels, (uint32_t)level_array_size,
SMC_RAM_END);
if (SMU7_VOLTAGE_CONTROL_NONE != data->mvdd_control) { /* find mvdd value which clock is more than request */ for (i = 0; i < table_info->vdd_dep_on_mclk->count; i++) { if (mclk <= table_info->vdd_dep_on_mclk->entries[i].clk) { /* Always round to higher voltage. */
smio_pattern->Voltage =
data->mvdd_voltage_table.entries[i].value; break;
}
}
PP_ASSERT_WITH_CODE(i < table_info->vdd_dep_on_mclk->count, "MVDD Voltage is outside the supported range.", return -EINVAL);
} else { return -EINVAL;
}
/* assign zero for now*/
table->ACPILevel.SclkFrequency = atomctrl_get_reference_clock(hwmgr);
/* get the engine clock dividers for this clock value*/
result = atomctrl_get_engine_pll_dividers_vi(hwmgr,
table->ACPILevel.SclkFrequency, ÷rs);
/* For various features to be enabled/disabled while this level is active.*/
CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.Flags); /* SCLK frequency in units of 10KHz*/
CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SclkFrequency);
CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl);
CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl2);
CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl3);
CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl4);
CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum);
CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum2);
CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm);
CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm1);
result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
table->UvdLevel[count].DclkFrequency, ÷rs);
PP_ASSERT_WITH_CODE((!result), "can not find divide id for Dclk clock", return result);
/* retrieve divider value for VBIOS */
result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
table->VceLevel[count].Frequency, ÷rs);
PP_ASSERT_WITH_CODE((!result), "can not find divide id for VCE engine clock", return result);
/* retrieve divider value for VBIOS */
result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
table->AcpLevel[count].Frequency, ÷rs);
PP_ASSERT_WITH_CODE((!result), "can not find divide id for engine clock", return result);
for (i = 0; i < data->dpm_table.sclk_table.count; i++) { for (j = 0; j < data->dpm_table.mclk_table.count; j++) {
result = tonga_populate_memory_timing_parameters
(hwmgr, data->dpm_table.sclk_table.dpm_levels[i].value,
data->dpm_table.mclk_table.dpm_levels[j].value,
&arb_regs.entries[i][j]);
if (result) break;
}
}
if (!result) {
result = smu7_copy_bytes_to_smc(
hwmgr,
smu_data->smu7_data.arb_table_start,
(uint8_t *)&arb_regs, sizeof(SMU72_Discrete_MCArbDramTimingTable),
SMC_RAM_END
);
}
/* Read SMU_Eefuse to read and calculate RO and determine * if the part is SS or FF. if RO >= 1660MHz, part is FF.
*/
efuse = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
ixSMU_EFUSE_0 + (146 * 4));
efuse2 = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
ixSMU_EFUSE_0 + (148 * 4));
efuse &= 0xFF000000;
efuse = efuse >> 24;
efuse2 &= 0xF;
/* Populate DDT Lookup Table */ for (i = 0; i < 4; i++) { /* Assign the minimum and maximum VID stored * in the last row of Clock Stretcher Voltage Table.
*/
smu_data->smc_state_table.ClockStretcherDataTable.
ClockStretcherDataTableEntry[i].minVID =
(uint8_t) tonga_clock_stretcher_ddt_table[type][i][2];
smu_data->smc_state_table.ClockStretcherDataTable.
ClockStretcherDataTableEntry[i].maxVID =
(uint8_t) tonga_clock_stretcher_ddt_table[type][i][3]; /* Loop through each SCLK and check the frequency * to see if it lies within the frequency for clock stretcher.
*/ for (j = 0; j < smu_data->smc_state_table.GraphicsDpmLevelCount; j++) {
cks_setting = 0;
clock_freq = PP_SMC_TO_HOST_UL(
smu_data->smc_state_table.GraphicsLevel[j].SclkFrequency); /* Check the allowed frequency against the sclk level[j]. * Sclk's endianness has already been converted, * and it's in 10Khz unit, * as opposed to Data table, which is in Mhz unit.
*/ if (clock_freq >= tonga_clock_stretcher_ddt_table[type][i][0] * 100) {
cks_setting |= 0x2; if (clock_freq < tonga_clock_stretcher_ddt_table[type][i][1] * 100)
cks_setting |= 0x1;
}
smu_data->smc_state_table.ClockStretcherDataTable.
ClockStretcherDataTableEntry[i].setting |= cks_setting << (j * 2);
}
CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.
ClockStretcherDataTable.
ClockStretcherDataTableEntry[i].setting);
}
value = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
ixPWR_CKS_CNTL);
value &= 0xFFFFFFFE;
cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
ixPWR_CKS_CNTL, value);
if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
config = VR_SVI2_PLANE_2;
table->VRConfig |= config;
} else {
pr_err("VDDC and VDDGFX should " "be both on SVI2 control in splitted mode !\n");
}
} else { /* Merged mode */
config = VR_MERGED_WITH_VDDC;
table->VRConfig |= (config<<VRCONF_VDDGFX_SHIFT);
/* Set Vddc Voltage Controller */ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
config = VR_SVI2_PLANE_1;
table->VRConfig |= config;
} else {
pr_err("VDDC should be on " "SVI2 control in merged mode !\n");
}
}
/* Set Vddci Voltage Controller */ if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) {
config = VR_SVI2_PLANE_2; /* only in merged mode */
table->VRConfig |= (config<<VRCONF_VDDCI_SHIFT);
} elseif (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
config = VR_SMIO_PATTERN_1;
table->VRConfig |= (config<<VRCONF_VDDCI_SHIFT);
}
/* Set Mvdd Voltage Controller */ if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
config = VR_SMIO_PATTERN_2;
table->VRConfig |= (config<<VRCONF_MVDD_SHIFT);
}
/* * This is a read-modify-write on the first byte of the ARB table. * The first byte in the SMU72_Discrete_MCArbDramTimingTable structure * is the field 'current'. * This solution is ugly, but we never write the whole table only * individual fields in it. * In reality this field should not be in that structure * but in a soft register.
*/
result = smu7_read_smc_sram_dword(hwmgr,
smu_data->smu7_data.arb_table_start, &tmp, SMC_RAM_END);
for (i = 0; i < SMU72_DTE_ITERATIONS; i++) { for (j = 0; j < SMU72_DTE_SOURCES; j++) { for (k = 0; k < SMU72_DTE_SINKS; k++) {
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet)
¤
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.