The Nodebox v3 CPU module runs on several carriers. They share one
kernel Image but need different DTBs and different DPAA2 DPC/DPL, so
the kernel FIT carries every carrier's set as fdt-<c> / dpc-<c> /
dpl-<c> with a conf-<c> configuration each, and U-Boot picks one:

  mcinitcmd   imxtract ... dpc-${carrier} / dpl-${carrier}
  mc_init     bootm ${kernel_addr_r}#conf-${carrier}

Signed-off-by: Vincent Jardin <[email protected]>
---

 board/nxp/lx2160a/nbxv3/Makefile    |  1 +
 board/nxp/lx2160a/nbxv3/carrier.c   | 87 +++++++++++++++++++++++++++++
 board/nxp/lx2160a/nbxv3/carrier.h   | 17 ++++++
 board/nxp/lx2160a/nbxv3/eth_nbxv3.c | 10 ++++
 4 files changed, 115 insertions(+)
 create mode 100644 board/nxp/lx2160a/nbxv3/carrier.c
 create mode 100644 board/nxp/lx2160a/nbxv3/carrier.h

diff --git a/board/nxp/lx2160a/nbxv3/Makefile b/board/nxp/lx2160a/nbxv3/Makefile
index 68fab9bf18f..4ce2508249d 100644
--- a/board/nxp/lx2160a/nbxv3/Makefile
+++ b/board/nxp/lx2160a/nbxv3/Makefile
@@ -3,6 +3,7 @@
 #
 
 obj-y += eth_nbxv3.o
+obj-y += carrier.o
 obj-y += mps_pmbus.o
 obj-y += psu_pmbus.o
 obj-y += vid_handoff.o
diff --git a/board/nxp/lx2160a/nbxv3/carrier.c 
b/board/nxp/lx2160a/nbxv3/carrier.c
new file mode 100644
index 00000000000..231aaf1e3f1
--- /dev/null
+++ b/board/nxp/lx2160a/nbxv3/carrier.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2026 Free Mobile - Vincent Jardin
+ *
+ * Nodebox v3 carrier selector.
+ *
+ * The CPU module is used on several carrier boards (nbv30, nbv32, ...).
+ * They share one kernel Image but need different DTBs and different
+ * DPAA2 DPC/DPL, it is up to the build framework (buildroot, etc.) to
+ * puts all of them into the single kernel FIT.
+ *
+ *   images:         fdt-<carrier>, dpc-<carrier>, dpl-<carrier>
+ *   configurations: conf-<carrier>
+ *
+ * and then the boot env picks one with ${carrier}:
+ *
+ *   mcinitcmd  imxtract ... dpc-${carrier} / dpl-${carrier}
+ *   mc_init    bootm ${kernel_addr_r}#conf-${carrier}
+ *
+ * Change it from the prompt and make it persistent:
+ *
+ *   setenv carrier nbv32 && saveenv && reset
+ *
+ * WHY THIS FILE EXISTS RATHER THAN JUST A DEFAULT IN CFG_EXTRA_ENV_SETTINGS:
+ * A default there only applies when the saved
+ * environment is absent or fails its CRC: env_import() calls himport_r()
+ * WITHOUT H_NOCLEAR, so a valid saved env in NOR replaces the built-in
+ * defaults wholesale rather than merging with them. Every board already
+ * running with a saved env would therefore come up with ${carrier}
+ * EMPTY on the first boot after this feature lands, `bootm
+ * ${kernel_addr_r}#conf-` would fail with
+ * "Could not find configuration node".
+ * Setting
+ * it from C on every boot when it is missing makes any upgrade
+ * invisible, and still lets an explicitly saved value.
+ *
+ * Called from reset_phy() in eth_nbxv3.c ; it is during initr_net,
+ * after initr_env has loaded the saved environment and BEFORE
+ * mc_env_boot() evaluates ${mcinitcmd}, which is the earliest consumer.
+ * (board_late_init() would also be early enough, but it this logic is
+ * specific for the nbx boards, we do not want to touch.
+ * shared arch/arm/cpu/armv8/fsl-layerscape/soc.c
+ *
+ */
+
+#include <env.h>
+#include <linux/kernel.h>
+#include <vsprintf.h>
+
+#include "carrier.h"
+
+#ifndef NBXV3_CARRIER_DEFAULT
+#define NBXV3_CARRIER_DEFAULT "nbv30"
+#endif
+
+/*
+ * Placeholder for strap/EEPROM based identification.
+ * Returning NULL means "cannot tell".
+ */
+static const char *nbxv3_carrier_detect(void)
+{
+       /* TODO, not available yet */
+       return NULL;
+}
+
+void nbxv3_carrier_env_init(void)
+{
+       const char *carrier = env_get("carrier");
+       const char *detected;
+
+       if (carrier && *carrier) {
+               printf("Carrier:       %s (from env)\n", carrier);
+               return;
+       }
+
+       detected = nbxv3_carrier_detect();
+       if (!detected)
+               detected = NBXV3_CARRIER_DEFAULT;
+
+       if (env_set("carrier", detected)) {
+               printf("Carrier:       WARNING: cannot set ${carrier}\n");
+               return;
+       }
+
+       /* Not saved on purpose: saveenv on every boot would burn our NOR */
+       printf("Carrier:       %s (default, not saved - `setenv carrier <name> 
&& saveenv` to pin)\n", detected);
+}
diff --git a/board/nxp/lx2160a/nbxv3/carrier.h 
b/board/nxp/lx2160a/nbxv3/carrier.h
new file mode 100644
index 00000000000..e7eb42265e4
--- /dev/null
+++ b/board/nxp/lx2160a/nbxv3/carrier.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2026 Free Mobile - Vincent Jardin
+ */
+
+#ifndef __NBXV3_CARRIER_H
+#define __NBXV3_CARRIER_H
+
+/*
+ * Ensure ${carrier} is set before anything consumes it.
+ * It is idempotent, and it never overrides a value already present
+ * in the uboot's environment.
+ * It shall run after initr_env and before ${mcinitcmd} is evaluated.
+ */
+void nbxv3_carrier_env_init(void);
+
+#endif /* __NBXV3_CARRIER_H */
diff --git a/board/nxp/lx2160a/nbxv3/eth_nbxv3.c 
b/board/nxp/lx2160a/nbxv3/eth_nbxv3.c
index eb91d47add5..58bf8246aa0 100644
--- a/board/nxp/lx2160a/nbxv3/eth_nbxv3.c
+++ b/board/nxp/lx2160a/nbxv3/eth_nbxv3.c
@@ -21,6 +21,8 @@
 #include <exports.h>
 #include <fsl-mc/fsl_mc.h>
 
+#include "carrier.h"
+
 DECLARE_GLOBAL_DATA_PTR;
 
 int board_eth_init(struct bd_info *bis)
@@ -31,6 +33,14 @@ int board_eth_init(struct bd_info *bis)
 #if defined(CONFIG_RESET_PHY_R)
 void reset_phy(void)
 {
+       /*
+        * MUST come first: mc_env_boot() evaluates ${mcinitcmd}, which
+        * imxtracts dpc-${carrier} / dpl-${carrier} out of the FIT. If
+        * ${carrier} is still unset a subimage named "dpc-" and the MC
+        * firmware never starts.
+        */
+       nbxv3_carrier_env_init();
+
 #if defined(CONFIG_FSL_MC_ENET)
        mc_env_boot();
 #endif
-- 
2.43.0

Reply via email to