1/* $NetBSD: amdgpu_smu_v12_0.c,v 1.2 2021/12/18 23:45:26 riastradh Exp $ */ 2 3/* 4 * Copyright 2019 Advanced Micro Devices, Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 */ 24 25#include <sys/cdefs.h> 26__KERNEL_RCSID(0, "$NetBSD: amdgpu_smu_v12_0.c,v 1.2 2021/12/18 23:45:26 riastradh Exp $"); 27 28#include "pp_debug.h" 29#include <linux/firmware.h> 30#include "amdgpu.h" 31#include "amdgpu_smu.h" 32#include "smu_internal.h" 33#include "atomfirmware.h" 34#include "amdgpu_atomfirmware.h" 35#include "smu_v12_0.h" 36#include "soc15_common.h" 37#include "atom.h" 38 39#include "asic_reg/mp/mp_12_0_0_offset.h" 40#include "asic_reg/mp/mp_12_0_0_sh_mask.h" 41 42#define smnMP1_FIRMWARE_FLAGS 0x3010024 43 44#define mmSMUIO_GFX_MISC_CNTL 0x00c8 45#define mmSMUIO_GFX_MISC_CNTL_BASE_IDX 0 46#define SMUIO_GFX_MISC_CNTL__PWR_GFXOFF_STATUS_MASK 0x00000006L 47#define SMUIO_GFX_MISC_CNTL__PWR_GFXOFF_STATUS__SHIFT 0x1 48 49int smu_v12_0_send_msg_without_waiting(struct smu_context *smu, 50 uint16_t msg) 51{ 52 struct amdgpu_device *adev = smu->adev; 53 54 WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_66, msg); 55 return 0; 56} 57 58int smu_v12_0_read_arg(struct smu_context *smu, uint32_t *arg) 59{ 60 struct amdgpu_device *adev = smu->adev; 61 62 *arg = RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_82); 63 return 0; 64} 65 66int smu_v12_0_wait_for_response(struct smu_context *smu) 67{ 68 struct amdgpu_device *adev = smu->adev; 69 uint32_t cur_value, i; 70 71 for (i = 0; i < adev->usec_timeout; i++) { 72 cur_value = RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90); 73 if ((cur_value & MP1_C2PMSG_90__CONTENT_MASK) != 0) 74 return cur_value == 0x1 ? 0 : -EIO; 75 76 udelay(1); 77 } 78 79 /* timeout means wrong logic */ 80 return -ETIME; 81} 82 83int 84smu_v12_0_send_msg_with_param(struct smu_context *smu, 85 enum smu_message_type msg, 86 uint32_t param) 87{ 88 struct amdgpu_device *adev = smu->adev; 89 int ret = 0, index = 0; 90 91 index = smu_msg_get_index(smu, msg); 92 if (index < 0) 93 return index; 94 95 ret = smu_v12_0_wait_for_response(smu); 96 if (ret) { 97 pr_err("Msg issuing pre-check failed and " 98 "SMU may be not in the right state!\n"); 99 return ret; 100 } 101 102 WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90, 0); 103 104 WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_82, param); 105 106 smu_v12_0_send_msg_without_waiting(smu, (uint16_t)index); 107 108 ret = smu_v12_0_wait_for_response(smu); 109 if (ret) 110 pr_err("Failed to send message 0x%x, response 0x%x param 0x%x\n", 111 index, ret, param); 112 113 return ret; 114} 115 116int smu_v12_0_check_fw_status(struct smu_context *smu) 117{ 118 struct amdgpu_device *adev = smu->adev; 119 uint32_t mp1_fw_flags; 120 121 mp1_fw_flags = RREG32_PCIE(MP1_Public | 122 (smnMP1_FIRMWARE_FLAGS & 0xffffffff)); 123 124 if ((mp1_fw_flags & MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED_MASK) >> 125 MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED__SHIFT) 126 return 0; 127 128 return -EIO; 129} 130 131int smu_v12_0_check_fw_version(struct smu_context *smu) 132{ 133 uint32_t if_version = 0xff, smu_version = 0xff; 134 uint16_t smu_major; 135 uint8_t smu_minor, smu_debug; 136 int ret = 0; 137 138 ret = smu_get_smc_version(smu, &if_version, &smu_version); 139 if (ret) 140 return ret; 141 142 smu_major = (smu_version >> 16) & 0xffff; 143 smu_minor = (smu_version >> 8) & 0xff; 144 smu_debug = (smu_version >> 0) & 0xff; 145 146 /* 147 * 1. if_version mismatch is not critical as our fw is designed 148 * to be backward compatible. 149 * 2. New fw usually brings some optimizations. But that's visible 150 * only on the paired driver. 151 * Considering above, we just leave user a warning message instead 152 * of halt driver loading. 153 */ 154 if (if_version != smu->smc_if_version) { 155 pr_info("smu driver if version = 0x%08x, smu fw if version = 0x%08x, " 156 "smu fw version = 0x%08x (%d.%d.%d)\n", 157 smu->smc_if_version, if_version, 158 smu_version, smu_major, smu_minor, smu_debug); 159 pr_warn("SMU driver if version not matched\n"); 160 } 161 162 return ret; 163} 164 165int smu_v12_0_powergate_sdma(struct smu_context *smu, bool gate) 166{ 167 if (!smu->is_apu) 168 return 0; 169 170 if (gate) 171 return smu_send_smc_msg(smu, SMU_MSG_PowerDownSdma); 172 else 173 return smu_send_smc_msg(smu, SMU_MSG_PowerUpSdma); 174} 175 176int smu_v12_0_powergate_vcn(struct smu_context *smu, bool gate) 177{ 178 if (!smu->is_apu) 179 return 0; 180 181 if (gate) 182 return smu_send_smc_msg(smu, SMU_MSG_PowerDownVcn); 183 else 184 return smu_send_smc_msg(smu, SMU_MSG_PowerUpVcn); 185} 186 187int smu_v12_0_powergate_jpeg(struct smu_context *smu, bool gate) 188{ 189 if (!smu->is_apu) 190 return 0; 191 192 if (gate) 193 return smu_send_smc_msg_with_param(smu, SMU_MSG_PowerDownJpeg, 0); 194 else 195 return smu_send_smc_msg_with_param(smu, SMU_MSG_PowerUpJpeg, 0); 196} 197 198int smu_v12_0_set_gfx_cgpg(struct smu_context *smu, bool enable) 199{ 200 if (!(smu->adev->pg_flags & AMD_PG_SUPPORT_GFX_PG)) 201 return 0; 202 203 return smu_v12_0_send_msg_with_param(smu, 204 SMU_MSG_SetGfxCGPG, enable ? 1 : 0); 205} 206 207int smu_v12_0_read_sensor(struct smu_context *smu, 208 enum amd_pp_sensors sensor, 209 void *data, uint32_t *size) 210{ 211 int ret = 0; 212 213 if(!data || !size) 214 return -EINVAL; 215 216 switch (sensor) { 217 case AMDGPU_PP_SENSOR_GFX_MCLK: 218 ret = smu_get_current_clk_freq(smu, SMU_UCLK, (uint32_t *)data); 219 *size = 4; 220 break; 221 case AMDGPU_PP_SENSOR_GFX_SCLK: 222 ret = smu_get_current_clk_freq(smu, SMU_GFXCLK, (uint32_t *)data); 223 *size = 4; 224 break; 225 case AMDGPU_PP_SENSOR_MIN_FAN_RPM: 226 *(uint32_t *)data = 0; 227 *size = 4; 228 break; 229 default: 230 ret = smu_common_read_sensor(smu, sensor, data, size); 231 break; 232 } 233 234 if (ret) 235 *size = 0; 236 237 return ret; 238} 239 240/** 241 * smu_v12_0_get_gfxoff_status - get gfxoff status 242 * 243 * @smu: amdgpu_device pointer 244 * 245 * This function will be used to get gfxoff status 246 * 247 * Returns 0=GFXOFF(default). 248 * Returns 1=Transition out of GFX State. 249 * Returns 2=Not in GFXOFF. 250 * Returns 3=Transition into GFXOFF. 251 */ 252uint32_t smu_v12_0_get_gfxoff_status(struct smu_context *smu) 253{ 254 uint32_t reg; 255 uint32_t gfxOff_Status = 0; 256 struct amdgpu_device *adev = smu->adev; 257 258 reg = RREG32_SOC15(SMUIO, 0, mmSMUIO_GFX_MISC_CNTL); 259 gfxOff_Status = (reg & SMUIO_GFX_MISC_CNTL__PWR_GFXOFF_STATUS_MASK) 260 >> SMUIO_GFX_MISC_CNTL__PWR_GFXOFF_STATUS__SHIFT; 261 262 return gfxOff_Status; 263} 264 265int smu_v12_0_gfx_off_control(struct smu_context *smu, bool enable) 266{ 267 int ret = 0, timeout = 500; 268 269 if (enable) { 270 ret = smu_send_smc_msg(smu, SMU_MSG_AllowGfxOff); 271 272 } else { 273 ret = smu_send_smc_msg(smu, SMU_MSG_DisallowGfxOff); 274 275 /* confirm gfx is back to "on" state, timeout is 0.5 second */ 276 while (!(smu_v12_0_get_gfxoff_status(smu) == 2)) { 277 msleep(1); 278 timeout--; 279 if (timeout == 0) { 280 DRM_ERROR("disable gfxoff timeout and failed!\n"); 281 break; 282 } 283 } 284 } 285 286 return ret; 287} 288 289int smu_v12_0_init_smc_tables(struct smu_context *smu) 290{ 291 struct smu_table_context *smu_table = &smu->smu_table; 292 struct smu_table *tables = NULL; 293 294 if (smu_table->tables) 295 return -EINVAL; 296 297 tables = kcalloc(SMU_TABLE_COUNT, sizeof(struct smu_table), 298 GFP_KERNEL); 299 if (!tables) 300 return -ENOMEM; 301 302 smu_table->tables = tables; 303 304 return smu_tables_init(smu, tables); 305} 306 307int smu_v12_0_fini_smc_tables(struct smu_context *smu) 308{ 309 struct smu_table_context *smu_table = &smu->smu_table; 310 311 if (!smu_table->tables) 312 return -EINVAL; 313 314 kfree(smu_table->clocks_table); 315 kfree(smu_table->tables); 316 317 smu_table->clocks_table = NULL; 318 smu_table->tables = NULL; 319 320 return 0; 321} 322 323int smu_v12_0_populate_smc_tables(struct smu_context *smu) 324{ 325 struct smu_table_context *smu_table = &smu->smu_table; 326 327 return smu_update_table(smu, SMU_TABLE_DPMCLOCKS, 0, smu_table->clocks_table, false); 328} 329 330int smu_v12_0_get_enabled_mask(struct smu_context *smu, 331 uint32_t *feature_mask, uint32_t num) 332{ 333 uint32_t feature_mask_high = 0, feature_mask_low = 0; 334 int ret = 0; 335 336 if (!feature_mask || num < 2) 337 return -EINVAL; 338 339 ret = smu_send_smc_msg(smu, SMU_MSG_GetEnabledSmuFeaturesHigh); 340 if (ret) 341 return ret; 342 ret = smu_read_smc_arg(smu, &feature_mask_high); 343 if (ret) 344 return ret; 345 346 ret = smu_send_smc_msg(smu, SMU_MSG_GetEnabledSmuFeaturesLow); 347 if (ret) 348 return ret; 349 ret = smu_read_smc_arg(smu, &feature_mask_low); 350 if (ret) 351 return ret; 352 353 feature_mask[0] = feature_mask_low; 354 feature_mask[1] = feature_mask_high; 355 356 return ret; 357} 358 359int smu_v12_0_get_current_clk_freq(struct smu_context *smu, 360 enum smu_clk_type clk_id, 361 uint32_t *value) 362{ 363 int ret = 0; 364 uint32_t freq = 0; 365 366 if (clk_id >= SMU_CLK_COUNT || !value) 367 return -EINVAL; 368 369 ret = smu_get_current_clk_freq_by_table(smu, clk_id, &freq); 370 if (ret) 371 return ret; 372 373 freq *= 100; 374 *value = freq; 375 376 return ret; 377} 378 379int smu_v12_0_get_dpm_ultimate_freq(struct smu_context *smu, enum smu_clk_type clk_type, 380 uint32_t *min, uint32_t *max) 381{ 382 int ret = 0; 383 uint32_t mclk_mask, soc_mask; 384 385 if (max) { 386 ret = smu_get_profiling_clk_mask(smu, AMD_DPM_FORCED_LEVEL_PROFILE_PEAK, 387 NULL, 388 &mclk_mask, 389 &soc_mask); 390 if (ret) 391 goto failed; 392 393 switch (clk_type) { 394 case SMU_GFXCLK: 395 case SMU_SCLK: 396 ret = smu_send_smc_msg(smu, SMU_MSG_GetMaxGfxclkFrequency); 397 if (ret) { 398 pr_err("Attempt to get max GX frequency from SMC Failed !\n"); 399 goto failed; 400 } 401 ret = smu_read_smc_arg(smu, max); 402 if (ret) 403 goto failed; 404 break; 405 case SMU_UCLK: 406 case SMU_FCLK: 407 case SMU_MCLK: 408 ret = smu_get_dpm_clk_limited(smu, clk_type, mclk_mask, max); 409 if (ret) 410 goto failed; 411 break; 412 case SMU_SOCCLK: 413 ret = smu_get_dpm_clk_limited(smu, clk_type, soc_mask, max); 414 if (ret) 415 goto failed; 416 break; 417 default: 418 ret = -EINVAL; 419 goto failed; 420 } 421 } 422 423 if (min) { 424 switch (clk_type) { 425 case SMU_GFXCLK: 426 case SMU_SCLK: 427 ret = smu_send_smc_msg(smu, SMU_MSG_GetMinGfxclkFrequency); 428 if (ret) { 429 pr_err("Attempt to get min GX frequency from SMC Failed !\n"); 430 goto failed; 431 } 432 ret = smu_read_smc_arg(smu, min); 433 if (ret) 434 goto failed; 435 break; 436 case SMU_UCLK: 437 case SMU_FCLK: 438 case SMU_MCLK: 439 ret = smu_get_dpm_clk_limited(smu, clk_type, 0, min); 440 if (ret) 441 goto failed; 442 break; 443 case SMU_SOCCLK: 444 ret = smu_get_dpm_clk_limited(smu, clk_type, 0, min); 445 if (ret) 446 goto failed; 447 break; 448 default: 449 ret = -EINVAL; 450 goto failed; 451 } 452 } 453failed: 454 return ret; 455} 456 457int smu_v12_0_mode2_reset(struct smu_context *smu){ 458 return smu_v12_0_send_msg_with_param(smu, SMU_MSG_GfxDeviceDriverReset, SMU_RESET_MODE_2); 459} 460 461int smu_v12_0_set_soft_freq_limited_range(struct smu_context *smu, enum smu_clk_type clk_type, 462 uint32_t min, uint32_t max) 463{ 464 int ret = 0; 465 466 if (max < min) 467 return -EINVAL; 468 469 switch (clk_type) { 470 case SMU_GFXCLK: 471 case SMU_SCLK: 472 ret = smu_send_smc_msg_with_param(smu, SMU_MSG_SetHardMinGfxClk, min); 473 if (ret) 474 return ret; 475 476 ret = smu_send_smc_msg_with_param(smu, SMU_MSG_SetSoftMaxGfxClk, max); 477 if (ret) 478 return ret; 479 break; 480 case SMU_FCLK: 481 case SMU_MCLK: 482 ret = smu_send_smc_msg_with_param(smu, SMU_MSG_SetHardMinFclkByFreq, min); 483 if (ret) 484 return ret; 485 486 ret = smu_send_smc_msg_with_param(smu, SMU_MSG_SetSoftMaxFclkByFreq, max); 487 if (ret) 488 return ret; 489 break; 490 case SMU_SOCCLK: 491 ret = smu_send_smc_msg_with_param(smu, SMU_MSG_SetHardMinSocclkByFreq, min); 492 if (ret) 493 return ret; 494 495 ret = smu_send_smc_msg_with_param(smu, SMU_MSG_SetSoftMaxSocclkByFreq, max); 496 if (ret) 497 return ret; 498 break; 499 case SMU_VCLK: 500 ret = smu_send_smc_msg_with_param(smu, SMU_MSG_SetHardMinVcn, min); 501 if (ret) 502 return ret; 503 504 ret = smu_send_smc_msg_with_param(smu, SMU_MSG_SetSoftMaxVcn, max); 505 if (ret) 506 return ret; 507 break; 508 default: 509 return -EINVAL; 510 } 511 512 return ret; 513} 514 515int smu_v12_0_set_driver_table_location(struct smu_context *smu) 516{ 517 struct smu_table *driver_table = &smu->smu_table.driver_table; 518 int ret = 0; 519 520 if (driver_table->mc_address) { 521 ret = smu_send_smc_msg_with_param(smu, 522 SMU_MSG_SetDriverDramAddrHigh, 523 upper_32_bits(driver_table->mc_address)); 524 if (!ret) 525 ret = smu_send_smc_msg_with_param(smu, 526 SMU_MSG_SetDriverDramAddrLow, 527 lower_32_bits(driver_table->mc_address)); 528 } 529 530 return ret; 531} 532