MI25 Driver Interfaces

Rough draft for review, 2026-08-23.

This document maps the externally exposed kernel interfaces of the Linux driver that controls the AMD MI25 GPU on beast.lan.

The driver implementation is the source of truth. Public documentation exists, but this document cites the driver source.

Environment

Verified 2026-08-23.

The local mainline tree is newer than the running DKMS driver. The hwmon and fan-control code is effectively identical between the two. Section “Source Drift” records the details.

hwmon surface

The hwmon directory is /sys/class/drm/card1/device/hwmon/hwmon1.

Temperature sensors

Verified 2026-08-23.

The driver defines exactly three temperature channels with these labels (amdgpu_pm.c, temp_label[], both trees):

The old amd-mi25-control README called temp2 “hotspot” and temp3 “VRM”. The driver’s own name for temp2 is junction. For Vega 10, junction is the hot spot of the die. temp3 is the memory temperature, which tracks the VRM area on this card. The naming is corrected here; the control logic (max of temp2, temp3) is unchanged.

Live values (millidegrees Celsius):

The temperature read path is amdgpu_hwmon_show_temp in amdgpu_pm.c. Junction maps to AMDGPU_PP_SENSOR_HOTSPOT_TEMP, edge to EDGE_TEMP, mem to MEM_TEMP.

Fan control

Verified 2026-08-23.

The hwmon attributes are defined in amdgpu_pm.c (hwmon_attributes table). Fan attributes are skipped when adev->pm.no_fan is set (hwmon_attributes_visible). The pwm1 and fan1_* attributes exist on this card, so no_fan is false.

Writing pwm1 requires pwm1_enable = 1 first. The driver rejects a pwm1 write in automatic mode with “manual fan speed control should be enabled first” (amdgpu_hwmon_set_pwm1, amdgpu_pm.c). pwm1_enable = 1 maps to AMD_FAN_CTRL_MANUAL, 2 to AMD_FAN_CTRL_AUTO.

Reading pwm1 returns the current hardware duty, scaled to 0-255 (vega10_fan_ctrl_get_fan_speed_pwm reads CG_THERMAL_STATUS.FDO_PWM_DUTY). The read value can differ from the last written value because the hardware steps the duty. The old service tracked the last written value, and that remains correct.

The driver source warns: do not set pwm1 and fan1_target at the same time; the former gets overridden. fan1_target control is unusable on this card anyway, because the tachometer returns garbage.

Other hwmon channels

Verified 2026-08-23.

Device sysfs surface

The device directory is /sys/class/drm/card1/device.

The attribute table is amdgpu_device_attrs in amdgpu_pm.c.

Verified 2026-08-23.

The power_dpm_force_performance_level values are documented in amdgpu_pm.c (DOC: power_dpm_force_performance_level). The old service writes “high”, which forces the highest DPM level.

debugfs surface

Debugfs is mounted on beast.lan (/sys/kernel/debug, rw). Reading the dri tree requires root; sudo is available.

Verified 2026-08-23.

amdgpu_pm_info live sample (2026-08-23): MCLK 945 MHz, SCLK 1500 MHz, VDDGFX 1081 mV, SoC power 27 W, GPU temperature 46 C.

debugfs availability is not guaranteed

Verified 2026-08-23 against the kernel sources and the live host.

debugfs is a diagnostics filesystem, not a stable interface. The kernel documentation says so directly (Documentation/filesystems/debugfs.rst):

A host may lack debugfs for several independent reasons:

On beast.lan the state is (verified 2026-08-23):

A fan-control service must treat debugfs as optional diagnostics. The service must not depend on debugfs for control or monitoring. The control path is sysfs hwmon, which is the stable interface. The service should read debugfs only when it is present and readable, and it should degrade cleanly when it is not.

/proc situation

amdgpu registers no /proc/driver/amdgpu directory. Only /proc/irq/160/amdgpu exists, which is the IRQ affinity mask. All driver state is exposed through sysfs and debugfs, not /proc.

Fan-control call path (Vega 10)

Traced in the local mainline tree; the DKMS 6.19.14 tree is identical.

pwm1 write:

  1. amdgpu_hwmon_set_pwm1 (amdgpu_pm.c).
  2. Checks current mode is AMD_FAN_CTRL_MANUAL, else returns -EINVAL.
  3. amdgpu_dpm_set_fan_speed_pwm (amdgpu_dpm.c).
  4. pp_funcs->set_fan_speed_pwm.
  5. vega10_fan_ctrl_set_fan_speed_pwm (vega10_thermal.c).
  6. Returns 0 without writing if fanInfo.bNoFan is set.
  7. Clamps speed to 255.
  8. Stops SMC fan control if PHM_PlatformCaps_MicrocodeFanControl.
  9. Reads FMAX_DUTY100 from CG_FDO_CTRL1; returns -EINVAL if zero.
  10. Writes duty = speed * duty100 / 255 to CG_FDO_CTRL0.FDO_STATIC_DUTY.
  11. Sets static mode (FDO_PWM_MODE_STATIC).

