Yesterday I wrote: > My next step is to cherry-pick that commit into my BL31: if EL3 > also reads zero, the silicon answer wins.
Done this morning, and the answer is in: the firewall wins, and Huseyin called it right. Setup: d2d6928641ba cherry-picked onto the upstream TF-A v2.12 my board runs (banner v2.12.0-10-g70d814213 verified over UART after flashing). With that BL31, clk_get_rate() goes through EL3 reading the PVTPLL status words directly. NPU, from EL3: NPUGRF+0x24 reads a real value. clk_get_rate() returns exactly 1000000000 at the 1 GHz OPP and exactly 700000000 at 700 MHz. At the same moment, the same register read from a kernel module (EL1) still returns 0x00000000. Same register, same instant: secure world sees the counter, non-secure gets zeros. So the counter is alive in silicon, and yesterday's dead end was the per-ip-core access restriction you suspected - not a missing clock and not an unbonded counter. GPU, from EL3, as cross-check under glmark2 load: five consecutive reads gave 1048, 1053, 1047, 1054, 1049 MHz at the 1000 MHz OPP - live jitter, matching the 1042 I measured from userspace mmap yesterday. So EL3 reports raw measurements, which makes the NPU result the more interesting one: The NPU PVTPLL apparently LOCKS to its target (readback == nominal to the MHz, at two operating points), while the GPU one free-runs about 5% above nominal. Same register layout, same CON2 contents as far as I can see - do you know what makes the two instances behave differently? Practical upshot for this series: with your TF-A commit in BL31, plain clk_get_rate() gives the kernel measured rates with no GRF access from the non-secure side at all. That is the clean telemetry path for rocket DVFS, and one more reason your commit earns its keep on RK3588. One honest gap: I could not yet take the voltage-vs-frequency curve (my devfreq module holds the NPU rail at 850 mV as a regulator consumer, so undervolting from a second consumer is refused). Future work. Igor
