Commit 99736703 authored by Oleg Chernovskiy's avatar Oleg Chernovskiy Committed by Alex Deucher

drm/radeon: add hwmon interface for managing fan pwm (v2)

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)

v2: agd5f: fix formatting
Signed-off-by: default avatarOleg Chernovskiy <algonkvel@gmail.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent a35a4b2b
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "radeon.h" #include "radeon.h"
#include "avivod.h" #include "avivod.h"
#include "atom.h" #include "atom.h"
#include "r600_dpm.h"
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h> #include <linux/hwmon-sysfs.h>
...@@ -554,6 +555,96 @@ static ssize_t radeon_set_dpm_forced_performance_level(struct device *dev, ...@@ -554,6 +555,96 @@ static ssize_t radeon_set_dpm_forced_performance_level(struct device *dev,
return count; 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);
/* never 0 (full-speed), fuse or smc-controlled always */
return sprintf(buf, "%i\n", pwm_mode == FDO_PWM_MODE_STATIC ? 1 : 2);
}
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_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_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); static DEVICE_ATTR(power_dpm_state, S_IRUGO | S_IWUSR, radeon_get_dpm_state, radeon_set_dpm_state);
...@@ -601,11 +692,20 @@ static ssize_t radeon_hwmon_show_temp_thresh(struct device *dev, ...@@ -601,11 +692,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_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, 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(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[] = { static struct attribute *hwmon_attributes[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_crit.dev_attr.attr, &sensor_dev_attr_temp1_crit.dev_attr.attr,
&sensor_dev_attr_temp1_crit_hyst.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 NULL
}; };
...@@ -614,6 +714,7 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj, ...@@ -614,6 +714,7 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj,
{ {
struct device *dev = container_of(kobj, struct device, kobj); struct device *dev = container_of(kobj, struct device, kobj);
struct radeon_device *rdev = dev_get_drvdata(dev); struct radeon_device *rdev = dev_get_drvdata(dev);
umode_t effective_mode = attr->mode;
/* Skip limit attributes if DPM is not enabled */ /* Skip limit attributes if DPM is not enabled */
if (rdev->pm.pm_method != PM_METHOD_DPM && if (rdev->pm.pm_method != PM_METHOD_DPM &&
...@@ -621,7 +722,35 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj, ...@@ -621,7 +722,35 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj,
attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr)) attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr))
return 0; 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 = { static const struct attribute_group hwmon_attrgroup = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment