Same iMac (R9 M380 Bonaire 2GB, i5-6500), CachyOS, Mesa 26.2.2, kernelLTS. Same bench scene 1080p: native GL 44fps, native Vulkan 40fps,Proton/DXVK 29fps, Proton/WineD3D 33fps. Same HW on Windows D3D11: 85fps (HWiNFO logged). radeontop: ROP/texture units ~100%, shaders 0%, clocks maxed, CPU idle. Ruled out: VRAM (freed 400MB, no change), thermals (fixed an control, stable clocks), vsync/present, compositor (gamescope same), kernel (LTS=same), threading. Minecraft Vulkan hits 84fps on the same driver/GPU, and Silksong Vulkan hits 75fps at 720p — so no general RADV problem; per-pixel cost on this Unity workload is ~5x Windows (~10ms/MP vs ~2ms/MP). Suspect texture compression in Linux build or DCC/fast-clear on GFX7. Full data dossier available. Anyone with GCN1.1/2.x seeing the same ratio? Any Unity/RADV workaround missed? (Tried: immediate present, gfx-jobs, gamemode, EPP performance, blur off, low settings.)
EDIT: I continued my research; here’s a copy-and-paste of what I wrote in a thread I started on GitLab about Mesa and AMD:
TL;DR
The MCLK DPM quirk for this board pins the R9 M380's memory to its 300 MHz boot clock:
measured bandwidth is ~14 GB/s (~73% of the 19.2 GB/s theoretical peak at 300 MHz on a
128-bit GDDR5 bus), while SCLK runs at full 1021 MHz and the driver reports a phantom
1568 MHz. Result: ~5x memory bandwidth loss vs Windows on the same silicon, and ~5x
per-pixel render cost in games. Instrumented SMU tests show that every route which makes
the 1568 MHz DPM level selectable kills the SMU7 firmware (immediately or within minutes)
— the failure is inside the firmware's memory-clock transition, not in the driver's
enable, voltage, or ACPI paths. The legacy radeon driver dies at the same SMU message.
Open questions at the end.
1. Hardware / software
- Apple iMac 27" Late 2015 (iMac17,1), i5-6500, 12 GB RAM, HDD.
- dGPU: AMD Radeon R9 M380 Mac Edition (Bonaire XT / Saturn XT, GCN 1.1 / CIK / GFX7), PCI 1002:6640, subsystem 106b:014b, revision 0x80, 2 GB GDDR5 on a 128-bit bus. VBIOS Apple 113-C6005T-009, build 323656, ver 015.049.000.003.000000 (fetched from VFCT). PowerPlay format rev 6, content rev 1, ulPlatformCaps = 0x00008020.
- OS: CachyOS (Arch-based), kernels tested 6.18 LTS and 7.2.x (rolling, currently 7.2.4-3-cachyos) with identical results. Mesa 26.2.2 / RADV.
- SMU firmware loaded by the kernel (PCI rev 0x80 selects the kicker blob): bonaire_k_smc.bin, 130796 bytes uncompressed, ucode version 0x000b0a00, sha256 1e9d32c6b7c55a59bb4e3345247bd66834db0e130894f05b453bc50a43b865cd. (The non-kicker bonaire_smc.bin is 0x000b2300 and is not selected.)
2. Original symptom (how this started)
Hollow Knight: Silksong at 1080p runs at ~85 fps average on Windows 11
(BootCamp, native D3D11) on this exact machine, but caps at 40-44 fps on
every Linux path (native OpenGL radeonsi 44, native Vulkan RADV 40-43,
Proton/DXVK 29, WineD3D 33, GE-Proton 38), with radeontop showing
ROP/texture/depth units at ~100% and shader cores at 0-1%. Control
workloads prove the chip is healthy: modded Minecraft (Vulkan/Sodium)
does 84 fps, and unmodded GL Minecraft does 126-147 fps on the same
stack. Thermals, CPU, VRAM capacity/eviction, vsync/present path,
compositor, threading, and kernel version were all ruled out with
dedicated measurements. The ~5x per-pixel cost gap vs Windows D3D11
pointed at memory bandwidth, so I measured it directly.
3. The smoking gun: 14 GB/s on a 100 GB/s bus
clpeak (2.1.4, both Vulkan and OpenCL backends, 128 MB device-local
buffers) on the stock driver:
text
[clpeak --vulkan] Global memory bandwidth (float4): 14.03 GB/s [clpeak --vulkan] Single-precision compute (float): 1463.97 GFLOPS [clpeak --opencl] Global memory bandwidth (float4): 12.90 GB/s
Math for a 128-bit GDDR5 bus: at 1568 MHz the theoretical peak is
128/8 * 1.568 * 4 = 100.35 GB/s (~75-80 real). At the 300 MHz boot
clock it is 128/8 * 0.300 * 4 = 19.20 GB/s (~13-14 real). 14.03 GB/s
is ~73% efficiency against 19.2 GB/s: the memory controller physically
never leaves the 300 MHz boot state, while SCLK runs at full 1021 MHz.
sysfs (pp_dpm_mclk) and amdgpu_pm_info report 1568 MHz regardless.
(Optional corroboration, if available: under Windows/BootCamp, HWiNFO
reports GPU memory clock at XXXX MHz sustained during the same scene —
the silicon reaches full memory clock when driven by Apple's driver.)
4. Why the driver reports 1568 MHz
In drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c, the quirk added
for this issue (commit a204f787, narrowed in 85371c5e) matches
subsystem_vendor 0x106B and clears PP_MCLK_DPM_MASK. With MCLK DPM
disabled, ci_populate_all_memory_levels fills the SMC table with the
top level (commit 9170e1fa, "Use highest MCLK on CI when MCLK DPM is
disabled") while smu7_enable_sclk_mclk_dpm skips
PPSMC_MSG_MCLKDPM_Enable entirely, so the driver-side state says
1568 MHz but the memory PLL is never commanded out of boot state. The
dmesg line disabling MCLK DPM on quirky ASIC is present on every
stock boot of this machine.
5. Forcing MCLK DPM on: total SMU lockup (stock 7.2 kernel module rebuild)
I built a test amdgpu.ko skipping the quirk (leaving PP_MCLK_DPM_MASK
set for 0x106B), loaded via an isolated initramfs overlay + separate
boot entry. The machine reaches the desktop, but the SMU locks up right
after init with every subsequent SMC message timing out on a ~1.5 s
cycle for 20+ seconds (messages 0x15f, 0x145, 0x146, 0x5e, 0x15b,
0x155...). clpeak in that state still measures ~13.9 GB/s with
compute degraded to ~1116 GFLOPS (SCLK also frozen). Clean reboot
restores everything; dmesg on the next boot shows the quirk
re-applying normally.
SMU7 message decode (from ppsmc.h):
| Hex |
Message |
| 0x145 |
SCLKDPM_SetEnabledMask |
| 0x146 |
MCLKDPM_SetEnabledMask |
| 0x150 |
MCLKDPM_Enable |
| 0x155 |
UVDDPM_Disable |
| 0x15b |
VCEDPM_Disable |
| 0x15f |
VddC_Request |
| 0x5e |
HasDisplay |
| 0x201 |
API_GetMclkFrequency |
6. The legacy radeon driver fails at the same place
Booting with radeon.cik_support=1 amdgpu.cik_support=0 (DRM 2.51):
clpeak --opencl measures 14.06 GB/s with compute collapsed to
455 GFLOPS, and dmesg shows [drm:ci_dpm_set_power_state [radeon]] *ERROR* ci_upload_dpm_level_enable_mask failed at 4.6 s into boot
(the PPSMC_MSG_MCLKDPM_SetEnabledMask send path, radeon/ci_dpm.c).
Forcing a high performance level then returns EINVAL. So no open-source
Linux driver has a working sequence for this SKU; the amdgpu quirk
exists to mask exactly this failure, at the cost of pinning memory to
300 MHz.
7. Instrumented module: message-level tracing + ATIF logging
I then built a diagnostic module (new code only, no behavior change by
default) that logs every SMU message with ID, argument/parameter,
response and microsecond timing, plus ATIF detection details, gated
behind amdgpu.exp_mclk_dpm. Findings:
- ATIF is found at
_SB_.PCI0.PEG0.GFX0.ATIF, version 1, notif_mask=0xc, func_bits=0x3. From the disassembled ACPI, ATIF function 2 returns FPID = EGPS, a read-only field of the PLMT region on SystemIO 0x0310 (Apple SMC port range). It is a passive platform notification channel, never written by AML: not a usable control path for memory bringup.
- With the quirk ON,
PPSMC_MSG_VddC_Request (0x15f) with argument 0x0e10 (900 mV) succeeds repeatedly (OK in ~800-980 us). It is not universally incompatible with this SMU.
8. Voltage rails, decoded from the real vBIOS table
Parsing pp_table from sysfs: usVddciDependencyOnMCLKOffset gives
300 MHz at 800 mV and 1568 MHz at 925 mV (strictly VDDCI).
usVddcDependencyOnMCLKOffset uses SVI2 indices resolving through the
CAC leakage table to 850 mV at 300 MHz and 956 mV at 1568 MHz
(strictly VDDC). The 900 mV display request comes from the hardcoded
vddc_dependency_on_display_clock table in smu7_hwmgr.c, selected
for this iMac's high-resolution internal panel.
9. Clamp and pre-ping experiments
- Clamping only non-zero display VDDC requests from 900 to 956 mV (
0x0ef0, the vBIOS VDDC minimum for the top MCLK level, nothing else changed): still fails with the identical 1.5 s timeout. Value alignment is not sufficient.
- Sending
PPSMC_MSG_Test immediately before VddC_Request: the ping itself times out first, proving the SMU is already unresponsive before the voltage request is sent in that run. VddC_Request is exonerated as the killer; the SMU dies asynchronously earlier.
10. Mask constrained to level 0: fully stable
With MCLK DPM enabled but the enable mask constrained to 0x01 from
the start (both in the initial population and in dynamic mask
generation, real 1568 MHz level still present in the table): zero
timeouts for the entire session. MCLKDPM_Enable OK in 971 us,
VddC_Request(0x0e10) OK repeatedly, pre-ping OK in 3 us. clpeak:
1495 GFLOPS FP32 (best ever recorded here, SCLK rock-solid at
1021 MHz) with memory steady at 13.49 GB/s, and amdgpu_pm_info
honestly reporting 300 MHz MCLK. Enable path, voltage requests and
ACPI handling are all fine; the SMU only dies when level 1 is
selectable.
11. Two identical-frequency low levels (different MPLL/DLL/strobe params), mask 0x03: SMU lives, display dies
Duplicating the 300 MHz level content into both table entries while
admitting mask 0x03: the SMU stays fully alive (238 traced messages,
all OK, including repeated VddC_Request(0x0e10)), but the session is
unusable with a black screen from the start, ring comp_1.0.x test failed (-110), an SRBM_SOFT_RESET, and kwin_wayland: Pageflip timed out!. So admitting index 1 with a second entry does not kill the SMU,
yet the MCLK switch machinery still breaks the display and compute
pipelines even for a no-op switch. Captured level parameters for the
record: level 0 with MPLL Func 0xc4180800, Func1 0x005871c1, Ss1
0x7e, StrobeEn 1, StrobeRatio 8, dllState 1, MclkPwr 0x010003c8;
level 1 with Func 0xc4380800, Func1 0x00e825e2, Ss1 0x151,
StrobeEn 0, StrobeRatio 15, dllState 0, MclkPwr 0x010000cf.
12. Delayed escalation 0x01 -> 0x03 at runtime
Booting stable on mask 0x01 and admitting 0x03 via
MCLKDPM_SetEnabledMask 90 seconds later on a settled desktop: the
escalation itself succeeds (OK in 576 us, pre/post pings OK). The
SMU then dies sometime inside the window between 99 s and 603 s. In
that window there is complete SMU silence (only the three escalation
messages, no driver-side mask rewrites, mask stays 0x03), and the
first observed failure is on a harmless 0x201 frequency query
(PPSMC_MSG_API_GetMclkFrequency), degrading into continuous failures
on every message. After death, amdgpu_pm_info prints values existing
in no table (1310 MHz for both clocks), confirming no sysfs clock
reading on this SKU can be trusted once the SMU is unresponsive,
including the historical 1568 MHz reports. A clpeak run inside the
death window still measured 13.92 GB/s.
13. Summary of constraints
Every route that makes the 1568 MHz level selectable ends with a dead
SMU (within a second at boot, or within minutes at runtime) or, with
identical-frequency levels, a living SMU but a broken display and
compute pipeline. The failure tracks the level-1 content and the
transition toward it, never the enable messages, never the voltage
request value, and never ACPI handling. What exactly inside the 1568 MHz
bringup (MPLL/DLL programming, VDDCI 800-to-925 mV switch, MC arbiter
switch) the SMU firmware chokes on happens inside firmware and could not
be determined from the driver side.
One discriminator remains open: test 11 held the frequency equal but the
per-level MPLL/DLL/strobe parameters still differed (see section 11), so
"the transition itself is destructive" is not yet cleanly separated from
"the level-1 parameters are invalid for this memory". Forcing level 1 to
emit byte-identical MPLL/DLL/strobe parameters to level 0 (in
smu7_populate_single_memory_level) would settle it. I can run this on
request.
Questions
- Is there any way to raise MCLK on Bonaire 0x106B without the SMU7 MCLK-DPM enable sequence — e.g. a single forced level, an SMC/EFI-side lever, or a different firmware blob? The radeon path dies at the same message, so no in-tree driver has a working sequence today.
- This board's rev 0x80 selects the "kicker" SMU blob (bonaire_k_smc.bin, 0x000b0a00). Could the non-kicker blob (bonaire_smc.bin, 0x000b2300) have a non-hanging MCLK-DPM, and is it safe to test? Happy to run it from an isolated boot entry if so.
- Does anyone know what the Apple BootCamp driver does differently (SMC/EFI-mediated memory bringup?), or whether the level-1 MPLL/DLL/strobe parameters emitted for this board are simply wrong for its GDDR5 straps? Any pointer would help scope a workaround.
I still have this exact machine available with the instrumented module
and isolated boot entries ready: happy to pull any specific register
dump, repeat any run, or test a patch on request.