brintos

brintos / linux-shallow public Read only

0
0
Text · 17.8 KiB · 5c07a9e Raw
726 lines · c
1/*2 * Copyright 2010 Red Hat Inc.3 *4 * Permission is hereby granted, free of charge, to any person obtaining a5 * copy of this software and associated documentation files (the "Software"),6 * to deal in the Software without restriction, including without limitation7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,8 * and/or sell copies of the Software, and to permit persons to whom the9 * Software is furnished to do so, subject to the following conditions:10 *11 * The above copyright notice and this permission notice shall be included in12 * all copies or substantial portions of the Software.13 *14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR20 * OTHER DEALINGS IN THE SOFTWARE.21 *22 * Authors: Ben Skeggs23 */24 25#ifdef CONFIG_ACPI26#include <linux/acpi.h>27#endif28#include <linux/power_supply.h>29#include <linux/hwmon.h>30#include <linux/hwmon-sysfs.h>31 32#include "nouveau_drv.h"33#include "nouveau_hwmon.h"34 35#include <nvkm/subdev/iccsense.h>36#include <nvkm/subdev/volt.h>37 38#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))39 40static ssize_t41nouveau_hwmon_show_temp1_auto_point1_pwm(struct device *d,42					 struct device_attribute *a, char *buf)43{44	return sysfs_emit(buf, "%d\n", 100);45}46static SENSOR_DEVICE_ATTR(temp1_auto_point1_pwm, 0444,47			  nouveau_hwmon_show_temp1_auto_point1_pwm, NULL, 0);48 49static ssize_t50nouveau_hwmon_temp1_auto_point1_temp(struct device *d,51				     struct device_attribute *a, char *buf)52{53	struct drm_device *dev = dev_get_drvdata(d);54	struct nouveau_drm *drm = nouveau_drm(dev);55	struct nvkm_therm *therm = nvxx_therm(drm);56 57	return sysfs_emit(buf, "%d\n",58			  therm->attr_get(therm, NVKM_THERM_ATTR_THRS_FAN_BOOST) * 1000);59}60static ssize_t61nouveau_hwmon_set_temp1_auto_point1_temp(struct device *d,62					 struct device_attribute *a,63					 const char *buf, size_t count)64{65	struct drm_device *dev = dev_get_drvdata(d);66	struct nouveau_drm *drm = nouveau_drm(dev);67	struct nvkm_therm *therm = nvxx_therm(drm);68	long value;69 70	if (kstrtol(buf, 10, &value))71		return -EINVAL;72 73	therm->attr_set(therm, NVKM_THERM_ATTR_THRS_FAN_BOOST,74			value / 1000);75 76	return count;77}78static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, 0644,79			  nouveau_hwmon_temp1_auto_point1_temp,80			  nouveau_hwmon_set_temp1_auto_point1_temp, 0);81 82static ssize_t83nouveau_hwmon_temp1_auto_point1_temp_hyst(struct device *d,84					  struct device_attribute *a, char *buf)85{86	struct drm_device *dev = dev_get_drvdata(d);87	struct nouveau_drm *drm = nouveau_drm(dev);88	struct nvkm_therm *therm = nvxx_therm(drm);89 90	return sysfs_emit(buf, "%d\n",91			  therm->attr_get(therm, NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST) * 1000);92}93static ssize_t94nouveau_hwmon_set_temp1_auto_point1_temp_hyst(struct device *d,95					      struct device_attribute *a,96					      const char *buf, size_t count)97{98	struct drm_device *dev = dev_get_drvdata(d);99	struct nouveau_drm *drm = nouveau_drm(dev);100	struct nvkm_therm *therm = nvxx_therm(drm);101	long value;102 103	if (kstrtol(buf, 10, &value))104		return -EINVAL;105 106	therm->attr_set(therm, NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST,107			value / 1000);108 109	return count;110}111static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp_hyst, 0644,112			  nouveau_hwmon_temp1_auto_point1_temp_hyst,113			  nouveau_hwmon_set_temp1_auto_point1_temp_hyst, 0);114 115static ssize_t116nouveau_hwmon_get_pwm1_max(struct device *d,117			   struct device_attribute *a, char *buf)118{119	struct drm_device *dev = dev_get_drvdata(d);120	struct nouveau_drm *drm = nouveau_drm(dev);121	struct nvkm_therm *therm = nvxx_therm(drm);122	int ret;123 124	ret = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MAX_DUTY);125	if (ret < 0)126		return ret;127 128	return sprintf(buf, "%i\n", ret);129}130 131static ssize_t132nouveau_hwmon_get_pwm1_min(struct device *d,133			   struct device_attribute *a, char *buf)134{135	struct drm_device *dev = dev_get_drvdata(d);136	struct nouveau_drm *drm = nouveau_drm(dev);137	struct nvkm_therm *therm = nvxx_therm(drm);138	int ret;139 140	ret = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MIN_DUTY);141	if (ret < 0)142		return ret;143 144	return sprintf(buf, "%i\n", ret);145}146 147static ssize_t148nouveau_hwmon_set_pwm1_min(struct device *d, struct device_attribute *a,149			   const char *buf, size_t count)150{151	struct drm_device *dev = dev_get_drvdata(d);152	struct nouveau_drm *drm = nouveau_drm(dev);153	struct nvkm_therm *therm = nvxx_therm(drm);154	long value;155	int ret;156 157	if (kstrtol(buf, 10, &value))158		return -EINVAL;159 160	ret = therm->attr_set(therm, NVKM_THERM_ATTR_FAN_MIN_DUTY, value);161	if (ret < 0)162		return ret;163 164	return count;165}166static SENSOR_DEVICE_ATTR(pwm1_min, 0644,167			  nouveau_hwmon_get_pwm1_min,168			  nouveau_hwmon_set_pwm1_min, 0);169 170static ssize_t171nouveau_hwmon_set_pwm1_max(struct device *d, struct device_attribute *a,172			   const char *buf, size_t count)173{174	struct drm_device *dev = dev_get_drvdata(d);175	struct nouveau_drm *drm = nouveau_drm(dev);176	struct nvkm_therm *therm = nvxx_therm(drm);177	long value;178	int ret;179 180	if (kstrtol(buf, 10, &value))181		return -EINVAL;182 183	ret = therm->attr_set(therm, NVKM_THERM_ATTR_FAN_MAX_DUTY, value);184	if (ret < 0)185		return ret;186 187	return count;188}189static SENSOR_DEVICE_ATTR(pwm1_max, 0644,190			  nouveau_hwmon_get_pwm1_max,191			  nouveau_hwmon_set_pwm1_max, 0);192 193static struct attribute *pwm_fan_sensor_attrs[] = {194	&sensor_dev_attr_pwm1_min.dev_attr.attr,195	&sensor_dev_attr_pwm1_max.dev_attr.attr,196	NULL197};198static const struct attribute_group pwm_fan_sensor_group = {199	.attrs = pwm_fan_sensor_attrs,200};201 202static struct attribute *temp1_auto_point_sensor_attrs[] = {203	&sensor_dev_attr_temp1_auto_point1_pwm.dev_attr.attr,204	&sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr,205	&sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr.attr,206	NULL207};208static const struct attribute_group temp1_auto_point_sensor_group = {209	.attrs = temp1_auto_point_sensor_attrs,210};211 212#define N_ATTR_GROUPS   3213 214static const struct hwmon_channel_info * const nouveau_info[] = {215	HWMON_CHANNEL_INFO(chip,216			   HWMON_C_UPDATE_INTERVAL),217	HWMON_CHANNEL_INFO(temp,218			   HWMON_T_INPUT |219			   HWMON_T_MAX | HWMON_T_MAX_HYST |220			   HWMON_T_CRIT | HWMON_T_CRIT_HYST |221			   HWMON_T_EMERGENCY | HWMON_T_EMERGENCY_HYST),222	HWMON_CHANNEL_INFO(fan,223			   HWMON_F_INPUT),224	HWMON_CHANNEL_INFO(in,225			   HWMON_I_INPUT |226			   HWMON_I_MIN | HWMON_I_MAX |227			   HWMON_I_LABEL),228	HWMON_CHANNEL_INFO(pwm,229			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE),230	HWMON_CHANNEL_INFO(power,231			   HWMON_P_INPUT | HWMON_P_CAP_MAX | HWMON_P_CRIT),232	NULL233};234 235static umode_t236nouveau_chip_is_visible(const void *data, u32 attr, int channel)237{238	switch (attr) {239	case hwmon_chip_update_interval:240		return 0444;241	default:242		return 0;243	}244}245 246static umode_t247nouveau_power_is_visible(const void *data, u32 attr, int channel)248{249	struct nouveau_drm *drm = nouveau_drm((struct drm_device *)data);250	struct nvkm_iccsense *iccsense = nvxx_iccsense(drm);251 252	if (!iccsense || !iccsense->data_valid || list_empty(&iccsense->rails))253		return 0;254 255	switch (attr) {256	case hwmon_power_input:257		return 0444;258	case hwmon_power_max:259		if (iccsense->power_w_max)260			return 0444;261		return 0;262	case hwmon_power_crit:263		if (iccsense->power_w_crit)264			return 0444;265		return 0;266	default:267		return 0;268	}269}270 271static umode_t272nouveau_temp_is_visible(const void *data, u32 attr, int channel)273{274	struct nouveau_drm *drm = nouveau_drm((struct drm_device *)data);275	struct nvkm_therm *therm = nvxx_therm(drm);276 277	if (!therm || !therm->attr_get || nvkm_therm_temp_get(therm) < 0)278		return 0;279 280	switch (attr) {281	case hwmon_temp_input:282		return 0444;283	case hwmon_temp_max:284	case hwmon_temp_max_hyst:285	case hwmon_temp_crit:286	case hwmon_temp_crit_hyst:287	case hwmon_temp_emergency:288	case hwmon_temp_emergency_hyst:289		return 0644;290	default:291		return 0;292	}293}294 295static umode_t296nouveau_pwm_is_visible(const void *data, u32 attr, int channel)297{298	struct nouveau_drm *drm = nouveau_drm((struct drm_device *)data);299	struct nvkm_therm *therm = nvxx_therm(drm);300 301	if (!therm || !therm->attr_get || !therm->fan_get ||302	    therm->fan_get(therm) < 0)303		return 0;304 305	switch (attr) {306	case hwmon_pwm_enable:307	case hwmon_pwm_input:308		return 0644;309	default:310		return 0;311	}312}313 314static umode_t315nouveau_input_is_visible(const void *data, u32 attr, int channel)316{317	struct nouveau_drm *drm = nouveau_drm((struct drm_device *)data);318	struct nvkm_volt *volt = nvxx_volt(drm);319 320	if (!volt || nvkm_volt_get(volt) < 0)321		return 0;322 323	switch (attr) {324	case hwmon_in_input:325	case hwmon_in_label:326	case hwmon_in_min:327	case hwmon_in_max:328		return 0444;329	default:330		return 0;331	}332}333 334static umode_t335nouveau_fan_is_visible(const void *data, u32 attr, int channel)336{337	struct nouveau_drm *drm = nouveau_drm((struct drm_device *)data);338	struct nvkm_therm *therm = nvxx_therm(drm);339 340	if (!therm || !therm->attr_get || nvkm_therm_fan_sense(therm) < 0)341		return 0;342 343	switch (attr) {344	case hwmon_fan_input:345		return 0444;346	default:347		return 0;348	}349}350 351static int352nouveau_chip_read(struct device *dev, u32 attr, int channel, long *val)353{354	switch (attr) {355	case hwmon_chip_update_interval:356		*val = 1000;357		break;358	default:359		return -EOPNOTSUPP;360	}361 362	return 0;363}364 365static int366nouveau_temp_read(struct device *dev, u32 attr, int channel, long *val)367{368	struct drm_device *drm_dev = dev_get_drvdata(dev);369	struct nouveau_drm *drm = nouveau_drm(drm_dev);370	struct nvkm_therm *therm = nvxx_therm(drm);371	int ret;372 373	if (!therm || !therm->attr_get)374		return -EOPNOTSUPP;375 376	switch (attr) {377	case hwmon_temp_input:378		if (drm_dev->switch_power_state != DRM_SWITCH_POWER_ON)379			return -EINVAL;380		ret = nvkm_therm_temp_get(therm);381		*val = ret < 0 ? ret : (ret * 1000);382		break;383	case hwmon_temp_max:384		*val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK)385					* 1000;386		break;387	case hwmon_temp_max_hyst:388		*val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST)389					* 1000;390		break;391	case hwmon_temp_crit:392		*val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_CRITICAL)393					* 1000;394		break;395	case hwmon_temp_crit_hyst:396		*val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_CRITICAL_HYST)397					* 1000;398		break;399	case hwmon_temp_emergency:400		*val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN)401					* 1000;402		break;403	case hwmon_temp_emergency_hyst:404		*val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST)405					* 1000;406		break;407	default:408		return -EOPNOTSUPP;409	}410 411	return 0;412}413 414static int415nouveau_fan_read(struct device *dev, u32 attr, int channel, long *val)416{417	struct drm_device *drm_dev = dev_get_drvdata(dev);418	struct nouveau_drm *drm = nouveau_drm(drm_dev);419	struct nvkm_therm *therm = nvxx_therm(drm);420 421	if (!therm)422		return -EOPNOTSUPP;423 424	switch (attr) {425	case hwmon_fan_input:426		if (drm_dev->switch_power_state != DRM_SWITCH_POWER_ON)427			return -EINVAL;428		*val = nvkm_therm_fan_sense(therm);429		break;430	default:431		return -EOPNOTSUPP;432	}433 434	return 0;435}436 437static int438nouveau_in_read(struct device *dev, u32 attr, int channel, long *val)439{440	struct drm_device *drm_dev = dev_get_drvdata(dev);441	struct nouveau_drm *drm = nouveau_drm(drm_dev);442	struct nvkm_volt *volt = nvxx_volt(drm);443	int ret;444 445	if (!volt)446		return -EOPNOTSUPP;447 448	switch (attr) {449	case hwmon_in_input:450		if (drm_dev->switch_power_state != DRM_SWITCH_POWER_ON)451			return -EINVAL;452		ret = nvkm_volt_get(volt);453		*val = ret < 0 ? ret : (ret / 1000);454		break;455	case hwmon_in_min:456		*val = volt->min_uv > 0 ? (volt->min_uv / 1000) : -ENODEV;457		break;458	case hwmon_in_max:459		*val = volt->max_uv > 0 ? (volt->max_uv / 1000) : -ENODEV;460		break;461	default:462		return -EOPNOTSUPP;463	}464 465	return 0;466}467 468static int469nouveau_pwm_read(struct device *dev, u32 attr, int channel, long *val)470{471	struct drm_device *drm_dev = dev_get_drvdata(dev);472	struct nouveau_drm *drm = nouveau_drm(drm_dev);473	struct nvkm_therm *therm = nvxx_therm(drm);474 475	if (!therm || !therm->attr_get || !therm->fan_get)476		return -EOPNOTSUPP;477 478	switch (attr) {479	case hwmon_pwm_enable:480		*val = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MODE);481		break;482	case hwmon_pwm_input:483		if (drm_dev->switch_power_state != DRM_SWITCH_POWER_ON)484			return -EINVAL;485		*val = therm->fan_get(therm);486		break;487	default:488		return -EOPNOTSUPP;489	}490 491	return 0;492}493 494static int495nouveau_power_read(struct device *dev, u32 attr, int channel, long *val)496{497	struct drm_device *drm_dev = dev_get_drvdata(dev);498	struct nouveau_drm *drm = nouveau_drm(drm_dev);499	struct nvkm_iccsense *iccsense = nvxx_iccsense(drm);500 501	if (!iccsense)502		return -EOPNOTSUPP;503 504	switch (attr) {505	case hwmon_power_input:506		if (drm_dev->switch_power_state != DRM_SWITCH_POWER_ON)507			return -EINVAL;508		*val = nvkm_iccsense_read_all(iccsense);509		break;510	case hwmon_power_max:511		*val = iccsense->power_w_max;512		break;513	case hwmon_power_crit:514		*val = iccsense->power_w_crit;515		break;516	default:517		return -EOPNOTSUPP;518	}519 520	return 0;521}522 523static int524nouveau_temp_write(struct device *dev, u32 attr, int channel, long val)525{526	struct drm_device *drm_dev = dev_get_drvdata(dev);527	struct nouveau_drm *drm = nouveau_drm(drm_dev);528	struct nvkm_therm *therm = nvxx_therm(drm);529 530	if (!therm || !therm->attr_set)531		return -EOPNOTSUPP;532 533	switch (attr) {534	case hwmon_temp_max:535		return therm->attr_set(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK,536					val / 1000);537	case hwmon_temp_max_hyst:538		return therm->attr_set(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST,539					val / 1000);540	case hwmon_temp_crit:541		return therm->attr_set(therm, NVKM_THERM_ATTR_THRS_CRITICAL,542					val / 1000);543	case hwmon_temp_crit_hyst:544		return therm->attr_set(therm, NVKM_THERM_ATTR_THRS_CRITICAL_HYST,545					val / 1000);546	case hwmon_temp_emergency:547		return therm->attr_set(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN,548					val / 1000);549	case hwmon_temp_emergency_hyst:550		return therm->attr_set(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST,551					val / 1000);552	default:553		return -EOPNOTSUPP;554	}555}556 557static int558nouveau_pwm_write(struct device *dev, u32 attr, int channel, long val)559{560	struct drm_device *drm_dev = dev_get_drvdata(dev);561	struct nouveau_drm *drm = nouveau_drm(drm_dev);562	struct nvkm_therm *therm = nvxx_therm(drm);563 564	if (!therm || !therm->attr_set)565		return -EOPNOTSUPP;566 567	switch (attr) {568	case hwmon_pwm_input:569		return therm->fan_set(therm, val);570	case hwmon_pwm_enable:571		return therm->attr_set(therm, NVKM_THERM_ATTR_FAN_MODE, val);572	default:573		return -EOPNOTSUPP;574	}575}576 577static umode_t578nouveau_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr,579			int channel)580{581	switch (type) {582	case hwmon_chip:583		return nouveau_chip_is_visible(data, attr, channel);584	case hwmon_temp:585		return nouveau_temp_is_visible(data, attr, channel);586	case hwmon_fan:587		return nouveau_fan_is_visible(data, attr, channel);588	case hwmon_in:589		return nouveau_input_is_visible(data, attr, channel);590	case hwmon_pwm:591		return nouveau_pwm_is_visible(data, attr, channel);592	case hwmon_power:593		return nouveau_power_is_visible(data, attr, channel);594	default:595		return 0;596	}597}598 599static const char input_label[] = "GPU core";600 601static int602nouveau_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,603		    int channel, const char **buf)604{605	if (type == hwmon_in && attr == hwmon_in_label) {606		*buf = input_label;607		return 0;608	}609 610	return -EOPNOTSUPP;611}612 613static int614nouveau_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,615							int channel, long *val)616{617	switch (type) {618	case hwmon_chip:619		return nouveau_chip_read(dev, attr, channel, val);620	case hwmon_temp:621		return nouveau_temp_read(dev, attr, channel, val);622	case hwmon_fan:623		return nouveau_fan_read(dev, attr, channel, val);624	case hwmon_in:625		return nouveau_in_read(dev, attr, channel, val);626	case hwmon_pwm:627		return nouveau_pwm_read(dev, attr, channel, val);628	case hwmon_power:629		return nouveau_power_read(dev, attr, channel, val);630	default:631		return -EOPNOTSUPP;632	}633}634 635static int636nouveau_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,637							int channel, long val)638{639	switch (type) {640	case hwmon_temp:641		return nouveau_temp_write(dev, attr, channel, val);642	case hwmon_pwm:643		return nouveau_pwm_write(dev, attr, channel, val);644	default:645		return -EOPNOTSUPP;646	}647}648 649static const struct hwmon_ops nouveau_hwmon_ops = {650	.is_visible = nouveau_is_visible,651	.read = nouveau_read,652	.read_string = nouveau_read_string,653	.write = nouveau_write,654};655 656static const struct hwmon_chip_info nouveau_chip_info = {657	.ops = &nouveau_hwmon_ops,658	.info = nouveau_info,659};660#endif661 662int663nouveau_hwmon_init(struct drm_device *dev)664{665#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))666	struct nouveau_drm *drm = nouveau_drm(dev);667	struct nvkm_iccsense *iccsense = nvxx_iccsense(drm);668	struct nvkm_therm *therm = nvxx_therm(drm);669	struct nvkm_volt *volt = nvxx_volt(drm);670	const struct attribute_group *special_groups[N_ATTR_GROUPS];671	struct nouveau_hwmon *hwmon;672	struct device *hwmon_dev;673	int ret = 0;674	int i = 0;675 676	if (!iccsense && !therm && !volt) {677		NV_DEBUG(drm, "Skipping hwmon registration\n");678		return 0;679	}680 681	hwmon = drm->hwmon = kzalloc(sizeof(*hwmon), GFP_KERNEL);682	if (!hwmon)683		return -ENOMEM;684	hwmon->dev = dev;685 686	if (therm && therm->attr_get && therm->attr_set) {687		if (nvkm_therm_temp_get(therm) >= 0)688			special_groups[i++] = &temp1_auto_point_sensor_group;689		if (therm->fan_get && therm->fan_get(therm) >= 0)690			special_groups[i++] = &pwm_fan_sensor_group;691	}692 693	special_groups[i] = NULL;694	hwmon_dev = hwmon_device_register_with_info(dev->dev, "nouveau", dev,695							&nouveau_chip_info,696							special_groups);697	if (IS_ERR(hwmon_dev)) {698		ret = PTR_ERR(hwmon_dev);699		NV_ERROR(drm, "Unable to register hwmon device: %d\n", ret);700		return ret;701	}702 703	hwmon->hwmon = hwmon_dev;704	return 0;705#else706	return 0;707#endif708}709 710void711nouveau_hwmon_fini(struct drm_device *dev)712{713#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))714	struct nouveau_hwmon *hwmon = nouveau_hwmon(dev);715 716	if (!hwmon)717		return;718 719	if (hwmon->hwmon)720		hwmon_device_unregister(hwmon->hwmon);721 722	nouveau_drm(dev)->hwmon = NULL;723	kfree(hwmon);724#endif725}726