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 cgs_write_register(hwmgr->device, mmSMC_MSG_ARG_0, FAN_CONTROL_FUZZY); 155 result = smum_send_msg_to_smc(hwmgr, PPSMC_StartFanControl); 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 cgs_write_register(hwmgr->device, mmSMC_MSG_ARG_0, FAN_CONTROL_TABLE); 168 result = smum_send_msg_to_smc(hwmgr, PPSMC_StartFanControl); 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 hwmgr->fan_ctrl_enabled = true; 178 179 return result; 180 } 181 182 183 int smu7_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr) 184 { 185 hwmgr->fan_ctrl_enabled = false; 186 return smum_send_msg_to_smc(hwmgr, PPSMC_StopFanControl); 187 } 188 189 /** 190 * Set Fan Speed in percent. 191 * @param hwmgr the address of the powerplay hardware manager. 192 * @param speed is the percentage value (0% - 100%) to be set. 193 * @exception Fails is the 100% setting appears to be 0. 194 */ 195 int smu7_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr, 196 uint32_t speed) 197 { 198 uint32_t duty100; 199 uint32_t duty; 200 uint64_t tmp64; 201 202 if (hwmgr->thermal_controller.fanInfo.bNoFan) 203 return 0; 204 205 if (speed > 100) 206 speed = 100; 207 208 if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl)) 209 smu7_fan_ctrl_stop_smc_fan_control(hwmgr); 210 211 duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 212 CG_FDO_CTRL1, FMAX_DUTY100); 213 214 if (duty100 == 0) 215 return -EINVAL; 216 217 tmp64 = (uint64_t)speed * duty100; 218 do_div(tmp64, 100); 219 duty = (uint32_t)tmp64; 220 221 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 222 CG_FDO_CTRL0, FDO_STATIC_DUTY, duty); 223 224 return smu7_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); 225 } 226 227 /** 228 * Reset Fan Speed to default. 229 * @param hwmgr the address of the powerplay hardware manager. 230 * @exception Always succeeds. 231 */ 232 int smu7_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr) 233 { 234 int result; 235 236 if (hwmgr->thermal_controller.fanInfo.bNoFan) 237 return 0; 238 239 if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl)) { 240 result = smu7_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); 241 if (!result) 242 result = smu7_fan_ctrl_start_smc_fan_control(hwmgr); 243 } else 244 result = smu7_fan_ctrl_set_default_mode(hwmgr); 245 246 return result; 247 } 248 249 /** 250 * Set Fan Speed in RPM. 251 * @param hwmgr the address of the powerplay hardware manager. 252 * @param speed is the percentage value (min - max) to be set. 253 * @exception Fails is the speed not lie between min and max. 254 */ 255 int smu7_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed) 256 { 257 uint32_t tach_period; 258 uint32_t crystal_clock_freq; 259 260 if (hwmgr->thermal_controller.fanInfo.bNoFan || 261 (hwmgr->thermal_controller.fanInfo. 262 ucTachometerPulsesPerRevolution == 0) || 263 (speed < hwmgr->thermal_controller.fanInfo.ulMinRPM) || 264 (speed > hwmgr->thermal_controller.fanInfo.ulMaxRPM)) 265 return 0; 266 267 if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl)) 268 smu7_fan_ctrl_stop_smc_fan_control(hwmgr); 269 270 crystal_clock_freq = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev); 271 272 tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed); 273 274 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 275 CG_TACH_STATUS, TACH_PERIOD, tach_period); 276 277 return smu7_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC_RPM); 278 } 279 280 /** 281 * Reads the remote temperature from the SIslands thermal controller. 282 * 283 * @param hwmgr The address of the hardware manager. 284 */ 285 int smu7_thermal_get_temperature(struct pp_hwmgr *hwmgr) 286 { 287 int temp; 288 289 temp = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 290 CG_MULT_THERMAL_STATUS, CTF_TEMP); 291 292 /* Bit 9 means the reading is lower than the lowest usable value. */ 293 if (temp & 0x200) 294 temp = SMU7_THERMAL_MAXIMUM_TEMP_READING; 295 else 296 temp = temp & 0x1ff; 297 298 temp *= PP_TEMPERATURE_UNITS_PER_CENTIGRADES; 299 300 return temp; 301 } 302 303 /** 304 * Set the requested temperature range for high and low alert signals 305 * 306 * @param hwmgr The address of the hardware manager. 307 * @param range Temperature range to be programmed for high and low alert signals 308 * @exception PP_Result_BadInput if the input data is not valid. 309 */ 310 static int smu7_thermal_set_temperature_range(struct pp_hwmgr *hwmgr, 311 int low_temp, int high_temp) 312 { 313 int low = SMU7_THERMAL_MINIMUM_ALERT_TEMP * 314 PP_TEMPERATURE_UNITS_PER_CENTIGRADES; 315 int high = SMU7_THERMAL_MAXIMUM_ALERT_TEMP * 316 PP_TEMPERATURE_UNITS_PER_CENTIGRADES; 317 318 if (low < low_temp) 319 low = low_temp; 320 if (high > high_temp) 321 high = high_temp; 322 323 if (low > high) 324 return -EINVAL; 325 326 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 327 CG_THERMAL_INT, DIG_THERM_INTH, 328 (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)); 329 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 330 CG_THERMAL_INT, DIG_THERM_INTL, 331 (low / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)); 332 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 333 CG_THERMAL_CTRL, DIG_THERM_DPM, 334 (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)); 335 336 return 0; 337 } 338 339 /** 340 * Programs thermal controller one-time setting registers 341 * 342 * @param hwmgr The address of the hardware manager. 343 */ 344 static int smu7_thermal_initialize(struct pp_hwmgr *hwmgr) 345 { 346 if (hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution) 347 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 348 CG_TACH_CTRL, EDGE_PER_REV, 349 hwmgr->thermal_controller.fanInfo. 350 ucTachometerPulsesPerRevolution - 1); 351 352 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 353 CG_FDO_CTRL2, TACH_PWM_RESP_RATE, 0x28); 354 355 return 0; 356 } 357 358 /** 359 * Enable thermal alerts on the RV770 thermal controller. 360 * 361 * @param hwmgr The address of the hardware manager. 362 */ 363 static void smu7_thermal_enable_alert(struct pp_hwmgr *hwmgr) 364 { 365 uint32_t alert; 366 367 alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 368 CG_THERMAL_INT, THERM_INT_MASK); 369 alert &= ~(SMU7_THERMAL_HIGH_ALERT_MASK | SMU7_THERMAL_LOW_ALERT_MASK); 370 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 371 CG_THERMAL_INT, THERM_INT_MASK, alert); 372 373 /* send message to SMU to enable internal thermal interrupts */ 374 smum_send_msg_to_smc(hwmgr, PPSMC_MSG_Thermal_Cntl_Enable); 375 } 376 377 /** 378 * Disable thermal alerts on the RV770 thermal controller. 379 * @param hwmgr The address of the hardware manager. 380 */ 381 int smu7_thermal_disable_alert(struct pp_hwmgr *hwmgr) 382 { 383 uint32_t alert; 384 385 alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 386 CG_THERMAL_INT, THERM_INT_MASK); 387 alert |= (SMU7_THERMAL_HIGH_ALERT_MASK | SMU7_THERMAL_LOW_ALERT_MASK); 388 PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, 389 CG_THERMAL_INT, THERM_INT_MASK, alert); 390 391 /* send message to SMU to disable internal thermal interrupts */ 392 return smum_send_msg_to_smc(hwmgr, PPSMC_MSG_Thermal_Cntl_Disable); 393 } 394 395 /** 396 * Uninitialize the thermal controller. 397 * Currently just disables alerts. 398 * @param hwmgr The address of the hardware manager. 399 */ 400 int smu7_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr) 401 { 402 int result = smu7_thermal_disable_alert(hwmgr); 403 404 if (!hwmgr->thermal_controller.fanInfo.bNoFan) 405 smu7_fan_ctrl_set_default_mode(hwmgr); 406 407 return result; 408 } 409 410 /** 411 * Start the fan control on the SMC. 412 * @param hwmgr the address of the powerplay hardware manager. 413 * @param pInput the pointer to input data 414 * @param pOutput the pointer to output data 415 * @param pStorage the pointer to temporary storage 416 * @param Result the last failure code 417 * @return result from set temperature range routine 418 */ 419 static int smu7_thermal_start_smc_fan_control(struct pp_hwmgr *hwmgr) 420 { 421 /* If the fantable setup has failed we could have disabled 422 * PHM_PlatformCaps_MicrocodeFanControl even after 423 * this function was included in the table. 424 * Make sure that we still think controlling the fan is OK. 425 */ 426 if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl)) { 427 smu7_fan_ctrl_start_smc_fan_control(hwmgr); 428 smu7_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); 429 } 430 431 return 0; 432 } 433 434 int smu7_start_thermal_controller(struct pp_hwmgr *hwmgr, 435 struct PP_TemperatureRange *range) 436 { 437 int ret = 0; 438 439 if (range == NULL) 440 return -EINVAL; 441 442 smu7_thermal_initialize(hwmgr); 443 ret = smu7_thermal_set_temperature_range(hwmgr, range->min, range->max); 444 if (ret) 445 return -EINVAL; 446 smu7_thermal_enable_alert(hwmgr); 447 ret = smum_thermal_avfs_enable(hwmgr); 448 if (ret) 449 return -EINVAL; 450 451 /* We should restrict performance levels to low before we halt the SMC. 452 * On the other hand we are still in boot state when we do this 453 * so it would be pointless. 454 * If this assumption changes we have to revisit this table. 455 */ 456 smum_thermal_setup_fan_table(hwmgr); 457 smu7_thermal_start_smc_fan_control(hwmgr); 458 return 0; 459 } 460 461 462 463 int smu7_thermal_ctrl_uninitialize_thermal_controller(struct pp_hwmgr *hwmgr) 464 { 465 if (!hwmgr->thermal_controller.fanInfo.bNoFan) 466 smu7_fan_ctrl_set_default_mode(hwmgr); 467 return 0; 468 } 469 470