diskOS installer: initial public beta

Flashes the diskOS custom UI onto the FiiO Snowsky Disc over Ingenic mask-ROM USB,
building the image from your own stock firmware. Runs from source via install.sh.
This commit is contained in:
b0hemia
2026-08-26 15:26:14 +10:00
commit e0bc4785e9
109 changed files with 12625 additions and 0 deletions
@@ -0,0 +1,120 @@
From 08f128d82f8e86dac5ca277d4fe4cc3fd7fef204 Mon Sep 17 00:00:00 2001
From: b0hemia <50309975+b0hemia@users.noreply.github.com>
Date: Mon, 24 Aug 2026 22:51:34 +1000
Subject: [PATCH 1/8] baseline: burner_x2000 SPL that loads+executes on device
(-fcommon + mask-ROM return shim)
---
arch/mips/cpu/xburst2/x2000/start.S | 23 +++++++++++++++++++++++
config.mk | 2 +-
tools/ingenic-tools/mmc_params | Bin 0 -> 14472 bytes
3 files changed, 24 insertions(+), 1 deletion(-)
create mode 100755 tools/ingenic-tools/mmc_params
diff --git a/arch/mips/cpu/xburst2/x2000/start.S b/arch/mips/cpu/xburst2/x2000/start.S
index caab455..2dcc1e2 100755
--- a/arch/mips/cpu/xburst2/x2000/start.S
+++ b/arch/mips/cpu/xburst2/x2000/start.S
@@ -167,6 +167,29 @@ cache_alloc_a_line:
li sp, 0x80001000
#endif
+#ifdef CONFIG_BURNER
+ /*
+ * DDR-only USB stage-1 (burner mode): board_init_f() brings up clocks and
+ * SDRAM and then RETURNS (see arch/mips/cpu/xburst2/x2000/soc.c, the
+ * CONFIG_BURNER branch). For usbboot to keep driving the USB session we
+ * must hand control back to the mask ROM afterwards. Preserve the mask-ROM
+ * return address (and sp) in a fixed TCSM scratch slot across the call --
+ * board_init_f repoints/uses the stack internally, so a stack-relative save
+ * is not reliable for the outer return. 0xb24017f0 lies in the param/pad
+ * region below the code (offset 0x800) and is untouched by DDR init.
+ */
+ li t0, 0xb24017f0
+ sw ra, 0(t0)
+ sw sp, 4(t0)
+ jal board_init_f
+ nop
+ li t0, 0xb24017f0
+ lw ra, 0(t0)
+ lw sp, 4(t0)
+ jr ra
+ nop
+#else
j board_init_f
nop
+#endif
diff --git a/config.mk b/config.mk
index f71e145..d78be30 100755
--- a/config.mk
+++ b/config.mk
@@ -195,7 +195,7 @@ endif
ARFLAGS = $(error update your Makefile to use cmd_link_o_target and not AR)
RELFLAGS= $(PLATFORM_RELFLAGS)
DBGFLAGS= -g # -DDEBUG
-OPTFLAGS= -Os #-fomit-frame-pointer
+OPTFLAGS= -Os -fcommon #-fomit-frame-pointer (-fcommon: 2013 tree relies on tentative defs; GCC>=10 defaults -fno-common)
OBJCFLAGS += --gap-fill=0xff
diff --git a/tools/ingenic-tools/mmc_params b/tools/ingenic-tools/mmc_params
new file mode 100755
index 0000000000000000000000000000000000000000..78adebd875be1ac2ec4d41c346622d76fea5e1c4
GIT binary patch
literal 14472
zcmeHOZ)_Y_5r1bp^~H70ra0gvZM_P1)lhu7a~#V_Wa58G)>Rwa)&?pXuIsx!`|$l^
zZ%v%4#MhV<pBzgrNVFASEIuVdkf<t^sYJLo5U7w!Q&eQWG-*YUt<vB^DQST$Gy7)F
z>%|uTKe1!o{@(m%=FQu=x9@iEo+l36bFj?k6D;N8lLEEbTO`v-1~p6#fKJgaHo@^W
zajUoq^a`7k_D%_4m9kK_8vVqV17ch~HrBvfB@G#*@CXqxZapbhN(-Z?l{&_4z$WQ#
zEYLU@*<Q-0U@(YcA4@on4j_!qIIPW7vuzer9WJE0%o1`Osu=ea#XUuFjLVddk?V;y
z!B;z-hZ{p+Lt%8sjZmB$du5nl#2hFOTs33=PQHGM%PV#_!i}wx7L1(lK8VA*{IkMu
z^Zk@>z4u|9>d#2!6+4rOp|;kY$yjqTkscdw9&c}JZfgzZGQk#m2skbUUO1-?9Uc_W
zO%XXNqbSQuo%=&u9of<U+uE}~f8$8mFW#Cy^L$|IJCB$Bc;EBrgX6{+^g+S?c#5Eo
z?P_?TkF3XI(2^YDCH>_T;(D?J40LI)a``%F$|^=G{QztEWB#@HPp`w@y$;{74u2MS
z1zv8!oNMK6T!%joyaF$`>;q!En?yYl$(dSoG!h*djl{J?5{RskNSkqFgrRFOk;&?5
zpkanY(b+H~DJ_9abX<$X6KO4(ctjV`WG1JJqejBiMKY)BqXGkP2DOPp_w;r5L|THo
zf~}?YN~<NfyTtBl6_LJydm}O3(1#N_Q#S_g?MY_R`hYf+)FJ0^DwC#BM5ODQjyQ9C
zFq<mig?~Pnxp?IH>I2I|aQb}Rm|GGXadyXvt4ky{;R<7rKDHI@<>FD&pMByRFdfUp
z6zC|t-tjt<{jlU`X+2{;M7+OK+L>p4VZY>YuH&3zx#HlR*T<rR=e{sz*}>n;od8~U
z@OWLZ%n^?tCtM@6op$hey|SEh@b>2n8Ri|lyk4cD=-}xi%QpPf!9#&d@`{76DzRR}
ztAJMluL52Lyb5>~@G9`HSKtpdw|}5c{?@NfZ}`<tA=Hyav&?!$o&1sig58<5`wKu@
zO-VS`G;|8|A3@8~o0et8XKfvC%$8nV(eZ|C>BSWtZ=jZ*S<&&vY3W-lI^HlXeQia@
z8>OWaCEaQ|4B6u~4ZXt7Z#DfUxLSYRrT@~UzwFX4y7cod{kc_o*Faz4-N6T-OiE0h
zu4uXwhNl*K8|%ZxpY%4`cg&Mknc1dJf8>XF>gvqyGXWtM=im(Hu{P@TKx2JOy{pa+
ztvR_7NGP%Lxg%W<T~Mcw`@_Za7`S*GA_^By7Hg(n0ySJzXOfL|Z&lVbi1OaXYWo3E
zEnF*HhUC?YVQ@ZKoVW^|S3^n-QrhXm#l<_og!Fs}x_Rsa@vsmRfteE)<o!NALO%Vc
z_pyt@ix79f5qIr|WrbfokMB?6^5Qwr)ESc!12F*v#MMq-uP|@xg9Ltv-|mS2E;uZ0
z#FMzZcmn$>E(y@s^(;I>@DL}_^>sKb?tufmxnF^#^8F3J-B-BudA0DSI{EA6{(%GG
zV)!L>rsIp?XKnr%d@=HFJjnjQ_W`5MZ0JVVbKTqwQ~4%r!&KgAU8!lvW4{*&!GDYG
zzxx)19XHg%viicc{pt(X%T?b+_0kQq4mx=46S4!pbw$oQj>q|)??AH{yK7LL?05#7
zYT>F`txk752g+jKU*V);FxHD3z5}}N2*j51WBk%lFhUfz?Q5@ewE^jgR{^gAUIn}g
zcopy};8nn@fL8&p0$v6FM-}kF<KSRU8`k$KO*tiQWK!`&Qdi6jLRF>s6#UkOmIH>7
zF{(r=6;*QCq!P`HrA?(nX$rNEE8WLTJ-41MnTd{qv1de&j_y@HgFlBk`V+ow<@@lr
zUi=DR?st}j-z_N1mNg3)0z8d8U=gtP_m<TUh=RYRqWB)UU--srecP%l{j>0Ri#+Zb
zn19Ez?m&m$O8Wu55t^7M1Ud46B2arUP=9yL%}4!tv488HkMC;Sjx@&O?_g8lX9pq)
zg1iZ@Jn&g0NJ4<8;I#n!Fh1)a4Ah<~>kZUDUfvZ@CM$Xaon?os0!mk)zAI4M9q@NK
ze|@4(Jn$;uRluu&R{^gAUIn}gcopy};8o!Nssg+}j`zdiqXY^*i=b3XC~eT&MEaw&
ze{Kuuyzg!s>3+hkguMR_pOH}5{>K|u2K95a*^T$G)l!4^t}T$A_n<9c5C}ij)Js)t
zCS;&6F-7~>@aIkxe7-}$=PMN63-?i}ih3fL|11rJ$6X{p-YduXZzrDp{%pzfgSy%j
z{FZ2+ub=vZU*v3^>kf~cw!TQ`|32w_9@PDR4S0XIyTcGYgmx146Fy9sCCn4f61w~O
zM>_AfJk-;(SGjX=2p;dpl<+Rtp&Dv#8?)7x<1MYhP-`%}gA76hb0ay^Fts5Oge|hV
zkrlyo#?*tu>9JtefPJ!tdCWl#jU`}1bs|PoS9f2tsSOL;eMHNRh+ynkI(IB3(KMvx
zOS+LuWYVjY2-plgsbK)MvPlz1k$@vKgTonW<n*Wrn)*1Xus1gW-kF$YY9gqQMB;{)
z(jy}==#4emM?^CW4Tg>p+=f(IDgm9z@S$8z1f!W$N>Ae#1OK7m^@DSduM7A8Mey~?
zX?UKpj>`aEBOJfp{(tu8>w<OsuEZJd{yzZx{=_M`MZhV0Kyk*Oq6>vlaXLfW9e)V8
z_uv}i@$)*w$oY94a*zK$h{qaoJg-xX?Nl%vBQ`mn*NX>%!y$7#uX~KV&Vh?9?)q!M
z;u^y7ylyb^`pe^IKgKk~<C?@euS<;mGOb->=lF>hUxo(Ol;e4wWGs>+=Xam~S1G=m
z{CM4EJne|bnz`ej01n5+{g*b9(|W|8N7T*Vz5n<fnloOZIL15&67n*~_@pa-jtq?K
z$K9~bIOmE#M+U|$2NH4|;~7`H@*(NS_#nSAu6R<#Syw!Nt~2uIJ^Q<_pYKt8DUCv&
zgcGDPxZ{5S2CP5F=N0M6*ecc1(H;LYusGxA73s=Ylxpe7^M{GopaC0+N_*}3K8F8q
z$L-HL;~NlL<A~?`oLSiAgTj7NThW<+8w@yi`SYDWkNN+w`2VeN{XpS3e*eD-9G*YN
z^L@_LPV|QV50g+jay;XEU~$HaPI3)N<UGWd^0WRIXkcHQzqV6aYAK%cNLJ?JHX{BD
hAm%6cl8NJ~j3pj@QJl!Rr9~b)sKY9DB%@0ee*=w<BPsv@
literal 0
HcmV?d00001
--
2.43.0
@@ -0,0 +1,76 @@
From 8f93e72bd6b1c770269ce746d401cebb063e71dc Mon Sep 17 00:00:00 2001
From: b0hemia <50309975+b0hemia@users.noreply.github.com>
Date: Mon, 24 Aug 2026 22:55:29 +1000
Subject: [PATCH 2/8] =?UTF-8?q?ddr:=20add=20LPDDR3=20support=20(mr11=20fie?=
=?UTF-8?q?ld,=20LPDDR3=20MR=20sequence,=20W63AH6NKB=20type-force)=20?=
=?UTF-8?q?=E2=80=94=20ported=20from=20tobunto=20X2000?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
arch/mips/cpu/xburst2/ddr_innophy.c | 20 ++++++++++++++++++++
arch/mips/cpu/xburst2/ddr_reg_data.h | 2 ++
2 files changed, 22 insertions(+)
diff --git a/arch/mips/cpu/xburst2/ddr_innophy.c b/arch/mips/cpu/xburst2/ddr_innophy.c
index 5257e3a..e08a5df 100755
--- a/arch/mips/cpu/xburst2/ddr_innophy.c
+++ b/arch/mips/cpu/xburst2/ddr_innophy.c
@@ -454,6 +454,21 @@ static void ddrc_dfi_init(enum ddr_type type, int bypass)
ddr_writel(DDRC_LMR_MR(2), DDRC_LMR); //MR2
ddr_writel(DDRC_LMR_MR(3), DDRC_LMR); //MR3
ddr_writel(DDRC_DLMR_VALUE | DDRC_LMR_START | DDRC_LMR_CMD_ZQCL_CS0, DDRC_LMR); //ZQCL
+#undef DDRC_LMR_MR
+ break;
+ case LPDDR3:
+ /* LPDDR3 MR sequence - ported from tobunto/u-boot X2000 (matches the vendor
+ * disc_spl.bin trace): MR63,10,1,2,3,11 each followed by mdelay(1); no ZQ. */
+#define DDRC_LMR_MR(n) \
+ DDRC_DLMR_VALUE | DDRC_LMR_START | DDRC_LMR_CMD_LMR | \
+ ((DDR_MR##n##_VALUE & 0xff) << 24) | \
+ (((DDR_MR##n##_VALUE >> 8) & 0xff) << (16))
+ ddr_writel(DDRC_LMR_MR(63), DDRC_LMR); mdelay(1); //set MRS reset
+ ddr_writel(DDRC_LMR_MR(10), DDRC_LMR); mdelay(1); //set IO calibration
+ ddr_writel(DDRC_LMR_MR(1), DDRC_LMR); mdelay(1); //set MR1
+ ddr_writel(DDRC_LMR_MR(2), DDRC_LMR); mdelay(1); //set MR2
+ ddr_writel(DDRC_LMR_MR(3), DDRC_LMR); mdelay(1); //set MR3
+ ddr_writel(DDRC_LMR_MR(11), DDRC_LMR); mdelay(1); //set MR11
#undef DDRC_LMR_MR
break;
default:
@@ -556,6 +571,11 @@ void sdram_init(void)
soc_ddr_init();
type = get_ddr_type();
+ /* Snowsky Disc = Winbond W63AH6NKB-BI LPDDR3. The controller CFG.TYPE field reads as
+ * LPDDR2(5) for this part (the vendor SPL dispatches on the logical record type=3, not
+ * CFG.TYPE), so force the LPDDR3 MR sequence here. Only ddrc_dfi_init consumes `type`
+ * (the X2000 prev_ddr_init hook is a no-op). */
+ type = LPDDR3;
clk_set_rate(DDR, gd->arch.gi->ddrfreq);
if(ddr_hook && ddr_hook->prev_ddr_init)
ddr_hook->prev_ddr_init(type);
diff --git a/arch/mips/cpu/xburst2/ddr_reg_data.h b/arch/mips/cpu/xburst2/ddr_reg_data.h
index 4878267..b48575f 100755
--- a/arch/mips/cpu/xburst2/ddr_reg_data.h
+++ b/arch/mips/cpu/xburst2/ddr_reg_data.h
@@ -27,6 +27,7 @@ struct ddr_registers
uint32_t ddr_mr2;
uint32_t ddr_mr3;
uint32_t ddr_mr10;
+ uint32_t ddr_mr11;
uint32_t ddr_mr63;
uint32_t ddr_chip0_size;
uint32_t ddr_chip1_size;
@@ -59,6 +60,7 @@ extern struct ddr_registers *g_ddr_param;
#define DDR_MR2_VALUE g_ddr_param->ddr_mr2
#define DDR_MR3_VALUE g_ddr_param->ddr_mr3
#define DDR_MR10_VALUE g_ddr_param->ddr_mr10
+#define DDR_MR11_VALUE g_ddr_param->ddr_mr11
#define DDR_MR63_VALUE g_ddr_param->ddr_mr63
#define DDR_CHIP_0_SIZE g_ddr_param->ddr_chip0_size
#define DDR_CHIP_1_SIZE g_ddr_param->ddr_chip1_size
--
2.43.0
@@ -0,0 +1,149 @@
From 15c4698350fdd6ef22a15ce8daaab4b332f9762a Mon Sep 17 00:00:00 2001
From: b0hemia <50309975+b0hemia@users.noreply.github.com>
Date: Mon, 24 Aug 2026 23:17:06 +1000
Subject: [PATCH 3/8] ddr: instrument DDR bring-up with TCSM breadcrumbs +
bounded polls (diagnostic build)
---
arch/mips/cpu/xburst2/ddr_innophy.c | 45 +++++++++++++++++++++++------
1 file changed, 36 insertions(+), 9 deletions(-)
diff --git a/arch/mips/cpu/xburst2/ddr_innophy.c b/arch/mips/cpu/xburst2/ddr_innophy.c
index e08a5df..91cf6a3 100755
--- a/arch/mips/cpu/xburst2/ddr_innophy.c
+++ b/arch/mips/cpu/xburst2/ddr_innophy.c
@@ -46,6 +46,23 @@
#include <asm/io.h>
#include <asm/arch/clk.h>
#define CONFIG_DWC_DEBUG 0
+
+/* ---- DDR bring-up diagnostics (breadcrumbs to TCSM pad @0xb24017c0, below the
+ * start.S return-shim scratch at 0x17f0). usbboot uploads this region after the SPL
+ * returns to the mask ROM, so ONE device cycle localises any DDR-init stall.
+ * Layout: [0]=magic [1]=last stage reached [2]=first fail id [3]=status@fail [4]=fail count */
+#define DDR_DIAG ((volatile unsigned int *)0xb24017c0)
+#define DDR_DIAG_MAGIC 0xD1A6C0DEu
+static inline void ddr_diag_init(void){ DDR_DIAG[0]=DDR_DIAG_MAGIC; DDR_DIAG[1]=0; DDR_DIAG[2]=0; DDR_DIAG[3]=0; DDR_DIAG[4]=0; __asm__ __volatile__("sync"); }
+static inline void ddr_diag_stage(unsigned int s){ DDR_DIAG[1]=s; __asm__ __volatile__("sync"); }
+static inline void ddr_diag_fail(unsigned int id, unsigned int status){ if(DDR_DIAG[2]==0){ DDR_DIAG[2]=id; DDR_DIAG[3]=status; } DDR_DIAG[4]++; __asm__ __volatile__("sync"); }
+/* bounded busy-wait: wait for (cond); on ~1e6-iter timeout record (id,statusexpr) and stop waiting
+ * (never spins forever, so the SPL always returns and the breadcrumb is readable). */
+#define DDR_BOUND(cond, id, statusexpr) do { \
+ unsigned int _bt = 1000000u; \
+ while(!(cond)) { if(!--_bt){ ddr_diag_fail((id), (unsigned int)(statusexpr)); break; } } \
+ } while(0)
+
#define ddr_hang() do{ \
printf("%s %d\n",__FUNCTION__,__LINE__); \
hang(); \
@@ -250,7 +267,7 @@ static void ddrp_pll_init(void)
ddr_writel(0, DDRP_INNOPHY_PLL_CTRL);
#endif
- while(!(ddr_readl(DDRP_INNOPHY_PLL_LOCK) & 1 << 3));
+ DDR_BOUND(ddr_readl(DDRP_INNOPHY_PLL_LOCK) & (1 << 3), 10 /*PLL_LOCK*/, ddr_readl(DDRP_INNOPHY_PLL_LOCK));
}
static void ddrp_register_cfg(void)
@@ -311,14 +328,15 @@ static void ddrp_hardware_calibration(void)
unsigned int timeout = 1000000;
/* ddr_writel(ddr_readl(DDRP_INNOPHY_TRAINING_CTRL) | 1, DDRP_INNOPHY_TRAINING_CTRL); */
ddr_writel(1, DDRP_INNOPHY_TRAINING_CTRL);
+ /* bounded (fixes the original timeout-- unsigned wrap); diagnostic, no hang() */
do
{
val = ddr_readl(DDRP_INNOPHY_CALIB_DONE);
- } while (((val & 0xf) != 0x3) && timeout--);
+ } while (((val & 0xf) != 0x3) && --timeout);
- if(!timeout) {
- printf("timeout:INNOPHY_CALIB_DONE %x\n", ddr_readl(DDRP_INNOPHY_CALIB_DONE));
- hang();
+ if(((val & 0xf) != 0x3)) {
+ ddr_diag_fail(30 /*CALIB_DONE*/, val);
+ ddr_writel(0, DDRP_INNOPHY_TRAINING_CTRL); /* clear training on failure (Codex) */
}
ddr_writel(0, DDRP_INNOPHY_TRAINING_CTRL);
@@ -424,7 +442,7 @@ static void ddrc_dfi_init(enum ddr_type type, int bypass)
FUNC_ENTER();
ddr_writel(DDRC_DWCFG_DFI_INIT_START, DDRC_DWCFG); // dfi_init_start high
ddr_writel(0, DDRC_DWCFG); // set buswidth 16bit
- while(!(ddr_readl(DDRC_DWSTATUS) & DDRC_DWSTATUS_DFI_INIT_COMP)); //polling dfi_init_complete
+ DDR_BOUND(ddr_readl(DDRC_DWSTATUS) & DDRC_DWSTATUS_DFI_INIT_COMP, 20 /*DFI_INIT_COMP*/, ddr_readl(DDRC_DWSTATUS)); //polling dfi_init_complete
ddr_writel(0, DDRC_CTRL); //set dfi_reset_n high
ddr_writel(DDRC_CFG_VALUE, DDRC_CFG);
@@ -496,8 +514,7 @@ static void ddr_calibration(struct ddr_calib_value *dcv, int div)
val = REG32(CPM_DDRCDR);
val |= ((1 << 29) | (1 << 25));
REG32(CPM_DDRCDR) = val;
- while((REG32(CPM_DDRCDR) & (1 << 24)))
- ;
+ DDR_BOUND(!(REG32(CPM_DDRCDR) & (1 << 24)), 40 /*DDRCDR_BUSY*/, REG32(CPM_DDRCDR));
/* // Set clock divider */
val = REG32(CPM_DDRCDR);
val &= ~(0xf);
@@ -505,7 +522,7 @@ static void ddr_calibration(struct ddr_calib_value *dcv, int div)
REG32(CPM_DDRCDR) = val;
// Polling PHY_FREQ_DONE
- while(((ddr_readl(DDRC_DWSTATUS) & (1 << 3 | 1 << 1)) & 0xf) != 0xa);
+ DDR_BOUND(((ddr_readl(DDRC_DWSTATUS) & (1 << 3 | 1 << 1)) & 0xf) == 0xa, 50 /*PHY_FREQ_DONE*/, ddr_readl(DDRC_DWSTATUS));
ddrp_hardware_calibration();
/* ddrp_software_calibration(); */
@@ -567,6 +584,8 @@ void sdram_init(void)
int bypass = 0;
debug("sdram init start\n");
+ ddr_diag_init(); /* breadcrumb record @0xb24017c0 */
+ ddr_diag_stage(1); /* entered sdram_init */
soc_ddr_init();
@@ -582,26 +601,33 @@ void sdram_init(void)
rate = clk_get_rate(DDR);
debug("DDR clk rate %d\n", rate);
+ ddr_diag_stage(2);
ddrc_reset_phy();
+ ddr_diag_stage(3);
ddr_phy_init();
dump_ddrp_register();
+ ddr_diag_stage(4);
ddrc_dfi_init(type, bypass);
+ ddr_diag_stage(5);
ddrc_prev_init();
/**
* bypass = 1 or calibration = 0
*/
bypass = 0;
+ ddr_diag_stage(6);
ddrp_calibration(bypass);
+ ddr_diag_stage(7);
ddrc_post_init();
if(ddr_hook && ddr_hook->post_ddr_init)
ddr_hook->post_ddr_init(type);
+ ddr_diag_stage(8);
get_dynamic_calib_value(rate);
if(DDRC_AUTOSR_EN_VALUE) {
@@ -616,6 +642,7 @@ void sdram_init(void)
dump_ddrc_register();
/* DDRC address remap configure*/
+ ddr_diag_stage(9); /* 9 = sdram_init completed (success if no fail id recorded) */
debug("sdram init finished\n");
}
--
2.43.0
@@ -0,0 +1,144 @@
From eb05eaef0f7dac0fb1f166f88d69e1b895eb2e1a Mon Sep 17 00:00:00 2001
From: b0hemia <50309975+b0hemia@users.noreply.github.com>
Date: Mon, 24 Aug 2026 23:46:16 +1000
Subject: [PATCH 4/8] ddr: bound clk.c DDR-clock polls + constrain
dynamic-calib div; board_init_f breadcrumbs
---
arch/mips/cpu/xburst2/ddr_innophy.c | 1 +
arch/mips/cpu/xburst2/x2000/clk.c | 6 +++---
arch/mips/cpu/xburst2/x2000/soc.c | 22 +++++++++++++++++++++
arch/mips/include/asm/arch-x2000/ddr_diag.h | 17 ++++++++++++++++
4 files changed, 43 insertions(+), 3 deletions(-)
create mode 100644 arch/mips/include/asm/arch-x2000/ddr_diag.h
diff --git a/arch/mips/cpu/xburst2/ddr_innophy.c b/arch/mips/cpu/xburst2/ddr_innophy.c
index 91cf6a3..c9b19f8 100755
--- a/arch/mips/cpu/xburst2/ddr_innophy.c
+++ b/arch/mips/cpu/xburst2/ddr_innophy.c
@@ -563,6 +563,7 @@ static void get_dynamic_calib_value(unsigned int rate)
ddr_calibration(&dcv[cur_div], cur_div);
break;
}
+ if(div > 0xf) { ddr_diag_fail(60 /*calib div overflow - bad DDR rate*/, (unsigned int)rate); break; }
dcv[div].rate = drate;
dcv[div].refcnt = get_refcnt_value(div);
ddr_calibration(&dcv[div], div);
diff --git a/arch/mips/cpu/xburst2/x2000/clk.c b/arch/mips/cpu/xburst2/x2000/clk.c
index c700a03..30764b6 100755
--- a/arch/mips/cpu/xburst2/x2000/clk.c
+++ b/arch/mips/cpu/xburst2/x2000/clk.c
@@ -27,6 +27,7 @@
#include <asm/gpio.h>
#include <asm/arch/cpm.h>
#include <asm/arch/clk.h>
+#include <asm/arch/ddr_diag.h>
#include <generated/clk_reg_values.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -68,7 +69,7 @@ void clk_prepare(void)
/*set div max*/
regval |= cgusetting[i].val;
writel(regval, reg);
- while (readl(reg) & (1 << cgusetting[i].busy));
+ DDR_BOUND(!(readl(reg) & (1 << cgusetting[i].busy)), 4 /*cgu init busy*/, readl(reg));
} else {
regval &= ~(1 << cgusetting[i].ce);
writel(regval, reg);
@@ -244,8 +245,7 @@ void clk_set_rate(int clk_id, unsigned long rate)
regval &= ~(3 << cgu->stop | 0xff);
regval |= ((1 << cgu->ce) | cdr);
writel(regval, reg);
- while (readl(reg) & (1 << cgu->busy))
- ;
+ DDR_BOUND(!(readl(reg) & (1 << cgu->busy)), 5 /*DDR clk busy (clk_set_rate)*/, readl(reg));
#ifdef DUMP_CGU_SELECT
printf("%s(0x%x) :0x%x\n",clk_name[clk_id] ,reg, readl(reg));
#endif
diff --git a/arch/mips/cpu/xburst2/x2000/soc.c b/arch/mips/cpu/xburst2/x2000/soc.c
index 54ff3b7..6b96c5b 100755
--- a/arch/mips/cpu/xburst2/x2000/soc.c
+++ b/arch/mips/cpu/xburst2/x2000/soc.c
@@ -67,11 +67,23 @@ extern void ddr_test_refresh(unsigned int start_addr, unsigned int end_addr);
extern void flush_cache_all(void);
+/* DIAG: breadcrumb record @0xb24017c0 (shared with ddr_innophy.c). Set 1 to early-return
+ * before sdram_init to prove the mask-ROM return path works in isolation. */
+#define DIAG_SKIP_SDRAM 0
+#define BIF_CRUMB(s) do { *(volatile unsigned int*)0xb24017c4 = (s); __asm__ __volatile__("sync"); } while(0)
+
void board_init_f(ulong dummy)
{
/* Set global data pointer */
gd = &gdata;
+ /* DIAG: mark that we reached board_init_f (magic + stage 0xF0) */
+ *(volatile unsigned int*)0xb24017c0 = 0xD1A6C0DEu;
+ *(volatile unsigned int*)0xb24017c8 = 0;
+ *(volatile unsigned int*)0xb24017cc = 0;
+ *(volatile unsigned int*)0xb24017d0 = 0;
+ BIF_CRUMB(0xF0);
+
/* Setup global info */
#ifndef CONFIG_BURNER
gd->arch.gi = &ginfo;
@@ -80,6 +92,7 @@ void board_init_f(ulong dummy)
#endif
gpio_init();
+ BIF_CRUMB(0xF1);
*(volatile unsigned int *)0xb0000020 = 0; //clk gate enable.
*(volatile unsigned int *)0xb0000028 = 0; //clk gate enable.
@@ -93,15 +106,24 @@ void board_init_f(ulong dummy)
#endif
debug("Timer init\n");
timer_init();
+ BIF_CRUMB(0xF2);
debug("CLK stop\n");
// clk_prepare();
debug("PLL init\n");
pll_init();
+ BIF_CRUMB(0xF3);
debug("CLK init\n");
clk_init();
+ BIF_CRUMB(0xF4);
+
+#if DIAG_SKIP_SDRAM
+ /* DIAG: return before DDR to test the mask-ROM return path in isolation. */
+ BIF_CRUMB(0xFE);
+ return;
+#endif
debug("SDRAM init\n");
sdram_init();
diff --git a/arch/mips/include/asm/arch-x2000/ddr_diag.h b/arch/mips/include/asm/arch-x2000/ddr_diag.h
new file mode 100644
index 0000000..68654ec
--- /dev/null
+++ b/arch/mips/include/asm/arch-x2000/ddr_diag.h
@@ -0,0 +1,17 @@
+#ifndef _ASM_ARCH_DDR_DIAG_H
+#define _ASM_ARCH_DDR_DIAG_H
+/* Shared DDR bring-up breadcrumb record @0xb24017c0 (TCSM pad below the start.S return
+ * shim scratch at 0x17f0). usbboot uploads it after the SPL returns to the mask ROM.
+ * Layout: [0]=magic [1]=stage [2]=first fail id [3]=status@fail [4]=fail count */
+#define DDR_DIAG ((volatile unsigned int *)0xb24017c0)
+#define DDR_DIAG_MAGIC 0xD1A6C0DEu
+#define ddr_diag_fail_raw(id, status) do { \
+ if(DDR_DIAG[2]==0){ DDR_DIAG[2]=(unsigned int)(id); DDR_DIAG[3]=(unsigned int)(status); } \
+ DDR_DIAG[4]++; __asm__ __volatile__("sync"); \
+ } while(0)
+/* bounded busy-wait: wait for (cond); on ~1e6-iter timeout record (id,statusexpr) and stop. */
+#define DDR_BOUND(cond, id, statusexpr) do { \
+ unsigned int _bt = 1000000u; \
+ while(!(cond)) { if(!--_bt){ ddr_diag_fail_raw((id), (statusexpr)); break; } } \
+ } while(0)
+#endif
--
2.43.0
@@ -0,0 +1,35 @@
From f51bc90392b4c0dc42456d876a37dc05926db445 Mon Sep 17 00:00:00 2001
From: b0hemia <50309975+b0hemia@users.noreply.github.com>
Date: Mon, 24 Aug 2026 23:48:18 +1000
Subject: [PATCH 5/8] ddr: disable verbose DEBUG/DWC_DEBUG printf dumps (slim
SPL, no UART-wait in DDR path)
---
arch/mips/cpu/xburst2/ddr_innophy.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/mips/cpu/xburst2/ddr_innophy.c b/arch/mips/cpu/xburst2/ddr_innophy.c
index c9b19f8..b394dad 100755
--- a/arch/mips/cpu/xburst2/ddr_innophy.c
+++ b/arch/mips/cpu/xburst2/ddr_innophy.c
@@ -21,7 +21,7 @@
* MA 02111-1307 USA
*/
-#define DEBUG
+/* #define DEBUG */ /* off: shrink SPL + remove printf UART-waits from DDR path (breadcrumbs replace it) */
/* #define DEBUG_READ_WRITE */
#include <config.h>
#include <common.h>
@@ -45,7 +45,7 @@
#endif
#include <asm/io.h>
#include <asm/arch/clk.h>
-#define CONFIG_DWC_DEBUG 0
+/* #define CONFIG_DWC_DEBUG 0 */ /* off: use the empty dump_ddr*_register() no-ops (shrinks SPL) */
/* ---- DDR bring-up diagnostics (breadcrumbs to TCSM pad @0xb24017c0, below the
* start.S return-shim scratch at 0x17f0). usbboot uploads this region after the SPL
--
2.43.0
@@ -0,0 +1,49 @@
From 26f15d13ea9b7e92eaf2326909b202799797f440 Mon Sep 17 00:00:00 2001
From: b0hemia <50309975+b0hemia@users.noreply.github.com>
Date: Mon, 24 Aug 2026 23:52:48 +1000
Subject: [PATCH 6/8] ddr: fix PHY PLL divisors 20/5 -> 8/4 for W63AH6NKB
LPDDR3 400MHz (calibration root cause)
---
arch/mips/cpu/xburst2/ddr_innophy.c | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/arch/mips/cpu/xburst2/ddr_innophy.c b/arch/mips/cpu/xburst2/ddr_innophy.c
index b394dad..4c7b921 100755
--- a/arch/mips/cpu/xburst2/ddr_innophy.c
+++ b/arch/mips/cpu/xburst2/ddr_innophy.c
@@ -247,25 +247,22 @@ static void ddrp_pll_init(void)
{
unsigned int val;
+ /* PHY PLL divisors for W63AH6NKB LPDDR3 @400MHz: FBDIV=8, PDIV=4 (from tobunto X2000,
+ * matches the vendor SPL). The tree's stock 20/5 sets the wrong PHY clock -> training
+ * (CALIB_DONE) never converges. */
val = ddr_readl(DDRP_INNOPHY_PLL_FBDIV);
val &= ~(0xff);
- val |= 0x14;
+ val |= 0x8;
ddr_writel(val, DDRP_INNOPHY_PLL_FBDIV);
val = ddr_readl(DDRP_INNOPHY_PLL_PDIV);
val &= ~(0xff);
- val |= 0x5;
+ val |= 4;
ddr_writel(val, DDRP_INNOPHY_PLL_PDIV);
- /* ddr_writel(0x14, DDRP_INNOPHY_PLL_FBDIV); */
- /* ddr_writel(0x5, DDRP_INNOPHY_PLL_PDIV); */
-#ifdef DEBUG_READ_WRITE
+ /* toggle PLLPDEN via RMW (preserve other PLL_CTRL bits), as tobunto/vendor do */
ddr_writel(ddr_readl(DDRP_INNOPHY_PLL_CTRL) | DDRP_PLL_CTRL_PLLPDEN, DDRP_INNOPHY_PLL_CTRL);
ddr_writel(ddr_readl(DDRP_INNOPHY_PLL_CTRL) & ~DDRP_PLL_CTRL_PLLPDEN, DDRP_INNOPHY_PLL_CTRL);
-#else
- ddr_writel(DDRP_PLL_CTRL_PLLPDEN, DDRP_INNOPHY_PLL_CTRL);
- ddr_writel(0, DDRP_INNOPHY_PLL_CTRL);
-#endif
DDR_BOUND(ddr_readl(DDRP_INNOPHY_PLL_LOCK) & (1 << 3), 10 /*PLL_LOCK*/, ddr_readl(DDRP_INNOPHY_PLL_LOCK));
}
--
2.43.0
@@ -0,0 +1,74 @@
From e951f0e6e47ab7b0ffe4df2de1120685a11cd63a Mon Sep 17 00:00:00 2001
From: b0hemia <50309975+b0hemia@users.noreply.github.com>
Date: Tue, 25 Aug 2026 00:02:46 +1000
Subject: [PATCH 7/8] spl: strip board_init_f test scaffolding (keep bounded
polls + sdram diag as hardening)
---
arch/mips/cpu/xburst2/x2000/soc.c | 21 ---------------------
1 file changed, 21 deletions(-)
diff --git a/arch/mips/cpu/xburst2/x2000/soc.c b/arch/mips/cpu/xburst2/x2000/soc.c
index 6b96c5b..ab9e437 100755
--- a/arch/mips/cpu/xburst2/x2000/soc.c
+++ b/arch/mips/cpu/xburst2/x2000/soc.c
@@ -67,23 +67,11 @@ extern void ddr_test_refresh(unsigned int start_addr, unsigned int end_addr);
extern void flush_cache_all(void);
-/* DIAG: breadcrumb record @0xb24017c0 (shared with ddr_innophy.c). Set 1 to early-return
- * before sdram_init to prove the mask-ROM return path works in isolation. */
-#define DIAG_SKIP_SDRAM 0
-#define BIF_CRUMB(s) do { *(volatile unsigned int*)0xb24017c4 = (s); __asm__ __volatile__("sync"); } while(0)
-
void board_init_f(ulong dummy)
{
/* Set global data pointer */
gd = &gdata;
- /* DIAG: mark that we reached board_init_f (magic + stage 0xF0) */
- *(volatile unsigned int*)0xb24017c0 = 0xD1A6C0DEu;
- *(volatile unsigned int*)0xb24017c8 = 0;
- *(volatile unsigned int*)0xb24017cc = 0;
- *(volatile unsigned int*)0xb24017d0 = 0;
- BIF_CRUMB(0xF0);
-
/* Setup global info */
#ifndef CONFIG_BURNER
gd->arch.gi = &ginfo;
@@ -92,7 +80,6 @@ void board_init_f(ulong dummy)
#endif
gpio_init();
- BIF_CRUMB(0xF1);
*(volatile unsigned int *)0xb0000020 = 0; //clk gate enable.
*(volatile unsigned int *)0xb0000028 = 0; //clk gate enable.
@@ -106,24 +93,16 @@ void board_init_f(ulong dummy)
#endif
debug("Timer init\n");
timer_init();
- BIF_CRUMB(0xF2);
debug("CLK stop\n");
// clk_prepare();
debug("PLL init\n");
pll_init();
- BIF_CRUMB(0xF3);
debug("CLK init\n");
clk_init();
- BIF_CRUMB(0xF4);
-#if DIAG_SKIP_SDRAM
- /* DIAG: return before DDR to test the mask-ROM return path in isolation. */
- BIF_CRUMB(0xFE);
- return;
-#endif
debug("SDRAM init\n");
sdram_init();
--
2.43.0
@@ -0,0 +1,52 @@
From 7caf048bae01c31460d4d675a52823e5b62ff091 Mon Sep 17 00:00:00 2001
From: b0hemia <50309975+b0hemia@users.noreply.github.com>
Date: Tue, 25 Aug 2026 01:52:48 +1000
Subject: [PATCH 8/8] spl: robust WDT disable (clear TCER.TCEN + TSSR) + drop
dynamic-calib sweep (flash-failure root cause)
---
arch/mips/cpu/xburst2/ddr_innophy.c | 8 +++++++-
arch/mips/cpu/xburst2/x2000/soc.c | 8 +++++++-
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/arch/mips/cpu/xburst2/ddr_innophy.c b/arch/mips/cpu/xburst2/ddr_innophy.c
index 4c7b921..b78c02b 100755
--- a/arch/mips/cpu/xburst2/ddr_innophy.c
+++ b/arch/mips/cpu/xburst2/ddr_innophy.c
@@ -626,7 +626,13 @@ void sdram_init(void)
ddr_hook->post_ddr_init(type);
ddr_diag_stage(8);
- get_dynamic_calib_value(rate);
+ /* Dropped get_dynamic_calib_value(rate): the vendor SPL does NOT do this multi-rate
+ * DVFS calibration sweep. Its result table is never consumed by the burner/writer path,
+ * and the extra frequency transitions + retraining can leave a marginal final PHY
+ * calibration that fails under the writer's sustained full-DRAM load (2026-08-25). The
+ * single ddrp_calibration() above already calibrates at the final 400MHz. */
+ /* get_dynamic_calib_value(rate); */
+ (void)rate;
if(DDRC_AUTOSR_EN_VALUE) {
/* ddr_writel(DDRC_AUTOSR_CNT_VALUE, DDRC_AUTOSR_CNT); */
diff --git a/arch/mips/cpu/xburst2/x2000/soc.c b/arch/mips/cpu/xburst2/x2000/soc.c
index ab9e437..6ea4b83 100755
--- a/arch/mips/cpu/xburst2/x2000/soc.c
+++ b/arch/mips/cpu/xburst2/x2000/soc.c
@@ -83,7 +83,13 @@ void board_init_f(ulong dummy)
*(volatile unsigned int *)0xb0000020 = 0; //clk gate enable.
*(volatile unsigned int *)0xb0000028 = 0; //clk gate enable.
- *(volatile unsigned int *)0xb000202c |= 1 << 16; // wdt disable.
+ /* Disable the watchdog ROBUSTLY (match the vendor SPL + the tree's hw_watchdog_disable):
+ * clear WDT_TCER.TCEN FIRST, then stop the WDT clock via TCU_TSSR (write-1, no RMW).
+ * The old code stopped only the clock, leaving the counter armed - if the mask ROM
+ * re-clocks it after we return, it fires and resets the device mid-flash (root cause of
+ * the 2026-08-25 flash failure: device reset ~36min into the writer and again idle). */
+ *(volatile unsigned int *)0xb0002004 &= ~(1u << 0); // WDT_TCER.TCEN = 0 (disable counter)
+ *(volatile unsigned int *)0xb000202c = (1u << 16); // TCU_TSSR: stop WDT clock
/* Init uart first */
enable_uart_clk();
--
2.43.0