[PATCH] drm/radeon/kms: add support for internal thermal sensors

Alex Deucher alexdeucher at gmail.com
Tue Jun 1 13:31:41 PDT 2010


rv6xx/rv7xx/evergreen families supported; older asics did
not have an internal thermal sensor.  Exposed via sysfs
gpu_temp attribute.

Note, not all oems use the internal thermal sensor, so it's
only exposed in cases where it is used.

Note also, that most laptops use an oem specific ACPI solution for
GPU thermal information rather than using the internal thermal
sensor directly.

Signed-off-by: Alex Deucher <alexdeucher at gmail.com>
---
 drivers/gpu/drm/radeon/Kconfig           |    1 +
 drivers/gpu/drm/radeon/evergreen.c       |   73 ++++++++++++++++++++++++++++++
 drivers/gpu/drm/radeon/evergreend.h      |    5 ++
 drivers/gpu/drm/radeon/r600.c            |   71 +++++++++++++++++++++++++++++
 drivers/gpu/drm/radeon/r600d.h           |    5 ++
 drivers/gpu/drm/radeon/radeon.h          |   16 +++++++
 drivers/gpu/drm/radeon/radeon_atombios.c |   18 ++++++-
 drivers/gpu/drm/radeon/radeon_pm.c       |   19 ++++++++
 drivers/gpu/drm/radeon/rv770.c           |   71 +++++++++++++++++++++++++++++
 drivers/gpu/drm/radeon/rv770d.h          |    5 ++
 10 files changed, 281 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/radeon/Kconfig b/drivers/gpu/drm/radeon/Kconfig
index 80c5b3e..2e3896d 100644
--- a/drivers/gpu/drm/radeon/Kconfig
+++ b/drivers/gpu/drm/radeon/Kconfig
@@ -2,6 +2,7 @@ config DRM_RADEON_KMS
 	bool "Enable modesetting on radeon by default - NEW DRIVER"
 	depends on DRM_RADEON
 	depends on POWER_SUPPLY
+	depends on HWMON
 	help
 	  Choose this option if you want kernel modesetting enabled by default.
 
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 69c4b27..5d5a7b1 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -24,6 +24,8 @@
 #include <linux/firmware.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
 #include "drmP.h"
 #include "radeon.h"
 #include "radeon_asic.h"
@@ -39,6 +41,77 @@
 static void evergreen_gpu_init(struct radeon_device *rdev);
 void evergreen_fini(struct radeon_device *rdev);
 
