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