From: Ben Skeggs <bskeggs@redhat.com>
Date: Thu, 20 Aug 2015 04:54:21 +0000 (+1000)
Subject: drm/nouveau/therm: convert to new-style nvkm_subdev
X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=57113c0170b9efeacb3e3e9d4c2178c30d9cd991;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git

drm/nouveau/therm: convert to new-style nvkm_subdev

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
---

diff --git a/drivers/gpu/drm/nouveau/include/nvif/device.h b/drivers/gpu/drm/nouveau/include/nvif/device.h
index 383a47270f3e..572310223296 100644
--- a/drivers/gpu/drm/nouveau/include/nvif/device.h
+++ b/drivers/gpu/drm/nouveau/include/nvif/device.h
@@ -57,7 +57,7 @@ u64  nvif_device_time(struct nvif_device *);
 #define nvxx_gpio(a) nvxx_device(a)->gpio
 #define nvxx_clk(a) nvxx_device(a)->clk
 #define nvxx_i2c(a) nvxx_device(a)->i2c
-#define nvxx_therm(a) nvkm_therm(nvxx_device(a))
+#define nvxx_therm(a) nvxx_device(a)->therm
 
 #include <core/device.h>
 #include <engine/fifo.h>
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/therm.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/therm.h
index 6e60f9bceaba..b268b96faece 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/therm.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/therm.h
@@ -2,6 +2,28 @@
 #define __NVKM_THERM_H__
 #include <core/subdev.h>
 
+#include <subdev/bios.h>
+#include <subdev/bios/therm.h>
+#include <subdev/timer.h>
+
+enum nvkm_therm_thrs_direction {
+	NVKM_THERM_THRS_FALLING = 0,
+	NVKM_THERM_THRS_RISING = 1
+};
+
+enum nvkm_therm_thrs_state {
+	NVKM_THERM_THRS_LOWER = 0,
+	NVKM_THERM_THRS_HIGHER = 1
+};
+
+enum nvkm_therm_thrs {
+	NVKM_THERM_THRS_FANBOOST = 0,
+	NVKM_THERM_THRS_DOWNCLOCK = 1,
+	NVKM_THERM_THRS_CRITICAL = 2,
+	NVKM_THERM_THRS_SHUTDOWN = 3,
+	NVKM_THERM_THRS_NR
+};
+
 enum nvkm_therm_fan_mode {
 	NVKM_THERM_CTRL_NONE = 0,
 	NVKM_THERM_CTRL_MANUAL = 1,
@@ -24,56 +46,54 @@ enum nvkm_therm_attr_type {
 };
 
 struct nvkm_therm {
+	const struct nvkm_therm_func *func;
 	struct nvkm_subdev subdev;
 
-	int (*pwm_ctrl)(struct nvkm_therm *, int line, bool);
-	int (*pwm_get)(struct nvkm_therm *, int line, u32 *, u32 *);
-	int (*pwm_set)(struct nvkm_therm *, int line, u32, u32);
-	int (*pwm_clock)(struct nvkm_therm *, int line);
+	/* automatic thermal management */
+	struct nvkm_alarm alarm;
+	spinlock_t lock;
+	struct nvbios_therm_trip_point *last_trip;
+	int mode;
+	int cstate;
+	int suspend;
+
+	/* bios */
+	struct nvbios_therm_sensor bios_sensor;
+
+	/* fan priv */
+	struct nvkm_fan *fan;
+
+	/* alarms priv */
+	struct {
+		spinlock_t alarm_program_lock;
+		struct nvkm_alarm therm_poll_alarm;
+		enum nvkm_therm_thrs_state alarm_state[NVKM_THERM_THRS_NR];
+	} sensor;
+
+	/* what should be done if the card overheats */
+	struct {
+		void (*downclock)(struct nvkm_therm *, bool active);
+		void (*pause)(struct nvkm_therm *, bool active);
+	} emergency;
+
+	/* ic */
+	struct i2c_client *ic;
 
 	int (*fan_get)(struct nvkm_therm *);
 	int (*fan_set)(struct nvkm_therm *, int);
-	int (*fan_sense)(struct nvkm_therm *);
-
-	int (*temp_get)(struct nvkm_therm *);
 
 	int (*attr_get)(struct nvkm_therm *, enum nvkm_therm_attr_type);
 	int (*attr_set)(struct nvkm_therm *, enum nvkm_therm_attr_type, int);
 };
 
-static inline struct nvkm_therm *
-nvkm_therm(void *obj)
-{
-	return (void *)nvkm_subdev(obj, NVDEV_SUBDEV_THERM);
-}
-
-#define nvkm_therm_create(p,e,o,d)                                          \
-	nvkm_therm_create_((p), (e), (o), sizeof(**d), (void **)d)
-#define nvkm_therm_destroy(p) ({                                            \
-	struct nvkm_therm *_therm = (p);                                     \
-        _nvkm_therm_dtor(nv_object(_therm));                                 \
-})
-#define nvkm_therm_init(p) ({                                               \
-	struct nvkm_therm *_therm = (p);                                     \
-        _nvkm_therm_init(nv_object(_therm));                                 \
-})
-#define nvkm_therm_fini(p,s) ({                                             \
-	struct nvkm_therm *_therm = (p);                                     \
-        _nvkm_therm_init(nv_object(_therm), (s));                            \
-})
-
-int  nvkm_therm_create_(struct nvkm_object *, struct nvkm_object *,
-			   struct nvkm_oclass *, int, void **);
-void _nvkm_therm_dtor(struct nvkm_object *);
-int  _nvkm_therm_init(struct nvkm_object *);
-int  _nvkm_therm_fini(struct nvkm_object *, bool);
-
-int  nvkm_therm_cstate(struct nvkm_therm *, int, int);
-
-extern struct nvkm_oclass nv40_therm_oclass;
-extern struct nvkm_oclass nv50_therm_oclass;
-extern struct nvkm_oclass g84_therm_oclass;
-extern struct nvkm_oclass gt215_therm_oclass;
-extern struct nvkm_oclass gf110_therm_oclass;
-extern struct nvkm_oclass gm107_therm_oclass;
+int nvkm_therm_temp_get(struct nvkm_therm *);
+int nvkm_therm_fan_sense(struct nvkm_therm *);
+int nvkm_therm_cstate(struct nvkm_therm *, int, int);
+
+int nv40_therm_new(struct nvkm_device *, int, struct nvkm_therm **);
+int nv50_therm_new(struct nvkm_device *, int, struct nvkm_therm **);
+int g84_therm_new(struct nvkm_device *, int, struct nvkm_therm **);
+int gt215_therm_new(struct nvkm_device *, int, struct nvkm_therm **);
+int gf119_therm_new(struct nvkm_device *, int, struct nvkm_therm **);
+int gm107_therm_new(struct nvkm_device *, int, struct nvkm_therm **);
 #endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_hwmon.c b/drivers/gpu/drm/nouveau/nouveau_hwmon.c
index 0dbe0060f86e..491c7149d197 100644
--- a/drivers/gpu/drm/nouveau/nouveau_hwmon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_hwmon.c
@@ -41,7 +41,7 @@ nouveau_hwmon_show_temp(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 nvkm_therm *therm = nvxx_therm(&drm->device);
-	int temp = therm->temp_get(therm);
+	int temp = nvkm_therm_temp_get(therm);
 
 	if (temp < 0)
 		return temp;
@@ -348,7 +348,7 @@ nouveau_hwmon_show_fan1_input(struct device *d, struct device_attribute *attr,
 	struct nouveau_drm *drm = nouveau_drm(dev);
 	struct nvkm_therm *therm = nvxx_therm(&drm->device);
 
-	return snprintf(buf, PAGE_SIZE, "%d\n", therm->fan_sense(therm));
+	return snprintf(buf, PAGE_SIZE, "%d\n", nvkm_therm_fan_sense(therm));
 }
 static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, nouveau_hwmon_show_fan1_input,
 			  NULL, 0);
@@ -571,7 +571,7 @@ nouveau_hwmon_init(struct drm_device *dev)
 		return -ENOMEM;
 	hwmon->dev = dev;
 
-	if (!therm || !therm->temp_get || !therm->attr_get || !therm->attr_set)
+	if (!therm || !therm->attr_get || !therm->attr_set)
 		return -ENODEV;
 
 	hwmon_dev = hwmon_device_register(&dev->pdev->dev);
@@ -588,7 +588,7 @@ nouveau_hwmon_init(struct drm_device *dev)
 		goto error;
 
 	/* if the card has a working thermal sensor */
-	if (therm->temp_get(therm) >= 0) {
+	if (nvkm_therm_temp_get(therm) >= 0) {
 		ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_temp_attrgroup);
 		if (ret)
 			goto error;
@@ -606,7 +606,7 @@ nouveau_hwmon_init(struct drm_device *dev)
 	}
 
 	/* if the card can read the fan rpm */
