From 5e8150a6876a2a7784b0029506c90ab3d26c9a41 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 7 Jan 2015 15:29:06 -0500 Subject: [PATCH] drm/radeon: bind fan control on SI cards to hwmon interface This adds a possibility to control fan on SI parts via exported hwmon variables. Note that automatic ucode fan management pauses if you choose to enable manual fan control. Use with caution! Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon_asic.c | 4 ++ drivers/gpu/drm/radeon/radeon_asic.h | 6 +++ drivers/gpu/drm/radeon/si_dpm.c | 65 ++++++++++++++++++++++------ drivers/gpu/drm/radeon/si_dpm.h | 1 + 4 files changed, 62 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index c5ec52388144..7c6e1930fa38 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1953,6 +1953,10 @@ static struct radeon_asic si_asic = { .debugfs_print_current_performance_level = &si_dpm_debugfs_print_current_performance_level, .force_performance_level = &si_dpm_force_performance_level, .vblank_too_short = &ni_dpm_vblank_too_short, + .fan_ctrl_set_mode = &si_fan_ctrl_set_mode, + .fan_ctrl_get_mode = &si_fan_ctrl_get_mode, + .get_fan_speed_percent = &si_fan_ctrl_get_fan_speed_percent, + .set_fan_speed_percent = &si_fan_ctrl_set_fan_speed_percent, }, .pflip = { .page_flip = &evergreen_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index f918a8b0f45b..5142bc97f6b6 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -744,6 +744,12 @@ void si_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, struct seq_file *m); int si_dpm_force_performance_level(struct radeon_device *rdev, enum radeon_dpm_forced_level level); +int si_fan_ctrl_get_fan_speed_percent(struct radeon_device *rdev, + u32 *speed); +int si_fan_ctrl_set_fan_speed_percent(struct radeon_device *rdev, + u32 speed); +u32 si_fan_ctrl_get_mode(struct radeon_device *rdev); +void si_fan_ctrl_set_mode(struct radeon_device *rdev, u32 mode); /* DCE8 - CIK */ void dce8_bandwidth_update(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index eff8a6444956..3cde272f82b2 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -1756,6 +1756,9 @@ static int si_calculate_sclk_params(struct radeon_device *rdev, u32 engine_clock, SISLANDS_SMC_SCLK_VALUE *sclk); +static void si_thermal_start_smc_fan_control(struct radeon_device *rdev); +static void si_fan_ctrl_set_default_mode(struct radeon_device *rdev); + static struct si_power_info *si_get_pi(struct radeon_device *rdev) { struct si_power_info *pi = rdev->pm.dpm.priv; @@ -6012,36 +6015,46 @@ static int si_thermal_setup_fan_table(struct radeon_device *rdev) static int si_fan_ctrl_start_smc_fan_control(struct radeon_device *rdev) { + struct si_power_info *si_pi = si_get_pi(rdev); PPSMC_Result ret; ret = si_send_msg_to_smc(rdev, PPSMC_StartFanControl); - if (ret == PPSMC_Result_OK) + if (ret == PPSMC_Result_OK) { + si_pi->fan_is_controlled_by_smc = true; return 0; - else + } else { return -EINVAL; + } } static int si_fan_ctrl_stop_smc_fan_control(struct radeon_device *rdev) { + struct si_power_info *si_pi = si_get_pi(rdev); PPSMC_Result ret; ret = si_send_msg_to_smc(rdev, PPSMC_StopFanControl); - if (ret == PPSMC_Result_OK) + + if (ret == PPSMC_Result_OK) { + si_pi->fan_is_controlled_by_smc = false; return 0; - else + } else { return -EINVAL; + } } -#if 0 -static int si_fan_ctrl_get_fan_speed_percent(struct radeon_device *rdev, - u32 *speed) +int si_fan_ctrl_get_fan_speed_percent(struct radeon_device *rdev, + u32 *speed) { + struct si_power_info *si_pi = si_get_pi(rdev); u32 duty, duty100; u64 tmp64; if (rdev->pm.no_fan) return -ENOENT; + if (si_pi->fan_is_controlled_by_smc) + return -EINVAL; + duty100 = (RREG32(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT; duty = (RREG32(CG_THERMAL_STATUS) & FDO_PWM_DUTY_MASK) >> FDO_PWM_DUTY_SHIFT; @@ -6058,8 +6071,8 @@ static int si_fan_ctrl_get_fan_speed_percent(struct radeon_device *rdev, return 0; } -static int si_fan_ctrl_set_fan_speed_percent(struct radeon_device *rdev, - u32 speed) +int si_fan_ctrl_set_fan_speed_percent(struct radeon_device *rdev, + u32 speed) { u32 tmp; u32 duty, duty100; @@ -6071,9 +6084,6 @@ static int si_fan_ctrl_set_fan_speed_percent(struct radeon_device *rdev, if (speed > 100) return -EINVAL; - if (rdev->pm.dpm.fan.ucode_fan_control) - si_fan_ctrl_stop_smc_fan_control(rdev); - duty100 = (RREG32(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT; if (duty100 == 0) @@ -6087,11 +6097,38 @@ static int si_fan_ctrl_set_fan_speed_percent(struct radeon_device *rdev, tmp |= FDO_STATIC_DUTY(duty); WREG32(CG_FDO_CTRL0, tmp); - si_fan_ctrl_set_static_mode(rdev, FDO_PWM_MODE_STATIC); - return 0; } +void si_fan_ctrl_set_mode(struct radeon_device *rdev, u32 mode) +{ + if (mode) { + /* stop auto-manage */ + if (rdev->pm.dpm.fan.ucode_fan_control) + si_fan_ctrl_stop_smc_fan_control(rdev); + si_fan_ctrl_set_static_mode(rdev, mode); + } else { + /* restart auto-manage */ + if (rdev->pm.dpm.fan.ucode_fan_control) + si_thermal_start_smc_fan_control(rdev); + else + si_fan_ctrl_set_default_mode(rdev); + } +} + +u32 si_fan_ctrl_get_mode(struct radeon_device *rdev) +{ + struct si_power_info *si_pi = si_get_pi(rdev); + u32 tmp; + + if (si_pi->fan_is_controlled_by_smc) + return 0; + + tmp = RREG32(CG_FDO_CTRL2) & FDO_PWM_MODE_MASK; + return (tmp >> FDO_PWM_MODE_SHIFT); +} + +#if 0 static int si_fan_ctrl_get_fan_speed_rpm(struct radeon_device *rdev, u32 *speed) { diff --git a/drivers/gpu/drm/radeon/si_dpm.h b/drivers/gpu/drm/radeon/si_dpm.h index d16bb1b5f10f..1032a68be792 100644 --- a/drivers/gpu/drm/radeon/si_dpm.h +++ b/drivers/gpu/drm/radeon/si_dpm.h @@ -202,6 +202,7 @@ struct si_power_info { bool fan_ctrl_is_in_default_mode; u32 t_min; u32 fan_ctrl_default_mode; + bool fan_is_controlled_by_smc; }; #define SISLANDS_INITIAL_STATE_ARB_INDEX 0