1 /* $NetBSD: amdgpu_vega20_thermal.c,v 1.2 2021/12/18 23:45:26 riastradh Exp $ */ 2 3 /* 4 * Copyright 2018 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_vega20_thermal.c,v 1.2 2021/12/18 23:45:26 riastradh Exp $"); 28 29 #include "vega20_thermal.h" 30 #include "vega20_hwmgr.h" 31 #include "vega20_smumgr.h" 32 #include "vega20_ppsmc.h" 33 #include "vega20_inc.h" 34 #include "soc15_common.h" 35 #include "pp_debug.h" 36 37 static int vega20_disable_fan_control_feature(struct pp_hwmgr *hwmgr) 38 { 39 struct vega20_hwmgr *data = hwmgr->backend; 40 int ret = 0; 41 42 if (data->smu_features[GNLD_FAN_CONTROL].supported) { 43 ret = vega20_enable_smc_features( 44 hwmgr, false, 45 data->smu_features[GNLD_FAN_CONTROL]. 46 smu_feature_bitmap); 47 PP_ASSERT_WITH_CODE(!ret, 48 "Disable FAN CONTROL feature Failed!", 49 return ret); 50 data->smu_features[GNLD_FAN_CONTROL].enabled = false; 51 } 52 53 return ret; 54 } 55 56 int vega20_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr) 57 { 58 struct vega20_hwmgr *data = hwmgr->backend; 59 60 if (data->smu_features[GNLD_FAN_CONTROL].supported) 61 return vega20_disable_fan_control_feature(hwmgr); 62 63 return 0; 64 } 65 66 static int vega20_enable_fan_control_feature(struct pp_hwmgr *hwmgr) 67 { 68 struct vega20_hwmgr *data = hwmgr->backend; 69 int ret = 0; 70 71 if (data->smu_features[GNLD_FAN_CONTROL].supported) { 72 ret = vega20_enable_smc_features( 73 hwmgr, true, 74 data->smu_features[GNLD_FAN_CONTROL]. 75 smu_feature_bitmap); 76 PP_ASSERT_WITH_CODE(!ret, 77 "Enable FAN CONTROL feature Failed!", 78 return ret); 79 data->smu_features[GNLD_FAN_CONTROL].enabled = true; 80 } 81 82 return ret; 83 } 84 85 int vega20_fan_ctrl_start_smc_fan_control(struct pp_hwmgr *hwmgr) 86 { 87 struct vega20_hwmgr *data = hwmgr->backend; 88 89 if (data->smu_features[GNLD_FAN_CONTROL].supported) 90 return vega20_enable_fan_control_feature(hwmgr); 91 92 return 0; 93 } 94 95 static int vega20_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr, uint32_t mode) 96 { 97 struct amdgpu_device *adev = hwmgr->adev; 98 99 WREG32_SOC15(THM, 0, mmCG_FDO_CTRL2, 100 REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL2), 101 CG_FDO_CTRL2, TMIN, 0)); 102 WREG32_SOC15(THM, 0, mmCG_FDO_CTRL2, 103 REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL2), 104 CG_FDO_CTRL2, FDO_PWM_MODE, mode)); 105 106 return 0; 107 } 108 109 static int vega20_get_current_rpm(struct pp_hwmgr *hwmgr, uint32_t *current_rpm) 110 { 111 int ret = 0; 112 113 PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc(hwmgr, 114 PPSMC_MSG_GetCurrentRpm)) == 0, 115 "Attempt to get current RPM from SMC Failed!", 116 return ret); 117 *current_rpm = smum_get_argument(hwmgr); 118 119 return 0; 120 } 121 122 int vega20_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr, 123 uint32_t *speed) 124 { 125 struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend); 126 PPTable_t *pp_table = &(data->smc_state_table.pp_table); 127 uint32_t current_rpm, percent = 0; 128 int ret = 0; 129 130 ret = vega20_get_current_rpm(hwmgr, ¤t_rpm); 131 if (ret) 132 return ret; 133 134 percent = current_rpm * 100 / pp_table->FanMaximumRpm; 135 136 *speed = percent > 100 ? 100 : percent; 137 138 return 0; 139 } 140 141 int vega20_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr, 142 uint32_t speed) 143 { 144 struct amdgpu_device *adev = hwmgr->adev; 145 uint32_t duty100; 146 uint32_t duty; 147 uint64_t tmp64; 148 149 if (speed > 100) 150 speed = 100; 151 152 if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl)) 153 vega20_fan_ctrl_stop_smc_fan_control(hwmgr); 154 155 duty100 = REG_GET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL1), 156 CG_FDO_CTRL1, FMAX_DUTY100); 157 158 if (duty100 == 0) 159 return -EINVAL; 160 161 tmp64 = (uint64_t)speed * duty100; 162 do_div(tmp64, 100); 163 duty = (uint32_t)tmp64; 164 165 WREG32_SOC15(THM, 0, mmCG_FDO_CTRL0, 166 REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL0), 167 CG_FDO_CTRL0, FDO_STATIC_DUTY, duty)); 168 169 return vega20_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); 170 } 171 172 int vega20_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr, 173 struct phm_fan_speed_info *fan_speed_info) 174 { 175 memset(fan_speed_info, 0, sizeof(*fan_speed_info)); 176 fan_speed_info->supports_percent_read = true; 177 fan_speed_info->supports_percent_write = true; 178 fan_speed_info->supports_rpm_read = true; 179 fan_speed_info->supports_rpm_write = true; 180 181 return 0; 182 } 183 184 int vega20_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed) 185 { 186 *speed = 0; 187 188 return vega20_get_current_rpm(hwmgr, speed); 189 } 190 191 int vega20_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed) 192 { 193 struct amdgpu_device *adev = hwmgr->adev; 194 uint32_t tach_period, crystal_clock_freq; 195 int result = 0; 196 197 if (!speed) 198 return -EINVAL; 199 200 if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl)) { 201 result = vega20_fan_ctrl_stop_smc_fan_control(hwmgr); 202 if (result) 203 return result; 204 } 205 206 crystal_clock_freq = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev); 207 tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed); 208 WREG32_SOC15(THM, 0, mmCG_TACH_CTRL, 209 REG_SET_FIELD(RREG32_SOC15(THM, 0, mmCG_TACH_CTRL), 210 CG_TACH_CTRL, TARGET_PERIOD, 211 tach_period)); 212 213 return vega20_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC_RPM); 214 } 215 216 /** 217 * Reads the remote temperature from the SIslands thermal controller. 218 * 219 * @param hwmgr The address of the hardware manager. 220 */ 221 int vega20_thermal_get_temperature(struct pp_hwmgr *hwmgr) 222 { 223 struct amdgpu_device *adev = hwmgr->adev; 224 int temp = 0; 225 226 temp = RREG32_SOC15(THM, 0, mmCG_MULT_THERMAL_STATUS); 227 228 temp = (temp & CG_MULT_THERMAL_STATUS__CTF_TEMP_MASK) >> 229 CG_MULT_THERMAL_STATUS__CTF_TEMP__SHIFT; 230 231 temp = temp & 0x1ff; 232 233 temp *= PP_TEMPERATURE_UNITS_PER_CENTIGRADES; 234 return temp; 235 } 236 237 /** 238 * Set the requested temperature range for high and low alert signals 239 * 240 * @param hwmgr The address of the hardware manager. 241 * @param range Temperature range to be programmed for 242 * high and low alert signals 243 * @exception PP_Result_BadInput if the input data is not valid. 244 */ 245 static int vega20_thermal_set_temperature_range(struct pp_hwmgr *hwmgr, 246 struct PP_TemperatureRange *range) 247 { 248 struct amdgpu_device *adev = hwmgr->adev; 249 int low = VEGA20_THERMAL_MINIMUM_ALERT_TEMP * 250 PP_TEMPERATURE_UNITS_PER_CENTIGRADES; 251 int high = VEGA20_THERMAL_MAXIMUM_ALERT_TEMP * 252 PP_TEMPERATURE_UNITS_PER_CENTIGRADES; 253 uint32_t val; 254 255 if (low < range->min) 256 low = range->min; 257 if (high > range->max) 258 high = range->max; 259 260 if (low > high) 261 return -EINVAL; 262 263 val = RREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_CTRL); 264 265 val = CGS_REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, MAX_IH_CREDIT, 5); 266 val = CGS_REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, THERM_IH_HW_ENA, 1); 267 val = CGS_REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, DIG_THERM_INTH, (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)); 268 val = CGS_REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, DIG_THERM_INTL, (low / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)); 269 val = val & (~THM_THERMAL_INT_CTRL__THERM_TRIGGER_MASK_MASK); 270 271 WREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_CTRL, val); 272 273 return 0; 274 } 275 276 /** 277 * Enable thermal alerts on the RV770 thermal controller. 278 * 279 * @param hwmgr The address of the hardware manager. 280 */ 281 static int vega20_thermal_enable_alert(struct pp_hwmgr *hwmgr) 282 { 283 struct amdgpu_device *adev = hwmgr->adev; 284 uint32_t val = 0; 285 286 val |= (1 << THM_THERMAL_INT_ENA__THERM_INTH_CLR__SHIFT); 287 val |= (1 << THM_THERMAL_INT_ENA__THERM_INTL_CLR__SHIFT); 288 val |= (1 << THM_THERMAL_INT_ENA__THERM_TRIGGER_CLR__SHIFT); 289 290 WREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_ENA, val); 291 292 return 0; 293 } 294 295 /** 296 * Disable thermal alerts on the RV770 thermal controller. 297 * @param hwmgr The address of the hardware manager. 298 */ 299 int vega20_thermal_disable_alert(struct pp_hwmgr *hwmgr) 300 { 301 struct amdgpu_device *adev = hwmgr->adev; 302 303 WREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_ENA, 0); 304 305 return 0; 306 } 307 308 /** 309 * Uninitialize the thermal controller. 310 * Currently just disables alerts. 311 * @param hwmgr The address of the hardware manager. 312 */ 313 int vega20_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr) 314 { 315 int result = vega20_thermal_disable_alert(hwmgr); 316 317 return result; 318 } 319 320 /** 321 * Set up the fan table to control the fan using the SMC. 322 * @param hwmgr the address of the powerplay hardware manager. 323 * @param pInput the pointer to input data 324 * @param pOutput the pointer to output data 325 * @param pStorage the pointer to temporary storage 326 * @param Result the last failure code 327 * @return result from set temperature range routine 328 */ 329 static int vega20_thermal_setup_fan_table(struct pp_hwmgr *hwmgr) 330 { 331 int ret; 332 struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend); 333 PPTable_t *table = &(data->smc_state_table.pp_table); 334 335 ret = smum_send_msg_to_smc_with_parameter(hwmgr, 336 PPSMC_MSG_SetFanTemperatureTarget, 337 (uint32_t)table->FanTargetTemperature); 338 339 return ret; 340 } 341 342 int vega20_start_thermal_controller(struct pp_hwmgr *hwmgr, 343 struct PP_TemperatureRange *range) 344 { 345 int ret = 0; 346 347 if (range == NULL) 348 return -EINVAL; 349 350 ret = vega20_thermal_set_temperature_range(hwmgr, range); 351 if (ret) 352 return ret; 353 354 ret = vega20_thermal_enable_alert(hwmgr); 355 if (ret) 356 return ret; 357 358 ret = vega20_thermal_setup_fan_table(hwmgr); 359 360 return ret; 361 }; 362