DF-4931 / rv740_ssv.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | /* * DF-4931 replica โ rv740_dpm.c spread-spectrum clk_v computation * * Verbatim userland replica of the two SS blocks of * sys/dev/drm/radeon/rv740_dpm.c: * - sclk block : rv740_dpm.c:159-175 (clk_v at :166) * - mclk block : rv740_dpm.c:246-268 (clk_v at :259-260) * with the field masks/shifts verbatim from sys/dev/drm/radeon/rv740d.h:100-116 * CG_SPLL_SPREAD_SPECTRUM : SSEN(1<<0), CLK_S(x)=(x)<<4, CLK_S_MASK=0xfff<<4 * CG_SPLL_SPREAD_SPECTRUM_2 : CLK_V(x)=(x)<<0, CLK_V_MASK=0x3ffffff<<0 * MPLL_SS1 : CLKV(x)=(x)<<0, CLKV_MASK=0x3ffffff<<0 * MPLL_SS2 : CLKS(x)=(x)<<0, CLKS_MASK=0xfff<<0 * * The numerators * 4 * ss.percentage * fbdiv (sclk, :166) * 0x40000 * ss.percentage * (whole_fb_div + frac/8) (mclk, :259-260) * are evaluated in 32-bit unsigned arithmetic. ss.percentage (u16) and the * fb divisors are VBIOS-derived; for entirely plausible values the product * exceeds 2^32, wraps, and the wrapped (wrong) clk_v is programmed into the * 26-bit CLKV/CLK_V SMC fields โ silently, because the *correct* value also * fits the field. * * do_div(tmp, reference_clock) is plain 64-bit division here (math64.h:36-42). * * Build: cc -O2 -Wall -std=gnu11 [-DFIX_DF4931] -o rv740_ssv rv740_ssv.c * Exit: 0 ok / 98 SS-depth misprogrammed (baseline bug demonstrated) */ #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> typedef uint32_t u32; typedef uint16_t u16; typedef uint64_t u64; /* ---- rv740d.h:100-116 verbatim ---- */ #define SSEN (1 << 0) #define CLK_S(x) ((x) << 4) #define CLK_S_MASK (0xfff << 4) #define CLK_V(x) ((x) << 0) #define CLK_V_MASK (0x3ffffff << 0) #define CLKV(x) ((x) << 0) #define CLKV_MASK (0x3ffffff << 0) #define CLKS(x) ((x) << 0) #define CLKS_MASK (0xfff << 0) /* struct radeon_atom_ss (radeon.h): VBIOS ASIC_InternalSS_Info fields */ struct radeon_atom_ss { u16 percentage; u16 rate; }; static int failures; /* ---------------- sclk block: rv740_dpm.c:121-185 (SS part :159-175) ---- */ static void sclk_case(const char *name, u32 engine_clock, u32 ref_div, u32 post_div, u32 reference_clock, struct radeon_atom_ss ss) { u32 reference_divider = 1 + ref_div; u64 tmp = (u64)engine_clock * reference_divider * post_div * 16384; u32 fbdiv = (u32)(tmp / reference_clock); /* do_div, :144-146 */ u32 vco_freq = engine_clock * post_div; /* :161 (SS match key) */ u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate); /* :165 */ u32 clk_v; u64 num_exact = 4ULL * ss.percentage * fbdiv; u64 clk_v_exact = num_exact / ((u64)clk_s * 10000); u32 cg_spll_spread_spectrum, cg_spll_spread_spectrum_2; int exact_fits = (clk_v_exact <= 0x3ffffff); (void)vco_freq; #ifdef FIX_DF4931 clk_v = (u32)clk_v_exact; /* 64-bit numerator */ #else /* rv740_dpm.c:166 verbatim โ u32 numerator wraps */ clk_v = 4 * ss.percentage * fbdiv / (clk_s * 10000); #endif cg_spll_spread_spectrum = CLK_S(clk_s) | SSEN; cg_spll_spread_spectrum_2 = CLK_V(clk_v); printf("[sclk %s] engine_clock=%u ref_div=%u post_div=%u ref_clk=%u\n", name, engine_clock, ref_div, post_div, reference_clock); printf(" ss.percentage=%u ss.rate=%u -> fbdiv=%u clk_s=%u\n", ss.percentage, ss.rate, fbdiv, clk_s); printf(" numerator 4*pct*fbdiv: exact=%llu u32-wrapped=%u%s\n", (unsigned long long)num_exact, (u32)(4u * ss.percentage * fbdiv), num_exact > 0xffffffffULL ? " << WRAPS u32" : ""); printf(" clk_v programmed=%u exact=%llu%s\n", clk_v, (unsigned long long)clk_v_exact, exact_fits ? " (exact FITS the 26-bit field)" : " (exact would overflow the field)"); printf(" SMC words: SPREAD_SPECTRUM=0x%08x SPREAD_SPECTRUM_2=0x%08x\n", cg_spll_spread_spectrum, cg_spll_spread_spectrum_2); if (exact_fits && clk_v != clk_v_exact) { printf(" [BUG] SSEN programmed with wrong depth: %u instead of " "%llu (off by %.1fx)\n", clk_v, (unsigned long long)clk_v_exact, (double)clk_v_exact / (clk_v ? clk_v : 1)); failures++; } printf("\n"); } /* ---------------- mclk block: rv740_dpm.c:187-287 (SS part :246-268) ---- */ static u32 rv740_get_decoded_reference_divider(u32 encoded_ref) /* :34-67 */ { switch (encoded_ref) { case 0: return 1; case 16: return 2; case 17: return 3; case 18: return 2; case 19: return 3; case 20: return 4; case 21: return 5; default: return 0; } } static void mclk_case(const char *name, u32 memory_clock, u32 encoded_ref_div, u32 whole_fb_div, u32 frac_fb_div, u32 reference_clock, struct radeon_atom_ss ss) { u32 decoded_ref = rv740_get_decoded_reference_divider(encoded_ref_div); u32 clk_s, clk_v, mpll_ss1 = 0, mpll_ss2 = 0; u64 num_exact, clk_v_exact; int exact_fits; if (!decoded_ref) return; /* :256-257 */ clk_s = reference_clock * 5 / (decoded_ref * ss.rate); /* :258 */ num_exact = 0x40000ULL * ss.percentage * (whole_fb_div + (frac_fb_div / 8)); clk_v_exact = num_exact / ((u64)clk_s * 10000); exact_fits = (clk_v_exact <= 0x3ffffff); #ifdef FIX_DF4931 clk_v = (u32)clk_v_exact; /* 64-bit numerator */ #else /* rv740_dpm.c:259-260 verbatim โ u32 numerator wraps */ clk_v = 0x40000 * ss.percentage * (whole_fb_div + (frac_fb_div / 8)) / (clk_s * 10000); #endif mpll_ss1 |= CLKV(clk_v); /* :262-263 */ mpll_ss2 |= CLKS(clk_s); /* :265-266 */ printf("[mclk %s] memory_clock=%u ref_clk=%u decoded_ref=%u\n", name, memory_clock, reference_clock, decoded_ref); printf(" ss.percentage=%u ss.rate=%u whole_fb_div=%u frac=%u -> clk_s=%u\n", ss.percentage, ss.rate, whole_fb_div, frac_fb_div, clk_s); printf(" numerator 0x40000*pct*fb: exact=%llu u32-wrapped=%u%s\n", (unsigned long long)num_exact, (u32)(0x40000u * ss.percentage * (whole_fb_div + (frac_fb_div / 8))), num_exact > 0xffffffffULL ? " << WRAPS u32" : ""); printf(" clk_v programmed=%u exact=%llu%s\n", clk_v, (unsigned long long)clk_v_exact, exact_fits ? " (exact FITS the 26-bit field)" : " (exact would overflow the field)"); printf(" SMC words: MPLL_SS1=0x%08x MPLL_SS2=0x%08x\n", mpll_ss1, mpll_ss2); if (exact_fits && clk_v != clk_v_exact) { printf(" [BUG] wrong SS depth in MPLL_SS1: %u instead of %llu " "(off by %.1fx)\n", clk_v, (unsigned long long)clk_v_exact, (double)clk_v_exact / (clk_v ? clk_v : 1)); failures++; } printf("\n"); } int main(void) { printf("== DF-4931 replica: rv740_dpm.c clk_v u32-overflow (sclk :166, " "mclk :259-260) ==\n%s\n\n", #ifdef FIX_DF4931 "mode: FIXED (64-bit numerators)" #else "mode: BASELINE (kernel arithmetic verbatim)" #endif ); /* * S1 โ realistic SS-enabled engine-clock level (kHz units, as the * radeon DPM code carries them): 740 MHz engine clock, 27 MHz SPLL * reference, post divider 8, 6% spread at 30 kHz. fbdiv lands in the * same ~1.8e7 range as the accepted DF-4857 "ok-mode" control. */ sclk_case("S1 realistic 6%-SS", 740000, 0, 8, 27000, (struct radeon_atom_ss){ .percentage = 600, .rate = 30 }); /* * S2 โ modest (crafted but u16-legal) 30% spread: bigger wrap. */ sclk_case("S2 crafted 30%-SS", 740000, 0, 8, 27000, (struct radeon_atom_ss){ .percentage = 3000, .rate = 30 }); /* * M1 โ realistic GDDR5 memory-clock level: 800 MHz mclk, 10 MHz MPLL * reference, encoded ref divider 16 (=2), fb divisor 200, 2% spread * at 30 kHz. */ mclk_case("M1 realistic 2%-SS", 80000, 16, 200, 0, 10000, (struct radeon_atom_ss){ .percentage = 200, .rate = 30 }); /* * M2 โ 5% spread (still ordinary board territory). */ mclk_case("M2 realistic 5%-SS", 80000, 16, 200, 0, 10000, (struct radeon_atom_ss){ .percentage = 500, .rate = 30 }); if (failures) { printf("RESULT: %d SS depth(s) misprogrammed by the u32 " "numerator wrap\n", failures); return 98; } printf("RESULT: all programmed depths match the exact 64-bit math\n"); return 0; } |