1/* $NetBSD: amdgpu_smu7_thermal.c,v 1.2 2021/12/18 23:45:26 riastradh Exp $ */ 2 3/* 4 * Copyright 2016 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 26#include <sys/cdefs.h> 27__KERNEL_RCSID(0, "$NetBSD: amdgpu_smu7_thermal.c,v 1.2 2021/12/18 23:45:26 riastradh Exp $"); 28 29#include <asm/div64.h> 30#include "smu7_thermal.h" 31#include "smu7_hwmgr.h" 32#include "smu7_common.h" 33 34int smu7_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr, 35 struct phm_fan_speed_info *fan_speed_info) 36{ 37 if (hwmgr->thermal_controller.fanInfo.bNoFan) 38 return -ENODEV; 39 40 fan_speed_info->supports_percent_read = true; 41 fan_speed_info->supports_percent_write = true; 42 fan_speed_info->min_percent = 0; 43 fan_speed_info->max_percent = 100; 44 45 if (PP_CAP(PHM_PlatformCaps_FanSpeedInTableIsRPM) && 46 hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution) { 47 fan_speed_info->supports_rpm_read = true; 48 fan_speed_info->supports_rpm_write = true; 49 fan_speed_info->min_rpm = hwmgr->thermal_controller.fanInfo.ulMinRPM; 50 fan_speed_info->max_rpm = hwmgr->thermal_controller.fanInfo.ulMaxRPM; 51 } else { 52 fan_speed_info->min_rpm = 0; 53 fan_speed_info->max_rpm = 0; 54 } 55 56 return 0; 57} 58 59int smu7_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr, 60 uint32_t *speed) 61{ 62 uint32_t duty100; 63 uint32_t duty; 64 uint64_t tmp64; 65 66 if (hwmgr->thermal_controller.fanInfo.bNoFan) 67 return -ENODEV; 68 69 duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 70 CG_FDO_CTRL1, FMAX_DUTY100); 71 duty = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 72 CG_THERMAL_STATUS, FDO_PWM_DUTY); 73 74 if (duty100 == 0) 75 return -EINVAL; 76 77 78 tmp64 = (uint64_t)duty * 100; 79 do_div(tmp64, duty100); 80 *speed = (uint32_t)tmp64; 81 82 if (*speed > 100) 83 *speed = 100; 84 85 return 0; 86} 87 88int smu7_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed) 89{ 90 uint32_t tach_period; 91 uint32_t crystal_clock_freq; 92 93 if (hwmgr->thermal_controller.fanInfo.bNoFan || 94 !hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution) 95 return -ENODEV; 96 97 tach_period = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 98 CG_TACH_STATUS, TACH_PERIOD); 99 100 if (tach_period == 0) 101 return -EINVAL; 102 103 crystal_clock_freq = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev); 104 105 *speed = 60 * crystal_clock_freq * 10000 / tach_period; 106 107 return 0; 108} 109 110/** 111* Set Fan Speed Control to static mode, so that the user can decide what speed to use. 112* @param hwmgr the address of the powerplay hardware manager. 113* mode the fan control mode, 0 default, 1 by percent, 5, by RPM 114* @exception Should always succeed. 115*/ 116int smu7_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr, uint32_t mode) 117{ 118 if (hwmgr->fan_ctrl_is_in_default_mode) { 119 hwmgr->fan_ctrl_default_mode = 120 PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 121 CG_FDO_CTRL2, FDO_PWM_MODE); 122 hwmgr->tmin = 123 PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 124 CG_FDO_CTRL2, TMIN); 125 hwmgr->fan_ctrl_is_in_default_mode = false; 126 } 127 128 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 129 CG_FDO_CTRL2, TMIN, 0); 130 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 131 CG_FDO_CTRL2, FDO_PWM_MODE, mode); 132 133 return 0; 134} 135 136/** 137* Reset Fan Speed Control to default mode. 138* @param hwmgr the address of the powerplay hardware manager. 139* @exception Should always succeed. 140*/ 141int smu7_fan_ctrl_set_default_mode(struct pp_hwmgr *hwmgr) 142{ 143 if (!hwmgr->fan_ctrl_is_in_default_mode) { 144 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 145 CG_FDO_CTRL2, FDO_PWM_MODE, hwmgr->fan_ctrl_default_mode); 146 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 147 CG_FDO_CTRL2, TMIN, hwmgr->tmin); 148 hwmgr->fan_ctrl_is_in_default_mode = true; 149 } 150 151 return 0; 152} 153 154int smu7_fan_ctrl_start_smc_fan_control(struct pp_hwmgr *hwmgr) 155{ 156 int result; 157 158 if (PP_CAP(PHM_PlatformCaps_ODFuzzyFanControlSupport)) { 159 cgs_write_register(hwmgr->device, mmSMC_MSG_ARG_0, FAN_CONTROL_FUZZY); 160 result = smum_send_msg_to_smc(hwmgr, PPSMC_StartFanControl); 161 162 if (PP_CAP(PHM_PlatformCaps_FanSpeedInTableIsRPM)) 163 hwmgr->hwmgr_func->set_max_fan_rpm_output(hwmgr, 164 hwmgr->thermal_controller. 165 advanceFanControlParameters.usMaxFanRPM); 166 else 167 hwmgr->hwmgr_func->set_max_fan_pwm_output(hwmgr, 168 hwmgr->thermal_controller. 169 advanceFanControlParameters.usMaxFanPWM); 170 171 } else { 172 cgs_write_register(hwmgr->device, mmSMC_MSG_ARG_0, FAN_CONTROL_TABLE); 173 result = smum_send_msg_to_smc(hwmgr, PPSMC_StartFanControl); 174 } 175 176 if (!result && hwmgr->thermal_controller. 177 advanceFanControlParameters.ucTargetTemperature) 178 result = smum_send_msg_to_smc_with_parameter(hwmgr, 179 PPSMC_MSG_SetFanTemperatureTarget, 180 hwmgr->thermal_controller. 181 advanceFanControlParameters.ucTargetTemperature); 182 hwmgr->fan_ctrl_enabled = true; 183 184 return result; 185} 186 187 188int smu7_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr) 189{ 190 hwmgr->fan_ctrl_enabled = false; 191 return smum_send_msg_to_smc(hwmgr, PPSMC_StopFanControl); 192} 193 194/** 195* Set Fan Speed in percent. 196* @param hwmgr the address of the powerplay hardware manager. 197* @param speed is the percentage value (0% - 100%) to be set. 198* @exception Fails is the 100% setting appears to be 0. 199*/ 200int smu7_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr, 201 uint32_t speed) 202{ 203 uint32_t duty100; 204 uint32_t duty; 205 uint64_t tmp64; 206 207 if (hwmgr->thermal_controller.fanInfo.bNoFan) 208 return 0; 209 210 if (speed > 100) 211 speed = 100; 212 213 if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl)) 214 smu7_fan_ctrl_stop_smc_fan_control(hwmgr); 215 216 duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 217 CG_FDO_CTRL1, FMAX_DUTY100); 218 219 if (duty100 == 0) 220 return -EINVAL; 221 222 tmp64 = (uint64_t)speed * duty100; 223 do_div(tmp64, 100); 224 duty = (uint32_t)tmp64; 225 226 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 227 CG_FDO_CTRL0, FDO_STATIC_DUTY, duty); 228 229 return smu7_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); 230} 231 232/** 233* Reset Fan Speed to default. 234* @param hwmgr the address of the powerplay hardware manager. 235* @exception Always succeeds. 236*/ 237int smu7_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr) 238{ 239 int result; 240 241 if (hwmgr->thermal_controller.fanInfo.bNoFan) 242 return 0; 243 244 if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl)) { 245 result = smu7_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); 246 if (!result) 247 result = smu7_fan_ctrl_start_smc_fan_control(hwmgr); 248 } else 249 result = smu7_fan_ctrl_set_default_mode(hwmgr); 250 251 return result; 252} 253 254/** 255* Set Fan Speed in RPM. 256* @param hwmgr the address of the powerplay hardware manager. 257* @param speed is the percentage value (min - max) to be set. 258* @exception Fails is the speed not lie between min and max. 259*/ 260int smu7_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed) 261{ 262 uint32_t tach_period; 263 uint32_t crystal_clock_freq; 264 265 if (hwmgr->thermal_controller.fanInfo.bNoFan || 266 (hwmgr->thermal_controller.fanInfo. 267 ucTachometerPulsesPerRevolution == 0) || 268 speed == 0 || 269 (speed < hwmgr->thermal_controller.fanInfo.ulMinRPM) || 270 (speed > hwmgr->thermal_controller.fanInfo.ulMaxRPM)) 271 return 0; 272 273 if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl)) 274 smu7_fan_ctrl_stop_smc_fan_control(hwmgr); 275 276 crystal_clock_freq = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev); 277 278 tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed); 279 280 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 281 CG_TACH_CTRL, TARGET_PERIOD, tach_period); 282 283 return smu7_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC_RPM); 284} 285 286/** 287* Reads the remote temperature from the SIslands thermal controller. 288* 289* @param hwmgr The address of the hardware manager. 290*/ 291int smu7_thermal_get_temperature(struct pp_hwmgr *hwmgr) 292{ 293 int temp; 294 295 temp = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 296 CG_MULT_THERMAL_STATUS, CTF_TEMP); 297 298 /* Bit 9 means the reading is lower than the lowest usable value. */ 299 if (temp & 0x200) 300 temp = SMU7_THERMAL_MAXIMUM_TEMP_READING; 301 else 302 temp = temp & 0x1ff; 303 304 temp *= PP_TEMPERATURE_UNITS_PER_CENTIGRADES; 305 306 return temp; 307} 308 309/** 310* Set the requested temperature range for high and low alert signals 311* 312* @param hwmgr The address of the hardware manager. 313* @param range Temperature range to be programmed for high and low alert signals 314* @exception PP_Result_BadInput if the input data is not valid. 315*/ 316static int smu7_thermal_set_temperature_range(struct pp_hwmgr *hwmgr, 317 int low_temp, int high_temp) 318{ 319 int low = SMU7_THERMAL_MINIMUM_ALERT_TEMP * 320 PP_TEMPERATURE_UNITS_PER_CENTIGRADES; 321 int high = SMU7_THERMAL_MAXIMUM_ALERT_TEMP * 322 PP_TEMPERATURE_UNITS_PER_CENTIGRADES; 323 324 if (low < low_temp) 325 low = low_temp; 326 if (high > high_temp) 327 high = high_temp; 328 329 if (low > high) 330 return -EINVAL; 331 332 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 333 CG_THERMAL_INT, DIG_THERM_INTH, 334 (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)); 335 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 336 CG_THERMAL_INT, DIG_THERM_INTL, 337 (low / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)); 338 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 339 CG_THERMAL_CTRL, DIG_THERM_DPM, 340 (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)); 341 342 return 0; 343} 344 345/** 346* Programs thermal controller one-time setting registers 347* 348* @param hwmgr The address of the hardware manager. 349*/ 350static int smu7_thermal_initialize(struct pp_hwmgr *hwmgr) 351{ 352 if (hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution) 353 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 354 CG_TACH_CTRL, EDGE_PER_REV, 355 hwmgr->thermal_controller.fanInfo. 356 ucTachometerPulsesPerRevolution - 1); 357 358 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 359 CG_FDO_CTRL2, TACH_PWM_RESP_RATE, 0x28); 360 361 return 0; 362} 363 364/** 365* Enable thermal alerts on the RV770 thermal controller. 366* 367* @param hwmgr The address of the hardware manager. 368*/ 369static void smu7_thermal_enable_alert(struct pp_hwmgr *hwmgr) 370{ 371 uint32_t alert; 372 373 alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 374 CG_THERMAL_INT, THERM_INT_MASK); 375 alert &= ~(SMU7_THERMAL_HIGH_ALERT_MASK | SMU7_THERMAL_LOW_ALERT_MASK); 376 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 377 CG_THERMAL_INT, THERM_INT_MASK, alert); 378 379 /* send message to SMU to enable internal thermal interrupts */ 380 smum_send_msg_to_smc(hwmgr, PPSMC_MSG_Thermal_Cntl_Enable); 381} 382 383/** 384* Disable thermal alerts on the RV770 thermal controller. 385* @param hwmgr The address of the hardware manager. 386*/ 387int smu7_thermal_disable_alert(struct pp_hwmgr *hwmgr) 388{ 389 uint32_t alert; 390 391 alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 392 CG_THERMAL_INT, THERM_INT_MASK); 393 alert |= (SMU7_THERMAL_HIGH_ALERT_MASK | SMU7_THERMAL_LOW_ALERT_MASK); 394 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 395 CG_THERMAL_INT, THERM_INT_MASK, alert); 396 397 /* send message to SMU to disable internal thermal interrupts */ 398 return smum_send_msg_to_smc(hwmgr, PPSMC_MSG_Thermal_Cntl_Disable); 399} 400 401/** 402* Uninitialize the thermal controller. 403* Currently just disables alerts. 404* @param hwmgr The address of the hardware manager. 405*/ 406int smu7_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr) 407{ 408 int result = smu7_thermal_disable_alert(hwmgr); 409 410 if (!hwmgr->thermal_controller.fanInfo.bNoFan) 411 smu7_fan_ctrl_set_default_mode(hwmgr); 412 413 return result; 414} 415 416/** 417* Start the fan control on the SMC. 418* @param hwmgr the address of the powerplay hardware manager. 419* @param pInput the pointer to input data 420* @param pOutput the pointer to output data 421* @param pStorage the pointer to temporary storage 422* @param Result the last failure code 423* @return result from set temperature range routine 424*/ 425static int smu7_thermal_start_smc_fan_control(struct pp_hwmgr *hwmgr) 426{ 427/* If the fantable setup has failed we could have disabled 428 * PHM_PlatformCaps_MicrocodeFanControl even after 429 * this function was included in the table. 430 * Make sure that we still think controlling the fan is OK. 431*/ 432 if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl)) { 433 smu7_fan_ctrl_start_smc_fan_control(hwmgr); 434 smu7_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); 435 } 436 437 return 0; 438} 439 440int smu7_start_thermal_controller(struct pp_hwmgr *hwmgr, 441 struct PP_TemperatureRange *range) 442{ 443 int ret = 0; 444 445 if (range == NULL) 446 return -EINVAL; 447 448 smu7_thermal_initialize(hwmgr); 449 ret = smu7_thermal_set_temperature_range(hwmgr, range->min, range->max); 450 if (ret) 451 return -EINVAL; 452 smu7_thermal_enable_alert(hwmgr); 453 ret = smum_thermal_avfs_enable(hwmgr); 454 if (ret) 455 return -EINVAL; 456 457/* We should restrict performance levels to low before we halt the SMC. 458 * On the other hand we are still in boot state when we do this 459 * so it would be pointless. 460 * If this assumption changes we have to revisit this table. 461 */ 462 smum_thermal_setup_fan_table(hwmgr); 463 smu7_thermal_start_smc_fan_control(hwmgr); 464 return 0; 465} 466 467 468 469int smu7_thermal_ctrl_uninitialize_thermal_controller(struct pp_hwmgr *hwmgr) 470{ 471 if (!hwmgr->thermal_controller.fanInfo.bNoFan) 472 smu7_fan_ctrl_set_default_mode(hwmgr); 473 return 0; 474} 475 476