⬢ DragonFlyBSD Kernel Audit
DF-4892 / rpm_wrap_demo.c
← back to finding ↓ download raw
/* DF-4892 — arithmetic demo of the smu7 fan-RPM 32-bit wrap
 * (DF-4761 vega10_thermal twin).
 *
 * sys/dev/drm/amd/powerplay/hwmgr/smu7_thermal.c:100
 *      *speed = 60 * crystal_clock_freq * 10000 / tach_period;
 * and :273
 *      tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed);
 *
 * crystal_clock_freq = amdgpu_asic_get_xclk() = vi_get_xclk() =
 * adev->clock.spll.reference_freq (25/27 MHz class, kHz units) on
 * Tonga/Fiji/Polaris dGPUs (vi.c:326-349).  60*25000*10000 = 1.5e10 wraps
 * u32 arithmetic before the division.  This userspace program evaluates the
 * exact source expressions in both u32 and u64 to document the wrap; it is
 * an arithmetic demo only (no SMU7 hardware required to see the divergence).
 */
#include <stdio.h>
#include <stdint.h>

int main(void)
{
	uint32_t xclk[] = { 25000, 27000, 25000 / 4 };
	uint32_t tach_period[] = { 1, 100, 1000, 100000, 2000000 };
	unsigned i, j;

	puts("DF-4892 smu7 fan RPM math: 60 * xclk * 10000 (u32 vs u64)");
	for (i = 0; i < sizeof(xclk)/sizeof(xclk[0]); i++) {
		uint32_t prod32 = 60 * xclk[i] * 10000;		/* kernel expr */
		uint64_t prod64 = (uint64_t)60 * xclk[i] * 10000;
		printf("xclk=%-6u u32: %-12u u64: %-14llu wrap: %s\n",
		       xclk[i], prod32, (unsigned long long)prod64,
		       prod32 != prod64 ? "YES" : "no");
	}

	puts("\nget_fan_speed_rpm (:100): reported RPM for real tach periods");
	for (j = 0; j < sizeof(tach_period)/sizeof(tach_period[0]); j++) {
		uint32_t r32 = 60 * 25000 * 10000 / tach_period[j];
		uint64_t r64 = (uint64_t)60 * 25000 * 10000 / tach_period[j];
		printf("tach_period=%-8u kernel(u32)=%-12u correct(u64)=%-12llu error=%lldx\n",
		       tach_period[j], r32, (unsigned long long)r64,
		       r64 ? (long long)(r64 - r32) : 0);
	}

	puts("\nset_fan_speed_rpm (:273): programmed TARGET_PERIOD for requests");
	{
		uint32_t req[] = { 800, 1500, 2200, 3000 };
		for (j = 0; j < sizeof(req)/sizeof(req[0]); j++) {
			uint32_t p32 = 60 * 25000 * 10000 / (8 * req[j]);
			uint64_t p64 = (uint64_t)60 * 25000 * 10000 / (8 * req[j]);
			printf("rpm=%-6u kernel(u32)=%-12u correct(u64)=%-12llu -> fan target off by %.2fx\n",
			       req[j], p32, (unsigned long long)p64,
			       p32 ? (double)p64 / (double)p32 : 0.0);
		}
	}
	return 0;
}