1/* 2 * Copyright 2012-16 Advanced Micro Devices, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: AMD 23 * 24 */ 25 26#include "core_types.h" 27#include "clk_mgr_internal.h" 28#include "reg_helper.h" 29#include "dm_helpers.h" 30 31#include "rn_clk_mgr_vbios_smu.h" 32 33#include <linux/delay.h> 34 35#include "renoir_ip_offset.h" 36 37#include "mp/mp_12_0_0_offset.h" 38#include "mp/mp_12_0_0_sh_mask.h" 39 40#define REG(reg_name) \ 41 (MP0_BASE.instance[0].segment[mm ## reg_name ## _BASE_IDX] + mm ## reg_name) 42 43#define FN(reg_name, field) \ 44 FD(reg_name##__##field) 45 46#include "logger_types.h" 47#undef DC_LOGGER 48#define DC_LOGGER \ 49 CTX->logger 50#define smu_print(str, ...) {DC_LOG_SMU(str, ##__VA_ARGS__); } 51 52#define VBIOSSMC_MSG_TestMessage 0x1 53#define VBIOSSMC_MSG_GetSmuVersion 0x2 54#define VBIOSSMC_MSG_PowerUpGfx 0x3 55#define VBIOSSMC_MSG_SetDispclkFreq 0x4 56#define VBIOSSMC_MSG_SetDprefclkFreq 0x5 57#define VBIOSSMC_MSG_PowerDownGfx 0x6 58#define VBIOSSMC_MSG_SetDppclkFreq 0x7 59#define VBIOSSMC_MSG_SetHardMinDcfclkByFreq 0x8 60#define VBIOSSMC_MSG_SetMinDeepSleepDcfclk 0x9 61#define VBIOSSMC_MSG_SetPhyclkVoltageByFreq 0xA 62#define VBIOSSMC_MSG_GetFclkFrequency 0xB 63#define VBIOSSMC_MSG_SetDisplayCount 0xC 64#define VBIOSSMC_MSG_EnableTmdp48MHzRefclkPwrDown 0xD 65#define VBIOSSMC_MSG_UpdatePmeRestore 0xE 66#define VBIOSSMC_MSG_IsPeriodicRetrainingDisabled 0xF 67 68#define VBIOSSMC_Status_BUSY 0x0 69#define VBIOSSMC_Result_OK 0x1 70#define VBIOSSMC_Result_Failed 0xFF 71#define VBIOSSMC_Result_UnknownCmd 0xFE 72#define VBIOSSMC_Result_CmdRejectedPrereq 0xFD 73#define VBIOSSMC_Result_CmdRejectedBusy 0xFC 74 75/* 76 * Function to be used instead of REG_WAIT macro because the wait ends when 77 * the register is NOT EQUAL to zero, and because the translation in msg_if.h 78 * won't work with REG_WAIT. 79 */ 80static uint32_t rn_smu_wait_for_response(struct clk_mgr_internal *clk_mgr, unsigned int delay_us, unsigned int max_retries) 81{ 82 uint32_t res_val = VBIOSSMC_Status_BUSY; 83 84 do { 85 res_val = REG_READ(MP1_SMN_C2PMSG_91); 86 if (res_val != VBIOSSMC_Status_BUSY) 87 break; 88 89 if (delay_us >= 1000) 90 msleep(delay_us/1000); 91 else if (delay_us > 0) 92 udelay(delay_us); 93 } while (max_retries--); 94 95 return res_val; 96} 97 98 99static int rn_vbios_smu_send_msg_with_param(struct clk_mgr_internal *clk_mgr, 100 unsigned int msg_id, 101 unsigned int param) 102{ 103 uint32_t result; 104 105 result = rn_smu_wait_for_response(clk_mgr, 10, 200000); 106 107 if (result != VBIOSSMC_Result_OK) 108 smu_print("SMU Response was not OK. SMU response after wait received is: %d\n", result); 109 110 if (result == VBIOSSMC_Status_BUSY) { 111 return -1; 112 } 113 114 /* First clear response register */ 115 REG_WRITE(MP1_SMN_C2PMSG_91, VBIOSSMC_Status_BUSY); 116 117 /* Set the parameter register for the SMU message, unit is Mhz */ 118 REG_WRITE(MP1_SMN_C2PMSG_83, param); 119 120 /* Trigger the message transaction by writing the message ID */ 121 REG_WRITE(MP1_SMN_C2PMSG_67, msg_id); 122 123 result = rn_smu_wait_for_response(clk_mgr, 10, 200000); 124 125 if (IS_SMU_TIMEOUT(result)) { 126 ASSERT(0); 127 dm_helpers_smu_timeout(CTX, msg_id, param, 10 * 200000); 128 } 129 130 /* Actual dispclk set is returned in the parameter register */ 131 return REG_READ(MP1_SMN_C2PMSG_83); 132} 133 134int rn_vbios_smu_get_smu_version(struct clk_mgr_internal *clk_mgr) 135{ 136 return rn_vbios_smu_send_msg_with_param( 137 clk_mgr, 138 VBIOSSMC_MSG_GetSmuVersion, 139 0); 140} 141 142 143int rn_vbios_smu_set_dispclk(struct clk_mgr_internal *clk_mgr, int requested_dispclk_khz) 144{ 145 int actual_dispclk_set_mhz = -1; 146 struct dc *dc = clk_mgr->base.ctx->dc; 147 struct dmcu *dmcu = dc->res_pool->dmcu; 148 149 /* Unit of SMU msg parameter is Mhz */ 150 actual_dispclk_set_mhz = rn_vbios_smu_send_msg_with_param( 151 clk_mgr, 152 VBIOSSMC_MSG_SetDispclkFreq, 153 khz_to_mhz_ceil(requested_dispclk_khz)); 154 155 if (dmcu && dmcu->funcs->is_dmcu_initialized(dmcu)) { 156 if (clk_mgr->dfs_bypass_disp_clk != actual_dispclk_set_mhz) 157 dmcu->funcs->set_psr_wait_loop(dmcu, 158 actual_dispclk_set_mhz / 7); 159 } 160 161 // pmfw always set clock more than or equal requested clock 162 ASSERT(actual_dispclk_set_mhz >= khz_to_mhz_ceil(requested_dispclk_khz)); 163 164 return actual_dispclk_set_mhz * 1000; 165} 166 167int rn_vbios_smu_set_dprefclk(struct clk_mgr_internal *clk_mgr) 168{ 169 int actual_dprefclk_set_mhz = -1; 170 171 actual_dprefclk_set_mhz = rn_vbios_smu_send_msg_with_param( 172 clk_mgr, 173 VBIOSSMC_MSG_SetDprefclkFreq, 174 khz_to_mhz_ceil(clk_mgr->base.dprefclk_khz)); 175 176 /* TODO: add code for programing DP DTO, currently this is down by command table */ 177 178 return actual_dprefclk_set_mhz * 1000; 179} 180 181int rn_vbios_smu_set_hard_min_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_dcfclk_khz) 182{ 183 int actual_dcfclk_set_mhz = -1; 184 185 if (clk_mgr->smu_ver < 0x370c00) 186 return actual_dcfclk_set_mhz; 187 188 actual_dcfclk_set_mhz = rn_vbios_smu_send_msg_with_param( 189 clk_mgr, 190 VBIOSSMC_MSG_SetHardMinDcfclkByFreq, 191 khz_to_mhz_ceil(requested_dcfclk_khz)); 192 193 return actual_dcfclk_set_mhz * 1000; 194} 195 196int rn_vbios_smu_set_min_deep_sleep_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_min_ds_dcfclk_khz) 197{ 198 int actual_min_ds_dcfclk_mhz = -1; 199 200 if (clk_mgr->smu_ver < 0x370c00) 201 return actual_min_ds_dcfclk_mhz; 202 203 actual_min_ds_dcfclk_mhz = rn_vbios_smu_send_msg_with_param( 204 clk_mgr, 205 VBIOSSMC_MSG_SetMinDeepSleepDcfclk, 206 khz_to_mhz_ceil(requested_min_ds_dcfclk_khz)); 207 208 return actual_min_ds_dcfclk_mhz * 1000; 209} 210 211void rn_vbios_smu_set_phyclk(struct clk_mgr_internal *clk_mgr, int requested_phyclk_khz) 212{ 213 rn_vbios_smu_send_msg_with_param( 214 clk_mgr, 215 VBIOSSMC_MSG_SetPhyclkVoltageByFreq, 216 khz_to_mhz_ceil(requested_phyclk_khz)); 217} 218 219int rn_vbios_smu_set_dppclk(struct clk_mgr_internal *clk_mgr, int requested_dpp_khz) 220{ 221 int actual_dppclk_set_mhz = -1; 222 223 actual_dppclk_set_mhz = rn_vbios_smu_send_msg_with_param( 224 clk_mgr, 225 VBIOSSMC_MSG_SetDppclkFreq, 226 khz_to_mhz_ceil(requested_dpp_khz)); 227 228 ASSERT(actual_dppclk_set_mhz >= khz_to_mhz_ceil(requested_dpp_khz)); 229 230 return actual_dppclk_set_mhz * 1000; 231} 232 233void rn_vbios_smu_set_dcn_low_power_state(struct clk_mgr_internal *clk_mgr, enum dcn_pwr_state state) 234{ 235 int disp_count; 236 237 if (state == DCN_PWR_STATE_LOW_POWER) 238 disp_count = 0; 239 else 240 disp_count = 1; 241 242 rn_vbios_smu_send_msg_with_param( 243 clk_mgr, 244 VBIOSSMC_MSG_SetDisplayCount, 245 disp_count); 246} 247 248void rn_vbios_smu_enable_48mhz_tmdp_refclk_pwrdwn(struct clk_mgr_internal *clk_mgr, bool enable) 249{ 250 rn_vbios_smu_send_msg_with_param( 251 clk_mgr, 252 VBIOSSMC_MSG_EnableTmdp48MHzRefclkPwrDown, 253 enable); 254} 255 256void rn_vbios_smu_enable_pme_wa(struct clk_mgr_internal *clk_mgr) 257{ 258 rn_vbios_smu_send_msg_with_param( 259 clk_mgr, 260 VBIOSSMC_MSG_UpdatePmeRestore, 261 0); 262} 263 264int rn_vbios_smu_is_periodic_retraining_disabled(struct clk_mgr_internal *clk_mgr) 265{ 266 return rn_vbios_smu_send_msg_with_param( 267 clk_mgr, 268 VBIOSSMC_MSG_IsPeriodicRetrainingDisabled, 269 1); // if PMFW doesn't support this message, assume retraining is disabled 270 // so we only use most optimal watermark if we know retraining is enabled. 271} 272