-	if (therm->fan_sense(therm) >= 0) {
+	if (nvkm_therm_fan_sense(therm) >= 0) {
 		ret = sysfs_create_group(&hwmon_dev->kobj,
 					 &hwmon_fan_rpm_attrgroup);
 		if (ret)
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
index 8f201022377f..3734d1fb7756 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
@@ -463,7 +463,7 @@ nv40_chipset = {
 	.imem = nv40_instmem_new,
 	.mc = nv40_mc_new,
 	.mmu = nv04_mmu_new,
-//	.therm = nv40_therm_new,
+	.therm = nv40_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = nv04_disp_new,
@@ -488,7 +488,7 @@ nv41_chipset = {
 	.imem = nv40_instmem_new,
 	.mc = nv40_mc_new,
 	.mmu = nv41_mmu_new,
-//	.therm = nv40_therm_new,
+	.therm = nv40_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = nv04_disp_new,
@@ -513,7 +513,7 @@ nv42_chipset = {
 	.imem = nv40_instmem_new,
 	.mc = nv40_mc_new,
 	.mmu = nv41_mmu_new,
-//	.therm = nv40_therm_new,
+	.therm = nv40_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = nv04_disp_new,
@@ -538,7 +538,7 @@ nv43_chipset = {
 	.imem = nv40_instmem_new,
 	.mc = nv40_mc_new,
 	.mmu = nv41_mmu_new,
-//	.therm = nv40_therm_new,
+	.therm = nv40_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = nv04_disp_new,
@@ -563,7 +563,7 @@ nv44_chipset = {
 	.imem = nv40_instmem_new,
 	.mc = nv44_mc_new,
 	.mmu = nv44_mmu_new,
-//	.therm = nv40_therm_new,
+	.therm = nv40_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = nv04_disp_new,
@@ -588,7 +588,7 @@ nv45_chipset = {
 	.imem = nv40_instmem_new,
 	.mc = nv40_mc_new,
 	.mmu = nv04_mmu_new,
-//	.therm = nv40_therm_new,
+	.therm = nv40_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = nv04_disp_new,
@@ -613,7 +613,7 @@ nv46_chipset = {
 	.imem = nv40_instmem_new,
 	.mc = nv44_mc_new,
 	.mmu = nv44_mmu_new,
-//	.therm = nv40_therm_new,
+	.therm = nv40_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = nv04_disp_new,
@@ -638,7 +638,7 @@ nv47_chipset = {
 	.imem = nv40_instmem_new,
 	.mc = nv40_mc_new,
 	.mmu = nv41_mmu_new,
-//	.therm = nv40_therm_new,
+	.therm = nv40_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = nv04_disp_new,
@@ -663,7 +663,7 @@ nv49_chipset = {
 	.imem = nv40_instmem_new,
 	.mc = nv40_mc_new,
 	.mmu = nv41_mmu_new,
-//	.therm = nv40_therm_new,
+	.therm = nv40_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = nv04_disp_new,
@@ -688,7 +688,7 @@ nv4a_chipset = {
 	.imem = nv40_instmem_new,
 	.mc = nv44_mc_new,
 	.mmu = nv44_mmu_new,
-//	.therm = nv40_therm_new,
+	.therm = nv40_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = nv04_disp_new,
@@ -713,7 +713,7 @@ nv4b_chipset = {
 	.imem = nv40_instmem_new,
 	.mc = nv40_mc_new,
 	.mmu = nv41_mmu_new,
-//	.therm = nv40_therm_new,
+	.therm = nv40_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = nv04_disp_new,
@@ -738,7 +738,7 @@ nv4c_chipset = {
 	.imem = nv40_instmem_new,
 	.mc = nv4c_mc_new,
 	.mmu = nv44_mmu_new,
-//	.therm = nv40_therm_new,
+	.therm = nv40_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = nv04_disp_new,
@@ -763,7 +763,7 @@ nv4e_chipset = {
 	.imem = nv40_instmem_new,
 	.mc = nv4c_mc_new,
 	.mmu = nv44_mmu_new,
-//	.therm = nv40_therm_new,
+	.therm = nv40_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = nv04_disp_new,
@@ -791,7 +791,7 @@ nv50_chipset = {
 	.mc = nv50_mc_new,
 	.mmu = nv50_mmu_new,
 	.mxm = nv50_mxm_new,
-//	.therm = nv50_therm_new,
+	.therm = nv50_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = nv50_disp_new,
@@ -816,7 +816,7 @@ nv63_chipset = {
 	.imem = nv40_instmem_new,
 	.mc = nv4c_mc_new,
 	.mmu = nv44_mmu_new,
-//	.therm = nv40_therm_new,
+	.therm = nv40_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = nv04_disp_new,
@@ -841,7 +841,7 @@ nv67_chipset = {
 	.imem = nv40_instmem_new,
 	.mc = nv4c_mc_new,
 	.mmu = nv44_mmu_new,
-//	.therm = nv40_therm_new,
+	.therm = nv40_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = nv04_disp_new,
@@ -866,7 +866,7 @@ nv68_chipset = {
 	.imem = nv40_instmem_new,
 	.mc = nv4c_mc_new,
 	.mmu = nv44_mmu_new,
-//	.therm = nv40_therm_new,
+	.therm = nv40_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = nv04_disp_new,
@@ -894,7 +894,7 @@ nv84_chipset = {
 	.mc = nv50_mc_new,
 	.mmu = nv50_mmu_new,
 	.mxm = nv50_mxm_new,
-//	.therm = g84_therm_new,
+	.therm = g84_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.bsp = g84_bsp_new,
@@ -925,7 +925,7 @@ nv86_chipset = {
 	.mc = nv50_mc_new,
 	.mmu = nv50_mmu_new,
 	.mxm = nv50_mxm_new,
-//	.therm = g84_therm_new,
+	.therm = g84_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.bsp = g84_bsp_new,
@@ -956,7 +956,7 @@ nv92_chipset = {
 	.mc = nv50_mc_new,
 	.mmu = nv50_mmu_new,
 	.mxm = nv50_mxm_new,
-//	.therm = g84_therm_new,
+	.therm = g84_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.bsp = g84_bsp_new,
@@ -987,7 +987,7 @@ nv94_chipset = {
 	.mc = g94_mc_new,
 	.mmu = nv50_mmu_new,
 	.mxm = nv50_mxm_new,
-//	.therm = g84_therm_new,
+	.therm = g84_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.bsp = g84_bsp_new,
@@ -1010,7 +1010,7 @@ nv96_chipset = {
 	.i2c = g94_i2c_new,
 	.fuse = nv50_fuse_new,
 	.clk = g84_clk_new,
-//	.therm = g84_therm_new,
+	.therm = g84_therm_new,
 	.mxm = nv50_mxm_new,
 	.devinit = g84_devinit_new,
 	.mc = g94_mc_new,
@@ -1041,7 +1041,7 @@ nv98_chipset = {
 	.i2c = g94_i2c_new,
 	.fuse = nv50_fuse_new,
 	.clk = g84_clk_new,
-//	.therm = g84_therm_new,
+	.therm = g84_therm_new,
 	.mxm = nv50_mxm_new,
 	.devinit = g98_devinit_new,
 	.mc = g98_mc_new,
@@ -1080,7 +1080,7 @@ nva0_chipset = {
 	.mc = g98_mc_new,
 	.mmu = nv50_mmu_new,
 	.mxm = nv50_mxm_new,
-//	.therm = g84_therm_new,
+	.therm = g84_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.bsp = g84_bsp_new,
@@ -1112,7 +1112,7 @@ nva3_chipset = {
 	.mmu = nv50_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gt215_pmu_new,
-//	.therm = gt215_therm_new,
+	.therm = gt215_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gt215_ce_new,
@@ -1145,7 +1145,7 @@ nva5_chipset = {
 	.mmu = nv50_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gt215_pmu_new,
-//	.therm = gt215_therm_new,
+	.therm = gt215_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gt215_ce_new,
@@ -1177,7 +1177,7 @@ nva8_chipset = {
 	.mmu = nv50_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gt215_pmu_new,
-//	.therm = gt215_therm_new,
+	.therm = gt215_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gt215_ce_new,
@@ -1208,7 +1208,7 @@ nvaa_chipset = {
 	.mc = g98_mc_new,
 	.mmu = nv50_mmu_new,
 	.mxm = nv50_mxm_new,
-//	.therm = g84_therm_new,
+	.therm = g84_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = g94_disp_new,
@@ -1239,7 +1239,7 @@ nvac_chipset = {
 	.mc = g98_mc_new,
 	.mmu = nv50_mmu_new,
 	.mxm = nv50_mxm_new,
-//	.therm = g84_therm_new,
+	.therm = g84_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.disp = g94_disp_new,
@@ -1271,7 +1271,7 @@ nvaf_chipset = {
 	.mmu = nv50_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gt215_pmu_new,
-//	.therm = gt215_therm_new,
+	.therm = gt215_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gt215_ce_new,
@@ -1305,7 +1305,7 @@ nvc0_chipset = {
 	.mmu = gf100_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gf100_pmu_new,
-//	.therm = gt215_therm_new,
+	.therm = gt215_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gf100_ce0_new,
@@ -1340,7 +1340,7 @@ nvc1_chipset = {
 	.mmu = gf100_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gf100_pmu_new,
-//	.therm = gt215_therm_new,
+	.therm = gt215_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gf100_ce0_new,
@@ -1374,7 +1374,7 @@ nvc3_chipset = {
 	.mmu = gf100_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gf100_pmu_new,
-//	.therm = gt215_therm_new,
+	.therm = gt215_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gf100_ce0_new,
@@ -1408,7 +1408,7 @@ nvc4_chipset = {
 	.mmu = gf100_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gf100_pmu_new,
-//	.therm = gt215_therm_new,
+	.therm = gt215_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gf100_ce0_new,
@@ -1443,7 +1443,7 @@ nvc8_chipset = {
 	.mmu = gf100_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gf100_pmu_new,
-//	.therm = gt215_therm_new,
+	.therm = gt215_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gf100_ce0_new,
@@ -1478,7 +1478,7 @@ nvce_chipset = {
 	.mmu = gf100_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gf100_pmu_new,
-//	.therm = gt215_therm_new,
+	.therm = gt215_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gf100_ce0_new,
@@ -1513,7 +1513,7 @@ nvcf_chipset = {
 	.mmu = gf100_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gf100_pmu_new,
-//	.therm = gt215_therm_new,
+	.therm = gt215_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gf100_ce0_new,
@@ -1546,7 +1546,7 @@ nvd7_chipset = {
 	.mc = gf106_mc_new,
 	.mmu = gf100_mmu_new,
 	.mxm = nv50_mxm_new,
-//	.therm = gf110_therm_new,
+	.therm = gf119_therm_new,
 //	.timer = nv04_timer_new,
 //	.ce[0] = gf100_ce0_new,
 //	.disp = gf119_disp_new,
@@ -1579,7 +1579,7 @@ nvd9_chipset = {
 	.mmu = gf100_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gf119_pmu_new,
-//	.therm = gf110_therm_new,
+	.therm = gf119_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gf100_ce0_new,
@@ -1613,7 +1613,7 @@ nve4_chipset = {
 	.mmu = gf100_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gk104_pmu_new,
-//	.therm = gf110_therm_new,
+	.therm = gf119_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gk104_ce0_new,
@@ -1649,7 +1649,7 @@ nve6_chipset = {
 	.mmu = gf100_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gk104_pmu_new,
-//	.therm = gf110_therm_new,
+	.therm = gf119_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gk104_ce0_new,
@@ -1685,7 +1685,7 @@ nve7_chipset = {
 	.mmu = gf100_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gf119_pmu_new,
-//	.therm = gf110_therm_new,
+	.therm = gf119_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gk104_ce0_new,
@@ -1745,7 +1745,7 @@ nvf0_chipset = {
 	.mmu = gf100_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gk110_pmu_new,
-//	.therm = gf110_therm_new,
+	.therm = gf119_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gk104_ce0_new,
@@ -1781,7 +1781,7 @@ nvf1_chipset = {
 	.mmu = gf100_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gk110_pmu_new,
-//	.therm = gf110_therm_new,
+	.therm = gf119_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gk104_ce0_new,
@@ -1817,7 +1817,7 @@ nv106_chipset = {
 	.mmu = gf100_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gk208_pmu_new,
-//	.therm = gf110_therm_new,
+	.therm = gf119_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gk104_ce0_new,
@@ -1852,7 +1852,7 @@ nv108_chipset = {
 	.mmu = gf100_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gk208_pmu_new,
-//	.therm = gf110_therm_new,
+	.therm = gf119_therm_new,
 //	.timer = nv04_timer_new,
 //	.volt = nv40_volt_new,
 //	.ce[0] = gk104_ce0_new,
@@ -1887,7 +1887,7 @@ nv117_chipset = {
 	.mmu = gf100_mmu_new,
 	.mxm = nv50_mxm_new,
 	.pmu = gm107_pmu_new,
-//	.therm = gm107_therm_new,
+	.therm = gm107_therm_new,
 //	.timer = gk20a_timer_new,
 //	.ce[0] = gk104_ce0_new,
 //	.ce[2] = gk104_ce2_new,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/gf100.c
index 556447727342..dcaa480cd310 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/gf100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/gf100.c
@@ -28,7 +28,6 @@ gf100_identify(struct nvkm_device *device)
 {
 	switch (device->chipset) {
 	case 0xc0:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gt215_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  gf100_dmaeng_oclass;
@@ -44,7 +43,6 @@ gf100_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] = gf100_pm_oclass;
 		break;
 	case 0xc4:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gt215_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  gf100_dmaeng_oclass;
@@ -60,7 +58,6 @@ gf100_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] = gf100_pm_oclass;
 		break;
 	case 0xc3:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gt215_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  gf100_dmaeng_oclass;
@@ -75,7 +72,6 @@ gf100_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] = gf100_pm_oclass;
 		break;
 	case 0xce:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gt215_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  gf100_dmaeng_oclass;
@@ -91,7 +87,6 @@ gf100_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] = gf100_pm_oclass;
 		break;
 	case 0xcf:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gt215_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  gf100_dmaeng_oclass;
@@ -106,7 +101,6 @@ gf100_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] = gf100_pm_oclass;
 		break;
 	case 0xc1:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gt215_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  gf100_dmaeng_oclass;
@@ -121,7 +115,6 @@ gf100_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] = gf108_pm_oclass;
 		break;
 	case 0xc8:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gt215_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  gf100_dmaeng_oclass;
@@ -137,7 +130,6 @@ gf100_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] = gf100_pm_oclass;
 		break;
 	case 0xd9:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gf110_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  gf110_dmaeng_oclass;
@@ -152,7 +144,6 @@ gf100_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] = gf117_pm_oclass;
 		break;
 	case 0xd7:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gf110_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  gf110_dmaeng_oclass;
 		device->oclass[NVDEV_ENGINE_FIFO   ] =  gf100_fifo_oclass;
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/gk104.c
index 9e05f8bbabc9..048f1beab81d 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/gk104.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/gk104.c
@@ -28,7 +28,6 @@ gk104_identify(struct nvkm_device *device)
 {
 	switch (device->chipset) {
 	case 0xe4:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gf110_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  gf110_dmaeng_oclass;
@@ -45,7 +44,6 @@ gk104_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] = gk104_pm_oclass;
 		break;
 	case 0xe7:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gf110_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  gf110_dmaeng_oclass;
@@ -62,7 +60,6 @@ gk104_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] = gk104_pm_oclass;
 		break;
 	case 0xe6:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gf110_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  gf110_dmaeng_oclass;
@@ -89,7 +86,6 @@ gk104_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &gk20a_volt_oclass;
 		break;
 	case 0xf0:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gf110_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  gf110_dmaeng_oclass;
@@ -106,7 +102,6 @@ gk104_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] = &gk110_pm_oclass;
 		break;
 	case 0xf1:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gf110_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  gf110_dmaeng_oclass;
@@ -123,7 +118,6 @@ gk104_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] = &gk110_pm_oclass;
 		break;
 	case 0x106:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gf110_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  gf110_dmaeng_oclass;
@@ -139,7 +133,6 @@ gk104_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_MSPPP  ] = &gf100_msppp_oclass;
 		break;
 	case 0x108:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gf110_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  gf110_dmaeng_oclass;
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/gm100.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/gm100.c
index 76c6b104a99a..e2d00b465b80 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/gm100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/gm100.c
@@ -28,7 +28,6 @@ gm100_identify(struct nvkm_device *device)
 {
 	switch (device->chipset) {
 	case 0x117:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gm107_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &gk20a_timer_oclass;
 
 #if 0
@@ -54,7 +53,6 @@ gm100_identify(struct nvkm_device *device)
 #if 0
 		/* looks to be some non-trivial changes */
 		/* priv ring says no to 0x10eb14 writes */
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gm107_therm_oclass;
 #endif
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &gk20a_timer_oclass;
 #if 0
@@ -78,7 +76,6 @@ gm100_identify(struct nvkm_device *device)
 #if 0
 		/* looks to be some non-trivial changes */
 		/* priv ring says no to 0x10eb14 writes */
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gm107_therm_oclass;
 #endif
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &gk20a_timer_oclass;
 #if 0
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/nv40.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/nv40.c
index 9af8044c16d2..5aa4cac00402 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/nv40.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/nv40.c
@@ -28,7 +28,6 @@ nv40_identify(struct nvkm_device *device)
 {
 	switch (device->chipset) {
 	case 0x40:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv04_dmaeng_oclass;
@@ -40,7 +39,6 @@ nv40_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  nv40_pm_oclass;
 		break;
 	case 0x41:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv04_dmaeng_oclass;
@@ -52,7 +50,6 @@ nv40_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  nv40_pm_oclass;
 		break;
 	case 0x42:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv04_dmaeng_oclass;
@@ -64,7 +61,6 @@ nv40_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  nv40_pm_oclass;
 		break;
 	case 0x43:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv04_dmaeng_oclass;
@@ -76,7 +72,6 @@ nv40_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  nv40_pm_oclass;
 		break;
 	case 0x45:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv04_dmaeng_oclass;
@@ -88,7 +83,6 @@ nv40_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  nv40_pm_oclass;
 		break;
 	case 0x47:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv04_dmaeng_oclass;
@@ -100,7 +94,6 @@ nv40_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  nv40_pm_oclass;
 		break;
 	case 0x49:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv04_dmaeng_oclass;
@@ -112,7 +105,6 @@ nv40_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  nv40_pm_oclass;
 		break;
 	case 0x4b:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv04_dmaeng_oclass;
@@ -124,7 +116,6 @@ nv40_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  nv40_pm_oclass;
 		break;
 	case 0x44:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv04_dmaeng_oclass;
@@ -136,7 +127,6 @@ nv40_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  nv40_pm_oclass;
 		break;
 	case 0x46:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv04_dmaeng_oclass;
@@ -148,7 +138,6 @@ nv40_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  nv40_pm_oclass;
 		break;
 	case 0x4a:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv04_dmaeng_oclass;
@@ -160,7 +149,6 @@ nv40_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  nv40_pm_oclass;
 		break;
 	case 0x4c:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv04_dmaeng_oclass;
@@ -172,7 +160,6 @@ nv40_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  nv40_pm_oclass;
 		break;
 	case 0x4e:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv04_dmaeng_oclass;
@@ -184,7 +171,6 @@ nv40_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  nv40_pm_oclass;
 		break;
 	case 0x63:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv04_dmaeng_oclass;
@@ -196,7 +182,6 @@ nv40_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  nv40_pm_oclass;
 		break;
 	case 0x67:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv04_dmaeng_oclass;
@@ -208,7 +193,6 @@ nv40_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  nv40_pm_oclass;
 		break;
 	case 0x68:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv04_dmaeng_oclass;
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/nv50.c
index f2956d45d32e..8cc924046b78 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/nv50.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/nv50.c
@@ -28,7 +28,6 @@ nv50_identify(struct nvkm_device *device)
 {
 	switch (device->chipset) {
 	case 0x50:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv50_dmaeng_oclass;
@@ -40,7 +39,6 @@ nv50_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  nv50_pm_oclass;
 		break;
 	case 0x84:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &g84_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv50_dmaeng_oclass;
@@ -55,7 +53,6 @@ nv50_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  g84_pm_oclass;
 		break;
 	case 0x86:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &g84_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv50_dmaeng_oclass;
@@ -70,7 +67,6 @@ nv50_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  g84_pm_oclass;
 		break;
 	case 0x92:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &g84_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv50_dmaeng_oclass;
@@ -85,7 +81,6 @@ nv50_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  g84_pm_oclass;
 		break;
 	case 0x94:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &g84_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv50_dmaeng_oclass;
@@ -100,7 +95,6 @@ nv50_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  g84_pm_oclass;
 		break;
 	case 0x96:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &g84_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv50_dmaeng_oclass;
@@ -115,7 +109,6 @@ nv50_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  g84_pm_oclass;
 		break;
 	case 0x98:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &g84_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv50_dmaeng_oclass;
@@ -130,7 +123,6 @@ nv50_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  g84_pm_oclass;
 		break;
 	case 0xa0:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &g84_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv50_dmaeng_oclass;
@@ -145,7 +137,6 @@ nv50_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  gt200_pm_oclass;
 		break;
 	case 0xaa:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &g84_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv50_dmaeng_oclass;
@@ -160,7 +151,6 @@ nv50_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  g84_pm_oclass;
 		break;
 	case 0xac:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &g84_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv50_dmaeng_oclass;
@@ -175,7 +165,6 @@ nv50_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  g84_pm_oclass;
 		break;
 	case 0xa3:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gt215_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv50_dmaeng_oclass;
@@ -191,7 +180,6 @@ nv50_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  gt215_pm_oclass;
 		break;
 	case 0xa5:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gt215_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv50_dmaeng_oclass;
@@ -206,7 +194,6 @@ nv50_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  gt215_pm_oclass;
 		break;
 	case 0xa8:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gt215_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv50_dmaeng_oclass;
@@ -221,7 +208,6 @@ nv50_identify(struct nvkm_device *device)
 		device->oclass[NVDEV_ENGINE_PM     ] =  gt215_pm_oclass;
 		break;
 	case 0xaf:
-		device->oclass[NVDEV_SUBDEV_THERM  ] = &gt215_therm_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 		device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
 		device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nv50_dmaeng_oclass;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/Kbuild
index 5837cf1292d9..135758ba3e28 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/Kbuild
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/Kbuild
@@ -9,5 +9,5 @@ nvkm-y += nvkm/subdev/therm/nv40.o
 nvkm-y += nvkm/subdev/therm/nv50.o
 nvkm-y += nvkm/subdev/therm/g84.o
 nvkm-y += nvkm/subdev/therm/gt215.o
-nvkm-y += nvkm/subdev/therm/gf110.o
+nvkm-y += nvkm/subdev/therm/gf119.o
 nvkm-y += nvkm/subdev/therm/gm107.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c
index e757fd9b7a07..304bdfc54445 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c
@@ -23,14 +23,21 @@
  */
 #include "priv.h"
 
+int
+nvkm_therm_temp_get(struct nvkm_therm *therm)
+{
+	if (therm->func->temp_get)
+		return therm->func->temp_get(therm);
+	return -ENODEV;
+}
+
 static int
-nvkm_therm_update_trip(struct nvkm_therm *obj)
+nvkm_therm_update_trip(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
 	struct nvbios_therm_trip_point *trip = therm->fan->bios.trip,
 				       *cur_trip = NULL,
 				       *last_trip = therm->last_trip;
-	u8  temp = therm->base.temp_get(&therm->base);
+	u8  temp = therm->func->temp_get(therm);
 	u16 duty, i;
 
 	/* look for the trip point corresponding to the current temperature */
@@ -57,12 +64,11 @@ nvkm_therm_update_trip(struct nvkm_therm *obj)
 }
 
 static int
-nvkm_therm_update_linear(struct nvkm_therm *obj)
+nvkm_therm_update_linear(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
 	u8  linear_min_temp = therm->fan->bios.linear_min_temp;
 	u8  linear_max_temp = therm->fan->bios.linear_max_temp;
-	u8  temp = therm->base.temp_get(&therm->base);
+	u8  temp = therm->func->temp_get(therm);
 	u16 duty;
 
 	/* handle the non-linear part first */
@@ -80,10 +86,9 @@ nvkm_therm_update_linear(struct nvkm_therm *obj)
 }
 
 static void
-nvkm_therm_update(struct nvkm_therm *obj, int mode)
+nvkm_therm_update(struct nvkm_therm *therm, int mode)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-	struct nvkm_subdev *subdev = &therm->base.subdev;
+	struct nvkm_subdev *subdev = &therm->subdev;
 	struct nvkm_timer *tmr = subdev->device->timer;
 	unsigned long flags;
 	bool immd = true;
@@ -98,7 +103,7 @@ nvkm_therm_update(struct nvkm_therm *obj, int mode)
 	switch (mode) {
 	case NVKM_THERM_CTRL_MANUAL:
 		tmr->alarm_cancel(tmr, &therm->alarm);
-		duty = nvkm_therm_fan_get(&therm->base);
+		duty = nvkm_therm_fan_get(therm);
 		if (duty < 0)
 			duty = 100;
 		poll = false;
@@ -106,10 +111,10 @@ nvkm_therm_update(struct nvkm_therm *obj, int mode)
 	case NVKM_THERM_CTRL_AUTO:
 		switch(therm->fan->bios.fan_mode) {
 		case NVBIOS_THERM_FAN_TRIP:
-			duty = nvkm_therm_update_trip(&therm->base);
+			duty = nvkm_therm_update_trip(therm);
 			break;
 		case NVBIOS_THERM_FAN_LINEAR:
-			duty = nvkm_therm_update_linear(&therm->base);
+			duty = nvkm_therm_update_linear(therm);
 			break;
 		case NVBIOS_THERM_FAN_OTHER:
 			if (therm->cstate)
@@ -131,20 +136,19 @@ nvkm_therm_update(struct nvkm_therm *obj, int mode)
 
 	if (duty >= 0) {
 		nvkm_debug(subdev, "FAN target request: %d%%\n", duty);
-		nvkm_therm_fan_set(&therm->base, immd, duty);
+		nvkm_therm_fan_set(therm, immd, duty);
 	}
 }
 
 int
-nvkm_therm_cstate(struct nvkm_therm *obj, int fan, int dir)
+nvkm_therm_cstate(struct nvkm_therm *therm, int fan, int dir)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-	struct nvkm_subdev *subdev = &therm->base.subdev;
+	struct nvkm_subdev *subdev = &therm->subdev;
 	if (!dir || (dir < 0 && fan < therm->cstate) ||
 		    (dir > 0 && fan > therm->cstate)) {
 		nvkm_debug(subdev, "default fan speed -> %d%%\n", fan);
 		therm->cstate = fan;
-		nvkm_therm_update(&therm->base, -1);
+		nvkm_therm_update(therm, -1);
 	}
 	return 0;
 }
@@ -152,16 +156,15 @@ nvkm_therm_cstate(struct nvkm_therm *obj, int fan, int dir)
 static void
 nvkm_therm_alarm(struct nvkm_alarm *alarm)
 {
-	struct nvkm_therm_priv *therm =
-	       container_of(alarm, struct nvkm_therm_priv, alarm);
-	nvkm_therm_update(&therm->base, -1);
+	struct nvkm_therm *therm =
+	       container_of(alarm, struct nvkm_therm, alarm);
+	nvkm_therm_update(therm, -1);
 }
 
 int
-nvkm_therm_fan_mode(struct nvkm_therm *obj, int mode)
+nvkm_therm_fan_mode(struct nvkm_therm *therm, int mode)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-	struct nvkm_subdev *subdev = &therm->base.subdev;
+	struct nvkm_subdev *subdev = &therm->subdev;
 	struct nvkm_device *device = subdev->device;
 	static const char *name[] = {
 		"disabled",
@@ -172,28 +175,26 @@ nvkm_therm_fan_mode(struct nvkm_therm *obj, int mode)
 	/* The default PPWR ucode on fermi interferes with fan management */
 	if ((mode >= ARRAY_SIZE(name)) ||
 	    (mode != NVKM_THERM_CTRL_NONE && device->card_type >= NV_C0 &&
-	     !nvkm_subdev(device, NVDEV_SUBDEV_PMU)))
+	     !device->pmu))
 		return -EINVAL;
 
 	/* do not allow automatic fan management if the thermal sensor is
 	 * not available */
 	if (mode == NVKM_THERM_CTRL_AUTO &&
-	    therm->base.temp_get(&therm->base) < 0)
+	    therm->func->temp_get(therm) < 0)
 		return -EINVAL;
 
 	if (therm->mode == mode)
 		return 0;
 
 	nvkm_debug(subdev, "fan management: %s\n", name[mode]);
-	nvkm_therm_update(&therm->base, mode);
+	nvkm_therm_update(therm, mode);
 	return 0;
 }
 
 int
-nvkm_therm_attr_get(struct nvkm_therm *obj, enum nvkm_therm_attr_type type)
+nvkm_therm_attr_get(struct nvkm_therm *therm, enum nvkm_therm_attr_type type)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-
 	switch (type) {
 	case NVKM_THERM_ATTR_FAN_MIN_DUTY:
 		return therm->fan->bios.min_duty;
@@ -223,11 +224,9 @@ nvkm_therm_attr_get(struct nvkm_therm *obj, enum nvkm_therm_attr_type type)
 }
 
 int
-nvkm_therm_attr_set(struct nvkm_therm *obj,
+nvkm_therm_attr_set(struct nvkm_therm *therm,
 		    enum nvkm_therm_attr_type type, int value)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-
 	switch (type) {
 	case NVKM_THERM_ATTR_FAN_MIN_DUTY:
 		if (value < 0)
@@ -244,123 +243,140 @@ nvkm_therm_attr_set(struct nvkm_therm *obj,
 		therm->fan->bios.max_duty = value;
 		return 0;
 	case NVKM_THERM_ATTR_FAN_MODE:
-		return nvkm_therm_fan_mode(&therm->base, value);
+		return nvkm_therm_fan_mode(therm, value);
 	case NVKM_THERM_ATTR_THRS_FAN_BOOST:
 		therm->bios_sensor.thrs_fan_boost.temp = value;
-		therm->sensor.program_alarms(&therm->base);
+		therm->func->program_alarms(therm);
 		return 0;
 	case NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST:
 		therm->bios_sensor.thrs_fan_boost.hysteresis = value;
-		therm->sensor.program_alarms(&therm->base);
+		therm->func->program_alarms(therm);
 		return 0;
 	case NVKM_THERM_ATTR_THRS_DOWN_CLK:
 		therm->bios_sensor.thrs_down_clock.temp = value;
-		therm->sensor.program_alarms(&therm->base);
+		therm->func->program_alarms(therm);
 		return 0;
 	case NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST:
 		therm->bios_sensor.thrs_down_clock.hysteresis = value;
-		therm->sensor.program_alarms(&therm->base);
+		therm->func->program_alarms(therm);
 		return 0;
 	case NVKM_THERM_ATTR_THRS_CRITICAL:
 		therm->bios_sensor.thrs_critical.temp = value;
-		therm->sensor.program_alarms(&therm->base);
+		therm->func->program_alarms(therm);
 		return 0;
 	case NVKM_THERM_ATTR_THRS_CRITICAL_HYST:
 		therm->bios_sensor.thrs_critical.hysteresis = value;
-		therm->sensor.program_alarms(&therm->base);
+		therm->func->program_alarms(therm);
 		return 0;
 	case NVKM_THERM_ATTR_THRS_SHUTDOWN:
 		therm->bios_sensor.thrs_shutdown.temp = value;
-		therm->sensor.program_alarms(&therm->base);
+		therm->func->program_alarms(therm);
 		return 0;
 	case NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST:
 		therm->bios_sensor.thrs_shutdown.hysteresis = value;
-		therm->sensor.program_alarms(&therm->base);
+		therm->func->program_alarms(therm);
 		return 0;
 	}
 
 	return -EINVAL;
 }
 
-int
-_nvkm_therm_init(struct nvkm_object *object)
+static void
+nvkm_therm_intr(struct nvkm_subdev *subdev)
 {
-	struct nvkm_therm_priv *therm = (void *)object;
-	int ret;
-
-	ret = nvkm_subdev_init_old(&therm->base.subdev);
-	if (ret)
-		return ret;
-
-	if (therm->suspend >= 0) {
-		/* restore the pwm value only when on manual or auto mode */
-		if (therm->suspend > 0)
-			nvkm_therm_fan_set(&therm->base, true, therm->fan->percent);
-
-		nvkm_therm_fan_mode(&therm->base, therm->suspend);
-	}
-	nvkm_therm_sensor_init(&therm->base);
-	nvkm_therm_fan_init(&therm->base);
-	return 0;
+	struct nvkm_therm *therm = nvkm_therm(subdev);
+	if (therm->func->intr)
+		therm->func->intr(therm);
 }
 
-int
-_nvkm_therm_fini(struct nvkm_object *object, bool suspend)
+static int
+nvkm_therm_fini(struct nvkm_subdev *subdev, bool suspend)
 {
-	struct nvkm_therm_priv *therm = (void *)object;
+	struct nvkm_therm *therm = nvkm_therm(subdev);
+
+	if (therm->func->fini)
+		therm->func->fini(therm);
+
+	nvkm_therm_fan_fini(therm, suspend);
+	nvkm_therm_sensor_fini(therm, suspend);
 
-	nvkm_therm_fan_fini(&therm->base, suspend);
-	nvkm_therm_sensor_fini(&therm->base, suspend);
 	if (suspend) {
 		therm->suspend = therm->mode;
 		therm->mode = NVKM_THERM_CTRL_NONE;
 	}
 
-	return nvkm_subdev_fini_old(&therm->base.subdev, suspend);
-}
-
-int
-nvkm_therm_create_(struct nvkm_object *parent, struct nvkm_object *engine,
-		   struct nvkm_oclass *oclass, int length, void **pobject)
-{
-	struct nvkm_therm_priv *therm;
-	int ret;
-
-	ret = nvkm_subdev_create_(parent, engine, oclass, 0, "PTHERM",
-				  "therm", length, pobject);
-	therm = *pobject;
-	if (ret)
-		return ret;
-
-	nvkm_alarm_init(&therm->alarm, nvkm_therm_alarm);
-	spin_lock_init(&therm->lock);
-	spin_lock_init(&therm->sensor.alarm_program_lock);
-
-	therm->base.fan_get = nvkm_therm_fan_user_get;
-	therm->base.fan_set = nvkm_therm_fan_user_set;
-	therm->base.fan_sense = nvkm_therm_fan_sense;
-	therm->base.attr_get = nvkm_therm_attr_get;
-	therm->base.attr_set = nvkm_therm_attr_set;
-	therm->mode = therm->suspend = -1; /* undefined */
 	return 0;
 }
 
-int
-nvkm_therm_preinit(struct nvkm_therm *therm)
+static int
+nvkm_therm_oneinit(struct nvkm_subdev *subdev)
 {
+	struct nvkm_therm *therm = nvkm_therm(subdev);
 	nvkm_therm_sensor_ctor(therm);
 	nvkm_therm_ic_ctor(therm);
 	nvkm_therm_fan_ctor(therm);
-
 	nvkm_therm_fan_mode(therm, NVKM_THERM_CTRL_AUTO);
 	nvkm_therm_sensor_preinit(therm);
 	return 0;
 }
 
-void
-_nvkm_therm_dtor(struct nvkm_object *object)
+static int
+nvkm_therm_init(struct nvkm_subdev *subdev)
+{
+	struct nvkm_therm *therm = nvkm_therm(subdev);
+
+	therm->func->init(therm);
+
+	if (therm->suspend >= 0) {
+		/* restore the pwm value only when on manual or auto mode */
+		if (therm->suspend > 0)
+			nvkm_therm_fan_set(therm, true, therm->fan->percent);
+
+		nvkm_therm_fan_mode(therm, therm->suspend);
+	}
+
+	nvkm_therm_sensor_init(therm);
+	nvkm_therm_fan_init(therm);
+	return 0;
+}
+
+static void *
+nvkm_therm_dtor(struct nvkm_subdev *subdev)
 {
-	struct nvkm_therm_priv *therm = (void *)object;
+	struct nvkm_therm *therm = nvkm_therm(subdev);
 	kfree(therm->fan);
-	nvkm_subdev_destroy(&therm->base.subdev);
+	return therm;
+}
+
+static const struct nvkm_subdev_func
+nvkm_therm = {
+	.dtor = nvkm_therm_dtor,
+	.oneinit = nvkm_therm_oneinit,
+	.init = nvkm_therm_init,
+	.fini = nvkm_therm_fini,
+	.intr = nvkm_therm_intr,
+};
+
+int
+nvkm_therm_new_(const struct nvkm_therm_func *func, struct nvkm_device *device,
+		int index, struct nvkm_therm **ptherm)
+{
+	struct nvkm_therm *therm;
+
+	if (!(therm = *ptherm = kzalloc(sizeof(*therm), GFP_KERNEL)))
+		return -ENOMEM;
+
+	nvkm_subdev_ctor(&nvkm_therm, device, index, 0, &therm->subdev);
+	therm->func = func;
+
+	nvkm_alarm_init(&therm->alarm, nvkm_therm_alarm);
+	spin_lock_init(&therm->lock);
+	spin_lock_init(&therm->sensor.alarm_program_lock);
+
+	therm->fan_get = nvkm_therm_fan_user_get;
+	therm->fan_set = nvkm_therm_fan_user_set;
+	therm->attr_get = nvkm_therm_attr_get;
+	therm->attr_set = nvkm_therm_attr_set;
+	therm->mode = therm->suspend = -1; /* undefined */
+	return 0;
 }
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c
index e017607673a5..a2be18167770 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c
@@ -31,8 +31,8 @@
 static int
 nvkm_fan_update(struct nvkm_fan *fan, bool immediate, int target)
 {
-	struct nvkm_therm_priv *therm = (void *)fan->parent;
-	struct nvkm_subdev *subdev = &therm->base.subdev;
+	struct nvkm_therm *therm = fan->parent;
+	struct nvkm_subdev *subdev = &therm->subdev;
 	struct nvkm_timer *tmr = subdev->device->timer;
 	unsigned long flags;
 	int ret = 0;
@@ -50,7 +50,7 @@ nvkm_fan_update(struct nvkm_fan *fan, bool immediate, int target)
 	}
 
 	/* check that we're not already at the target duty cycle */
-	duty = fan->get(&therm->base);
+	duty = fan->get(therm);
 	if (duty == target) {
 		spin_unlock_irqrestore(&fan->lock, flags);
 		return 0;
@@ -71,7 +71,7 @@ nvkm_fan_update(struct nvkm_fan *fan, bool immediate, int target)
 	}
 
 	nvkm_debug(subdev, "FAN update: %d\n", duty);
-	ret = fan->set(&therm->base, duty);
+	ret = fan->set(therm, duty);
 	if (ret) {
 		spin_unlock_irqrestore(&fan->lock, flags);
 		return ret;
@@ -109,29 +109,29 @@ nvkm_fan_alarm(struct nvkm_alarm *alarm)
 }
 
 int
-nvkm_therm_fan_get(struct nvkm_therm *obj)
+nvkm_therm_fan_get(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-	return therm->fan->get(&therm->base);
+	return therm->fan->get(therm);
 }
 
 int
-nvkm_therm_fan_set(struct nvkm_therm *obj, bool immediate, int percent)
+nvkm_therm_fan_set(struct nvkm_therm *therm, bool immediate, int percent)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
 	return nvkm_fan_update(therm->fan, immediate, percent);
 }
 
 int
-nvkm_therm_fan_sense(struct nvkm_therm *obj)
+nvkm_therm_fan_sense(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-	struct nvkm_device *device = therm->base.subdev.device;
+	struct nvkm_device *device = therm->subdev.device;
 	struct nvkm_timer *tmr = device->timer;
 	struct nvkm_gpio *gpio = device->gpio;
 	u32 cycles, cur, prev;
 	u64 start, end, tach;
 
+	if (therm->func->fan_sense)
+		return therm->func->fan_sense(therm);
+
 	if (therm->fan->tach.func == DCB_GPIO_UNUSED)
 		return -ENODEV;
 
@@ -166,28 +166,23 @@ nvkm_therm_fan_sense(struct nvkm_therm *obj)
 }
 
 int
-nvkm_therm_fan_user_get(struct nvkm_therm *obj)
+nvkm_therm_fan_user_get(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-	return nvkm_therm_fan_get(&therm->base);
+	return nvkm_therm_fan_get(therm);
 }
 
 int
-nvkm_therm_fan_user_set(struct nvkm_therm *obj, int percent)
+nvkm_therm_fan_user_set(struct nvkm_therm *therm, int percent)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-
 	if (therm->mode != NVKM_THERM_CTRL_MANUAL)
 		return -EINVAL;
 
-	return nvkm_therm_fan_set(&therm->base, true, percent);
+	return nvkm_therm_fan_set(therm, true, percent);
 }
 
 static void
-nvkm_therm_fan_set_defaults(struct nvkm_therm *obj)
+nvkm_therm_fan_set_defaults(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-
 	therm->fan->bios.pwm_freq = 0;
 	therm->fan->bios.min_duty = 0;
 	therm->fan->bios.max_duty = 100;
@@ -198,10 +193,8 @@ nvkm_therm_fan_set_defaults(struct nvkm_therm *obj)
 }
 
 static void
-nvkm_therm_fan_safety_checks(struct nvkm_therm *obj)
+nvkm_therm_fan_safety_checks(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-
 	if (therm->fan->bios.min_duty > 100)
 		therm->fan->bios.min_duty = 100;
 	if (therm->fan->bios.max_duty > 100)
@@ -212,27 +205,24 @@ nvkm_therm_fan_safety_checks(struct nvkm_therm *obj)
 }
 
 int
-nvkm_therm_fan_init(struct nvkm_therm *obj)
+nvkm_therm_fan_init(struct nvkm_therm *therm)
 {
 	return 0;
 }
 
 int
-nvkm_therm_fan_fini(struct nvkm_therm *obj, bool suspend)
+nvkm_therm_fan_fini(struct nvkm_therm *therm, bool suspend)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-	struct nvkm_timer *tmr = nvkm_timer(therm);
-
+	struct nvkm_timer *tmr = therm->subdev.device->timer;
 	if (suspend)
 		tmr->alarm_cancel(tmr, &therm->fan->alarm);
 	return 0;
 }
 
 int
-nvkm_therm_fan_ctor(struct nvkm_therm *obj)
+nvkm_therm_fan_ctor(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-	struct nvkm_subdev *subdev = &therm->base.subdev;
+	struct nvkm_subdev *subdev = &therm->subdev;
 	struct nvkm_device *device = subdev->device;
 	struct nvkm_gpio *gpio = device->gpio;
 	struct nvkm_bios *bios = device->bios;
@@ -247,15 +237,15 @@ nvkm_therm_fan_ctor(struct nvkm_therm *obj)
 			nvkm_debug(subdev, "GPIO_FAN is in input mode\n");
 			ret = -EINVAL;
 		} else {
-			ret = nvkm_fanpwm_create(&therm->base, &func);
+			ret = nvkm_fanpwm_create(therm, &func);
 			if (ret != 0)
-				ret = nvkm_fantog_create(&therm->base, &func);
+				ret = nvkm_fantog_create(therm, &func);
 		}
 	}
 
 	/* no controllable fan found, create a dummy fan module */
 	if (ret != 0) {
-		ret = nvkm_fannil_create(&therm->base);
+		ret = nvkm_fannil_create(therm);
 		if (ret)
 			return ret;
 	}
@@ -263,7 +253,7 @@ nvkm_therm_fan_ctor(struct nvkm_therm *obj)
 	nvkm_debug(subdev, "FAN control: %s\n", therm->fan->type);
 
 	/* read the current speed, it is useful when resuming */
-	therm->fan->percent = nvkm_therm_fan_get(&therm->base);
+	therm->fan->percent = nvkm_therm_fan_get(therm);
 
 	/* attempt to detect a tachometer connection */
 	ret = nvkm_gpio_find(gpio, 0, DCB_GPIO_FAN_SENSE, 0xff,
@@ -272,18 +262,18 @@ nvkm_therm_fan_ctor(struct nvkm_therm *obj)
 		therm->fan->tach.func = DCB_GPIO_UNUSED;
 
 	/* initialise fan bump/slow update handling */
-	therm->fan->parent = &therm->base;
+	therm->fan->parent = therm;
 	nvkm_alarm_init(&therm->fan->alarm, nvkm_fan_alarm);
 	spin_lock_init(&therm->fan->lock);
 
 	/* other random init... */
-	nvkm_therm_fan_set_defaults(&therm->base);
+	nvkm_therm_fan_set_defaults(therm);
 	nvbios_perf_fan_parse(bios, &therm->fan->perf);
 	if (!nvbios_fan_parse(bios, &therm->fan->bios)) {
 		nvkm_debug(subdev, "parsing the fan table failed\n");
 		if (nvbios_therm_fan_parse(bios, &therm->fan->bios))
 			nvkm_error(subdev, "parsing both fan tables failed\n");
 	}
-	nvkm_therm_fan_safety_checks(&therm->base);
+	nvkm_therm_fan_safety_checks(therm);
 	return 0;
 }
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fannil.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fannil.c
index 693b4efd1841..8ae300f911b6 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fannil.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fannil.c
@@ -36,9 +36,8 @@ nvkm_fannil_set(struct nvkm_therm *therm, int percent)
 }
 
 int
-nvkm_fannil_create(struct nvkm_therm *obj)
+nvkm_fannil_create(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
 	struct nvkm_fan *priv;
 
 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c
index 944b7905b4a5..340f37a299dc 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c
@@ -35,17 +35,16 @@ struct nvkm_fanpwm {
 };
 
 static int
-nvkm_fanpwm_get(struct nvkm_therm *obj)
+nvkm_fanpwm_get(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
 	struct nvkm_fanpwm *fan = (void *)therm->fan;
-	struct nvkm_device *device = therm->base.subdev.device;
+	struct nvkm_device *device = therm->subdev.device;
 	struct nvkm_gpio *gpio = device->gpio;
 	int card_type = device->card_type;
 	u32 divs, duty;
 	int ret;
 
-	ret = therm->base.pwm_get(&therm->base, fan->func.line, &divs, &duty);
+	ret = therm->func->pwm_get(therm, fan->func.line, &divs, &duty);
 	if (ret == 0 && divs) {
 		divs = max(divs, duty);
 		if (card_type <= NV_40 || (fan->func.log[0] & 1))
@@ -57,20 +56,18 @@ nvkm_fanpwm_get(struct nvkm_therm *obj)
 }
 
 static int
-nvkm_fanpwm_set(struct nvkm_therm *obj, int percent)
+nvkm_fanpwm_set(struct nvkm_therm *therm, int percent)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
 	struct nvkm_fanpwm *fan = (void *)therm->fan;
-	int card_type = nv_device(therm)->card_type;
+	int card_type = therm->subdev.device->card_type;
 	u32 divs, duty;
 	int ret;
 
 	divs = fan->base.perf.pwm_divisor;
 	if (fan->base.bios.pwm_freq) {
 		divs = 1;
-		if (therm->base.pwm_clock)
-			divs = therm->base.pwm_clock(&therm->base,
-						     fan->func.line);
+		if (therm->func->pwm_clock)
+			divs = therm->func->pwm_clock(therm, fan->func.line);
 		divs /= fan->base.bios.pwm_freq;
 	}
 
@@ -78,27 +75,26 @@ nvkm_fanpwm_set(struct nvkm_therm *obj, int percent)
 	if (card_type <= NV_40 || (fan->func.log[0] & 1))
 		duty = divs - duty;
 
-	ret = therm->base.pwm_set(&therm->base, fan->func.line, divs, duty);
+	ret = therm->func->pwm_set(therm, fan->func.line, divs, duty);
 	if (ret == 0)
-		ret = therm->base.pwm_ctrl(&therm->base, fan->func.line, true);
+		ret = therm->func->pwm_ctrl(therm, fan->func.line, true);
 	return ret;
 }
 
 int
-nvkm_fanpwm_create(struct nvkm_therm *obj, struct dcb_gpio_func *func)
+nvkm_fanpwm_create(struct nvkm_therm *therm, struct dcb_gpio_func *func)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-	struct nvkm_device *device = therm->base.subdev.device;
+	struct nvkm_device *device = therm->subdev.device;
 	struct nvkm_bios *bios = device->bios;
 	struct nvkm_fanpwm *fan;
-	struct nvbios_therm_fan info;
+	struct nvbios_therm_fan info = {};
 	u32 divs, duty;
 
 	nvbios_fan_parse(bios, &info);
 
 	if (!nvkm_boolopt(device->cfgopt, "NvFanPWM", func->param) ||
-	    !therm->base.pwm_ctrl || info.type == NVBIOS_THERM_FAN_TOGGLE ||
-	     therm->base.pwm_get(&therm->base, func->line, &divs, &duty) == -ENODEV)
+	    !therm->func->pwm_ctrl || info.type == NVBIOS_THERM_FAN_TOGGLE ||
+	     therm->func->pwm_get(therm, func->line, &divs, &duty) == -ENODEV)
 		return -ENODEV;
 
 	fan = kzalloc(sizeof(*fan), GFP_KERNEL);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c
index 7e1f21d81eb1..64fe8f22336c 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c
@@ -38,8 +38,8 @@ struct nvkm_fantog {
 static void
 nvkm_fantog_update(struct nvkm_fantog *fan, int percent)
 {
-	struct nvkm_therm_priv *therm = (void *)fan->base.parent;
-	struct nvkm_device *device = therm->base.subdev.device;
+	struct nvkm_therm *therm = fan->base.parent;
+	struct nvkm_device *device = therm->subdev.device;
 	struct nvkm_timer *tmr = device->timer;
 	struct nvkm_gpio *gpio = device->gpio;
 	unsigned long flags;
@@ -71,33 +71,30 @@ nvkm_fantog_alarm(struct nvkm_alarm *alarm)
 }
 
 static int
-nvkm_fantog_get(struct nvkm_therm *obj)
+nvkm_fantog_get(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
 	struct nvkm_fantog *fan = (void *)therm->fan;
 	return fan->percent;
 }
 
 static int
-nvkm_fantog_set(struct nvkm_therm *obj, int percent)
+nvkm_fantog_set(struct nvkm_therm *therm, int percent)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
 	struct nvkm_fantog *fan = (void *)therm->fan;
-	if (therm->base.pwm_ctrl)
-		therm->base.pwm_ctrl(&therm->base, fan->func.line, false);
+	if (therm->func->pwm_ctrl)
+		therm->func->pwm_ctrl(therm, fan->func.line, false);
 	nvkm_fantog_update(fan, percent);
 	return 0;
 }
 
 int
-nvkm_fantog_create(struct nvkm_therm *obj, struct dcb_gpio_func *func)
+nvkm_fantog_create(struct nvkm_therm *therm, struct dcb_gpio_func *func)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
 	struct nvkm_fantog *fan;
 	int ret;
 
-	if (therm->base.pwm_ctrl) {
-		ret = therm->base.pwm_ctrl(&therm->base, func->line, false);
+	if (therm->func->pwm_ctrl) {
+		ret = therm->func->pwm_ctrl(therm, func->line, false);
 		if (ret)
 			return ret;
 	}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c
index c70b79f8786c..86e81930d8ee 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c
@@ -51,11 +51,10 @@ g84_sensor_setup(struct nvkm_therm *therm)
 }
 
 static void
-g84_therm_program_alarms(struct nvkm_therm *obj)
+g84_therm_program_alarms(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
 	struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
-	struct nvkm_subdev *subdev = &therm->base.subdev;
+	struct nvkm_subdev *subdev = &therm->subdev;
 	struct nvkm_device *device = subdev->device;
 	unsigned long flags;
 
@@ -116,7 +115,7 @@ g84_therm_threshold_hyst_emulation(struct nvkm_therm *therm,
 	}
 
 	/* fix the state (in case someone reprogrammed the alarms) */
-	cur = therm->temp_get(therm);
+	cur = therm->func->temp_get(therm);
 	if (new_state == NVKM_THERM_THRS_LOWER && cur > thrs->temp)
 		new_state = NVKM_THERM_THRS_HIGHER;
 	else if (new_state == NVKM_THERM_THRS_HIGHER &&
@@ -137,10 +136,10 @@ g84_therm_threshold_hyst_emulation(struct nvkm_therm *therm,
 }
 
 static void
-g84_therm_intr(struct nvkm_subdev *subdev)
+g84_therm_intr(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = (void *)subdev;
-	struct nvkm_device *device = therm->base.subdev.device;
+	struct nvkm_subdev *subdev = &therm->subdev;
+	struct nvkm_device *device = subdev->device;
 	struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
 	unsigned long flags;
 	uint32_t intr;
@@ -151,7 +150,7 @@ g84_therm_intr(struct nvkm_subdev *subdev)
 
 	/* THRS_4: downclock */
 	if (intr & 0x002) {
-		g84_therm_threshold_hyst_emulation(&therm->base, 0x20414, 24,
+		g84_therm_threshold_hyst_emulation(therm, 0x20414, 24,
 						   &sensor->thrs_down_clock,
 						   NVKM_THERM_THRS_DOWNCLOCK);
 		intr &= ~0x002;
@@ -159,7 +158,7 @@ g84_therm_intr(struct nvkm_subdev *subdev)
 
 	/* shutdown */
 	if (intr & 0x004) {
-		g84_therm_threshold_hyst_emulation(&therm->base, 0x20480, 20,
+		g84_therm_threshold_hyst_emulation(therm, 0x20480, 20,
 						   &sensor->thrs_shutdown,
 						   NVKM_THERM_THRS_SHUTDOWN);
 		intr &= ~0x004;
@@ -167,7 +166,7 @@ g84_therm_intr(struct nvkm_subdev *subdev)
 
 	/* THRS_1 : fan boost */
 	if (intr & 0x008) {
-		g84_therm_threshold_hyst_emulation(&therm->base, 0x204c4, 21,
+		g84_therm_threshold_hyst_emulation(therm, 0x204c4, 21,
 						   &sensor->thrs_fan_boost,
 						   NVKM_THERM_THRS_FANBOOST);
 		intr &= ~0x008;
@@ -175,7 +174,7 @@ g84_therm_intr(struct nvkm_subdev *subdev)
 
 	/* THRS_2 : critical */
 	if (intr & 0x010) {
-		g84_therm_threshold_hyst_emulation(&therm->base, 0x204c0, 22,
+		g84_therm_threshold_hyst_emulation(therm, 0x204c0, 22,
 						   &sensor->thrs_critical,
 						   NVKM_THERM_THRS_CRITICAL);
 		intr &= ~0x010;
@@ -191,62 +190,9 @@ g84_therm_intr(struct nvkm_subdev *subdev)
 	spin_unlock_irqrestore(&therm->sensor.alarm_program_lock, flags);
 }
 
-static int
-g84_therm_init(struct nvkm_object *object)
-{
-	struct nvkm_therm_priv *therm = (void *)object;
-	int ret;
-
-	ret = nvkm_therm_init(&therm->base);
-	if (ret)
-		return ret;
-
-	g84_sensor_setup(&therm->base);
-	return 0;
-}
-
-static int
-g84_therm_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
-	       struct nvkm_oclass *oclass, void *data, u32 size,
-	       struct nvkm_object **pobject)
-{
-	struct nvkm_therm_priv *therm;
-	int ret;
-
-	ret = nvkm_therm_create(parent, engine, oclass, &therm);
-	*pobject = nv_object(therm);
-	if (ret)
-		return ret;
-
-	therm->base.pwm_ctrl = nv50_fan_pwm_ctrl;
-	therm->base.pwm_get = nv50_fan_pwm_get;
-	therm->base.pwm_set = nv50_fan_pwm_set;
-	therm->base.pwm_clock = nv50_fan_pwm_clock;
-	therm->base.temp_get = g84_temp_get;
-	therm->sensor.program_alarms = g84_therm_program_alarms;
-	nv_subdev(therm)->intr = g84_therm_intr;
-
-	/* init the thresholds */
-	nvkm_therm_sensor_set_threshold_state(&therm->base,
-					      NVKM_THERM_THRS_SHUTDOWN,
-					      NVKM_THERM_THRS_LOWER);
-	nvkm_therm_sensor_set_threshold_state(&therm->base,
-					      NVKM_THERM_THRS_FANBOOST,
-					      NVKM_THERM_THRS_LOWER);
-	nvkm_therm_sensor_set_threshold_state(&therm->base,
-					      NVKM_THERM_THRS_CRITICAL,
-					      NVKM_THERM_THRS_LOWER);
-	nvkm_therm_sensor_set_threshold_state(&therm->base,
-					      NVKM_THERM_THRS_DOWNCLOCK,
-					      NVKM_THERM_THRS_LOWER);
-
-	return nvkm_therm_preinit(&therm->base);
-}
-
-int
-g84_therm_fini(struct nvkm_object *object, bool suspend)
+void
+g84_therm_fini(struct nvkm_therm *therm)
 {
-	struct nvkm_therm *therm = (void *)object;
 	struct nvkm_device *device = therm->subdev.device;
 
 	/* Disable PTherm IRQs */
@@ -255,17 +201,46 @@ g84_therm_fini(struct nvkm_object *object, bool suspend)
 	/* ACK all PTherm IRQs */
 	nvkm_wr32(device, 0x20100, 0xffffffff);
 	nvkm_wr32(device, 0x1100, 0x10000); /* PBUS */
+}
 
-	return _nvkm_therm_fini(object, suspend);
+static void
+g84_therm_init(struct nvkm_therm *therm)
+{
+	g84_sensor_setup(therm);
 }
 
-struct nvkm_oclass
-g84_therm_oclass = {
-	.handle = NV_SUBDEV(THERM, 0x84),
-	.ofuncs = &(struct nvkm_ofuncs) {
-		.ctor = g84_therm_ctor,
-		.dtor = _nvkm_therm_dtor,
-		.init = g84_therm_init,
-		.fini = g84_therm_fini,
-	},
+static const struct nvkm_therm_func
+g84_therm = {
+	.init = g84_therm_init,
+	.fini = g84_therm_fini,
+	.intr = g84_therm_intr,
+	.pwm_ctrl = nv50_fan_pwm_ctrl,
+	.pwm_get = nv50_fan_pwm_get,
+	.pwm_set = nv50_fan_pwm_set,
+	.pwm_clock = nv50_fan_pwm_clock,
+	.temp_get = g84_temp_get,
+	.program_alarms = g84_therm_program_alarms,
 };
+
+int
+g84_therm_new(struct nvkm_device *device, int index, struct nvkm_therm **ptherm)
+{
+	struct nvkm_therm *therm;
+	int ret;
+
+	ret = nvkm_therm_new_(&g84_therm, device, index, &therm);
+	*ptherm = therm;
+	if (ret)
+		return ret;
+
+	/* init the thresholds */
+	nvkm_therm_sensor_set_threshold_state(therm, NVKM_THERM_THRS_SHUTDOWN,
+						     NVKM_THERM_THRS_LOWER);
+	nvkm_therm_sensor_set_threshold_state(therm, NVKM_THERM_THRS_FANBOOST,
+						     NVKM_THERM_THRS_LOWER);
+	nvkm_therm_sensor_set_threshold_state(therm, NVKM_THERM_THRS_CRITICAL,
+						     NVKM_THERM_THRS_LOWER);
+	nvkm_therm_sensor_set_threshold_state(therm, NVKM_THERM_THRS_DOWNCLOCK,
+						     NVKM_THERM_THRS_LOWER);
+	return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf110.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf110.c
deleted file mode 100644
index 4d017f62d7e4..000000000000
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf110.c
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright 2012 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Authors: Ben Skeggs
- */
-#include "priv.h"
-
-static int
-pwm_info(struct nvkm_therm *therm, int line)
-{
-	struct nvkm_subdev *subdev = &therm->subdev;
-	struct nvkm_device *device = subdev->device;
-	u32 gpio = nvkm_rd32(device, 0x00d610 + (line * 0x04));
-
-	switch (gpio & 0x000000c0) {
-	case 0x00000000: /* normal mode, possibly pwm forced off by us */
-	case 0x00000040: /* nvio special */
-		switch (gpio & 0x0000001f) {
-		case 0x00: return 2;
-		case 0x19: return 1;
-		case 0x1c: return 0;
-		case 0x1e: return 2;
-		default:
-			break;
-		}
-	default:
-		break;
-	}
-
-	nvkm_error(subdev, "GPIO %d unknown PWM: %08x\n", line, gpio);
-	return -ENODEV;
-}
-
-static int
-gf110_fan_pwm_ctrl(struct nvkm_therm *therm, int line, bool enable)
-{
-	struct nvkm_device *device = therm->subdev.device;
-	u32 data = enable ? 0x00000040 : 0x00000000;
-	int indx = pwm_info(therm, line);
-	if (indx < 0)
-		return indx;
-	else if (indx < 2)
-		nvkm_mask(device, 0x00d610 + (line * 0x04), 0x000000c0, data);
-	/* nothing to do for indx == 2, it seems hardwired to PTHERM */
-	return 0;
-}
-
-static int
-gf110_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty)
-{
-	struct nvkm_device *device = therm->subdev.device;
-	int indx = pwm_info(therm, line);
-	if (indx < 0)
-		return indx;
-	else if (indx < 2) {
-		if (nvkm_rd32(device, 0x00d610 + (line * 0x04)) & 0x00000040) {
-			*divs = nvkm_rd32(device, 0x00e114 + (indx * 8));
-			*duty = nvkm_rd32(device, 0x00e118 + (indx * 8));
-			return 0;
-		}
-	} else if (indx == 2) {
-		*divs = nvkm_rd32(device, 0x0200d8) & 0x1fff;
-		*duty = nvkm_rd32(device, 0x0200dc) & 0x1fff;
-		return 0;
-	}
-
-	return -EINVAL;
-}
-
-static int
-gf110_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty)
-{
-	struct nvkm_device *device = therm->subdev.device;
-	int indx = pwm_info(therm, line);
-	if (indx < 0)
-		return indx;
-	else if (indx < 2) {
-		nvkm_wr32(device, 0x00e114 + (indx * 8), divs);
-		nvkm_wr32(device, 0x00e118 + (indx * 8), duty | 0x80000000);
-	} else if (indx == 2) {
-		nvkm_mask(device, 0x0200d8, 0x1fff, divs); /* keep the high bits */
-		nvkm_wr32(device, 0x0200dc, duty | 0x40000000);
-	}
-	return 0;
-}
-
-static int
-gf110_fan_pwm_clock(struct nvkm_therm *therm, int line)
-{
-	struct nvkm_device *device = therm->subdev.device;
-	int indx = pwm_info(therm, line);
-	if (indx < 0)
-		return 0;
-	else if (indx < 2)
-		return (device->crystal * 1000) / 20;
-	else
-		return device->crystal * 1000 / 10;
-}
-
-int
-gf110_therm_init(struct nvkm_object *object)
-{
-	struct nvkm_therm_priv *therm = (void *)object;
-	struct nvkm_device *device = therm->base.subdev.device;
-	int ret;
-
-	ret = nvkm_therm_init(&therm->base);
-	if (ret)
-		return ret;
-
-	/* enable fan tach, count revolutions per-second */
-	nvkm_mask(device, 0x00e720, 0x00000003, 0x00000002);
-	if (therm->fan->tach.func != DCB_GPIO_UNUSED) {
-		nvkm_mask(device, 0x00d79c, 0x000000ff, therm->fan->tach.line);
-		nvkm_wr32(device, 0x00e724, nv_device(therm)->crystal * 1000);
-		nvkm_mask(device, 0x00e720, 0x00000001, 0x00000001);
-	}
-	nvkm_mask(device, 0x00e720, 0x00000002, 0x00000000);
-
-	return 0;
-}
-
-static int
-gf110_therm_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
-		 struct nvkm_oclass *oclass, void *data, u32 size,
-		 struct nvkm_object **pobject)
-{
-	struct nvkm_therm_priv *therm;
-	int ret;
-
-	ret = nvkm_therm_create(parent, engine, oclass, &therm);
-	*pobject = nv_object(therm);
-	if (ret)
-		return ret;
-
-	g84_sensor_setup(&therm->base);
-
-	therm->base.pwm_ctrl = gf110_fan_pwm_ctrl;
-	therm->base.pwm_get = gf110_fan_pwm_get;
-	therm->base.pwm_set = gf110_fan_pwm_set;
-	therm->base.pwm_clock = gf110_fan_pwm_clock;
-	therm->base.temp_get = g84_temp_get;
-	therm->base.fan_sense = gt215_therm_fan_sense;
-	therm->sensor.program_alarms = nvkm_therm_program_alarms_polling;
-	return nvkm_therm_preinit(&therm->base);
-}
-
-struct nvkm_oclass
-gf110_therm_oclass = {
-	.handle = NV_SUBDEV(THERM, 0xd0),
-	.ofuncs = &(struct nvkm_ofuncs) {
-		.ctor = gf110_therm_ctor,
-		.dtor = _nvkm_therm_dtor,
-		.init = gf110_therm_init,
-		.fini = g84_therm_fini,
-	},
-};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf119.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf119.c
new file mode 100644
index 000000000000..06dcfd6ee966
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf119.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+#include "priv.h"
+
+static int
+pwm_info(struct nvkm_therm *therm, int line)
+{
+	struct nvkm_subdev *subdev = &therm->subdev;
+	struct nvkm_device *device = subdev->device;
+	u32 gpio = nvkm_rd32(device, 0x00d610 + (line * 0x04));
+
+	switch (gpio & 0x000000c0) {
+	case 0x00000000: /* normal mode, possibly pwm forced off by us */
+	case 0x00000040: /* nvio special */
+		switch (gpio & 0x0000001f) {
+		case 0x00: return 2;
+		case 0x19: return 1;
+		case 0x1c: return 0;
+		case 0x1e: return 2;
+		default:
+			break;
+		}
+	default:
+		break;
+	}
+
+	nvkm_error(subdev, "GPIO %d unknown PWM: %08x\n", line, gpio);
+	return -ENODEV;
+}
+
+static int
+gf119_fan_pwm_ctrl(struct nvkm_therm *therm, int line, bool enable)
+{
+	struct nvkm_device *device = therm->subdev.device;
+	u32 data = enable ? 0x00000040 : 0x00000000;
+	int indx = pwm_info(therm, line);
+	if (indx < 0)
+		return indx;
+	else if (indx < 2)
+		nvkm_mask(device, 0x00d610 + (line * 0x04), 0x000000c0, data);
+	/* nothing to do for indx == 2, it seems hardwired to PTHERM */
+	return 0;
+}
+
+static int
+gf119_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty)
+{
+	struct nvkm_device *device = therm->subdev.device;
+	int indx = pwm_info(therm, line);
+	if (indx < 0)
+		return indx;
+	else if (indx < 2) {
+		if (nvkm_rd32(device, 0x00d610 + (line * 0x04)) & 0x00000040) {
+			*divs = nvkm_rd32(device, 0x00e114 + (indx * 8));
+			*duty = nvkm_rd32(device, 0x00e118 + (indx * 8));
+			return 0;
+		}
+	} else if (indx == 2) {
+		*divs = nvkm_rd32(device, 0x0200d8) & 0x1fff;
+		*duty = nvkm_rd32(device, 0x0200dc) & 0x1fff;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int
+gf119_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty)
+{
+	struct nvkm_device *device = therm->subdev.device;
+	int indx = pwm_info(therm, line);
+	if (indx < 0)
+		return indx;
+	else if (indx < 2) {
+		nvkm_wr32(device, 0x00e114 + (indx * 8), divs);
+		nvkm_wr32(device, 0x00e118 + (indx * 8), duty | 0x80000000);
+	} else if (indx == 2) {
+		nvkm_mask(device, 0x0200d8, 0x1fff, divs); /* keep the high bits */
+		nvkm_wr32(device, 0x0200dc, duty | 0x40000000);
+	}
+	return 0;
+}
+
+static int
+gf119_fan_pwm_clock(struct nvkm_therm *therm, int line)
+{
+	struct nvkm_device *device = therm->subdev.device;
+	int indx = pwm_info(therm, line);
+	if (indx < 0)
+		return 0;
+	else if (indx < 2)
+		return (device->crystal * 1000) / 20;
+	else
+		return device->crystal * 1000 / 10;
+}
+
+void
+gf119_therm_init(struct nvkm_therm *therm)
+{
+	struct nvkm_device *device = therm->subdev.device;
+
+	g84_sensor_setup(therm);
+
+	/* enable fan tach, count revolutions per-second */
+	nvkm_mask(device, 0x00e720, 0x00000003, 0x00000002);
+	if (therm->fan->tach.func != DCB_GPIO_UNUSED) {
+		nvkm_mask(device, 0x00d79c, 0x000000ff, therm->fan->tach.line);
+		nvkm_wr32(device, 0x00e724, device->crystal * 1000);
+		nvkm_mask(device, 0x00e720, 0x00000001, 0x00000001);
+	}
+	nvkm_mask(device, 0x00e720, 0x00000002, 0x00000000);
+}
+
+static const struct nvkm_therm_func
+gf119_therm = {
+	.init = gf119_therm_init,
+	.fini = g84_therm_fini,
+	.pwm_ctrl = gf119_fan_pwm_ctrl,
+	.pwm_get = gf119_fan_pwm_get,
+	.pwm_set = gf119_fan_pwm_set,
+	.pwm_clock = gf119_fan_pwm_clock,
+	.temp_get = g84_temp_get,
+	.fan_sense = gt215_therm_fan_sense,
+	.program_alarms = nvkm_therm_program_alarms_polling,
+};
+
+int
+gf119_therm_new(struct nvkm_device *device, int index,
+	       struct nvkm_therm **ptherm)
+{
+	return nvkm_therm_new_(&gf119_therm, device, index, ptherm);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm107.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm107.c
index bd17e631e2db..86848ece4d89 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm107.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gm107.c
@@ -54,36 +54,22 @@ gm107_fan_pwm_clock(struct nvkm_therm *therm, int line)
 	return therm->subdev.device->crystal * 1000;
 }
 
-static int
-gm107_therm_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
-		 struct nvkm_oclass *oclass, void *data, u32 size,
-		 struct nvkm_object **pobject)
-{
-	struct nvkm_therm_priv *therm;
-	int ret;
-
-	ret = nvkm_therm_create(parent, engine, oclass, &therm);
-	*pobject = nv_object(therm);
-	if (ret)
-		return ret;
+static const struct nvkm_therm_func
+gm107_therm = {
+	.init = gf119_therm_init,
+	.fini = g84_therm_fini,
+	.pwm_ctrl = gm107_fan_pwm_ctrl,
+	.pwm_get = gm107_fan_pwm_get,
+	.pwm_set = gm107_fan_pwm_set,
+	.pwm_clock = gm107_fan_pwm_clock,
+	.temp_get = g84_temp_get,
+	.fan_sense = gt215_therm_fan_sense,
+	.program_alarms = nvkm_therm_program_alarms_polling,
+};
 
-	therm->base.pwm_ctrl = gm107_fan_pwm_ctrl;
-	therm->base.pwm_get = gm107_fan_pwm_get;
-	therm->base.pwm_set = gm107_fan_pwm_set;
-	therm->base.pwm_clock = gm107_fan_pwm_clock;
-	therm->base.temp_get = g84_temp_get;
-	therm->base.fan_sense = gt215_therm_fan_sense;
-	therm->sensor.program_alarms = nvkm_therm_program_alarms_polling;
-	return nvkm_therm_preinit(&therm->base);
+int
+gm107_therm_new(struct nvkm_device *device, int index,
+		struct nvkm_therm **ptherm)
+{
+	return nvkm_therm_new_(&gm107_therm, device, index, ptherm);
 }
-
-struct nvkm_oclass
-gm107_therm_oclass = {
-	.handle = NV_SUBDEV(THERM, 0x117),
-	.ofuncs = &(struct nvkm_ofuncs) {
-		.ctor = gm107_therm_ctor,
-		.dtor = _nvkm_therm_dtor,
-		.init = gf110_therm_init,
-		.fini = g84_therm_fini,
-	},
-};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gt215.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gt215.c
index 8814d8511ea5..c08097f2aff5 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gt215.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/gt215.c
@@ -36,62 +36,40 @@ gt215_therm_fan_sense(struct nvkm_therm *therm)
 	return -ENODEV;
 }
 
-static int
-gt215_therm_init(struct nvkm_object *object)
+static void
+gt215_therm_init(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = (void *)object;
-	struct nvkm_device *device = therm->base.subdev.device;
+	struct nvkm_device *device = therm->subdev.device;
 	struct dcb_gpio_func *tach = &therm->fan->tach;
-	int ret;
-
-	ret = nvkm_therm_init(&therm->base);
-	if (ret)
-		return ret;
 
-	g84_sensor_setup(&therm->base);
+	g84_sensor_setup(therm);
 
 	/* enable fan tach, count revolutions per-second */
 	nvkm_mask(device, 0x00e720, 0x00000003, 0x00000002);
 	if (tach->func != DCB_GPIO_UNUSED) {
-		nvkm_wr32(device, 0x00e724, nv_device(therm)->crystal * 1000);
+		nvkm_wr32(device, 0x00e724, device->crystal * 1000);
 		nvkm_mask(device, 0x00e720, 0x001f0000, tach->line << 16);
 		nvkm_mask(device, 0x00e720, 0x00000001, 0x00000001);
 	}
 	nvkm_mask(device, 0x00e720, 0x00000002, 0x00000000);
-
-	return 0;
 }
 
-static int
-gt215_therm_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
-		 struct nvkm_oclass *oclass, void *data, u32 size,
-		 struct nvkm_object **pobject)
-{
-	struct nvkm_therm_priv *therm;
-	int ret;
-
-	ret = nvkm_therm_create(parent, engine, oclass, &therm);
-	*pobject = nv_object(therm);
-	if (ret)
-		return ret;
+static const struct nvkm_therm_func
+gt215_therm = {
+	.init = gt215_therm_init,
+	.fini = g84_therm_fini,
+	.pwm_ctrl = nv50_fan_pwm_ctrl,
+	.pwm_get = nv50_fan_pwm_get,
+	.pwm_set = nv50_fan_pwm_set,
+	.pwm_clock = nv50_fan_pwm_clock,
+	.temp_get = g84_temp_get,
+	.fan_sense = gt215_therm_fan_sense,
+	.program_alarms = nvkm_therm_program_alarms_polling,
+};
 
-	therm->base.pwm_ctrl = nv50_fan_pwm_ctrl;
-	therm->base.pwm_get = nv50_fan_pwm_get;
-	therm->base.pwm_set = nv50_fan_pwm_set;
-	therm->base.pwm_clock = nv50_fan_pwm_clock;
-	therm->base.temp_get = g84_temp_get;
-	therm->base.fan_sense = gt215_therm_fan_sense;
-	therm->sensor.program_alarms = nvkm_therm_program_alarms_polling;
-	return nvkm_therm_preinit(&therm->base);
+int
+gt215_therm_new(struct nvkm_device *device, int index,
+	       struct nvkm_therm **ptherm)
+{
+	return nvkm_therm_new_(&gt215_therm, device, index, ptherm);
 }
-
-struct nvkm_oclass
-gt215_therm_oclass = {
-	.handle = NV_SUBDEV(THERM, 0xa3),
-	.ofuncs = &(struct nvkm_ofuncs) {
-		.ctor = gt215_therm_ctor,
-		.dtor = _nvkm_therm_dtor,
-		.init = gt215_therm_init,
-		.fini = g84_therm_fini,
-	},
-};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c
index c19af7d24239..6e0ddc1bb583 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c
@@ -30,7 +30,7 @@ static bool
 probe_monitoring_device(struct nvkm_i2c_bus *bus,
 			struct i2c_board_info *info, void *data)
 {
-	struct nvkm_therm_priv *therm = data;
+	struct nvkm_therm *therm = data;
 	struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
 	struct i2c_client *client;
 
@@ -46,7 +46,7 @@ probe_monitoring_device(struct nvkm_i2c_bus *bus,
 		return false;
 	}
 
-	nvkm_debug(&therm->base.subdev,
+	nvkm_debug(&therm->subdev,
 		   "Found an %s at address 0x%x (controlled by lm_sensors, "
 		   "temp offset %+i C)\n",
 		   info->type, info->addr, sensor->offset_constant);
@@ -80,10 +80,9 @@ nv_board_infos[] = {
 };
 
 void
-nvkm_therm_ic_ctor(struct nvkm_therm *obj)
+nvkm_therm_ic_ctor(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-	struct nvkm_device *device = therm->base.subdev.device;
+	struct nvkm_device *device = therm->subdev.device;
 	struct nvkm_bios *bios = device->bios;
 	struct nvkm_i2c *i2c = device->i2c;
 	struct nvkm_i2c_bus *bus;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c
index f2dd7520eff4..6326fdc5a48d 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c
@@ -29,15 +29,12 @@ enum nv40_sensor_style { INVALID_STYLE = -1, OLD_STYLE = 0, NEW_STYLE = 1 };
 static enum nv40_sensor_style
 nv40_sensor_style(struct nvkm_therm *therm)
 {
-	struct nvkm_device *device = nv_device(therm);
-
-	switch (device->chipset) {
+	switch (therm->subdev.device->chipset) {
 	case 0x43:
 	case 0x44:
 	case 0x4a:
 	case 0x47:
 		return OLD_STYLE;
-
 	case 0x46:
 	case 0x49:
 	case 0x4b:
@@ -73,12 +70,11 @@ nv40_sensor_setup(struct nvkm_therm *therm)
 }
 
 static int
-nv40_temp_get(struct nvkm_therm *obj)
+nv40_temp_get(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-	struct nvkm_device *device = therm->base.subdev.device;
+	struct nvkm_device *device = therm->subdev.device;
 	struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
-	enum nv40_sensor_style style = nv40_sensor_style(&therm->base);
+	enum nv40_sensor_style style = nv40_sensor_style(therm);
 	int core_temp;
 
 	if (style == NEW_STYLE) {
@@ -169,10 +165,10 @@ nv40_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty)
 }
 
 void
-nv40_therm_intr(struct nvkm_subdev *subdev)
+nv40_therm_intr(struct nvkm_therm *therm)
 {
-	struct nvkm_therm *therm = nvkm_therm(subdev);
-	struct nvkm_device *device = therm->subdev.device;
+	struct nvkm_subdev *subdev = &therm->subdev;
+	struct nvkm_device *device = subdev->device;
 	uint32_t stat = nvkm_rd32(device, 0x1100);
 
 	/* traitement */
@@ -183,46 +179,26 @@ nv40_therm_intr(struct nvkm_subdev *subdev)
 	nvkm_error(subdev, "THERM received an IRQ: stat = %x\n", stat);
 }
 
-static int
-nv40_therm_ctor(struct nvkm_object *parent,
-		struct nvkm_object *engine,
-		struct nvkm_oclass *oclass, void *data, u32 size,
-		struct nvkm_object **pobject)
+static void
+nv40_therm_init(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm;
-	int ret;
-
-	ret = nvkm_therm_create(parent, engine, oclass, &therm);
-	*pobject = nv_object(therm);
-	if (ret)
-		return ret;
-
-	therm->base.pwm_ctrl = nv40_fan_pwm_ctrl;
-	therm->base.pwm_get = nv40_fan_pwm_get;
-	therm->base.pwm_set = nv40_fan_pwm_set;
-	therm->base.temp_get = nv40_temp_get;
-	therm->sensor.program_alarms = nvkm_therm_program_alarms_polling;
-	nv_subdev(therm)->intr = nv40_therm_intr;
-	return nvkm_therm_preinit(&therm->base);
-}
-
-static int
-nv40_therm_init(struct nvkm_object *object)
-{
-	struct nvkm_therm *therm = (void *)object;
-
 	nv40_sensor_setup(therm);
-
-	return _nvkm_therm_init(object);
 }
 
-struct nvkm_oclass
-nv40_therm_oclass = {
-	.handle = NV_SUBDEV(THERM, 0x40),
-	.ofuncs = &(struct nvkm_ofuncs) {
-		.ctor = nv40_therm_ctor,
-		.dtor = _nvkm_therm_dtor,
-		.init = nv40_therm_init,
-		.fini = _nvkm_therm_fini,
-	},
+static const struct nvkm_therm_func
+nv40_therm = {
+	.init = nv40_therm_init,
+	.intr = nv40_therm_intr,
+	.pwm_ctrl = nv40_fan_pwm_ctrl,
+	.pwm_get = nv40_fan_pwm_get,
+	.pwm_set = nv40_fan_pwm_set,
+	.temp_get = nv40_temp_get,
+	.program_alarms = nvkm_therm_program_alarms_polling,
 };
+
+int
+nv40_therm_new(struct nvkm_device *device, int index,
+	       struct nvkm_therm **ptherm)
+{
+	return nvkm_therm_new_(&nv40_therm, device, index, ptherm);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c
index 787f3cf19747..9b57b433d4cf 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c
@@ -126,10 +126,9 @@ nv50_sensor_setup(struct nvkm_therm *therm)
 }
 
 static int
-nv50_temp_get(struct nvkm_therm *obj)
+nv50_temp_get(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-	struct nvkm_device *device = therm->base.subdev.device;
+	struct nvkm_device *device = therm->subdev.device;
 	struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
 	int core_temp;
 
@@ -151,48 +150,27 @@ nv50_temp_get(struct nvkm_therm *obj)
 	return core_temp;
 }
 
-static int
-nv50_therm_ctor(struct nvkm_object *parent,
-		struct nvkm_object *engine,
-		struct nvkm_oclass *oclass, void *data, u32 size,
-		struct nvkm_object **pobject)
-{
-	struct nvkm_therm_priv *therm;
-	int ret;
-
-	ret = nvkm_therm_create(parent, engine, oclass, &therm);
-	*pobject = nv_object(therm);
-	if (ret)
-		return ret;
-
-	therm->base.pwm_ctrl = nv50_fan_pwm_ctrl;
-	therm->base.pwm_get = nv50_fan_pwm_get;
-	therm->base.pwm_set = nv50_fan_pwm_set;
-	therm->base.pwm_clock = nv50_fan_pwm_clock;
-	therm->base.temp_get = nv50_temp_get;
-	therm->sensor.program_alarms = nvkm_therm_program_alarms_polling;
-	nv_subdev(therm)->intr = nv40_therm_intr;
-
-	return nvkm_therm_preinit(&therm->base);
-}
-
-static int
-nv50_therm_init(struct nvkm_object *object)
+static void
+nv50_therm_init(struct nvkm_therm *therm)
 {
-	struct nvkm_therm *therm = (void *)object;
-
 	nv50_sensor_setup(therm);
-
-	return _nvkm_therm_init(object);
 }
 
-struct nvkm_oclass
-nv50_therm_oclass = {
-	.handle = NV_SUBDEV(THERM, 0x50),
-	.ofuncs = &(struct nvkm_ofuncs) {
-		.ctor = nv50_therm_ctor,
-		.dtor = _nvkm_therm_dtor,
-		.init = nv50_therm_init,
-		.fini = _nvkm_therm_fini,
-	},
+static const struct nvkm_therm_func
+nv50_therm = {
+	.init = nv50_therm_init,
+	.intr = nv40_therm_intr,
+	.pwm_ctrl = nv50_fan_pwm_ctrl,
+	.pwm_get = nv50_fan_pwm_get,
+	.pwm_set = nv50_fan_pwm_set,
+	.pwm_clock = nv50_fan_pwm_clock,
+	.temp_get = nv50_temp_get,
+	.program_alarms = nvkm_therm_program_alarms_polling,
 };
+
+int
+nv50_therm_new(struct nvkm_device *device, int index,
+	       struct nvkm_therm **ptherm)
+{
+	return nvkm_therm_new_(&nv50_therm, device, index, ptherm);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/priv.h
index 916a149efe6e..235a5d8daff6 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/priv.h
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/priv.h
@@ -1,5 +1,6 @@
 #ifndef __NVTHERM_PRIV_H__
 #define __NVTHERM_PRIV_H__
+#define nvkm_therm(p) container_of((p), struct nvkm_therm, subdev)
 /*
  * Copyright 2012 The Nouveau community
  *
@@ -28,8 +29,9 @@
 #include <subdev/bios/extdev.h>
 #include <subdev/bios/gpio.h>
 #include <subdev/bios/perf.h>
-#include <subdev/bios/therm.h>
-#include <subdev/timer.h>
+
+int nvkm_therm_new_(const struct nvkm_therm_func *, struct nvkm_device *,
+		    int index, struct nvkm_therm **);
 
 struct nvkm_fan {
 	struct nvkm_therm *parent;
@@ -48,59 +50,6 @@ struct nvkm_fan {
 	struct dcb_gpio_func tach;
 };
 
-enum nvkm_therm_thrs_direction {
-	NVKM_THERM_THRS_FALLING = 0,
-	NVKM_THERM_THRS_RISING = 1
-};
-
-enum nvkm_therm_thrs_state {
-	NVKM_THERM_THRS_LOWER = 0,
-	NVKM_THERM_THRS_HIGHER = 1
-};
-
-enum nvkm_therm_thrs {
-	NVKM_THERM_THRS_FANBOOST = 0,
-	NVKM_THERM_THRS_DOWNCLOCK = 1,
-	NVKM_THERM_THRS_CRITICAL = 2,
-	NVKM_THERM_THRS_SHUTDOWN = 3,
-	NVKM_THERM_THRS_NR
-};
-
-struct nvkm_therm_priv {
-	struct nvkm_therm base;
-
-	/* automatic thermal management */
-	struct nvkm_alarm alarm;
-	spinlock_t lock;
-	struct nvbios_therm_trip_point *last_trip;
-	int mode;
-	int cstate;
-	int suspend;
-
-	/* bios */
-	struct nvbios_therm_sensor bios_sensor;
-
-	/* fan priv */
-	struct nvkm_fan *fan;
-
-	/* alarms priv */
-	struct {
-		spinlock_t alarm_program_lock;
-		struct nvkm_alarm therm_poll_alarm;
-		enum nvkm_therm_thrs_state alarm_state[NVKM_THERM_THRS_NR];
-		void (*program_alarms)(struct nvkm_therm *);
-	} sensor;
-
-	/* what should be done if the card overheats */
-	struct {
-		void (*downclock)(struct nvkm_therm *, bool active);
-		void (*pause)(struct nvkm_therm *, bool active);
-	} emergency;
-
-	/* ic */
-	struct i2c_client *ic;
-};
-
 int nvkm_therm_fan_mode(struct nvkm_therm *, int mode);
 int nvkm_therm_attr_get(struct nvkm_therm *, enum nvkm_therm_attr_type);
 int nvkm_therm_attr_set(struct nvkm_therm *, enum nvkm_therm_attr_type, int);
@@ -117,8 +66,6 @@ int nvkm_therm_fan_set(struct nvkm_therm *, bool now, int percent);
 int nvkm_therm_fan_user_get(struct nvkm_therm *);
 int nvkm_therm_fan_user_set(struct nvkm_therm *, int percent);
 
-int nvkm_therm_fan_sense(struct nvkm_therm *);
-
 int nvkm_therm_preinit(struct nvkm_therm *);
 
 int  nvkm_therm_sensor_init(struct nvkm_therm *);
@@ -134,18 +81,37 @@ void nvkm_therm_sensor_event(struct nvkm_therm *, enum nvkm_therm_thrs,
 			     enum nvkm_therm_thrs_direction);
 void nvkm_therm_program_alarms_polling(struct nvkm_therm *);
 
-void nv40_therm_intr(struct nvkm_subdev *);
+struct nvkm_therm_func {
+	void (*init)(struct nvkm_therm *);
+	void (*fini)(struct nvkm_therm *);
+	void (*intr)(struct nvkm_therm *);
+
+	int (*pwm_ctrl)(struct nvkm_therm *, int line, bool);
+	int (*pwm_get)(struct nvkm_therm *, int line, u32 *, u32 *);
+	int (*pwm_set)(struct nvkm_therm *, int line, u32, u32);
+	int (*pwm_clock)(struct nvkm_therm *, int line);
+
+	int (*temp_get)(struct nvkm_therm *);
+
+	int (*fan_sense)(struct nvkm_therm *);
+
+	void (*program_alarms)(struct nvkm_therm *);
+};
+
+void nv40_therm_intr(struct nvkm_therm *);
+
 int  nv50_fan_pwm_ctrl(struct nvkm_therm *, int, bool);
 int  nv50_fan_pwm_get(struct nvkm_therm *, int, u32 *, u32 *);
 int  nv50_fan_pwm_set(struct nvkm_therm *, int, u32, u32);
 int  nv50_fan_pwm_clock(struct nvkm_therm *, int);
+
 int  g84_temp_get(struct nvkm_therm *);
 void g84_sensor_setup(struct nvkm_therm *);
-int  g84_therm_fini(struct nvkm_object *, bool suspend);
+void g84_therm_fini(struct nvkm_therm *);
 
 int gt215_therm_fan_sense(struct nvkm_therm *);
 
-int gf110_therm_init(struct nvkm_object *);
+void gf119_therm_init(struct nvkm_therm *);
 
 int nvkm_fanpwm_create(struct nvkm_therm *, struct dcb_gpio_func *);
 int nvkm_fantog_create(struct nvkm_therm *, struct dcb_gpio_func *);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c
index 2622c4403a73..4ab7ef7da254 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c
@@ -24,10 +24,8 @@
 #include "priv.h"
 
 static void
-nvkm_therm_temp_set_defaults(struct nvkm_therm *obj)
+nvkm_therm_temp_set_defaults(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-
 	therm->bios_sensor.offset_constant = 0;
 
 	therm->bios_sensor.thrs_fan_boost.temp = 90;
@@ -43,11 +41,9 @@ nvkm_therm_temp_set_defaults(struct nvkm_therm *obj)
 	therm->bios_sensor.thrs_shutdown.hysteresis = 5; /*not that it matters */
 }
 
-
 static void
-nvkm_therm_temp_safety_checks(struct nvkm_therm *obj)
+nvkm_therm_temp_safety_checks(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
 	struct nvbios_therm_sensor *s = &therm->bios_sensor;
 
 	/* enforce a minimum hysteresis on thresholds */
@@ -59,20 +55,18 @@ nvkm_therm_temp_safety_checks(struct nvkm_therm *obj)
 
 /* must be called with alarm_program_lock taken ! */
 void
-nvkm_therm_sensor_set_threshold_state(struct nvkm_therm *obj,
+nvkm_therm_sensor_set_threshold_state(struct nvkm_therm *therm,
 				      enum nvkm_therm_thrs thrs,
 				      enum nvkm_therm_thrs_state st)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
 	therm->sensor.alarm_state[thrs] = st;
 }
 
 /* must be called with alarm_program_lock taken ! */
 enum nvkm_therm_thrs_state
-nvkm_therm_sensor_get_threshold_state(struct nvkm_therm *obj,
+nvkm_therm_sensor_get_threshold_state(struct nvkm_therm *therm,
 				      enum nvkm_therm_thrs thrs)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
 	return therm->sensor.alarm_state[thrs];
 }
 
@@ -84,16 +78,15 @@ nv_poweroff_work(struct work_struct *work)
 }
 
 void
-nvkm_therm_sensor_event(struct nvkm_therm *obj, enum nvkm_therm_thrs thrs,
+nvkm_therm_sensor_event(struct nvkm_therm *therm, enum nvkm_therm_thrs thrs,
 			enum nvkm_therm_thrs_direction dir)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-	struct nvkm_subdev *subdev = &therm->base.subdev;
+	struct nvkm_subdev *subdev = &therm->subdev;
 	bool active;
 	const char *thresolds[] = {
 		"fanboost", "downclock", "critical", "shutdown"
 	};
-	int temperature = therm->base.temp_get(&therm->base);
+	int temperature = therm->func->temp_get(therm);
 
 	if (thrs < 0 || thrs > 3)
 		return;
@@ -110,17 +103,17 @@ nvkm_therm_sensor_event(struct nvkm_therm *obj, enum nvkm_therm_thrs thrs,
 	switch (thrs) {
 	case NVKM_THERM_THRS_FANBOOST:
 		if (active) {
-			nvkm_therm_fan_set(&therm->base, true, 100);
-			nvkm_therm_fan_mode(&therm->base, NVKM_THERM_CTRL_AUTO);
+			nvkm_therm_fan_set(therm, true, 100);
+			nvkm_therm_fan_mode(therm, NVKM_THERM_CTRL_AUTO);
 		}
 		break;
 	case NVKM_THERM_THRS_DOWNCLOCK:
 		if (therm->emergency.downclock)
-			therm->emergency.downclock(&therm->base, active);
+			therm->emergency.downclock(therm, active);
 		break;
 	case NVKM_THERM_THRS_CRITICAL:
 		if (therm->emergency.pause)
-			therm->emergency.pause(&therm->base, active);
+			therm->emergency.pause(therm, active);
 		break;
 	case NVKM_THERM_THRS_SHUTDOWN:
 		if (active) {
@@ -147,7 +140,7 @@ nvkm_therm_threshold_hyst_polling(struct nvkm_therm *therm,
 {
 	enum nvkm_therm_thrs_direction direction;
 	enum nvkm_therm_thrs_state prev_state, new_state;
-	int temp = therm->temp_get(therm);
+	int temp = therm->func->temp_get(therm);
 
 	prev_state = nvkm_therm_sensor_get_threshold_state(therm, thrs_name);
 
@@ -168,41 +161,40 @@ nvkm_therm_threshold_hyst_polling(struct nvkm_therm *therm,
 static void
 alarm_timer_callback(struct nvkm_alarm *alarm)
 {
-	struct nvkm_therm_priv *therm =
-	container_of(alarm, struct nvkm_therm_priv, sensor.therm_poll_alarm);
+	struct nvkm_therm *therm =
+		container_of(alarm, struct nvkm_therm, sensor.therm_poll_alarm);
 	struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
-	struct nvkm_timer *tmr = nvkm_timer(therm);
+	struct nvkm_timer *tmr = therm->subdev.device->timer;
 	unsigned long flags;
 
 	spin_lock_irqsave(&therm->sensor.alarm_program_lock, flags);
 
-	nvkm_therm_threshold_hyst_polling(&therm->base, &sensor->thrs_fan_boost,
+	nvkm_therm_threshold_hyst_polling(therm, &sensor->thrs_fan_boost,
 					  NVKM_THERM_THRS_FANBOOST);
 
-	nvkm_therm_threshold_hyst_polling(&therm->base,
+	nvkm_therm_threshold_hyst_polling(therm,
 					  &sensor->thrs_down_clock,
 					  NVKM_THERM_THRS_DOWNCLOCK);
 
-	nvkm_therm_threshold_hyst_polling(&therm->base, &sensor->thrs_critical,
+	nvkm_therm_threshold_hyst_polling(therm, &sensor->thrs_critical,
 					  NVKM_THERM_THRS_CRITICAL);
 
-	nvkm_therm_threshold_hyst_polling(&therm->base, &sensor->thrs_shutdown,
+	nvkm_therm_threshold_hyst_polling(therm, &sensor->thrs_shutdown,
 					  NVKM_THERM_THRS_SHUTDOWN);
 
 	spin_unlock_irqrestore(&therm->sensor.alarm_program_lock, flags);
 
 	/* schedule the next poll in one second */
-	if (therm->base.temp_get(&therm->base) >= 0 && list_empty(&alarm->head))
+	if (therm->func->temp_get(therm) >= 0 && list_empty(&alarm->head))
 		tmr->alarm(tmr, 1000000000ULL, alarm);
 }
 
 void
-nvkm_therm_program_alarms_polling(struct nvkm_therm *obj)
+nvkm_therm_program_alarms_polling(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
 	struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
 
-	nvkm_debug(&therm->base.subdev,
+	nvkm_debug(&therm->subdev,
 		   "programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]\n",
 		   sensor->thrs_fan_boost.temp,
 		   sensor->thrs_fan_boost.hysteresis,
@@ -217,19 +209,16 @@ nvkm_therm_program_alarms_polling(struct nvkm_therm *obj)
 }
 
 int
-nvkm_therm_sensor_init(struct nvkm_therm *obj)
+nvkm_therm_sensor_init(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-	therm->sensor.program_alarms(&therm->base);
+	therm->func->program_alarms(therm);
 	return 0;
 }
 
 int
-nvkm_therm_sensor_fini(struct nvkm_therm *obj, bool suspend)
+nvkm_therm_sensor_fini(struct nvkm_therm *therm, bool suspend)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-	struct nvkm_timer *tmr = nvkm_timer(therm);
-
+	struct nvkm_timer *tmr = therm->subdev.device->timer;
 	if (suspend)
 		tmr->alarm_cancel(tmr, &therm->sensor.therm_poll_alarm);
 	return 0;
@@ -240,26 +229,25 @@ nvkm_therm_sensor_preinit(struct nvkm_therm *therm)
 {
 	const char *sensor_avail = "yes";
 
-	if (therm->temp_get(therm) < 0)
+	if (therm->func->temp_get(therm) < 0)
 		sensor_avail = "no";
 
 	nvkm_debug(&therm->subdev, "internal sensor: %s\n", sensor_avail);
 }
 
 int
-nvkm_therm_sensor_ctor(struct nvkm_therm *obj)
+nvkm_therm_sensor_ctor(struct nvkm_therm *therm)
 {
-	struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
-	struct nvkm_subdev *subdev = &therm->base.subdev;
+	struct nvkm_subdev *subdev = &therm->subdev;
 	struct nvkm_bios *bios = subdev->device->bios;
 
 	nvkm_alarm_init(&therm->sensor.therm_poll_alarm, alarm_timer_callback);
 
-	nvkm_therm_temp_set_defaults(&therm->base);
+	nvkm_therm_temp_set_defaults(therm);
 	if (nvbios_therm_sensor_parse(bios, NVBIOS_THERM_DOMAIN_CORE,
 				      &therm->bios_sensor))
 		nvkm_error(subdev, "nvbios_therm_sensor_parse failed\n");
-	nvkm_therm_temp_safety_checks(&therm->base);
+	nvkm_therm_temp_safety_checks(therm);
 
 	return 0;
 }