diff mbox

[2/3] add hwmon interface for managing fan pwm

Message ID 1417986646-8989-2-git-send-email-algonkvel@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

?????? ?????? Dec. 7, 2014, 9:10 p.m. UTC
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 mbox

Patch

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 = {