1 /* 2 * Copyright 2020 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 <linux/delay.h> 27 #include "dcn30_clk_mgr_smu_msg.h" 28 29 #include "clk_mgr_internal.h" 30 #include "reg_helper.h" 31 #include "dalsmc.h" 32 33 #define mmDAL_MSG_REG 0x1628A 34 #define mmDAL_ARG_REG 0x16273 35 #define mmDAL_RESP_REG 0x16274 36 37 #define REG(reg_name) \ 38 mm ## reg_name 39 40 #include "logger_types.h" 41 #undef DC_LOGGER 42 #define DC_LOGGER \ 43 CTX->logger 44 #define smu_print(str, ...) {DC_LOG_SMU(str, ##__VA_ARGS__); } 45 46 47 /* 48 * Function to be used instead of REG_WAIT macro because the wait ends when 49 * the register is NOT EQUAL to zero, and because the translation in msg_if.h 50 * won't work with REG_WAIT. 51 */ 52 static uint32_t dcn30_smu_wait_for_response(struct clk_mgr_internal *clk_mgr, unsigned int delay_us, unsigned int max_retries) 53 { 54 uint32_t reg = 0; 55 56 do { 57 reg = REG_READ(DAL_RESP_REG); 58 if (reg) 59 break; 60 61 if (delay_us >= 1000) 62 msleep(delay_us/1000); 63 else if (delay_us > 0) 64 udelay(delay_us); 65 } while (max_retries--); 66 67 /* handle DALSMC_Result_CmdRejectedBusy? */ 68 69 /* Log? */ 70 71 return reg; 72 } 73 74 static bool dcn30_smu_send_msg_with_param(struct clk_mgr_internal *clk_mgr, uint32_t msg_id, uint32_t param_in, uint32_t *param_out) 75 { 76 /* Wait for response register to be ready */ 77 dcn30_smu_wait_for_response(clk_mgr, 10, 200000); 78 79 /* Clear response register */ 80 REG_WRITE(DAL_RESP_REG, 0); 81 82 /* Set the parameter register for the SMU message */ 83 REG_WRITE(DAL_ARG_REG, param_in); 84 85 /* Trigger the message transaction by writing the message ID */ 86 REG_WRITE(DAL_MSG_REG, msg_id); 87 88 /* Wait for response */ 89 if (dcn30_smu_wait_for_response(clk_mgr, 10, 200000) == DALSMC_Result_OK) { 90 if (param_out) 91 *param_out = REG_READ(DAL_ARG_REG); 92 93 return true; 94 } 95 96 return false; 97 } 98 99 /* Test message should return input + 1 */ 100 bool dcn30_smu_test_message(struct clk_mgr_internal *clk_mgr, uint32_t input) 101 { 102 uint32_t response = 0; 103 104 smu_print("SMU Test message: %d\n", input); 105 106 if (dcn30_smu_send_msg_with_param(clk_mgr, 107 DALSMC_MSG_TestMessage, input, &response)) 108 if (response == input + 1) 109 return true; 110 111 return false; 112 } 113 114 bool dcn30_smu_get_smu_version(struct clk_mgr_internal *clk_mgr, unsigned int *version) 115 { 116 smu_print("SMU Get SMU version\n"); 117 118 if (dcn30_smu_send_msg_with_param(clk_mgr, 119 DALSMC_MSG_GetSmuVersion, 0, version)) { 120 121 smu_print("SMU version: %d\n", *version); 122 123 return true; 124 } 125 126 return false; 127 } 128 129 /* Message output should match SMU11_DRIVER_IF_VERSION in smu11_driver_if.h */ 130 bool dcn30_smu_check_driver_if_version(struct clk_mgr_internal *clk_mgr) 131 { 132 uint32_t response = 0; 133 134 smu_print("SMU Check driver if version\n"); 135 136 if (dcn30_smu_send_msg_with_param(clk_mgr, 137 DALSMC_MSG_GetDriverIfVersion, 0, &response)) { 138 139 smu_print("SMU driver if version: %d\n", response); 140 141 if (response == SMU11_DRIVER_IF_VERSION) 142 return true; 143 } 144 145 return false; 146 } 147 148 /* Message output should match DALSMC_VERSION in dalsmc.h */ 149 bool dcn30_smu_check_msg_header_version(struct clk_mgr_internal *clk_mgr) 150 { 151 uint32_t response = 0; 152 153 smu_print("SMU Check msg header version\n"); 154 155 if (dcn30_smu_send_msg_with_param(clk_mgr, 156 DALSMC_MSG_GetMsgHeaderVersion, 0, &response)) { 157 158 smu_print("SMU msg header version: %d\n", response); 159 160 if (response == DALSMC_VERSION) 161 return true; 162 } 163 164 return false; 165 } 166 167 void dcn30_smu_set_dram_addr_high(struct clk_mgr_internal *clk_mgr, uint32_t addr_high) 168 { 169 smu_print("SMU Set DRAM addr high: %d\n", addr_high); 170 171 dcn30_smu_send_msg_with_param(clk_mgr, 172 DALSMC_MSG_SetDalDramAddrHigh, addr_high, NULL); 173 } 174 175 void dcn30_smu_set_dram_addr_low(struct clk_mgr_internal *clk_mgr, uint32_t addr_low) 176 { 177 smu_print("SMU Set DRAM addr low: %d\n", addr_low); 178 179 dcn30_smu_send_msg_with_param(clk_mgr, 180 DALSMC_MSG_SetDalDramAddrLow, addr_low, NULL); 181 } 182 183 void dcn30_smu_transfer_wm_table_smu_2_dram(struct clk_mgr_internal *clk_mgr) 184 { 185 smu_print("SMU Transfer WM table SMU 2 DRAM\n"); 186 187 dcn30_smu_send_msg_with_param(clk_mgr, 188 DALSMC_MSG_TransferTableSmu2Dram, TABLE_WATERMARKS, NULL); 189 } 190 191 void dcn30_smu_transfer_wm_table_dram_2_smu(struct clk_mgr_internal *clk_mgr) 192 { 193 smu_print("SMU Transfer WM table DRAM 2 SMU\n"); 194 195 dcn30_smu_send_msg_with_param(clk_mgr, 196 DALSMC_MSG_TransferTableDram2Smu, TABLE_WATERMARKS, NULL); 197 } 198 199 /* Returns the actual frequency that was set in MHz, 0 on failure */ 200 unsigned int dcn30_smu_set_hard_min_by_freq(struct clk_mgr_internal *clk_mgr, PPCLK_e clk, uint16_t freq_mhz) 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 = (clk << 16) | freq_mhz; 206 207 smu_print("SMU Set hard min by freq: clk = %d, freq_mhz = %d MHz\n", clk, freq_mhz); 208 209 dcn30_smu_send_msg_with_param(clk_mgr, 210 DALSMC_MSG_SetHardMinByFreq, param, &response); 211 212 smu_print("SMU Frequency set = %d MHz\n", response); 213 214 return response; 215 } 216 217 /* Returns the actual frequency that was set in MHz, 0 on failure */ 218 unsigned int dcn30_smu_set_hard_max_by_freq(struct clk_mgr_internal *clk_mgr, PPCLK_e clk, uint16_t freq_mhz) 219 { 220 uint32_t response = 0; 221 222 /* bits 23:16 for clock type, lower 16 bits for frequency in MHz */ 223 uint32_t param = (clk << 16) | freq_mhz; 224 225 smu_print("SMU Set hard max by freq: clk = %d, freq_mhz = %d MHz\n", clk, freq_mhz); 226 227 dcn30_smu_send_msg_with_param(clk_mgr, 228 DALSMC_MSG_SetHardMaxByFreq, param, &response); 229 230 smu_print("SMU Frequency set = %d MHz\n", response); 231 232 return response; 233 } 234 235 /* 236 * Frequency in MHz returned in lower 16 bits for valid DPM level 237 * 238 * Call with dpm_level = 0xFF to query features, return value will be: 239 * Bits 7:0 - number of DPM levels 240 * Bit 28 - 1 = auto DPM on 241 * Bit 29 - 1 = sweep DPM on 242 * Bit 30 - 1 = forced DPM on 243 * Bit 31 - 0 = discrete, 1 = fine-grained 244 * 245 * With fine-grained DPM, only min and max frequencies will be reported 246 * 247 * Returns 0 on failure 248 */ 249 unsigned int dcn30_smu_get_dpm_freq_by_index(struct clk_mgr_internal *clk_mgr, PPCLK_e clk, uint8_t dpm_level) 250 { 251 uint32_t response = 0; 252 253 /* bits 23:16 for clock type, lower 8 bits for DPM level */ 254 uint32_t param = (clk << 16) | dpm_level; 255 256 smu_print("SMU Get dpm freq by index: clk = %d, dpm_level = %d\n", clk, dpm_level); 257 258 dcn30_smu_send_msg_with_param(clk_mgr, 259 DALSMC_MSG_GetDpmFreqByIndex, param, &response); 260 261 smu_print("SMU dpm freq: %d MHz\n", response); 262 263 return response; 264 } 265 266 /* Returns the max DPM frequency in DC mode in MHz, 0 on failure */ 267 unsigned int dcn30_smu_get_dc_mode_max_dpm_freq(struct clk_mgr_internal *clk_mgr, PPCLK_e clk) 268 { 269 uint32_t response = 0; 270 271 /* bits 23:16 for clock type */ 272 uint32_t param = clk << 16; 273 274 smu_print("SMU Get DC mode max DPM freq: clk = %d\n", clk); 275 276 dcn30_smu_send_msg_with_param(clk_mgr, 277 DALSMC_MSG_GetDcModeMaxDpmFreq, param, &response); 278 279 smu_print("SMU DC mode max DMP freq: %d MHz\n", response); 280 281 return response; 282 } 283 284 void dcn30_smu_set_min_deep_sleep_dcef_clk(struct clk_mgr_internal *clk_mgr, uint32_t freq_mhz) 285 { 286 smu_print("SMU Set min deep sleep dcef clk: freq_mhz = %d MHz\n", freq_mhz); 287 288 dcn30_smu_send_msg_with_param(clk_mgr, 289 DALSMC_MSG_SetMinDeepSleepDcefclk, freq_mhz, NULL); 290 } 291 292 void dcn30_smu_set_num_of_displays(struct clk_mgr_internal *clk_mgr, uint32_t num_displays) 293 { 294 smu_print("SMU Set num of displays: num_displays = %d\n", num_displays); 295 296 dcn30_smu_send_msg_with_param(clk_mgr, 297 DALSMC_MSG_NumOfDisplays, num_displays, NULL); 298 } 299 300 void dcn30_smu_set_display_refresh_from_mall(struct clk_mgr_internal *clk_mgr, bool enable, uint8_t cache_timer_delay, uint8_t cache_timer_scale) 301 { 302 /* bits 8:7 for cache timer scale, bits 6:1 for cache timer delay, bit 0 = 1 for enable, = 0 for disable */ 303 uint32_t param = (cache_timer_scale << 7) | (cache_timer_delay << 1) | (enable ? 1 : 0); 304 305 dcn30_smu_send_msg_with_param(clk_mgr, 306 DALSMC_MSG_SetDisplayRefreshFromMall, param, NULL); 307 } 308 309 void dcn30_smu_set_external_client_df_cstate_allow(struct clk_mgr_internal *clk_mgr, bool enable) 310 { 311 smu_print("SMU Set external client df cstate allow: enable = %d\n", enable); 312 313 dcn30_smu_send_msg_with_param(clk_mgr, 314 DALSMC_MSG_SetExternalClientDfCstateAllow, enable ? 1 : 0, NULL); 315 } 316 317 void dcn30_smu_set_pme_workaround(struct clk_mgr_internal *clk_mgr) 318 { 319 smu_print("SMU Set PME workaround\n"); 320 321 dcn30_smu_send_msg_with_param(clk_mgr, 322 DALSMC_MSG_BacoAudioD3PME, 0, NULL); 323 } 324