From 399f0a1b57afad56bb2fcfd7e61a31ffe9a0281c Mon Sep 17 00:00:00 2001
From: Gianni Vialetto <gianni@rootcube.net>
Date: Tue, 8 Jul 2014 13:08:57 +0200
Subject: [PATCH 2/2] Expose LINEAR_MIN and LINEAR_MAX as hwmon sysfs
 properties

Signed-off-by: Gianni Vialetto <gianni@rootcube.net>
---
 .../gpu/drm/nouveau/core/include/subdev/therm.h    |  2 +
 drivers/gpu/drm/nouveau/core/subdev/therm/base.c   | 14 ++++
 drivers/gpu/drm/nouveau/nouveau_sysfs.c            | 82 +++++++++++++++++++++-
 3 files changed, 96 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/therm.h b/drivers/gpu/drm/nouveau/core/include/subdev/therm.h
index d4a6817..29ccf23 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/therm.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/therm.h
@@ -23,6 +23,8 @@ enum nouveau_therm_attr_type {
 	NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST = 15,
 	NOUVEAU_THERM_ATTR_THRS_SHUTDOWN = 16,
 	NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST = 17,
+	NOUVEAU_THERM_ATTR_THRS_LINEAR_MIN = 18,
+	NOUVEAU_THERM_ATTR_THRS_LINEAR_MAX = 19,
 };
 
 struct nouveau_therm {
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/base.c b/drivers/gpu/drm/nouveau/core/subdev/therm/base.c
index 9ad01da..a56018f 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/base.c
@@ -221,6 +221,10 @@ nouveau_therm_attr_get(struct nouveau_therm *therm,
 		return priv->bios_sensor.thrs_shutdown.temp;
 	case NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST:
 		return priv->bios_sensor.thrs_shutdown.hysteresis;
+	case NOUVEAU_THERM_ATTR_THRS_LINEAR_MIN:
+		return priv->fan->bios.linear_min_temp;
+	case NOUVEAU_THERM_ATTR_THRS_LINEAR_MAX:
+		return priv->fan->bios.linear_max_temp;
 	}
 
 	return -EINVAL;
@@ -281,6 +285,16 @@ nouveau_therm_attr_set(struct nouveau_therm *therm,
 		priv->bios_sensor.thrs_shutdown.hysteresis = value;
 		priv->sensor.program_alarms(therm);
 		return 0;
+	case NOUVEAU_THERM_ATTR_THRS_LINEAR_MIN:
+		if (value < 0)
+			value = 0;
+		priv->fan->bios.linear_min_temp = value;
+		return 0;
+	case NOUVEAU_THERM_ATTR_THRS_LINEAR_MAX:
+		if (value < 0)
+			value = 0;
+		priv->fan->bios.linear_max_temp = value;
+		return 0;
 	}
 
 	return -EINVAL;
diff --git a/drivers/gpu/drm/nouveau/nouveau_sysfs.c b/drivers/gpu/drm/nouveau/nouveau_sysfs.c
index 75dda2b..8281cf2 100644
--- a/drivers/gpu/drm/nouveau/nouveau_sysfs.c
+++ b/drivers/gpu/drm/nouveau/nouveau_sysfs.c
@@ -23,6 +23,7 @@
  */
 
 #include "nouveau_sysfs.h"
+#include <subdev/therm.h>
 
 #include <core/object.h>
 #include <core/class.h>
@@ -123,10 +124,78 @@ nouveau_sysfs_pstate_set(struct device *d, struct device_attribute *a,
 
 	return count;
 }
-
 static DEVICE_ATTR(pstate, S_IRUGO | S_IWUSR,
 		   nouveau_sysfs_pstate_get, nouveau_sysfs_pstate_set);
 
+static ssize_t
+nouveau_sysfs_temp1_fan_linear_min_get(struct device *d,
+				       struct device_attribute *a,
+				       char *buf)
+{
+	struct drm_device *dev = dev_get_drvdata(d);
+	struct nouveau_drm *drm = nouveau_drm(dev);
+	struct nouveau_therm *therm = nouveau_therm(drm->device);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+		therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_LINEAR_MIN));
+}
+static ssize_t
+nouveau_sysfs_temp1_fan_linear_min_set(struct device *d,
+				       struct device_attribute *a,
+				       const char *buf,
+				       size_t count)
+{
+	struct drm_device *dev = dev_get_drvdata(d);
+	struct nouveau_drm *drm = nouveau_drm(dev);
+	struct nouveau_therm *therm = nouveau_therm(drm->device);
+	long value;
+
+	if (kstrtol(buf, 10, &value) == -EINVAL)
+		return count;
+
+	therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_LINEAR_MIN, value);
+
+	return count;
+}
+static DEVICE_ATTR(temp1_fan_linear_min, S_IRUGO | S_IWUSR,
+		   nouveau_sysfs_temp1_fan_linear_min_get,
+		   nouveau_sysfs_temp1_fan_linear_min_set);
+
+static ssize_t
+nouveau_sysfs_temp1_fan_linear_max_get(struct device *d,
+				       struct device_attribute *a,
+				       char *buf)
+{
+	struct drm_device *dev = dev_get_drvdata(d);
+	struct nouveau_drm *drm = nouveau_drm(dev);
+	struct nouveau_therm *therm = nouveau_therm(drm->device);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+	       therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_LINEAR_MAX));
+}
+static ssize_t
+nouveau_sysfs_temp1_fan_linear_max_set(struct device *d,
+				       struct device_attribute *a,
+				       const char *buf,
+				       size_t count)
+{
+	struct drm_device *dev = dev_get_drvdata(d);
+	struct nouveau_drm *drm = nouveau_drm(dev);
+	struct nouveau_therm *therm = nouveau_therm(drm->device);
+	long value;
+
+	if (kstrtol(buf, 10, &value) == -EINVAL)
+		return count;
+
+	therm->attr_set(therm,
+			NOUVEAU_THERM_ATTR_THRS_LINEAR_MAX, value / 1000);
+
+	return count;
+}
+static DEVICE_ATTR(temp1_fan_linear_max, S_IRUGO | S_IWUSR,
+		   nouveau_sysfs_temp1_fan_linear_max_get,
+		   nouveau_sysfs_temp1_fan_linear_max_set);
+
 void
 nouveau_sysfs_fini(struct drm_device *dev)
 {
@@ -136,6 +205,10 @@ nouveau_sysfs_fini(struct drm_device *dev)
 
 	if (sysfs->ctrl) {
 		device_remove_file(nv_device_base(device), &dev_attr_pstate);
+		device_remove_file(nv_device_base(device),
+					&dev_attr_temp1_fan_linear_min);
+		device_remove_file(nv_device_base(device),
+					&dev_attr_temp1_fan_linear_max);
 		nouveau_object_del(nv_object(drm), NVDRM_DEVICE, NVDRM_CONTROL);
 	}
 
@@ -157,8 +230,13 @@ nouveau_sysfs_init(struct drm_device *dev)
 
 	ret = nouveau_object_new(nv_object(drm), NVDRM_DEVICE, NVDRM_CONTROL,
 				 NV_CONTROL_CLASS, NULL, 0, &sysfs->ctrl);
-	if (ret == 0)
+	if (ret == 0) {
 		device_create_file(nv_device_base(device), &dev_attr_pstate);
+		device_create_file(nv_device_base(device),
+					&dev_attr_temp1_fan_linear_min);
+		device_create_file(nv_device_base(device),
+					&dev_attr_temp1_fan_linear_max);
+	}
 
 	return 0;
 }
-- 
2.0.1