1106686Stjr/* 2128005Stjr * Copyright 2021 Advanced Micro Devices, Inc. 3106686Stjr * 4106686Stjr * Permission is hereby granted, free of charge, to any person obtaining a 5106686Stjr * copy of this software and associated documentation files (the "Software"), 6106686Stjr * to deal in the Software without restriction, including without limitation 7106686Stjr * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8106686Stjr * and/or sell copies of the Software, and to permit persons to whom the 9106686Stjr * Software is furnished to do so, subject to the following conditions: 10106686Stjr * 11106686Stjr * The above copyright notice and this permission notice shall be included in 12106686Stjr * all copies or substantial portions of the Software. 13106686Stjr * 14106686Stjr * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15106686Stjr * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16106686Stjr * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17106686Stjr * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18106686Stjr * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19106686Stjr * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20106686Stjr * OTHER DEALINGS IN THE SOFTWARE. 21106686Stjr * 22106686Stjr * Authors: AMD 23106686Stjr * 24106686Stjr */ 25106686Stjr 26106686Stjr#include "dcn32_clk_mgr_smu_msg.h" 27106686Stjr 28106686Stjr#include "clk_mgr_internal.h" 29106686Stjr#include "reg_helper.h" 30106686Stjr#include "dalsmc.h" 31106686Stjr#include "smu13_driver_if.h" 32106686Stjr 33106686Stjr#define mmDAL_MSG_REG 0x1628A 34106686Stjr#define mmDAL_ARG_REG 0x16273 35106686Stjr#define mmDAL_RESP_REG 0x16274 36106686Stjr 37106686Stjr#define REG(reg_name) \ 38106686Stjr mm ## reg_name 39106686Stjr 40106686Stjr#include "logger_types.h" 41106686Stjr 42106686Stjr#define smu_print(str, ...) {DC_LOG_SMU(str, ##__VA_ARGS__); } 43106686Stjr 44106686Stjr 45290532Sngie/* 46290532Sngie * Function to be used instead of REG_WAIT macro because the wait ends when 47290532Sngie * the register is NOT EQUAL to zero, and because the translation in msg_if.h 48290532Sngie * won't work with REG_WAIT. 49106686Stjr */ 50106686Stjrstatic uint32_t dcn32_smu_wait_for_response(struct clk_mgr_internal *clk_mgr, unsigned int delay_us, unsigned int max_retries) 51106686Stjr{ 52106686Stjr uint32_t reg = 0; 53290532Sngie 54106686Stjr do { 55290532Sngie reg = REG_READ(DAL_RESP_REG); 56137587Snik if (reg) 57106686Stjr break; 58290532Sngie 59106686Stjr if (delay_us >= 1000) 60106686Stjr msleep(delay_us/1000); 61106686Stjr else if (delay_us > 0) 62106686Stjr udelay(delay_us); 63290532Sngie } while (max_retries--); 64290532Sngie 65106686Stjr return reg; 66106686Stjr} 67106686Stjr 68106686Stjrstatic bool dcn32_smu_send_msg_with_param(struct clk_mgr_internal *clk_mgr, uint32_t msg_id, uint32_t param_in, uint32_t *param_out) 69290532Sngie{ 70290532Sngie /* Wait for response register to be ready */ 71106686Stjr dcn32_smu_wait_for_response(clk_mgr, 10, 200000); 72106686Stjr 73290532Sngie /* Clear response register */ 74290532Sngie REG_WRITE(DAL_RESP_REG, 0); 75106686Stjr 76106686Stjr /* Set the parameter register for the SMU message */ 77106686Stjr REG_WRITE(DAL_ARG_REG, param_in); 78106686Stjr 79106686Stjr /* Trigger the message transaction by writing the message ID */ 80290532Sngie REG_WRITE(DAL_MSG_REG, msg_id); 81290532Sngie 82106686Stjr /* Wait for response */ 83106686Stjr if (dcn32_smu_wait_for_response(clk_mgr, 10, 200000) == DALSMC_Result_OK) { 84290532Sngie if (param_out) 85106686Stjr *param_out = REG_READ(DAL_ARG_REG); 86106686Stjr 87106686Stjr return true; 88106686Stjr } 89290532Sngie 90290532Sngie return false; 91106686Stjr} 92106686Stjr 93106686Stjr/* 94106686Stjr * Use these functions to return back delay information so we can aggregate the total 95290532Sngie * delay when requesting hardmin clk 96290532Sngie * 97106686Stjr * dcn32_smu_wait_for_response_delay 98106686Stjr * dcn32_smu_send_msg_with_param_delay 99106686Stjr * 100106686Stjr */ 101290532Sngiestatic uint32_t dcn32_smu_wait_for_response_delay(struct clk_mgr_internal *clk_mgr, unsigned int delay_us, unsigned int max_retries, unsigned int *total_delay_us) 102290532Sngie{ 103106686Stjr uint32_t reg = 0; 104106686Stjr *total_delay_us = 0; 105290532Sngie 106106686Stjr do { 107290532Sngie reg = REG_READ(DAL_RESP_REG); 108290532Sngie if (reg) 109106686Stjr break; 110290532Sngie 111290532Sngie if (delay_us >= 1000) 112290532Sngie msleep(delay_us/1000); 113106686Stjr else if (delay_us > 0) 114 udelay(delay_us); 115 *total_delay_us += delay_us; 116 } while (max_retries--); 117 118 return reg; 119} 120 121static bool dcn32_smu_send_msg_with_param_delay(struct clk_mgr_internal *clk_mgr, uint32_t msg_id, uint32_t param_in, uint32_t *param_out, unsigned int *total_delay_us) 122{ 123 unsigned int delay1_us, delay2_us; 124 *total_delay_us = 0; 125 126 /* Wait for response register to be ready */ 127 dcn32_smu_wait_for_response_delay(clk_mgr, 10, 200000, &delay1_us); 128 129 /* Clear response register */ 130 REG_WRITE(DAL_RESP_REG, 0); 131 132 /* Set the parameter register for the SMU message */ 133 REG_WRITE(DAL_ARG_REG, param_in); 134 135 /* Trigger the message transaction by writing the message ID */ 136 REG_WRITE(DAL_MSG_REG, msg_id); 137 138 /* Wait for response */ 139 if (dcn32_smu_wait_for_response_delay(clk_mgr, 10, 200000, &delay2_us) == DALSMC_Result_OK) { 140 if (param_out) 141 *param_out = REG_READ(DAL_ARG_REG); 142 143 *total_delay_us = delay1_us + delay2_us; 144 return true; 145 } 146 147 *total_delay_us = delay1_us + 2000000; 148 return false; 149} 150 151void dcn32_smu_send_fclk_pstate_message(struct clk_mgr_internal *clk_mgr, bool enable) 152{ 153 smu_print("FCLK P-state support value is : %d\n", enable); 154 155 dcn32_smu_send_msg_with_param(clk_mgr, 156 DALSMC_MSG_SetFclkSwitchAllow, enable ? FCLK_PSTATE_SUPPORTED : FCLK_PSTATE_NOTSUPPORTED, NULL); 157} 158 159void dcn32_smu_send_cab_for_uclk_message(struct clk_mgr_internal *clk_mgr, unsigned int num_ways) 160{ 161 uint32_t param = (num_ways << 1) | (num_ways > 0); 162 163 dcn32_smu_send_msg_with_param(clk_mgr, DALSMC_MSG_SetCabForUclkPstate, param, NULL); 164 smu_print("Numways for SubVP : %d\n", num_ways); 165} 166 167void dcn32_smu_transfer_wm_table_dram_2_smu(struct clk_mgr_internal *clk_mgr) 168{ 169 smu_print("SMU Transfer WM table DRAM 2 SMU\n"); 170 171 dcn32_smu_send_msg_with_param(clk_mgr, 172 DALSMC_MSG_TransferTableDram2Smu, TABLE_WATERMARKS, NULL); 173} 174 175void dcn32_smu_set_pme_workaround(struct clk_mgr_internal *clk_mgr) 176{ 177 smu_print("SMU Set PME workaround\n"); 178 179 dcn32_smu_send_msg_with_param(clk_mgr, 180 DALSMC_MSG_BacoAudioD3PME, 0, NULL); 181} 182 183/* Check PMFW version if it supports ReturnHardMinStatus message */ 184static bool dcn32_get_hard_min_status_supported(struct clk_mgr_internal *clk_mgr) 185{ 186 if (ASICREV_IS_GC_11_0_0(clk_mgr->base.ctx->asic_id.hw_internal_rev)) { 187 if (clk_mgr->smu_ver >= 0x4e6a00) 188 return true; 189 } else if (ASICREV_IS_GC_11_0_2(clk_mgr->base.ctx->asic_id.hw_internal_rev)) { 190 if (clk_mgr->smu_ver >= 0x524e00) 191 return true; 192 } else { /* ASICREV_IS_GC_11_0_3 */ 193 if (clk_mgr->smu_ver >= 0x503900) 194 return true; 195 } 196 return false; 197} 198 199/* Returns the clocks which were fulfilled by the DAL hard min arbiter in PMFW */ 200static unsigned int dcn32_smu_get_hard_min_status(struct clk_mgr_internal *clk_mgr, bool *no_timeout, unsigned int *total_delay_us) 201{ 202 uint32_t response = 0; 203 204 /* bits 23:16 for clock type, lower 16 bits for frequency in MHz */ 205 uint32_t param = 0; 206 207 *no_timeout = dcn32_smu_send_msg_with_param_delay(clk_mgr, 208 DALSMC_MSG_ReturnHardMinStatus, param, &response, total_delay_us); 209 210 smu_print("SMU Get hard min status: no_timeout %d delay %d us clk bits %x\n", 211 *no_timeout, *total_delay_us, response); 212 213 return response; 214} 215 216static bool dcn32_smu_wait_get_hard_min_status(struct clk_mgr_internal *clk_mgr, 217 uint32_t clk) 218{ 219 int readDalHardMinClkBits, checkDalHardMinClkBits; 220 unsigned int total_delay_us, read_total_delay_us; 221 bool no_timeout, hard_min_done; 222 223 static unsigned int cur_wait_get_hard_min_max_us; 224 static unsigned int cur_wait_get_hard_min_max_timeouts; 225 226 checkDalHardMinClkBits = CHECK_HARD_MIN_CLK_DPREFCLK; 227 if (clk == PPCLK_DISPCLK) 228 checkDalHardMinClkBits |= CHECK_HARD_MIN_CLK_DISPCLK; 229 if (clk == PPCLK_DPPCLK) 230 checkDalHardMinClkBits |= CHECK_HARD_MIN_CLK_DPPCLK; 231 if (clk == PPCLK_DCFCLK) 232 checkDalHardMinClkBits |= CHECK_HARD_MIN_CLK_DCFCLK; 233 if (clk == PPCLK_DTBCLK) 234 checkDalHardMinClkBits |= CHECK_HARD_MIN_CLK_DTBCLK; 235 if (clk == PPCLK_UCLK) 236 checkDalHardMinClkBits |= CHECK_HARD_MIN_CLK_UCLK; 237 238 if (checkDalHardMinClkBits == CHECK_HARD_MIN_CLK_DPREFCLK) 239 return 0; 240 241 total_delay_us = 0; 242 hard_min_done = false; 243 while (1) { 244 readDalHardMinClkBits = dcn32_smu_get_hard_min_status(clk_mgr, &no_timeout, &read_total_delay_us); 245 total_delay_us += read_total_delay_us; 246 if (checkDalHardMinClkBits == (readDalHardMinClkBits & checkDalHardMinClkBits)) { 247 hard_min_done = true; 248 break; 249 } 250 251 252 if (total_delay_us >= 2000000) { 253 cur_wait_get_hard_min_max_timeouts++; 254 smu_print("SMU Wait get hard min status: %d timeouts\n", cur_wait_get_hard_min_max_timeouts); 255 break; 256 } 257 msleep(1); 258 total_delay_us += 1000; 259 } 260 261 if (total_delay_us > cur_wait_get_hard_min_max_us) 262 cur_wait_get_hard_min_max_us = total_delay_us; 263 264 smu_print("SMU Wait get hard min status: no_timeout %d, delay %d us, max %d us, read %x, check %x\n", 265 no_timeout, total_delay_us, cur_wait_get_hard_min_max_us, readDalHardMinClkBits, checkDalHardMinClkBits); 266 267 return hard_min_done; 268} 269 270/* Returns the actual frequency that was set in MHz, 0 on failure */ 271unsigned int dcn32_smu_set_hard_min_by_freq(struct clk_mgr_internal *clk_mgr, uint32_t clk, uint16_t freq_mhz) 272{ 273 uint32_t response = 0; 274 bool hard_min_done = false; 275 276 /* bits 23:16 for clock type, lower 16 bits for frequency in MHz */ 277 uint32_t param = (clk << 16) | freq_mhz; 278 279 smu_print("SMU Set hard min by freq: clk = %d, freq_mhz = %d MHz\n", clk, freq_mhz); 280 281 dcn32_smu_send_msg_with_param(clk_mgr, 282 DALSMC_MSG_SetHardMinByFreq, param, &response); 283 284 if (dcn32_get_hard_min_status_supported(clk_mgr)) { 285 hard_min_done = dcn32_smu_wait_get_hard_min_status(clk_mgr, clk); 286 smu_print("SMU Frequency set = %d KHz hard_min_done %d\n", response, hard_min_done); 287 } else 288 smu_print("SMU Frequency set = %d KHz\n", response); 289 290 return response; 291} 292 293void dcn32_smu_wait_for_dmub_ack_mclk(struct clk_mgr_internal *clk_mgr, bool enable) 294{ 295 smu_print("PMFW to wait for DMCUB ack for MCLK : %d\n", enable); 296 297 dcn32_smu_send_msg_with_param(clk_mgr, 0x14, enable ? 1 : 0, NULL); 298} 299