+static ssize_t evergreen_hwmon_show_temp(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
+	struct radeon_device *rdev = ddev->dev_private;
+	u32 temp = (RREG32(CG_MULT_THERMAL_STATUS) & ASIC_T_MASK) >>
+		ASIC_T_SHIFT;
+	u32 actual_temp = 0;
+
+	if ((temp >> 10) & 1)
+		actual_temp = 0;
+	else if ((temp >> 9) & 1)
+		actual_temp = 255;
+	else
+		actual_temp = (temp >> 1) & 0xff;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", actual_temp);
+}
+
+static SENSOR_DEVICE_ATTR(gpu_temp, S_IRUGO, evergreen_hwmon_show_temp, NULL, 0);
+
+static struct attribute *evergreen_hwmon_attributes[2] = {
+	&sensor_dev_attr_gpu_temp.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group evergreen_hwmon_attrgroup = {
+	.attrs = evergreen_hwmon_attributes,
+};
+
+int evergreen_hwmon_init(struct radeon_device *rdev)
+{
+	struct device *int_hwmon_dev;
+	int ret;
+
+	rdev->pm.int_hwmon_dev = NULL;
+
+	int_hwmon_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+	if (!int_hwmon_dev) {
+		dev_err(rdev->dev, "no memory\n");
+		return -ENOMEM;
+	}
+
+	/* where's the best place to put this? */
+	sysfs_create_group(&rdev->dev->kobj, &evergreen_hwmon_attrgroup);
+
+	int_hwmon_dev = hwmon_device_register(rdev->dev);
+	if (IS_ERR(int_hwmon_dev)) {
+		dev_err(rdev->dev, "error registering with hwmon\n");
+		ret = PTR_ERR(int_hwmon_dev);
+		sysfs_remove_group(&rdev->dev->kobj, &evergreen_hwmon_attrgroup);
+		kfree(int_hwmon_dev);
+		return ret;
+	}
+
+	rdev->pm.int_hwmon_dev = int_hwmon_dev;
+
+	return 0;
+}
+
+void evergreen_hwmon_fini(struct radeon_device *rdev)
+{
+	if (rdev->pm.int_hwmon_dev) {
+		sysfs_remove_group(&rdev->dev->kobj, &evergreen_hwmon_attrgroup);
+		hwmon_device_unregister(rdev->pm.int_hwmon_dev);
+		kfree(rdev->pm.int_hwmon_dev);
+		rdev->pm.int_hwmon_dev = NULL;
+	}
+}
+
 void evergreen_pm_misc(struct radeon_device *rdev)
 {
 	int requested_index = rdev->pm.requested_power_state_index;
diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h
index 79683f6..50c5e97 100644
--- a/drivers/gpu/drm/radeon/evergreend.h
+++ b/drivers/gpu/drm/radeon/evergreend.h
@@ -165,6 +165,11 @@
 #define		SE_DB_BUSY					(1 << 30)
 #define		SE_CB_BUSY					(1 << 31)
 
+#define	CG_MULT_THERMAL_STATUS				0x740
+#define		ASIC_T(x)			        ((x) << 16)
+#define		ASIC_T_MASK			        0x7FF0000
+#define		ASIC_T_SHIFT			        16
+
 #define	HDP_HOST_PATH_CNTL				0x2C00
 #define	HDP_NONSURFACE_BASE				0x2C04
 #define	HDP_NONSURFACE_INFO				0x2C08
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index d84d7cf..a57f590 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -29,6 +29,8 @@
 #include <linux/seq_file.h>
 #include <linux/firmware.h>
 #include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
 #include "drmP.h"
 #include "radeon_drm.h"
 #include "radeon.h"
@@ -92,6 +94,75 @@ void r600_gpu_init(struct radeon_device *rdev);
 void r600_fini(struct radeon_device *rdev);
 void r600_irq_disable(struct radeon_device *rdev);
 
+static ssize_t rv6xx_hwmon_show_temp(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
+	struct radeon_device *rdev = ddev->dev_private;
+	u32 temp = (RREG32(CG_THERMAL_STATUS) & ASIC_T_MASK) >>
+		ASIC_T_SHIFT;
+	u32 actual_temp = 0;
+
+	if ((temp >> 7) & 1)
+		actual_temp = 0;
+	else
+		actual_temp = (temp >> 1) & 0xff;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", actual_temp);
+}
+
+static SENSOR_DEVICE_ATTR(gpu_temp, S_IRUGO, rv6xx_hwmon_show_temp, NULL, 0);
+
+static struct attribute *rv6xx_hwmon_attributes[2] = {
+	&sensor_dev_attr_gpu_temp.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group rv6xx_hwmon_attrgroup = {
+	.attrs = rv6xx_hwmon_attributes,
+};
+
+int rv6xx_hwmon_init(struct radeon_device *rdev)
+{
+	struct device *int_hwmon_dev;
+	int ret;
+
+	rdev->pm.int_hwmon_dev = NULL;
+
+	int_hwmon_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+	if (!int_hwmon_dev) {
+		dev_err(rdev->dev, "no memory\n");
+		return -ENOMEM;
+	}
+
+	/* where's the best place to put this? */
+	sysfs_create_group(&rdev->dev->kobj, &rv6xx_hwmon_attrgroup);
+
+	int_hwmon_dev = hwmon_device_register(rdev->dev);
+	if (IS_ERR(int_hwmon_dev)) {
+		dev_err(rdev->dev, "error registering with hwmon\n");
+		ret = PTR_ERR(int_hwmon_dev);
+		sysfs_remove_group(&rdev->dev->kobj, &rv6xx_hwmon_attrgroup);
+		kfree(int_hwmon_dev);
+		return ret;
+	}
+
+	rdev->pm.int_hwmon_dev = int_hwmon_dev;
+
+	return 0;
+}
+
+void rv6xx_hwmon_fini(struct radeon_device *rdev)
+{
+	if (rdev->pm.int_hwmon_dev) {
+		sysfs_remove_group(&rdev->dev->kobj, &rv6xx_hwmon_attrgroup);
+		hwmon_device_unregister(rdev->pm.int_hwmon_dev);
+		kfree(rdev->pm.int_hwmon_dev);
+		rdev->pm.int_hwmon_dev = NULL;
+	}
+}
+
 void r600_pm_get_dynpm_state(struct radeon_device *rdev)
 {
 	int i;
diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h
index 0a354b5..b7318ac 100644
--- a/drivers/gpu/drm/radeon/r600d.h
+++ b/drivers/gpu/drm/radeon/r600d.h
@@ -239,6 +239,11 @@
 #define	GRBM_SOFT_RESET					0x8020
 #define		SOFT_RESET_CP					(1<<0)
 
+#define	CG_THERMAL_STATUS				0x7F4
+#define		ASIC_T(x)			        ((x) << 0)
+#define		ASIC_T_MASK			        0x1FF
+#define		ASIC_T_SHIFT			        0
+
 #define	HDP_HOST_PATH_CNTL				0x2C00
 #define	HDP_NONSURFACE_BASE				0x2C04
 #define	HDP_NONSURFACE_INFO				0x2C08
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index a2561c0..943f042 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -177,6 +177,12 @@ void radeon_pm_resume(struct radeon_device *rdev);
 void radeon_combios_get_power_modes(struct radeon_device *rdev);
 void radeon_atombios_get_power_modes(struct radeon_device *rdev);
 void radeon_atom_set_voltage(struct radeon_device *rdev, u16 level);
+extern int rv6xx_hwmon_init(struct radeon_device *rdev);
+extern void rv6xx_hwmon_fini(struct radeon_device *rdev);
+extern int rv770_hwmon_init(struct radeon_device *rdev);
+extern void rv770_hwmon_fini(struct radeon_device *rdev);
+extern int evergreen_hwmon_init(struct radeon_device *rdev);
+extern void evergreen_hwmon_fini(struct radeon_device *rdev);
 
 /*
  * Fences.
@@ -663,6 +669,13 @@ struct radeon_pm_profile {
 	int dpms_on_cm_idx;
 };
 
+enum radeon_int_thermal_type {
+	THERMAL_TYPE_NONE,
+	THERMAL_TYPE_RV6XX,
+	THERMAL_TYPE_RV770,
+	THERMAL_TYPE_EVERGREEN,
+};
+
 struct radeon_voltage {
 	enum radeon_voltage_type type;
 	/* gpio voltage */
@@ -757,6 +770,9 @@ struct radeon_pm {
 	enum radeon_pm_profile_type profile;
 	int                     profile_index;
 	struct radeon_pm_profile profiles[PM_PROFILE_MAX];
+	/* internal thermal controller on rv6xx+ */
+	enum radeon_int_thermal_type int_thermal_type;
+	struct device	        *int_hwmon_dev;
 };
 
 
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index 107ada0..34249b2 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -1766,12 +1766,24 @@ void radeon_atombios_get_power_modes(struct radeon_device *rdev)
 			/* add the i2c bus for thermal/fan chip */
 			/* no support for internal controller yet */
 			if (controller->ucType > 0) {
-				if ((controller->ucType == ATOM_PP_THERMALCONTROLLER_RV6xx) ||
-				    (controller->ucType == ATOM_PP_THERMALCONTROLLER_RV770) ||
-				    (controller->ucType == ATOM_PP_THERMALCONTROLLER_EVERGREEN)) {
+				if (controller->ucType == ATOM_PP_THERMALCONTROLLER_RV6xx) {
 					DRM_INFO("Internal thermal controller %s fan control\n",
 						 (controller->ucFanParameters &
 						  ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with");
+					rdev->pm.int_thermal_type = THERMAL_TYPE_RV6XX;
+					rv6xx_hwmon_init(rdev);
+				} else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_RV770) {
+					DRM_INFO("Internal thermal controller %s fan control\n",
+						 (controller->ucFanParameters &
+						  ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with");
+					rdev->pm.int_thermal_type = THERMAL_TYPE_RV770;
+					rv770_hwmon_init(rdev);
+				} else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_EVERGREEN) {
+					DRM_INFO("Internal thermal controller %s fan control\n",
+						 (controller->ucFanParameters &
+						  ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with");
+					rdev->pm.int_thermal_type = THERMAL_TYPE_EVERGREEN;
+					evergreen_hwmon_init(rdev);
 				} else if ((controller->ucType ==
 					    ATOM_PP_THERMALCONTROLLER_EXTERNAL_GPIO) ||
 					   (controller->ucType ==
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index a51326c..f497394 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -399,6 +399,8 @@ int radeon_pm_init(struct radeon_device *rdev)
 	rdev->pm.dynpm_can_downclock = true;
 	rdev->pm.current_sclk = 0;
 	rdev->pm.current_mclk = 0;
+	rdev->pm.int_thermal_type = THERMAL_TYPE_NONE;
+	rdev->pm.int_hwmon_dev = NULL;
 
 	if (rdev->bios) {
 		if (rdev->is_atom_bios)
@@ -464,6 +466,23 @@ void radeon_pm_fini(struct radeon_device *rdev)
 #endif
 	}
 
+	switch (rdev->pm.int_thermal_type) {
+	case THERMAL_TYPE_RV6XX:
+		if (rdev->pm.int_hwmon_dev)
+			rv6xx_hwmon_fini(rdev);
+		break;
+	case THERMAL_TYPE_RV770:
+		if (rdev->pm.int_hwmon_dev)
+			rv770_hwmon_fini(rdev);
+		break;
+	case THERMAL_TYPE_EVERGREEN:
+		if (rdev->pm.int_hwmon_dev)
+			evergreen_hwmon_fini(rdev);
+		break;
+	default:
+		break;
+	}
+
 	if (rdev->pm.i2c_bus)
 		radeon_i2c_destroy(rdev->pm.i2c_bus);
 }
diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c
index 5f76938..01904d3 100644
--- a/drivers/gpu/drm/radeon/rv770.c
+++ b/drivers/gpu/drm/radeon/rv770.c
@@ -28,6 +28,8 @@
 #include <linux/firmware.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
 #include "drmP.h"
 #include "radeon.h"
 #include "radeon_asic.h"
@@ -42,6 +44,75 @@
 static void rv770_gpu_init(struct radeon_device *rdev);
 void rv770_fini(struct radeon_device *rdev);
 
+static ssize_t rv770_hwmon_show_temp(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
+	struct radeon_device *rdev = ddev->dev_private;
+	u32 temp = (RREG32(CG_MULT_THERMAL_STATUS) & ASIC_T_MASK) >>
+		ASIC_T_SHIFT;
+	u32 actual_temp = 0;
+
+	if ((temp >> 9) & 1)
+		actual_temp = 0;
+	else
+		actual_temp = (temp >> 1) & 0xff;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", actual_temp);
+}
+
+static SENSOR_DEVICE_ATTR(gpu_temp, S_IRUGO, rv770_hwmon_show_temp, NULL, 0);
+
+static struct attribute *rv770_hwmon_attributes[2] = {
+	&sensor_dev_attr_gpu_temp.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group rv770_hwmon_attrgroup = {
+	.attrs = rv770_hwmon_attributes,
+};
+
+int rv770_hwmon_init(struct radeon_device *rdev)
+{
+	struct device *int_hwmon_dev;
+	int ret;
+
+	rdev->pm.int_hwmon_dev = NULL;
+
+	int_hwmon_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+	if (!int_hwmon_dev) {
+		dev_err(rdev->dev, "no memory\n");
+		return -ENOMEM;
+	}
+
+	/* where's the best place to put this? */
+	sysfs_create_group(&rdev->dev->kobj, &rv770_hwmon_attrgroup);
+
+	int_hwmon_dev = hwmon_device_register(rdev->dev);
+	if (IS_ERR(int_hwmon_dev)) {
+		dev_err(rdev->dev, "error registering with hwmon\n");
+		ret = PTR_ERR(int_hwmon_dev);
+		sysfs_remove_group(&rdev->dev->kobj, &rv770_hwmon_attrgroup);
+		kfree(int_hwmon_dev);
+		return ret;
+	}
+
+	rdev->pm.int_hwmon_dev = int_hwmon_dev;
+
+	return 0;
+}
+
+void rv770_hwmon_fini(struct radeon_device *rdev)
+{
+	if (rdev->pm.int_hwmon_dev) {
+		sysfs_remove_group(&rdev->dev->kobj, &rv770_hwmon_attrgroup);
+		hwmon_device_unregister(rdev->pm.int_hwmon_dev);
+		kfree(rdev->pm.int_hwmon_dev);
+		rdev->pm.int_hwmon_dev = NULL;
+	}
+}
+
 void rv770_pm_misc(struct radeon_device *rdev)
 {
 	int requested_index = rdev->pm.requested_power_state_index;
diff --git a/drivers/gpu/drm/radeon/rv770d.h b/drivers/gpu/drm/radeon/rv770d.h
index 9506f8c..fd733f2 100644
--- a/drivers/gpu/drm/radeon/rv770d.h
+++ b/drivers/gpu/drm/radeon/rv770d.h
@@ -122,6 +122,11 @@
 #define		GUI_ACTIVE					(1<<31)
 #define	GRBM_STATUS2					0x8014
 
+#define	CG_MULT_THERMAL_STATUS				0x740
+#define		ASIC_T(x)			        ((x) << 16)
+#define		ASIC_T_MASK			        0x3FF0000
+#define		ASIC_T_SHIFT			        16
+
 #define	HDP_HOST_PATH_CNTL				0x2C00
 #define	HDP_NONSURFACE_BASE				0x2C04
 #define	HDP_NONSURFACE_INFO				0x2C08
-- 
1.5.6.3



More information about the dri-devel mailing list