linux/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.c
<<
>>
Prefs
   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
  29int 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
  54int 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
  83int 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*/
 111int 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*/
 136int 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
 149int 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
 183int 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*/
 195int 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*/
 232int 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*/
 255int 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 == 0 ||
 264                        (speed < hwmgr->thermal_controller.fanInfo.ulMinRPM) ||
 265                        (speed > hwmgr->thermal_controller.fanInfo.ulMaxRPM))
 266                return 0;
 267
 268        if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl))
 269                smu7_fan_ctrl_stop_smc_fan_control(hwmgr);
 270
 271        crystal_clock_freq = amdgpu_asic_get_xclk((struct amdgpu_device *)hwmgr->adev);
 272
 273        tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed);
 274
 275        PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
 276                                CG_TACH_CTRL, TARGET_PERIOD, tach_period);
 277
 278        return smu7_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC_RPM);
 279}
 280
 281/**
 282* Reads the remote temperature from the SIslands thermal controller.
 283*
 284* @param    hwmgr The address of the hardware manager.
 285*/
 286int smu7_thermal_get_temperature(struct pp_hwmgr *hwmgr)
 287{
 288        int temp;
 289
 290        temp = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
 291                        CG_MULT_THERMAL_STATUS, CTF_TEMP);
 292
 293        /* Bit 9 means the reading is lower than the lowest usable value. */
 294        if (temp & 0x200)
 295                temp = SMU7_THERMAL_MAXIMUM_TEMP_READING;
 296        else
 297                temp = temp & 0x1ff;
 298
 299        temp *= PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
 300
 301        return temp;
 302}
 303
 304/**
 305* Set the requested temperature range for high and low alert signals
 306*
 307* @param    hwmgr The address of the hardware manager.
 308* @param    range Temperature range to be programmed for high and low alert signals
 309* @exception PP_Result_BadInput if the input data is not valid.
 310*/
 311static int smu7_thermal_set_temperature_range(struct pp_hwmgr *hwmgr,
 312                int low_temp, int high_temp)
 313{
 314        int low = SMU7_THERMAL_MINIMUM_ALERT_TEMP *
 315                        PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
 316        int high = SMU7_THERMAL_MAXIMUM_ALERT_TEMP *
 317                        PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
 318
 319        if (low < low_temp)
 320                low = low_temp;
 321        if (high > high_temp)
 322                high = high_temp;
 323
 324        if (low > high)
 325                return -EINVAL;
 326
 327        PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
 328                        CG_THERMAL_INT, DIG_THERM_INTH,
 329                        (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
 330        PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
 331                        CG_THERMAL_INT, DIG_THERM_INTL,
 332                        (low / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
 333        PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
 334                        CG_THERMAL_CTRL, DIG_THERM_DPM,
 335                        (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
 336
 337        return 0;
 338}
 339
 340/**
 341* Programs thermal controller one-time setting registers
 342*
 343* @param    hwmgr The address of the hardware manager.
 344*/
 345static int smu7_thermal_initialize(struct pp_hwmgr *hwmgr)
 346{
 347        if (hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution)
 348                PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
 349                                CG_TACH_CTRL, EDGE_PER_REV,
 350                                hwmgr->thermal_controller.fanInfo.
 351                                ucTachometerPulsesPerRevolution - 1);
 352
 353        PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
 354                        CG_FDO_CTRL2, TACH_PWM_RESP_RATE, 0x28);
 355
 356        return 0;
 357}
 358
 359/**
 360* Enable thermal alerts on the RV770 thermal controller.
 361*
 362* @param    hwmgr The address of the hardware manager.
 363*/
 364static void smu7_thermal_enable_alert(struct pp_hwmgr *hwmgr)
 365{
 366        uint32_t alert;
 367
 368        alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
 369                        CG_THERMAL_INT, THERM_INT_MASK);
 370        alert &= ~(SMU7_THERMAL_HIGH_ALERT_MASK | SMU7_THERMAL_LOW_ALERT_MASK);
 371        PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
 372                        CG_THERMAL_INT, THERM_INT_MASK, alert);
 373
 374        /* send message to SMU to enable internal thermal interrupts */
 375        smum_send_msg_to_smc(hwmgr, PPSMC_MSG_Thermal_Cntl_Enable);
 376}
 377
 378/**
 379* Disable thermal alerts on the RV770 thermal controller.
 380* @param    hwmgr The address of the hardware manager.
 381*/
 382int smu7_thermal_disable_alert(struct pp_hwmgr *hwmgr)
 383{
 384        uint32_t alert;
 385
 386        alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
 387                        CG_THERMAL_INT, THERM_INT_MASK);
 388        alert |= (SMU7_THERMAL_HIGH_ALERT_MASK | SMU7_THERMAL_LOW_ALERT_MASK);
 389        PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
 390                        CG_THERMAL_INT, THERM_INT_MASK, alert);
 391
 392        /* send message to SMU to disable internal thermal interrupts */
 393        return smum_send_msg_to_smc(hwmgr, PPSMC_MSG_Thermal_Cntl_Disable);
 394}
 395
 396/**
 397* Uninitialize the thermal controller.
 398* Currently just disables alerts.
 399* @param    hwmgr The address of the hardware manager.
 400*/
 401int smu7_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr)
 402{
 403        int result = smu7_thermal_disable_alert(hwmgr);
 404
 405        if (!hwmgr->thermal_controller.fanInfo.bNoFan)
 406                smu7_fan_ctrl_set_default_mode(hwmgr);
 407
 408        return result;
 409}
 410
 411/**
 412* Start the fan control on the SMC.
 413* @param    hwmgr  the address of the powerplay hardware manager.
 414* @param    pInput the pointer to input data
 415* @param    pOutput the pointer to output data
 416* @param    pStorage the pointer to temporary storage
 417* @param    Result the last failure code
 418* @return   result from set temperature range routine
 419*/
 420static int smu7_thermal_start_smc_fan_control(struct pp_hwmgr *hwmgr)
 421{
 422/* If the fantable setup has failed we could have disabled
 423 * PHM_PlatformCaps_MicrocodeFanControl even after
 424 * this function was included in the table.
 425 * Make sure that we still think controlling the fan is OK.
 426*/
 427        if (PP_CAP(PHM_PlatformCaps_MicrocodeFanControl)) {
 428                smu7_fan_ctrl_start_smc_fan_control(hwmgr);
 429                smu7_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
 430        }
 431
 432        return 0;
 433}
 434
 435int smu7_start_thermal_controller(struct pp_hwmgr *hwmgr,
 436                                struct PP_TemperatureRange *range)
 437{
 438        int ret = 0;
 439
 440        if (range == NULL)
 441                return -EINVAL;
 442
 443        smu7_thermal_initialize(hwmgr);
 444        ret = smu7_thermal_set_temperature_range(hwmgr, range->min, range->max);
 445        if (ret)
 446                return -EINVAL;
 447        smu7_thermal_enable_alert(hwmgr);
 448        ret = smum_thermal_avfs_enable(hwmgr);
 449        if (ret)
 450                return -EINVAL;
 451
 452/* We should restrict performance levels to low before we halt the SMC.
 453 * On the other hand we are still in boot state when we do this
 454 * so it would be pointless.
 455 * If this assumption changes we have to revisit this table.
 456 */
 457        smum_thermal_setup_fan_table(hwmgr);
 458        smu7_thermal_start_smc_fan_control(hwmgr);
 459        return 0;
 460}
 461
 462
 463
 464int smu7_thermal_ctrl_uninitialize_thermal_controller(struct pp_hwmgr *hwmgr)
 465{
 466        if (!hwmgr->thermal_controller.fanInfo.bNoFan)
 467                smu7_fan_ctrl_set_default_mode(hwmgr);
 468        return 0;
 469}
 470
 471