pwm1_enable write:

  1. amdgpu_hwmon_set_pwm1_enable (amdgpu_pm.c).
  2. Maps 0/1/2 to AMD_FAN_CTRL_NONE/MANUAL/AUTO.
  3. amdgpu_dpm_set_fan_control_mode (amdgpu_dpm.c).
  4. vega10_set_fan_control_mode (vega10_hwmgr.c).
  5. NONE: set fan speed pwm to 255.
  6. MANUAL: stop SMC fan control (if MicrocodeFanControl).
  7. AUTO: start SMC fan control (if MicrocodeFanControl).

Fan RPM read:

  1. amdgpu_hwmon_get_fan1_input (amdgpu_pm.c).
  2. vega10_fan_ctrl_get_fan_speed_rpm (vega10_thermal.c).
  3. If GNLD_FAN_CONTROL supported, vega10_get_current_rpm sends PPSMC_MSG_GetCurrentRpm to the SMC.
  4. The SMC response on this card is 2^27, which is not a valid RPM.

The MicrocodeFanControl capability decides whether the SMC owns fan control. The old README’s observation that the MI25 firmware cannot control the fan is consistent with the no_fan and MicrocodeFanControl paths in this code.

Implications for the fan-control service

Source Drift

Verified 2026-08-23.

Driver version detection and adaptation

The service should recognize and adapt to changing driver behavior across kernel and driver versions, as far as the interfaces allow.

What identifies the driver

Verified 2026-08-23.

The driver version is exposed in two places:

The DKMS driver on beast.lan reports version 6.19.14.31400100. The in-tree driver on athena (mainline 7.0) reports no version: /sys/module/amdgpu/version is empty and modinfo shows no version line. The kernel release is available from uname -r in both cases.

The service should record the driver version when present. The service should record the kernel release always. An empty driver version is expected for in-tree drivers and is not an error.

Distinguishing in-tree from out-of-tree drivers

Verified 2026-08-23 against the kernel source and both live hosts.

/proc/modules shows taint flags for each loaded module (kernel/module/main.c, kernel/panic.c). The flags that matter here:

The DKMS amdgpu on beast.lan shows “Live … (OE)”: out-of-tree and unsigned. The in-tree amdgpu on athena shows “Live …” with no flags.

The service can read /proc/modules to tell the two apart. An out-of-tree driver (OE) is the AMD DKMS build, which reports its version in /sys/module/amdgpu/version. An in-tree driver has no flags and no version file. This is a cheap, reliable adaptation signal.

What the service must probe, not assume

The interface surface is stable enough to rely on, but the service should verify each attribute before using it, so a changed driver degrades gracefully instead of failing hard.

Probe at startup:

Probe behavior empirically:

Adaptation strategy

Known stability across versions

Verified 2026-08-23.

This is evidence the interface is stable across recent versions, but the service should still probe rather than assume.

Special filesystems for driver and hardware introspection

Four special filesystems are relevant to introspecting kernel drivers and hardware device trees: /sys, /proc, /dev, and /run. Their usefulness differs sharply.

/sys (sysfs) — the primary interface

sysfs is the main interface for driver and hardware introspection. All driver state and device-tree structure is exposed here.

The fan-control service reads and writes almost entirely under /sys.

/proc (procfs) — secondary, still useful

procfs is process-centric but carries system-wide kernel state.

amdgpu registers no /proc/driver/amdgpu directory (see the /proc section).

/dev (devtmpfs) — access nodes, not introspection

/dev holds device nodes, which are the access path, not an introspection surface.

Presence of a node tells you a device exists. Attribute data lives in /sys.

/run (tmpfs) — runtime state, not kernel introspection

/run holds userspace runtime state.

/run is not a kernel interface and carries no driver attribute data. udev metadata is derived from the kernel; /sys remains the source of truth.

Summary for the service

Kernel version: uname or /proc?

Both work; the choice is about robustness, not information.

For the service, call uname(2) directly in code. The syscall needs no external utility and works even if procfs is not mounted. Reading /proc/sys/kernel/osrelease is an acceptable alternative when procfs is present. Do not parse /proc/version for the release; use osrelease or the syscall.

The driver version is a separate fact from the kernel release. The DKMS driver exposes /sys/module/amdgpu/version; the in-tree driver exposes nothing there. The kernel release is always available from uname(2) or osrelease.

References