From: "Poosa, Karthik" <karthik.poosa@intel.com>
To: "Nilawar, Badal" <badal.nilawar@intel.com>,
<intel-xe@lists.freedesktop.org>
Cc: <rodrigo.vivi@intel.com>, <anshuman.gupta@intel.com>,
<raag.jadav@intel.com>, <riana.tauro@intel.com>,
<sk.anirban@intel.com>, <mallesh.koujalagi@intel.com>,
<soham.purkait@intel.com>
Subject: Re: [PATCH v2 2/9] drm/xe/hwmon: initialize fan-control backend and table cache
Date: Tue, 25 Aug 2026 21:40:59 +0530 [thread overview]
Message-ID: <24b6ba1b-e3d7-4a83-a6dd-7e4873ccbefa@intel.com> (raw)
In-Reply-To: <9277b78f-94a9-4b88-aa27-13b7d42fc223@intel.com>
[-- Attachment #1: Type: text/plain, Size: 10692 bytes --]
On 10-08-2026 11:33, Nilawar, Badal wrote:
>
>
> On 17-07-2026 09:47, Karthik Poosa wrote:
>> Initialize Xe hwmon fan-control support by detecting fan count,
>> reading stock fan control points and min PWM.
>>
>> v2:
>> - Avoid user table initialization during probe. (Badal)
>> - Move unused code to appropriate patches.
>> - Use xe helpers for dmesg logs.
>>
>> Signed-off-by: Karthik Poosa<karthik.poosa@intel.com>
>> Assisted-by: Codex:gpt-5-4
>> ---
>> drivers/gpu/drm/xe/xe_hwmon.c | 159 +++++++++++++++++++++++++++---
>> drivers/gpu/drm/xe/xe_pcode_api.h | 5 +
>> 2 files changed, 149 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
>> index de3f2aeffc3f..0a416e3e7b8c 100644
>> --- a/drivers/gpu/drm/xe/xe_hwmon.c
>> +++ b/drivers/gpu/drm/xe/xe_hwmon.c
>> @@ -20,6 +20,7 @@
>> #include "xe_pcode_api.h"
>> #include "xe_sriov.h"
>> #include "xe_pm.h"
>> +#include "xe_printk.h"
>> #include "xe_vsec.h"
>> #include "regs/xe_pmt.h"
>>
>> @@ -122,14 +123,50 @@ struct xe_hwmon_energy_info {
>> long accum_energy;
>> };
>>
>> +enum fan_table_type {
>> + /** @STOCK_FAN_TABLE: firmware-provided fan table */
>> + STOCK_FAN_TABLE,
>> + /** @USER_FAN_TABLE: user fan table written through sysfs */
>> + USER_FAN_TABLE,
>> + /** @FAN_TABLE_MAX: number of fan table slots tracked per fan */
>> + FAN_TABLE_MAX,
>> +};
>> +
>> +/* Maximum number of fan control points supported by each fan */
>> +#define MAX_FAN_CONTROL_POINTS (10)
>> +
>> +/* Fan control point index: bit31 selects fan table type; bits30:0 control point index */
>> +#define FCP_INDEX(FAN_TABLE_TYPE, POINT_NUM) (((FAN_TABLE_TYPE) << 31) | \
>> + ((POINT_NUM) & REG_GENMASK(30, 0)))
>> +
>> +/* PCODE operations timeout for fan control commands */
>> +#define XE_PCODE_FAN_CONTROL_TIMEOUT_MS (10)
>> +
>> /**
>> - * struct xe_hwmon_fan_info - to cache previous fan reading
>> + * struct xe_hwmon_fan_info - cached fan telemetry and control state
>> + *
>> + * Each fan keeps the latest tachometer sampling state along with two fan
>> + * tables: the stock table discovered from firmware and the user table managed
>> + * by hwmon sysfs writes.
>> */
>> struct xe_hwmon_fan_info {
>> /** @reg_val_prev: previous fan reg val */
>> u32 reg_val_prev;
>> /** @time_prev: previous timestamp */
>> u64 time_prev;
>> + /** @fan_table: fan control tables */
>> + struct fan_table {
>> + /** @fan_control_point_count: number of supported fan control points */
>> + u8 fan_control_point_count;
>> + struct fan_control_point {
>> + /** @temp: temperature in degree celsius */
>> + u8 temp;
>> + /** @speed: fan speed in percentage */
>> + u8 speed;
>> + } fcp[MAX_FAN_CONTROL_POINTS];
>> + } fan_table[FAN_TABLE_MAX];
>> + /** @min_pwm: minimum fan PWM */
>> + u32 min_pwm;
>> };
>>
>> /**
>> @@ -168,6 +205,8 @@ struct xe_hwmon {
>> int scl_shift_time;
>> /** @ei: Energy info for energyN_input */
>> struct xe_hwmon_energy_info ei[CHANNEL_MAX];
>> + /** @num_fans: number of fans available */
>> + u8 num_fans;
>> /** @fi: Fan info for fanN_input */
>> struct xe_hwmon_fan_info fi[FAN_MAX];
>> /** @boot_power_limit_read: is boot power limits read */
>> @@ -856,17 +895,100 @@ static int xe_hwmon_pcode_write_i1(const struct xe_hwmon *hwmon, u32 uval)
>> (uval & POWER_SETUP_I1_DATA_MASK));
>> }
>>
>> -static int xe_hwmon_pcode_read_fan_control(const struct xe_hwmon *hwmon, u32 subcmd, u32 *uval)
>> +static int xe_hwmon_pcode_read_fan_control(const struct xe_hwmon *hwmon, u32 subcmd, u8 fan_num,
>> + u32 *uval)
>> {
>> struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
>> + return xe_pcode_read_timeout(root_tile, PCODE_MBOX(FAN_SPEED_CONTROL, subcmd, fan_num),
>> + uval, NULL, XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
>> +}
>> +
>> +static int xe_hwmon_get_num_fans(const struct xe_hwmon *hwmon, u32 *num_fans)
>> +{
>> + u32 fan_mask = 0;
>> + int ret;
>>
>> /* Platforms that don't return correct value */
>> - if (hwmon->xe->info.platform == XE_DG2 && subcmd == FSC_READ_NUM_FANS) {
>> - *uval = 2;
>> + if (hwmon->xe->info.platform == XE_DG2) {
>> + *num_fans = 2;
>> return 0;
>> }
>>
>> - return xe_pcode_read(root_tile, PCODE_MBOX(FAN_SPEED_CONTROL, subcmd, 0), uval, NULL);
>> + ret = xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_NUM_FANS, 0, &fan_mask);
>> + if (ret) {
>> + xe_warn(hwmon->xe, "failed to read number of fans, ret=%d\n", ret);
>> + return ret;
>> + }
>> +
>> + *num_fans = min_t(u32, hweight32(fan_mask), FAN_MAX);
>> +
>> + return 0;
>> +}
>> +
>> +static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon)
>> +{
>> + struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
>> + int point;
>> + int fan;
>> + int ret;
>> +
>> + for (fan = 0; fan < hwmon->num_fans; fan++) {
>> + u32 stock_fcp_count = 0;
>> +
>> + struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
>> +
>> + ret = xe_hwmon_pcode_read_fan_control(hwmon,
>> + FSC_READ_STOCK_FAN_CONTROL_POINTS,
>> + fan, &stock_fcp_count);
>> + if (ret) {
>> + xe_err(hwmon->xe,
>> + "failed to read fan %d stock control point count, ret=%d\n",
>> + fan, ret);
>> + return ret;
>> + }
>> + fi->fan_table[STOCK_FAN_TABLE].fan_control_point_count =
>> + min_t(u8, stock_fcp_count, MAX_FAN_CONTROL_POINTS);
>> +
>> + xe_dbg(hwmon->xe, "fan %d stock points %u\n", fan,
>> + fi->fan_table[STOCK_FAN_TABLE].fan_control_point_count);
>> +
>> + /* Dump the stock fan control points for debugging purposes. */
>> + for (point = 0; point < fi->fan_table[STOCK_FAN_TABLE].fan_control_point_count;
>> + point++) {
>> + u32 fcp = 0;
>> +
>> + fcp = FCP_INDEX(STOCK_FAN_TABLE, point);
>> + ret = xe_pcode_read_timeout(root_tile,
>> + PCODE_MBOX(FAN_SPEED_CONTROL,
>> + FSC_READ_FAN_TABLE, fan),
>> + &fcp, NULL, XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
>> + if (ret) {
>> + xe_err(hwmon->xe, "failed to read fan %d stock point %d, ret=%d\n",
>> + fan, point, ret);
>> + continue;
>> + }
>> +
>> + /* Cache the stock fan control points in local structure for later use. */
>> + fi->fan_table[STOCK_FAN_TABLE].fcp[point].temp =
>> + REG_FIELD_GET(FAN_CONTROL_POINT_TEMP_MASK, fcp);
>> + fi->fan_table[STOCK_FAN_TABLE].fcp[point].speed =
>> + REG_FIELD_GET(FAN_CONTROL_POINT_SPEED_MASK, fcp);
>> + xe_dbg(hwmon->xe, "fan %d stock point %d: temp %u C, speed %u %%\n",
>> + fan, point, fi->fan_table[STOCK_FAN_TABLE].fcp[point].temp,
>> + fi->fan_table[STOCK_FAN_TABLE].fcp[point].speed);
>> + }
>> +
>> + /* Read minimum fan PWM */
>> + ret = xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_FAN_MIN_PWM, fan,
>> + &fi->min_pwm);
>> + if (ret) {
>> + xe_err(hwmon->xe, "failed to read fan %d min PWM, ret=%d\n", fan, ret);
>> + continue;
>> + }
>> +
>> + xe_dbg(hwmon->xe, "fan %d min PWM %u\n", fan, fi->min_pwm);
>> + }
>> + return 0;
>> }
>>
>> static int xe_hwmon_power_curr_crit_read(struct xe_hwmon *hwmon, int channel,
>> @@ -1279,17 +1401,12 @@ xe_hwmon_energy_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
>> static umode_t
>> xe_hwmon_fan_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
>> {
>> - u32 uval = 0;
>> -
>> if (!hwmon->xe->info.has_fan_control)
>> return 0;
>>
>> switch (attr) {
>> case hwmon_fan_input:
>> - if (xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_NUM_FANS, &uval))
>> - return 0;
>> -
>> - return channel < uval ? 0444 : 0;
>> + return channel < hwmon->num_fans ? 0444 : 0;
>> default:
>> return 0;
>> }
>> @@ -1476,6 +1593,7 @@ xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
>> u64 val_sku_unit = 0;
>> int channel;
>> struct xe_reg pkg_power_sku_unit;
>> + u32 num_fans = 0;
>>
>> if (hwmon->xe->info.has_mbx_power_limits) {
>> /* Check if GPU firmware support mailbox power limits commands. */
>> @@ -1531,10 +1649,21 @@ xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
>> if (xe_hwmon_is_visible(hwmon, hwmon_energy, hwmon_energy_input, channel))
>> xe_hwmon_energy_get(hwmon, channel, &energy);
>>
>> - /* Initialize 'struct xe_hwmon_fan_info' with initial fan register reading. */
>> - for (channel = 0; channel < FAN_MAX; channel++)
>> - if (xe_hwmon_is_visible(hwmon, hwmon_fan, hwmon_fan_input, channel))
>> - xe_hwmon_fan_input_read(hwmon, channel, &fan_speed);
>> + if (hwmon->xe->info.has_fan_control) {
>> + xe_hwmon_get_num_fans(hwmon, &num_fans);
>> +
>> + xe_info(hwmon->xe, "Number of fans detected: %u\n", num_fans);
>> + hwmon->num_fans = num_fans;
>> +
>> + /* Initialize 'struct xe_hwmon_fan_info' with initial fan register reading. */
>> + for (channel = 0; channel < hwmon->num_fans; channel++)
>> + if (xe_hwmon_is_visible(hwmon, hwmon_fan, hwmon_fan_input, channel))
>> + xe_hwmon_fan_input_read(hwmon, channel, &fan_speed);
>> +
>> + /* Fan control tables initialization */
>> + if (xe_hwmon_read_fan_control_info(hwmon))
>> + xe_warn(hwmon->xe, "Fan control tables are not available\n");
>
> The table appears to be captured before late binding. Do we need
> separate pre-/post-LB tables, or should the table be updated again
> after late binding done?
>
> Thanks,
> Badal
>
Yes, the stock table is captured before late binding.
I don't think separate pre-/post-LB tables are necessary. We can simply
overwrite the cached stock table after late binding completes via
|xe_hwmon_fan_update_post_lb()|.
When late binding is disabled, the default stock table should remain in
use. So we can cache the default table initially and update it only when
late binding is applied.
>> + }
>>
>> if (hwmon->xe->info.has_mbx_thermal_info && xe_hwmon_pcode_read_thermal_info(hwmon))
>> drm_warn(&hwmon->xe->drm, "Thermal mailbox not supported by card firmware\n");
>> diff --git a/drivers/gpu/drm/xe/xe_pcode_api.h b/drivers/gpu/drm/xe/xe_pcode_api.h
>> index 94575c476e3d..669010f1e2d0 100644
>> --- a/drivers/gpu/drm/xe/xe_pcode_api.h
>> +++ b/drivers/gpu/drm/xe/xe_pcode_api.h
>> @@ -85,6 +85,11 @@
>>
>> #define FAN_SPEED_CONTROL 0x7D
>> #define FSC_READ_NUM_FANS 0x4
>> +#define FSC_READ_STOCK_FAN_CONTROL_POINTS 0x5
>> +#define FSC_READ_FAN_TABLE 0x7
>> +#define FAN_CONTROL_POINT_TEMP_MASK REG_GENMASK(7, 0)
>> +#define FAN_CONTROL_POINT_SPEED_MASK REG_GENMASK(15, 8)
>> +#define FSC_READ_FAN_MIN_PWM 0x8
>>
>> #define PCODE_SCRATCH(x) XE_REG(0x138320 + ((x) * 4))
>> /* PCODE_SCRATCH0 */
[-- Attachment #2: Type: text/html, Size: 11785 bytes --]
next prev parent reply other threads:[~2026-08-25 16:11 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-17 4:17 [PATCH v2 0/9] Add fan control support Karthik Poosa
2026-07-17 4:16 ` ✓ CI.KUnit: success for Add fan control support (rev2) Patchwork
2026-07-17 4:17 ` [PATCH v2 1/9] drm/xe/pcode: Introduce xe_pcode_read_timeout() API Karthik Poosa
2026-07-17 4:17 ` [PATCH v2 2/9] drm/xe/hwmon: initialize fan-control backend and table cache Karthik Poosa
2026-08-10 6:03 ` Nilawar, Badal
2026-08-25 16:10 ` Poosa, Karthik [this message]
2026-08-11 13:11 ` Nilawar, Badal
2026-08-17 17:01 ` Poosa, Karthik
2026-08-25 16:14 ` Poosa, Karthik
2026-07-17 4:17 ` [PATCH v2 3/9] drm/xe/hwmon: expose fanN_max Karthik Poosa
2026-07-23 5:34 ` Purkait, Soham
2026-07-17 4:17 ` [PATCH v2 4/9] drm/xe/hwmon: expose pwm[1-3] Karthik Poosa
2026-07-24 7:02 ` Purkait, Soham
2026-08-11 13:58 ` Poosa, Karthik
2026-07-17 4:17 ` [PATCH v2 5/9] drm/xe/hwmon: expose pwm[1-3]_enable Karthik Poosa
2026-07-17 4:17 ` [PATCH v2 6/9] drm/xe/hwmon: Enable fan curve control support Karthik Poosa
2026-07-22 18:16 ` Purkait, Soham
2026-08-27 12:07 ` Poosa, Karthik
2026-07-17 4:17 ` [PATCH v2 7/9] drm/xe/hwmon: Add kernel-doc for fan control Karthik Poosa
2026-07-17 4:17 ` [PATCH v2 8/9] drm/xe/hwmon: preserve fan user table across suspend resume Karthik Poosa
2026-08-10 5:53 ` Nilawar, Badal
2026-08-27 10:27 ` Poosa, Karthik
2026-07-17 4:17 ` [PATCH v2 9/9] drm/xe/hwmon: Update fan info after late binding Karthik Poosa
2026-07-20 6:20 ` Purkait, Soham
2026-08-27 10:36 ` Poosa, Karthik
2026-07-17 5:00 ` ✓ Xe.CI.BAT: success for Add fan control support (rev2) Patchwork
2026-07-17 8:18 ` ✗ Xe.CI.FULL: failure " Patchwork
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=24b6ba1b-e3d7-4a83-a6dd-7e4873ccbefa@intel.com \
--to=karthik.poosa@intel.com \
--cc=anshuman.gupta@intel.com \
--cc=badal.nilawar@intel.com \
--cc=intel-xe@lists.freedesktop.org \
--cc=mallesh.koujalagi@intel.com \
--cc=raag.jadav@intel.com \
--cc=riana.tauro@intel.com \
--cc=rodrigo.vivi@intel.com \
--cc=sk.anirban@intel.com \
--cc=soham.purkait@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox