From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f50.google.com (mail-wm1-f50.google.com [209.85.128.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B31EF42BC59 for ; Tue, 14 Jul 2026 21:16:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.50 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784063771; cv=none; b=LNxq5KkRPeBTxWXwv0KTzJ15ZYvXZtcAbFaL/DMkOP7NExo29ZrdHzK3l2fczVVLzZ38xxKAtp3mEv8ud0bsN37ti+FmJjouMjF6YxYkX0WUnVY4ejBaCVB8LaTyb5o8uMu2V0OeCo3sBhns4YmUyh0PCwQaoFvBALUoD9zEPNU= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784063771; c=relaxed/simple; bh=QE1nq02eqG6qT9I4/XK2MwdLO2CEWN4FA9ga1rzT2dU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=opCvJPUntiUyS5w6oJ51XsGaGiknxzjgWvO/3VXkoX/Ovu+q7x03uQH33YYDNoiElDjfb3Vc5a/F0htsGLI81Nv+DCxnUIicnxiimDJ8ucDosRzWNV0q/Jb+vlNEviHnBoBkbZRYcCrQ3NNADBeNmSqXBVF36wY7rMkT5FCV9x8= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=k1KjTt6G; arc=none smtp.client-ip=209.85.128.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="k1KjTt6G" Received: by mail-wm1-f50.google.com with SMTP id 5b1f17b1804b1-4921eed3fa2so39898935e9.0 for ; Tue, 14 Jul 2026 14:16:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784063767; x=1784668567; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=bz9kHIGdnWOPLJJAKqHaqyiFFf2vvIYbS2hXbNMvzaM=; b=k1KjTt6G21Xb7FiPTUhz3G97brjpk4yKBRlHAwE679Vcgp7Gpd7H7hdeVDl1B87f16 qhZnTz573R+inQSRKEZuKEbztRIHZxv+6usMDqq7l5H87Ff7rHmDrZ328oi6WnR7D0hy IchaZiejUBO+ZXBevNHmHA1KGDIAIv03KoPPlU/ImKqG/PucSPuN7a2QeYh3LUyCmB7C 5Al2CEzAmlQTrCLMEnyHNqtg7W+4LH42HrXuppi2JjYRilKYOE/cud+71JnSFHBn2D7/ wh9d7p08EdbEDF+9dwFuEZ+JvVki20yfSPyux9tT0mlgcbfI0DN7w7nlGO6KHcMS2Q9o V9ng== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784063767; x=1784668567; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=bz9kHIGdnWOPLJJAKqHaqyiFFf2vvIYbS2hXbNMvzaM=; b=XlZNWkb9R9SX9Lb+WxVgGFFGlBx87tqoresPhhuyeza1m3URwZOmcKQaIf+ZLtzTPR Vgf6R3qXUkVofGV7CmPZ6NQ68NZI88ElxCPx7Ue7grBq6R68G96IHoyiaxIlFtuC2Kyn tEjuBiwAn5Aa5h/ZszldlZm/FE+wZzs0J0hlM0KCdbYvLRr5zGd/0egI1IRTqXoeHbFa fxdXV2Y+Nq2kM6iVp7DPUIO5Xmv8XAeGUQ91C6SiN7xuumwvaq1WMjor+lB24vqz39ZY eGHDlbRoXoHl5kYmcSfBhsDhpZBPtG6P6hggoy8XenhOLegDc7dh+KkJK0dr8GoWDGFa CLEQ== X-Gm-Message-State: AOJu0YyjB3jXDUbadxc+iOCUF/vGzUtZQC1027AjLTz8pilmkTMUtoCn ZOXMY/zAvA+nPuiYHd69s96hJtuNdLEJAP0I8pUEfdb7+Gl9uis11CqQlGPwoqob X-Gm-Gg: AfdE7ckJYx8GIok0IZdeCWMGTTyoEDySRyvwT9fRSqlEoz7pUeXx4l3ASmuuuIA2iaC rqCXp4PKbnYlBtRdfiynWa8UtFeQrjIUZiOKVcpAkf6ORijIWIxFRgsK907wQj/bbc51VfjJKPY GWUsfr6a2XomRzpHZRid0G1KdM9BAW5J+R+mBcTEJ/kYa1b6tRqUzjHEMEsvR3/JEUvfVeayhxf /X5Doc/aUaw1NU/F4OVGDUlr96SvVK27zcP4xNgN4oEntieAENirAoaJczQyQvo9YmKFUPHCti+ /PSzPK0PP0PQPdEcZCyC1n+3TqlR5y3Kc7Bm9hEwpxAR1wNGig4s+B4KtB4pfjKbgF5ATRHmff8 nF6p+Bd4UvAs73IoFeY5v9HTV56otOcjL+tc6MfscpdaHdL1QOdZLUY25yQmxACRw6bRl3tHjIE 5K7ne6GWfsolMPbnb1eN+1 X-Received: by 2002:a05:600d:6454:10b0:493:f764:1f69 with SMTP id 5b1f17b1804b1-49538992d06mr33976505e9.8.1784063766675; Tue, 14 Jul 2026 14:16:06 -0700 (PDT) Received: from fedora ([94.205.182.68]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4950a32a2casm149843875e9.12.2026.07.14.14.16.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Jul 2026 14:16:05 -0700 (PDT) From: Mohamed Ahmed To: linux-kernel@vger.kernel.org Cc: dri-devel@lists.freedesktop.org, Lyude Paul , Danilo Krummrich , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Mary Guillemard , Milos Tijanic , nouveau@lists.freedesktop.org, Mohamed Ahmed Subject: [PATCH 3/6] drm/nouveau: add GSP hwmon support Date: Wed, 15 Jul 2026 01:14:26 +0400 Message-ID: <20260714211429.238355-4-mohamedahmedegypt2001@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260714211429.238355-1-mohamedahmedegypt2001@gmail.com> References: <20260714211429.238355-1-mohamedahmedegypt2001@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit GSP boards currently register no hwmon device at all, so every Turing through Blackwell user has been running without any temperature or power readout. Wire the RUSD telemetry up as the first one: temp1_input/_label "gpu" temperatures[GPU] temp2_input/_label "vram" temperatures[HBM], HBM boards only power1_input/average "gpu" inst/avg GPU power power1_cap enforced power limit (read-only) power2_input/average "board" module power, where supported power3_average "vram" memory rail average, where supported update_interval the programmed RUSD poll interval All channels are standard hwmon ABI, per-channel visibility is decided at registration time from live section validity, since support varies per SKU at runtime. If nothing becomes valid, no hwmon device is registered, matching today's behaviour on GSP boards. The legacy therm/volt/iccsense paths are untouched and the new channels and attributes are gated on RUSD, so pre-GSP boards see no change. Signed-off-by: Mohamed Ahmed --- drivers/gpu/drm/nouveau/nouveau_hwmon.c | 375 +++++++++++++++++++----- 1 file changed, 294 insertions(+), 81 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_hwmon.c b/drivers/gpu/drm/nouveau/nouveau_hwmon.c index 726397ab035d..d449431d4a40 100644 --- a/drivers/gpu/drm/nouveau/nouveau_hwmon.c +++ b/drivers/gpu/drm/nouveau/nouveau_hwmon.c @@ -25,6 +25,8 @@ #ifdef CONFIG_ACPI #include #endif +#include +#include #include #include #include @@ -32,6 +34,7 @@ #include "nouveau_drv.h" #include "nouveau_hwmon.h" +#include #include #include @@ -215,10 +218,11 @@ static const struct hwmon_channel_info * const nouveau_info[] = { HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), HWMON_CHANNEL_INFO(temp, - HWMON_T_INPUT | + HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_CRIT | HWMON_T_CRIT_HYST | - HWMON_T_EMERGENCY | HWMON_T_EMERGENCY_HYST), + HWMON_T_EMERGENCY | HWMON_T_EMERGENCY_HYST, + HWMON_T_INPUT | HWMON_T_LABEL), HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT), HWMON_CHANNEL_INFO(in, @@ -228,7 +232,10 @@ static const struct hwmon_channel_info * const nouveau_info[] = { HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE), HWMON_CHANNEL_INFO(power, - HWMON_P_INPUT | HWMON_P_CAP_MAX | HWMON_P_CRIT), + HWMON_P_INPUT | HWMON_P_MAX | HWMON_P_CRIT | + HWMON_P_AVERAGE | HWMON_P_CAP | HWMON_P_LABEL, + HWMON_P_INPUT | HWMON_P_AVERAGE | HWMON_P_LABEL, + HWMON_P_AVERAGE | HWMON_P_LABEL), NULL }; @@ -243,52 +250,144 @@ nouveau_chip_is_visible(const void *data, u32 attr, int channel) } } -static umode_t -nouveau_power_is_visible(const void *data, u32 attr, int channel) +static bool +nouveau_rusd_ok(struct nvkm_gsp *gsp, enum nvkm_gsp_rusd_item item) { - struct nouveau_drm *drm = nouveau_drm((struct drm_device *)data); - struct nvkm_iccsense *iccsense = nvxx_iccsense(drm); + int retries = 3; + s64 val; + int ret; - if (!iccsense || !iccsense->data_valid || list_empty(&iccsense->rails)) - return 0; + /* Visibility decisions are made only once at registration. So retry + * to avoid letting racing a GSP write window (-EAGAIN) hide a channel + * for good. + */ + do { + ret = nvkm_gsp_rusd_read(gsp, item, &val); + if (ret != -EAGAIN) + break; + usleep_range(100, 200); + } while (--retries); + + return ret == 0; +} +static umode_t +nouveau_rusd_power_is_visible(struct nvkm_gsp *gsp, u32 attr, int channel) +{ switch (attr) { case hwmon_power_input: - return 0444; - case hwmon_power_max: - if (iccsense->power_w_max) + if (channel == 0 && nouveau_rusd_ok(gsp, NVKM_GSP_RUSD_POWER_GPU)) + return 0444; + if (channel == 1 && nouveau_rusd_ok(gsp, NVKM_GSP_RUSD_POWER_BOARD)) + return 0444; + return 0; + case hwmon_power_average: + if (channel == 0 && nouveau_rusd_ok(gsp, NVKM_GSP_RUSD_POWER_GPU_AVG)) + return 0444; + if (channel == 1 && nouveau_rusd_ok(gsp, NVKM_GSP_RUSD_POWER_BOARD_AVG)) + return 0444; + if (channel == 2 && nouveau_rusd_ok(gsp, NVKM_GSP_RUSD_POWER_VRAM_AVG)) return 0444; return 0; - case hwmon_power_crit: - if (iccsense->power_w_crit) + case hwmon_power_cap: + /* The enforced limit is only telemetry here. Setting it needs ctrls nouveau + * doesn't implement. + */ + if (channel == 0 && nouveau_rusd_ok(gsp, NVKM_GSP_RUSD_POWER_CAP)) return 0444; return 0; + case hwmon_power_label: + switch (channel) { + case 0: + if (nouveau_rusd_ok(gsp, NVKM_GSP_RUSD_POWER_GPU) || + nouveau_rusd_ok(gsp, NVKM_GSP_RUSD_POWER_GPU_AVG) || + nouveau_rusd_ok(gsp, NVKM_GSP_RUSD_POWER_CAP)) + return 0444; + break; + case 1: + if (nouveau_rusd_ok(gsp, NVKM_GSP_RUSD_POWER_BOARD) || + nouveau_rusd_ok(gsp, NVKM_GSP_RUSD_POWER_BOARD_AVG)) + return 0444; + break; + case 2: + if (nouveau_rusd_ok(gsp, NVKM_GSP_RUSD_POWER_VRAM_AVG)) + return 0444; + break; + } + return 0; default: return 0; } } static umode_t -nouveau_temp_is_visible(const void *data, u32 attr, int channel) +nouveau_power_is_visible(const void *data, u32 attr, int channel) { struct nouveau_drm *drm = nouveau_drm((struct drm_device *)data); - struct nvkm_therm *therm = nvxx_therm(drm); + struct nvkm_iccsense *iccsense = nvxx_iccsense(drm); + struct nvkm_device *device = drm->nvkm; + struct nvkm_gsp *gsp = device->gsp; - if (!therm || !therm->attr_get || nvkm_therm_temp_get(therm) < 0) - return 0; + if (nvkm_gsp_rusd(gsp)) + return nouveau_rusd_power_is_visible(gsp, attr, channel); + else { + if (!iccsense || !iccsense->data_valid || list_empty(&iccsense->rails)) + return 0; - switch (attr) { - case hwmon_temp_input: - return 0444; - case hwmon_temp_max: - case hwmon_temp_max_hyst: - case hwmon_temp_crit: - case hwmon_temp_crit_hyst: - case hwmon_temp_emergency: - case hwmon_temp_emergency_hyst: - return 0644; - default: - return 0; + switch (attr) { + case hwmon_power_input: + return 0444; + case hwmon_power_max: + if (iccsense->power_w_max) + return 0444; + return 0; + case hwmon_power_crit: + if (iccsense->power_w_crit) + return 0444; + return 0; + default: + return 0; + } + } +} + +static umode_t +nouveau_temp_is_visible(const void *data, u32 attr, int channel) +{ + struct nouveau_drm *drm = nouveau_drm((struct drm_device *)data); + struct nvkm_therm *therm = nvxx_therm(drm); + struct nvkm_device *device = drm->nvkm; + struct nvkm_gsp *gsp = device->gsp; + + if (nvkm_gsp_rusd(gsp)) { + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_label: + if (nouveau_rusd_ok(gsp, channel ? NVKM_GSP_RUSD_TEMP_HBM : + NVKM_GSP_RUSD_TEMP_GPU)) + return 0444; + return 0; + default: + /* No threshold data in the RUSD buffer. */ + return 0; + } + } else { + if (!therm || !therm->attr_get || nvkm_therm_temp_get(therm) < 0) + return 0; + + switch (attr) { + case hwmon_temp_input: + return 0444; + case hwmon_temp_max: + case hwmon_temp_max_hyst: + case hwmon_temp_crit: + case hwmon_temp_crit_hyst: + case hwmon_temp_emergency: + case hwmon_temp_emergency_hyst: + return 0644; + default: + return 0; + } } } @@ -351,9 +450,13 @@ nouveau_fan_is_visible(const void *data, u32 attr, int channel) static int nouveau_chip_read(struct device *dev, u32 attr, int channel, long *val) { + struct nouveau_drm *drm = nouveau_drm(dev_get_drvdata(dev)); + struct nvkm_device *device = drm->nvkm; + struct nvkm_gsp *gsp = device->gsp; + switch (attr) { case hwmon_chip_update_interval: - *val = 1000; + *val = nvkm_gsp_rusd(gsp) ? gsp->rusd.poll_ms : 1000; break; default: return -EOPNOTSUPP; @@ -368,47 +471,67 @@ nouveau_temp_read(struct device *dev, u32 attr, int channel, long *val) struct drm_device *drm_dev = dev_get_drvdata(dev); struct nouveau_drm *drm = nouveau_drm(drm_dev); struct nvkm_therm *therm = nvxx_therm(drm); + struct nvkm_device *device = drm->nvkm; + struct nvkm_gsp *gsp = device->gsp; int ret; - if (!therm || !therm->attr_get) - return -EOPNOTSUPP; + if (nvkm_gsp_rusd(gsp)) { + s64 value; + + if (attr != hwmon_temp_input) + return -EOPNOTSUPP; - switch (attr) { - case hwmon_temp_input: if (drm_dev->switch_power_state != DRM_SWITCH_POWER_ON) return -EINVAL; - ret = nvkm_therm_temp_get(therm); - *val = ret < 0 ? ret : (ret * 1000); - break; - case hwmon_temp_max: - *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK) - * 1000; - break; - case hwmon_temp_max_hyst: - *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST) - * 1000; - break; - case hwmon_temp_crit: - *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_CRITICAL) - * 1000; - break; - case hwmon_temp_crit_hyst: - *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_CRITICAL_HYST) - * 1000; - break; - case hwmon_temp_emergency: - *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN) - * 1000; - break; - case hwmon_temp_emergency_hyst: - *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST) - * 1000; - break; - default: - return -EOPNOTSUPP; - } - return 0; + ret = nvkm_gsp_rusd_read(gsp, channel ? NVKM_GSP_RUSD_TEMP_HBM : + NVKM_GSP_RUSD_TEMP_GPU, &value); + if (ret) + return ret; + + *val = value; + return 0; + } else { + if (!therm || !therm->attr_get) + return -EOPNOTSUPP; + + switch (attr) { + case hwmon_temp_input: + if (drm_dev->switch_power_state != DRM_SWITCH_POWER_ON) + return -EINVAL; + ret = nvkm_therm_temp_get(therm); + *val = ret < 0 ? ret : (ret * 1000); + break; + case hwmon_temp_max: + *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK) + * 1000; + break; + case hwmon_temp_max_hyst: + *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST) + * 1000; + break; + case hwmon_temp_crit: + *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_CRITICAL) + * 1000; + break; + case hwmon_temp_crit_hyst: + *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_CRITICAL_HYST) + * 1000; + break; + case hwmon_temp_emergency: + *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN) + * 1000; + break; + case hwmon_temp_emergency_hyst: + *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST) + * 1000; + break; + default: + return -EOPNOTSUPP; + } + + return 0; + } } static int @@ -492,34 +615,84 @@ nouveau_pwm_read(struct device *dev, u32 attr, int channel, long *val) } static int -nouveau_power_read(struct device *dev, u32 attr, int channel, long *val) +nouveau_rusd_power_read(struct drm_device *drm_dev, struct nvkm_gsp *gsp, + u32 attr, int channel, long *val) { - struct drm_device *drm_dev = dev_get_drvdata(dev); - struct nouveau_drm *drm = nouveau_drm(drm_dev); - struct nvkm_iccsense *iccsense = nvxx_iccsense(drm); - - if (!iccsense) - return -EOPNOTSUPP; + enum nvkm_gsp_rusd_item item; + s64 value; + int ret; switch (attr) { case hwmon_power_input: - if (drm_dev->switch_power_state != DRM_SWITCH_POWER_ON) - return -EINVAL; - *val = nvkm_iccsense_read_all(iccsense); + if (channel == 2) + return -EOPNOTSUPP; + item = channel ? NVKM_GSP_RUSD_POWER_BOARD : + NVKM_GSP_RUSD_POWER_GPU; break; - case hwmon_power_max: - *val = iccsense->power_w_max; + case hwmon_power_average: + if (channel == 0) + item = NVKM_GSP_RUSD_POWER_GPU_AVG; + else if (channel == 1) + item = NVKM_GSP_RUSD_POWER_BOARD_AVG; + else + item = NVKM_GSP_RUSD_POWER_VRAM_AVG; break; - case hwmon_power_crit: - *val = iccsense->power_w_crit; + case hwmon_power_cap: + if (channel) + return -EOPNOTSUPP; + item = NVKM_GSP_RUSD_POWER_CAP; break; default: return -EOPNOTSUPP; } + if (drm_dev->switch_power_state != DRM_SWITCH_POWER_ON) + return -EINVAL; + + ret = nvkm_gsp_rusd_read(gsp, item, &value); + if (ret) + return ret; + + *val = value; + return 0; } +static int +nouveau_power_read(struct device *dev, u32 attr, int channel, long *val) +{ + struct drm_device *drm_dev = dev_get_drvdata(dev); + struct nouveau_drm *drm = nouveau_drm(drm_dev); + struct nvkm_iccsense *iccsense = nvxx_iccsense(drm); + struct nvkm_device *device = drm->nvkm; + struct nvkm_gsp *gsp = device->gsp; + + if (nvkm_gsp_rusd(gsp)) + return nouveau_rusd_power_read(drm_dev, gsp, attr, channel, val); + else { + if (!iccsense) + return -EOPNOTSUPP; + + switch (attr) { + case hwmon_power_input: + if (drm_dev->switch_power_state != DRM_SWITCH_POWER_ON) + return -EINVAL; + *val = nvkm_iccsense_read_all(iccsense); + break; + case hwmon_power_max: + *val = iccsense->power_w_max; + break; + case hwmon_power_crit: + *val = iccsense->power_w_crit; + break; + default: + return -EOPNOTSUPP; + } + + return 0; + } +} + static int nouveau_temp_write(struct device *dev, u32 attr, int channel, long val) { @@ -597,6 +770,8 @@ nouveau_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, } static const char input_label[] = "GPU core"; +static const char * const nouveau_temp_labels[] = { "gpu", "vram" }; +static const char * const nouveau_power_labels[] = { "gpu", "board", "vram" }; static int nouveau_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, @@ -607,6 +782,18 @@ nouveau_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, return 0; } + if (type == hwmon_temp && attr == hwmon_temp_label && + channel < ARRAY_SIZE(nouveau_temp_labels)) { + *buf = nouveau_temp_labels[channel]; + return 0; + } + + if (type == hwmon_power && attr == hwmon_power_label && + channel < ARRAY_SIZE(nouveau_power_labels)) { + *buf = nouveau_power_labels[channel]; + return 0; + } + return -EOPNOTSUPP; } @@ -667,13 +854,39 @@ nouveau_hwmon_init(struct drm_device *dev) struct nvkm_iccsense *iccsense = nvxx_iccsense(drm); struct nvkm_therm *therm = nvxx_therm(drm); struct nvkm_volt *volt = nvxx_volt(drm); + struct nvkm_device *device = drm->nvkm; + struct nvkm_gsp *gsp = device->gsp; const struct attribute_group *special_groups[N_ATTR_GROUPS]; struct nouveau_hwmon *hwmon; struct device *hwmon_dev; + bool rusd_active = false; int ret = 0; int i = 0; - if (!iccsense && !therm && !volt) { + if (nvkm_gsp_rusd(gsp)) { + /* Per-channel visibility is latched below from live RUSD + * section validity, so wait for GSP-RM's first poll of both + * requested groups (thermal, power). It is normally completed + * long before we get here and this is a last resort. + */ + unsigned long timeout = jiffies + + msecs_to_jiffies(2 * gsp->rusd.poll_ms); + bool thermal, power; + + for (;;) { + thermal = nouveau_rusd_ok(gsp, NVKM_GSP_RUSD_TEMP_GPU); + power = nouveau_rusd_ok(gsp, NVKM_GSP_RUSD_POWER_GPU) || + nouveau_rusd_ok(gsp, NVKM_GSP_RUSD_POWER_GPU_AVG) || + nouveau_rusd_ok(gsp, NVKM_GSP_RUSD_POWER_CAP); + if ((thermal && power) || !time_before(jiffies, timeout)) + break; + msleep(20); + } + + rusd_active = thermal || power; + } + + if (!iccsense && !therm && !volt && !rusd_active) { NV_DEBUG(drm, "Skipping hwmon registration\n"); return 0; } -- 2.55.0