[RFC PATCH 1/1] drm/xe/hwmon: wake render domain for BMG package temperature
From: Yuta Higuchi
Date: Sat Aug 22 2026 - 04:54:25 EST
On one Arc Pro B70 (8086:e223), the BMG package-temperature value
exposed as temp2_input updates continuously while the GPU is active. After
the final workload closes, it emits a short snapshot burst and then remains
stale for more than 200 seconds while VRAM, mctrl, PCIe, fan, energy, and
runtime state continue to change. An independent PID reading the same sysfs
node sees the same result, excluding the collector as the source of the
staleness.
The package-temperature read currently reaches BMG_PACKAGE_TEMPERATURE
(0x138434) through xe_mmio_read32() without an explicit forcewake
reference. Experiments on this Arc Pro B70 isolated the observable
dependency as follows:
- stock forcewake_all kept package temperature updating;
- a persistent main-GT XE_FW_RENDER reference kept it updating in three of
three independent rounds;
- releasing that reference was followed by renewed staleness in all three
rounds;
- main-GT XE_FW_GT alone was negative in the single persistent-holder round
tested;
- transient XE_FW_RENDER acquisition produced a fresh value in 1.17 to
2.172 ms; and
- a 20 ms RENDER hold with no sysfs reads yielded a fresh first read.
Acquire the main GT render forcewake domain around BMG package-temperature
reads, allow 3.0 to 3.5 ms for the observed publication to settle, read the
register, and release the reference automatically. The delay is empirical,
not an architectural contract. FORCEWAKE_ACK_RENDER only acknowledges the
wake domain; no public temperature-producer-ready indication was found.
These experiments do not establish that register 0x138434 architecturally
belongs to XE_FW_RENDER or identify whether PCODE, GuC, or other firmware
produces the value.
Keep the change limited to the BMG package-temperature channel. Runtime
validation was performed on one Arc Pro B70. XE_FW_RENDER was
single-domain sufficient among the directly compared states, but media,
GSC, and other individual forcewake domains were not tested after that
positive result. This does not claim that RENDER is the unique minimum for
all BMG devices or steppings.
The equivalent Ubuntu 7.0 diagnostic implementation passed three
independent 180-second cooldown rounds; 1, 5, 10, and 30 second read
cadences; four concurrent readers; a 30-minute run; OpenVINO workloads;
suspend/resume; and Xe unbind/rebind. The directly tested drm-tip
submission candidate, based on 75140c4ee9ad, additionally passed one
clpeak/dual-reader/180-second-cooldown smoke and one bounded OpenVINO
two-model smoke. No GPU hang, reset, fault, wedge, or kernel taint
occurred.
Fixes: dac328dea701 ("drm/xe/hwmon: expose package and vram temperature")
Link: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/7805
Link: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/4560
Assisted-by: OpenAI Codex:gpt-5.6-sol
Assisted-by: Claude Code:claude-fable-5
Assisted-by: ChatGPT:GPT-5.6 Sol Pro
Signed-off-by: Yuta Higuchi <avablaba@xxxxxxxxx>
---
drivers/gpu/drm/xe/xe_hwmon.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
index 5284cab67..1c0c6d3ab 100644
--- a/drivers/gpu/drm/xe/xe_hwmon.c
+++ b/drivers/gpu/drm/xe/xe_hwmon.c
@@ -3,6 +3,7 @@
* Copyright © 2023 Intel Corporation
*/
+#include <linux/delay.h>
#include <linux/hwmon-sysfs.h>
#include <linux/hwmon.h>
#include <linux/jiffies.h>
@@ -14,6 +15,7 @@
#include "regs/xe_mchbar_regs.h"
#include "regs/xe_pcode_regs.h"
#include "xe_device.h"
+#include "xe_force_wake.h"
#include "xe_hwmon.h"
#include "xe_mmio.h"
#include "xe_pcode.h"
@@ -330,6 +332,28 @@ static struct xe_reg xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg
return XE_REG(0);
}
+/*
+ * Experiments on Arc Pro B70 show that package-temperature updates resume
+ * after acquiring the main GT render forcewake domain. Allow time for a fresh
+ * value before reading. FORCEWAKE_ACK_RENDER acknowledges the forcewake
+ * domain; it is not known to indicate temperature-producer readiness.
+ */
+static int xe_hwmon_bmg_pkg_temp_read(struct xe_hwmon *hwmon, u64 *reg_val)
+{
+ struct xe_force_wake *fw = gt_to_fw(xe_root_mmio_gt(hwmon->xe));
+ struct xe_mmio *mmio = xe_root_tile_mmio(hwmon->xe);
+
+ CLASS(xe_force_wake, fw_ref)(fw, XE_FW_RENDER);
+
+ if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FW_RENDER))
+ return -ETIMEDOUT;
+
+ usleep_range(3000, 3500);
+ *reg_val = xe_mmio_read32(mmio, xe_hwmon_get_reg(hwmon, REG_TEMP, CHANNEL_PKG));
+
+ return 0;
+}
+
#define PL_DISABLE 0
/*
@@ -1037,6 +1061,17 @@ xe_hwmon_temp_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
case hwmon_temp_input:
switch (channel) {
case CHANNEL_PKG:
+ if (hwmon->xe->info.platform == XE_BATTLEMAGE) {
+ int ret = xe_hwmon_bmg_pkg_temp_read(hwmon, ®_val);
+
+ if (ret)
+ return ret;
+
+ *val = REG_FIELD_GET(TEMP_MASK, reg_val) *
+ MILLIDEGREE_PER_DEGREE;
+ return 0;
+ }
+ fallthrough;
case CHANNEL_VRAM:
reg_val = xe_mmio_read32(mmio, xe_hwmon_get_reg(hwmon, REG_TEMP, channel));
--
2.43.0