Adds 4 callbacks for managing fan state/speed * fan_ctrl_set_mode - manages fan control mode (nonzero == manual) * fan_ctrl_get_mode - queries fan control mode * set_fan_speed_percent - manages fan speed as percentage value * get_fan_speed_percent - queries fan speed as percentage value
Signed-off-by: Oleg Chernovskiy algonkvel@gmail.com --- drivers/gpu/drm/radeon/radeon.h | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 54529b8..4195e6c 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1967,6 +1967,10 @@ struct radeon_asic { bool (*vblank_too_short)(struct radeon_device *rdev); void (*powergate_uvd)(struct radeon_device *rdev, bool gate); void (*enable_bapm)(struct radeon_device *rdev, bool enable); + void (*fan_ctrl_set_mode)(struct radeon_device *rdev, u32 mode); + u32 (*fan_ctrl_get_mode)(struct radeon_device *rdev); + int (*set_fan_speed_percent)(struct radeon_device *rdev, u32 speed); + int (*get_fan_speed_percent)(struct radeon_device *rdev, u32 *speed); } dpm; /* pageflipping */ struct {
This adds percent-based fan control. Attributes (I hope) follow the sysfs-interface specification: * pwm1 - fan speed query/manage * pwm1_max, pwm1_min - min/max values for fan pwm (constant) * pwm1_enable - fan control query/manage (enable/disable)
(There is no rpm-based control for now)
Signed-off-by: Oleg Chernovskiy algonkvel@gmail.com --- drivers/gpu/drm/radeon/radeon_pm.c | 129 ++++++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 32522cc..58daa23 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -24,6 +24,7 @@ #include "radeon.h" #include "avivod.h" #include "atom.h" +#include "r600_dpm.h" #include <linux/power_supply.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> @@ -554,6 +555,94 @@ fail: return count; }
+static ssize_t radeon_hwmon_get_pwm1_enable(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct radeon_device *rdev = dev_get_drvdata(dev); + u32 pwm_mode = 0; + + if(rdev->asic->dpm.fan_ctrl_get_mode) + pwm_mode = rdev->asic->dpm.fan_ctrl_get_mode(rdev); + + return sprintf(buf, "%i\n", pwm_mode == FDO_PWM_MODE_STATIC); +} + +static ssize_t radeon_hwmon_set_pwm1_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct radeon_device *rdev = dev_get_drvdata(dev); + int err; + int value; + + if(!rdev->asic->dpm.fan_ctrl_set_mode) + return -EINVAL; + + err = kstrtoint(buf, 10, &value); + if(err) + return err; + + switch(value) { + case 1: // manual, percent-based + rdev->asic->dpm.fan_ctrl_set_mode(rdev, FDO_PWM_MODE_STATIC); + break; + default: // disable + rdev->asic->dpm.fan_ctrl_set_mode(rdev, 0); + break; + } + + return count; +} + +static ssize_t radeon_hwmon_get_pwm1_min(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%i\n", 0); +} + +static ssize_t radeon_hwmon_get_pwm1_max(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%i\n", 100); // pwm uses percent-based fan-control +} + +static ssize_t radeon_hwmon_set_pwm1(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct radeon_device *rdev = dev_get_drvdata(dev); + int err; + u32 value; + + err = kstrtou32(buf, 10, &value); + if(err) + return err; + + err = rdev->asic->dpm.set_fan_speed_percent(rdev, value); + if(err) + return err; + + return count; +} + +static ssize_t radeon_hwmon_get_pwm1(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct radeon_device *rdev = dev_get_drvdata(dev); + int err; + u32 speed; + err = rdev->asic->dpm.get_fan_speed_percent(rdev, &speed); + if(err) + return err; + + return sprintf(buf, "%i\n", speed); +} + static DEVICE_ATTR(power_profile, S_IRUGO | S_IWUSR, radeon_get_pm_profile, radeon_set_pm_profile); static DEVICE_ATTR(power_method, S_IRUGO | S_IWUSR, radeon_get_pm_method, radeon_set_pm_method); static DEVICE_ATTR(power_dpm_state, S_IRUGO | S_IWUSR, radeon_get_dpm_state, radeon_set_dpm_state); @@ -601,11 +690,20 @@ static ssize_t radeon_hwmon_show_temp_thresh(struct device *dev, static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, radeon_hwmon_show_temp, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, radeon_hwmon_show_temp_thresh, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, radeon_hwmon_show_temp_thresh, NULL, 1); +static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, radeon_hwmon_get_pwm1, radeon_hwmon_set_pwm1, 0); +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, radeon_hwmon_get_pwm1_enable, radeon_hwmon_set_pwm1_enable, 0); +static SENSOR_DEVICE_ATTR(pwm1_min, S_IRUGO, radeon_hwmon_get_pwm1_min, NULL, 0); +static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO, radeon_hwmon_get_pwm1_max, NULL, 0); +
static struct attribute *hwmon_attributes[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_crit.dev_attr.attr, &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_pwm1_min.dev_attr.attr, + &sensor_dev_attr_pwm1_max.dev_attr.attr, NULL };
@@ -614,6 +712,7 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj, { struct device *dev = container_of(kobj, struct device, kobj); struct radeon_device *rdev = dev_get_drvdata(dev); + umode_t effective_mode = attr->mode;
/* Skip limit attributes if DPM is not enabled */ if (rdev->pm.pm_method != PM_METHOD_DPM && @@ -621,7 +720,35 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj, attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr)) return 0;
- return attr->mode; + /* Skip fan attributes if fan is not present */ + if(rdev->pm.no_fan && + (attr == &sensor_dev_attr_pwm1.dev_attr.attr || + attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr || + attr == &sensor_dev_attr_pwm1_max.dev_attr.attr || + attr == &sensor_dev_attr_pwm1_min.dev_attr.attr)) + return 0; + + /* mask fan attributes if we have no bindings for this asic to expose */ + if((!rdev->asic->dpm.get_fan_speed_percent && + attr == &sensor_dev_attr_pwm1.dev_attr.attr) || // can't query fan + (!rdev->asic->dpm.fan_ctrl_get_mode && + attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr)) // can't query state + effective_mode &= ~S_IRUGO; + + if((!rdev->asic->dpm.set_fan_speed_percent && + attr == &sensor_dev_attr_pwm1.dev_attr.attr) || // can't manage fan + (!rdev->asic->dpm.fan_ctrl_set_mode && + attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr)) // can't manage state + effective_mode &= ~S_IWUSR; + + /* hide max/min values if we can't both query and manage the fan */ + if((!rdev->asic->dpm.set_fan_speed_percent && + !rdev->asic->dpm.get_fan_speed_percent) && + (attr == &sensor_dev_attr_pwm1_max.dev_attr.attr || + attr == &sensor_dev_attr_pwm1_min.dev_attr.attr)) + return 0; + + return effective_mode; }
static const struct attribute_group hwmon_attrgroup = {
This adds a possibility to control fan on CI parts via exported hwmon variables. Note that automatic ucode fan management pauses if you choose to enable manual fan control
Signed-off-by: Oleg Chernovskiy algonkvel@gmail.com --- drivers/gpu/drm/radeon/ci_dpm.c | 32 ++++++++++++++++++++++++-------- drivers/gpu/drm/radeon/radeon_asic.c | 4 ++++ drivers/gpu/drm/radeon/radeon_asic.h | 7 +++++++ 3 files changed, 35 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c index f373a81..cbba2b6 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.c +++ b/drivers/gpu/drm/radeon/ci_dpm.c @@ -187,6 +187,8 @@ static int ci_update_uvd_dpm(struct radeon_device *rdev, bool gate); static PPSMC_Result ci_send_msg_to_smc_with_parameter(struct radeon_device *rdev, PPSMC_Msg msg, u32 parameter);
+static void ci_thermal_start_smc_fan_control(struct radeon_device *rdev); + static struct ci_power_info *ci_get_pi(struct radeon_device *rdev) { struct ci_power_info *pi = rdev->pm.dpm.priv; @@ -1046,7 +1048,6 @@ static int ci_fan_ctrl_start_smc_fan_control(struct radeon_device *rdev) return 0; }
-#if 0 static int ci_fan_ctrl_stop_smc_fan_control(struct radeon_device *rdev) { PPSMC_Result ret; @@ -1058,7 +1059,7 @@ static int ci_fan_ctrl_stop_smc_fan_control(struct radeon_device *rdev) return -EINVAL; }
-static int ci_fan_ctrl_get_fan_speed_percent(struct radeon_device *rdev, +int ci_fan_ctrl_get_fan_speed_percent(struct radeon_device *rdev, u32 *speed) { u32 duty, duty100; @@ -1083,7 +1084,7 @@ static int ci_fan_ctrl_get_fan_speed_percent(struct radeon_device *rdev, return 0; }
-static int ci_fan_ctrl_set_fan_speed_percent(struct radeon_device *rdev, +int ci_fan_ctrl_set_fan_speed_percent(struct radeon_device *rdev, u32 speed) { u32 tmp; @@ -1096,9 +1097,6 @@ static int ci_fan_ctrl_set_fan_speed_percent(struct radeon_device *rdev, if (speed > 100) return -EINVAL;
- if (rdev->pm.dpm.fan.ucode_fan_control) - ci_fan_ctrl_stop_smc_fan_control(rdev); - duty100 = (RREG32_SMC(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT;
if (duty100 == 0) @@ -1112,11 +1110,29 @@ static int ci_fan_ctrl_set_fan_speed_percent(struct radeon_device *rdev, tmp |= FDO_STATIC_DUTY(duty); WREG32_SMC(CG_FDO_CTRL0, tmp);
- ci_fan_ctrl_set_static_mode(rdev, FDO_PWM_MODE_STATIC); - return 0; }
+void ci_fan_ctrl_set_mode(struct radeon_device *rdev, u32 mode) +{ + if(mode) { + // stop auto-manage + if (rdev->pm.dpm.fan.ucode_fan_control) + ci_fan_ctrl_stop_smc_fan_control(rdev); + ci_fan_ctrl_set_static_mode(rdev, mode); + } else { + // restart auto-manage + ci_thermal_start_smc_fan_control(rdev); + } +} + +u32 ci_fan_cntrl_get_mode(struct radeon_device *rdev) +{ + u32 tmp = RREG32_SMC(CG_FDO_CTRL2) & FDO_PWM_MODE_MASK; + return (tmp >> FDO_PWM_MODE_SHIFT); +} + +#if 0 static int ci_fan_ctrl_get_fan_speed_rpm(struct radeon_device *rdev, u32 *speed) { diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 850de57..d2c517c 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -2104,6 +2104,10 @@ static struct radeon_asic ci_asic = { .force_performance_level = &ci_dpm_force_performance_level, .vblank_too_short = &ci_dpm_vblank_too_short, .powergate_uvd = &ci_dpm_powergate_uvd, + .fan_ctrl_set_mode = &ci_fan_ctrl_set_mode, + .fan_ctrl_get_mode = &ci_fan_cntrl_get_mode, + .get_fan_speed_percent = &ci_fan_ctrl_get_fan_speed_percent, + .set_fan_speed_percent = &ci_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 2a45d54..edd64dc 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -861,6 +861,13 @@ int ci_dpm_force_performance_level(struct radeon_device *rdev, bool ci_dpm_vblank_too_short(struct radeon_device *rdev); void ci_dpm_powergate_uvd(struct radeon_device *rdev, bool gate);
+int ci_fan_ctrl_get_fan_speed_percent(struct radeon_device *rdev, + u32 *speed); +int ci_fan_ctrl_set_fan_speed_percent(struct radeon_device *rdev, + u32 speed); +u32 ci_fan_cntrl_get_mode(struct radeon_device *rdev); +void ci_fan_ctrl_set_mode(struct radeon_device *rdev, u32 mode); + int kv_dpm_init(struct radeon_device *rdev); int kv_dpm_enable(struct radeon_device *rdev); int kv_dpm_late_enable(struct radeon_device *rdev);
dri-devel@lists.freedesktop.org