* [PATCH v1 0/5] Add support for Freebox Nodebox 10G (Armada 8040)
@ 2026-03-10 14:12 Vincent Jardin
2026-03-10 14:12 ` [PATCH v1 1/5] arm: mach-mvebu: armada8k: add CPU and clock info display Vincent Jardin
` (4 more replies)
0 siblings, 5 replies; 11+ messages in thread
From: Vincent Jardin @ 2026-03-10 14:12 UTC (permalink / raw)
To: u-boot; +Cc: Stefan Roese, Tom Rini, Peng Fan, Jaehoon Chung, vjardin
This series adds board support for the Freebox Nodebox 10G, a network
appliance based on the Marvell Armada 8040 SoC (AP806 + dual CP110).
The series is structured as follows:
Patch 1 adds CPU and clock information display for Armada 8K platforms
by reading the AP806 Sample-At-Reset register. This is ported from
Marvell's vendor U-Boot 2015.01.
Patch 2 adds the board directory, device tree, defconfig, and board
initialization (management PHY reset via GPIO).
Patch 3 adds a U-Boot dtsi overlay for conditional OP-TEE support.
Patch 4 adds the "emmcboot" command to load and boot firmware stored
in the board-specific image format (mvebu_image_tag) at fixed eMMC
offsets. Each image bundles a kernel and device tree under a CRC32-
validated tag header. Two image banks are supported with automatic
fallback based on a bit-field reboot counter.
Patch 5 adds the "fbxserial" command to read factory-programmed device
serial numbers and MAC addresses from a dedicated eMMC region. The
board file hooks EVT_SETTINGS_R to set MAC addresses before network
drivers probe.
Note: the emmcboot and fbxserial commands are hardware-specific and
require the actual board for testing, so no sandbox tests are included.
Based on v2026.04-rc4.
Vincent Jardin (5):
arm: mach-mvebu: armada8k: add CPU and clock info display
board: freebox: add Nodebox 10G board support
arm: dts: armada-8040-nbx: add U-Boot dtsi for conditional OP-TEE
cmd: mvebu: add emmcboot for Nodebox image format
cmd: mvebu: add device serial and MAC address initialization
arch/arm/dts/Makefile | 1 +
arch/arm/dts/armada-8040-nbx-u-boot.dtsi | 15 +
arch/arm/dts/armada-8040-nbx.dts | 259 +++++++++++++++++
arch/arm/mach-mvebu/Kconfig | 9 +
arch/arm/mach-mvebu/armada8k/Makefile | 2 +-
arch/arm/mach-mvebu/armada8k/cpu.c | 10 +
arch/arm/mach-mvebu/armada8k/soc_info.c | 192 +++++++++++++
arch/arm/mach-mvebu/armada8k/soc_info.h | 14 +
board/freebox/nbx10g/Kconfig | 12 +
board/freebox/nbx10g/MAINTAINERS | 13 +
board/freebox/nbx10g/Makefile | 3 +
board/freebox/nbx10g/board.c | 79 ++++++
cmd/mvebu/Kconfig | 80 ++++++
cmd/mvebu/Makefile | 2 +
cmd/mvebu/mvebu_emmcboot.c | 340 +++++++++++++++++++++++
cmd/mvebu/nbx_fbxserial.c | 289 +++++++++++++++++++
configs/mvebu_nbx_88f8040_defconfig | 76 +++++
include/configs/nbx10g.h | 29 ++
include/mvebu_imagetag.h | 82 ++++++
include/mvebu_nrboot.h | 51 ++++
include/nbx_fbxserial.h | 134 +++++++++
21 files changed, 1691 insertions(+), 1 deletion(-)
create mode 100644 arch/arm/dts/armada-8040-nbx-u-boot.dtsi
create mode 100644 arch/arm/dts/armada-8040-nbx.dts
create mode 100644 arch/arm/mach-mvebu/armada8k/soc_info.c
create mode 100644 arch/arm/mach-mvebu/armada8k/soc_info.h
create mode 100644 board/freebox/nbx10g/Kconfig
create mode 100644 board/freebox/nbx10g/MAINTAINERS
create mode 100644 board/freebox/nbx10g/Makefile
create mode 100644 board/freebox/nbx10g/board.c
create mode 100644 cmd/mvebu/mvebu_emmcboot.c
create mode 100644 cmd/mvebu/nbx_fbxserial.c
create mode 100644 configs/mvebu_nbx_88f8040_defconfig
create mode 100644 include/configs/nbx10g.h
create mode 100644 include/mvebu_imagetag.h
create mode 100644 include/mvebu_nrboot.h
create mode 100644 include/nbx_fbxserial.h
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v1 1/5] arm: mach-mvebu: armada8k: add CPU and clock info display
2026-03-10 14:12 [PATCH v1 0/5] Add support for Freebox Nodebox 10G (Armada 8040) Vincent Jardin
@ 2026-03-10 14:12 ` Vincent Jardin
2026-03-31 12:18 ` Stefan Roese
2026-03-10 14:12 ` [PATCH v1 2/5] board: freebox: add Nodebox 10G board support Vincent Jardin
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Vincent Jardin @ 2026-03-10 14:12 UTC (permalink / raw)
To: u-boot; +Cc: Stefan Roese, Tom Rini, Peng Fan, Jaehoon Chung, vjardin
Add CPU information display for Armada 8040 platforms.
The soc_info.c reads the AP806 Sample-At-Reset (SAR) register to
determine the PLL clock configuration and converts it to actual
CPU, DDR, and Fabric frequencies using the PLL frequency table.
It also reports LLC (Last Level Cache) status.
Based on soc_info.c from Marvell's U-Boot 2015.01 vendor tree.
Signed-off-by: Vincent Jardin <vjardin@free.fr>
---
arch/arm/mach-mvebu/armada8k/Makefile | 2 +-
arch/arm/mach-mvebu/armada8k/cpu.c | 10 ++
arch/arm/mach-mvebu/armada8k/soc_info.c | 192 ++++++++++++++++++++++++
arch/arm/mach-mvebu/armada8k/soc_info.h | 14 ++
4 files changed, 217 insertions(+), 1 deletion(-)
create mode 100644 arch/arm/mach-mvebu/armada8k/soc_info.c
create mode 100644 arch/arm/mach-mvebu/armada8k/soc_info.h
diff --git a/arch/arm/mach-mvebu/armada8k/Makefile b/arch/arm/mach-mvebu/armada8k/Makefile
index 0a4756717a3..723239d9894 100644
--- a/arch/arm/mach-mvebu/armada8k/Makefile
+++ b/arch/arm/mach-mvebu/armada8k/Makefile
@@ -2,4 +2,4 @@
#
# Copyright (C) 2016 Stefan Roese <sr@denx.de>
-obj-y = cpu.o cache_llc.o dram.o
+obj-y = cpu.o cache_llc.o dram.o soc_info.o
diff --git a/arch/arm/mach-mvebu/armada8k/cpu.c b/arch/arm/mach-mvebu/armada8k/cpu.c
index 3eb93c82387..22767f143c2 100644
--- a/arch/arm/mach-mvebu/armada8k/cpu.c
+++ b/arch/arm/mach-mvebu/armada8k/cpu.c
@@ -15,6 +15,8 @@
#include <asm/armv8/mmu.h>
#include <mach/fw_info.h>
+#include "soc_info.h"
+
/* Armada 7k/8k */
#define MVEBU_RFU_BASE (MVEBU_REGISTER(0x6f0000))
#define RFU_GLOBAL_SW_RST (MVEBU_RFU_BASE + 0x84)
@@ -111,3 +113,11 @@ int mmc_get_env_dev(void)
return CONFIG_ENV_MMC_DEVICE_INDEX;
}
+
+int print_cpuinfo(void)
+{
+ soc_print_clock_info();
+ soc_print_soc_info();
+
+ return 0;
+}
diff --git a/arch/arm/mach-mvebu/armada8k/soc_info.c b/arch/arm/mach-mvebu/armada8k/soc_info.c
new file mode 100644
index 00000000000..a5c3b17cb7d
--- /dev/null
+++ b/arch/arm/mach-mvebu/armada8k/soc_info.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Marvell International Ltd.
+ *
+ * Marvell Armada 8K SoC info: SAR, Clock frequencies, LLC status
+ * Ported from Marvell U-Boot 2015.01 to mainline U-Boot.
+ */
+
+#include <config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <vsprintf.h>
+#include <asm/io.h>
+#include <asm/arch/soc.h>
+
+/* Clock frequency unit */
+#define MHz 1000000
+
+/* AP806 SAR (Sample-At-Reset) register */
+#define AP806_SAR_REG_BASE (SOC_REGS_PHY_BASE + 0x6F4400)
+#define SAR_CLOCK_FREQ_MODE_OFFSET 0
+#define SAR_CLOCK_FREQ_MODE_MASK (0x1f << SAR_CLOCK_FREQ_MODE_OFFSET)
+
+/* LLC (Last Level Cache) registers */
+#define LLC_BASE (SOC_REGS_PHY_BASE + 0x8000)
+#define LLC_CTRL 0x100
+#define LLC_CTRL_EN 0x1
+#define LLC_EXCLUSIVE_EN 0x100
+
+/* MSS clock is fixed at 200MHz on AP806 */
+#define AP806_MSS_CLOCK (200 * MHz)
+
+/* Clock ID indices in PLL frequency table */
+#define CPU_CLOCK_ID 0
+#define DDR_CLOCK_ID 1
+#define RING_CLOCK_ID 2
+
+/* Clocking options (SAR field values) */
+enum clocking_options {
+ CPU_2000_DDR_1200_RCLK_1200 = 0x0,
+ CPU_2000_DDR_1050_RCLK_1050 = 0x1,
+ CPU_1600_DDR_800_RCLK_800 = 0x4,
+ CPU_1800_DDR_1200_RCLK_1200 = 0x6,
+ CPU_1800_DDR_1050_RCLK_1050 = 0x7,
+ CPU_1600_DDR_900_RCLK_900 = 0x0b,
+ CPU_1600_DDR_1050_RCLK_1050 = 0x0d,
+ CPU_1600_DDR_900_RCLK_900_2 = 0x0e,
+ CPU_1000_DDR_650_RCLK_650 = 0x13,
+ CPU_1300_DDR_800_RCLK_800 = 0x14,
+ CPU_1300_DDR_650_RCLK_650 = 0x17,
+ CPU_1200_DDR_800_RCLK_800 = 0x19,
+ CPU_1400_DDR_800_RCLK_800 = 0x1a,
+ CPU_600_DDR_800_RCLK_800 = 0x1b,
+ CPU_800_DDR_800_RCLK_800 = 0x1c,
+ CPU_1000_DDR_800_RCLK_800 = 0x1d,
+};
+
+/*
+ * PLL frequency table: maps SAR clock mode to actual frequencies.
+ * Format: { CPU_freq, DDR_freq, RING_freq, SAR_value }
+ */
+static const u32 pll_freq_tbl[16][4] = {
+ /* CPU */ /* DDR */ /* Ring */
+ {2000 * MHz, 1200 * MHz, 1200 * MHz, CPU_2000_DDR_1200_RCLK_1200},
+ {2000 * MHz, 1050 * MHz, 1050 * MHz, CPU_2000_DDR_1050_RCLK_1050},
+ {1800 * MHz, 1200 * MHz, 1200 * MHz, CPU_1800_DDR_1200_RCLK_1200},
+ {1800 * MHz, 1050 * MHz, 1050 * MHz, CPU_1800_DDR_1050_RCLK_1050},
+ {1600 * MHz, 1050 * MHz, 1050 * MHz, CPU_1600_DDR_1050_RCLK_1050},
+ {1600 * MHz, 900 * MHz, 900 * MHz, CPU_1600_DDR_900_RCLK_900_2},
+ {1300 * MHz, 800 * MHz, 800 * MHz, CPU_1300_DDR_800_RCLK_800},
+ {1300 * MHz, 650 * MHz, 650 * MHz, CPU_1300_DDR_650_RCLK_650},
+ {1600 * MHz, 800 * MHz, 800 * MHz, CPU_1600_DDR_800_RCLK_800},
+ {1600 * MHz, 900 * MHz, 900 * MHz, CPU_1600_DDR_900_RCLK_900},
+ {1000 * MHz, 650 * MHz, 650 * MHz, CPU_1000_DDR_650_RCLK_650},
+ {1200 * MHz, 800 * MHz, 800 * MHz, CPU_1200_DDR_800_RCLK_800},
+ {1400 * MHz, 800 * MHz, 800 * MHz, CPU_1400_DDR_800_RCLK_800},
+ {600 * MHz, 800 * MHz, 800 * MHz, CPU_600_DDR_800_RCLK_800},
+ {800 * MHz, 800 * MHz, 800 * MHz, CPU_800_DDR_800_RCLK_800},
+ {1000 * MHz, 800 * MHz, 800 * MHz, CPU_1000_DDR_800_RCLK_800}
+};
+
+/*
+ * Get the clock frequency mode index from SAR register.
+ * Returns index into pll_freq_tbl, or -1 if not found.
+ */
+static int sar_get_clock_freq_mode(void)
+{
+ u32 i;
+ u32 clock_freq;
+
+ clock_freq = (readl(AP806_SAR_REG_BASE) & SAR_CLOCK_FREQ_MODE_MASK)
+ >> SAR_CLOCK_FREQ_MODE_OFFSET;
+
+ for (i = 0; i < ARRAY_SIZE(pll_freq_tbl); i++) {
+ if (pll_freq_tbl[i][3] == clock_freq)
+ return i;
+ }
+
+ pr_err("SAR: unsupported clock freq mode %d\n", clock_freq);
+ return -1;
+}
+
+/*
+ * Get CPU clock frequency in Hz.
+ */
+static u32 soc_cpu_clk_get(void)
+{
+ int mode = sar_get_clock_freq_mode();
+
+ if (mode < 0)
+ return 0;
+ return pll_freq_tbl[mode][CPU_CLOCK_ID];
+}
+
+/*
+ * Get DDR clock frequency in Hz.
+ */
+static u32 soc_ddr_clk_get(void)
+{
+ int mode = sar_get_clock_freq_mode();
+
+ if (mode < 0)
+ return 0;
+ return pll_freq_tbl[mode][DDR_CLOCK_ID];
+}
+
+/*
+ * Get Ring (Fabric) clock frequency in Hz.
+ */
+static u32 soc_ring_clk_get(void)
+{
+ int mode = sar_get_clock_freq_mode();
+
+ if (mode < 0)
+ return 0;
+ return pll_freq_tbl[mode][RING_CLOCK_ID];
+}
+
+/*
+ * Get MSS clock frequency in Hz.
+ */
+static u32 soc_mss_clk_get(void)
+{
+ return AP806_MSS_CLOCK;
+}
+
+/*
+ * Get LLC status and mode.
+ * Returns 1 if LLC is enabled, 0 otherwise.
+ * If excl_mode is not NULL, sets it to 1 if exclusive mode is enabled.
+ */
+static int llc_mode_get(int *excl_mode)
+{
+ u32 val;
+ int ret = 0, excl = 0;
+
+ val = readl(LLC_BASE + LLC_CTRL);
+ if (val & LLC_CTRL_EN) {
+ ret = 1;
+ if (val & LLC_EXCLUSIVE_EN)
+ excl = 1;
+ }
+ if (excl_mode)
+ *excl_mode = excl;
+
+ return ret;
+}
+
+/*
+ * Print SoC clock information.
+ */
+void soc_print_clock_info(void)
+{
+ printf("Clock: CPU %-4d [MHz]\n", soc_cpu_clk_get() / MHz);
+ printf("\tDDR %-4d [MHz]\n", soc_ddr_clk_get() / MHz);
+ printf("\tFABRIC %-4d [MHz]\n", soc_ring_clk_get() / MHz);
+ printf("\tMSS %-4d [MHz]\n", soc_mss_clk_get() / MHz);
+}
+
+/*
+ * Print SoC-specific information: DDR width and LLC status.
+ */
+void soc_print_soc_info(void)
+{
+ int llc_en, llc_excl_mode;
+
+ printf("\tDDR 64 Bit width\n");
+
+ llc_en = llc_mode_get(&llc_excl_mode);
+ printf("\tLLC %s%s\n", llc_en ? "Enabled" : "Disabled",
+ llc_excl_mode ? " (Exclusive Mode)" : "");
+}
diff --git a/arch/arm/mach-mvebu/armada8k/soc_info.h b/arch/arm/mach-mvebu/armada8k/soc_info.h
new file mode 100644
index 00000000000..41afe7a2508
--- /dev/null
+++ b/arch/arm/mach-mvebu/armada8k/soc_info.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2015 Marvell International Ltd.
+ *
+ * Marvell Armada 8K SoC info functions
+ */
+
+#ifndef _ARMADA8K_SOC_INFO_H_
+#define _ARMADA8K_SOC_INFO_H_
+
+void soc_print_clock_info(void);
+void soc_print_soc_info(void);
+
+#endif /* _ARMADA8K_SOC_INFO_H_ */
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v1 2/5] board: freebox: add Nodebox 10G board support
2026-03-10 14:12 [PATCH v1 0/5] Add support for Freebox Nodebox 10G (Armada 8040) Vincent Jardin
2026-03-10 14:12 ` [PATCH v1 1/5] arm: mach-mvebu: armada8k: add CPU and clock info display Vincent Jardin
@ 2026-03-10 14:12 ` Vincent Jardin
2026-03-31 12:35 ` Stefan Roese
2026-03-10 14:12 ` [PATCH v1 3/5] arm: dts: armada-8040-nbx: add U-Boot dtsi for conditional OP-TEE Vincent Jardin
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Vincent Jardin @ 2026-03-10 14:12 UTC (permalink / raw)
To: u-boot; +Cc: Stefan Roese, Tom Rini, Peng Fan, Jaehoon Chung, vjardin
Add board support for the Freebox Nodebox 10G based on the Marvell
Armada 8040 SoC. This board features:
- Quad-core ARMv8 AP806 with dual CP110 companions
- eMMC storage via Xenon SDHCI controller
- 1G SGMII and 10G SFI Ethernet on CP0 and CP1
- I2C buses for peripheral access
- NS16550 UART console at 115200 baud
The implementation includes:
- Device tree for the Nodebox 10G hardware
- Dedicated board directory (board/freebox/nbx10g/)
- Board-specific Kconfig and defconfig
- Management PHY reset via GPIO in board_late_init()
Signed-off-by: Vincent Jardin <vjardin@free.fr>
---
arch/arm/dts/Makefile | 1 +
arch/arm/dts/armada-8040-nbx.dts | 259 ++++++++++++++++++++++++++++
arch/arm/mach-mvebu/Kconfig | 9 +
board/freebox/nbx10g/Kconfig | 12 ++
board/freebox/nbx10g/MAINTAINERS | 13 ++
board/freebox/nbx10g/Makefile | 3 +
board/freebox/nbx10g/board.c | 63 +++++++
configs/mvebu_nbx_88f8040_defconfig | 73 ++++++++
include/configs/nbx10g.h | 29 ++++
9 files changed, 462 insertions(+)
create mode 100644 arch/arm/dts/armada-8040-nbx.dts
create mode 100644 board/freebox/nbx10g/Kconfig
create mode 100644 board/freebox/nbx10g/MAINTAINERS
create mode 100644 board/freebox/nbx10g/Makefile
create mode 100644 board/freebox/nbx10g/board.c
create mode 100644 configs/mvebu_nbx_88f8040_defconfig
create mode 100644 include/configs/nbx10g.h
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index 82ad3035308..1638d19e44d 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -180,6 +180,7 @@ dtb-$(CONFIG_ARCH_MVEBU) += \
armada-8040-clearfog-gt-8k.dtb \
armada-8040-db.dtb \
armada-8040-mcbin.dtb \
+ armada-8040-nbx.dtb \
armada-8040-puzzle-m801.dtb \
cn9130-db-A.dtb \
cn9130-db-B.dtb \
diff --git a/arch/arm/dts/armada-8040-nbx.dts b/arch/arm/dts/armada-8040-nbx.dts
new file mode 100644
index 00000000000..bfa8927c5e5
--- /dev/null
+++ b/arch/arm/dts/armada-8040-nbx.dts
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Device Tree file for NBX board (Freebox Nodebox10G)
+ * Based on Marvell Armada 8040 SoC
+ *
+ * Copyright (C) 2024 Freebox SA
+ */
+
+#include "armada-8040.dtsi"
+
+/ {
+ model = "NBX Armada 8040";
+ compatible = "nbx,armada8040", "marvell,armada8040";
+
+ chosen {
+ stdout-path = "serial0:115200n8";
+ };
+
+ aliases {
+ i2c0 = &cp0_i2c0;
+ i2c1 = &cp0_i2c1;
+ gpio0 = &ap_gpio0;
+ gpio1 = &cp0_gpio0;
+ gpio2 = &cp0_gpio1;
+ };
+
+ memory@00000000 {
+ device_type = "memory";
+ reg = <0x0 0x0 0x0 0x80000000>; /* 2GB */
+ };
+};
+
+/* AP806 UART - active */
+&uart0 {
+ status = "okay";
+};
+
+/* AP806 pinctrl */
+&ap_pinctl {
+ /*
+ * MPP Bus:
+ * eMMC [0-10]
+ * UART0 [11,19]
+ */
+ /* 0 1 2 3 4 5 6 7 8 9 */
+ pin-func = < 1 1 1 1 1 1 1 1 1 1
+ 1 3 0 0 0 0 0 0 0 3 >;
+};
+
+/* AP806 on-board eMMC */
+&ap_sdhci0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&ap_emmc_pins>;
+ bus-width = <8>;
+ non-removable;
+ status = "okay";
+};
+
+/* CP0 pinctrl */
+&cp0_pinctl {
+ /*
+ * MPP Bus:
+ * [0-31] = 0xff: Keep default CP0_shared_pins
+ * [32,34] GE_MDIO/MDC
+ * [35-36] I2C1
+ * [37-38] I2C0
+ * [57-58] MSS I2C
+ */
+ /* 0 1 2 3 4 5 6 7 8 9 */
+ pin-func = < 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
+ 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
+ 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
+ 0xff 0xff 7 0 7 2 2 2 2 0
+ 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 2 2 0
+ 0 0 0 >;
+
+ cp0_smi_pins: cp0-smi-pins {
+ marvell,pins = <32 34>;
+ marvell,function = <7>;
+ };
+};
+
+/* CP0 I2C0 */
+&cp0_i2c0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&cp0_i2c0_pins>;
+ status = "okay";
+ clock-frequency = <100000>;
+};
+
+/* CP0 I2C1 */
+&cp0_i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&cp0_i2c1_pins>;
+ status = "okay";
+ clock-frequency = <100000>;
+};
+
+/* CP0 MSS I2C0 - Management SubSystem I2C (pins 57-58, func 2) */
+&cp0_mss_i2c0 {
+ status = "okay";
+};
+
+/* CP0 MDIO for PHY */
+&cp0_mdio {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&cp0_smi_pins>;
+
+ nbx_phy0: ethernet-phy@0 {
+ reg = <0>;
+ };
+};
+
+/* CP0 ComPhy - SerDes configuration */
+&cp0_comphy {
+ /*
+ * CP0 Serdes Configuration:
+ * Lane 0-3: Unconnected
+ * Lane 4: SFI (10G Ethernet)
+ * Lane 5: SGMII2 (1G Ethernet)
+ */
+ phy0 {
+ phy-type = <COMPHY_TYPE_UNCONNECTED>;
+ };
+ phy1 {
+ phy-type = <COMPHY_TYPE_UNCONNECTED>;
+ };
+ phy2 {
+ phy-type = <COMPHY_TYPE_UNCONNECTED>;
+ };
+ phy3 {
+ phy-type = <COMPHY_TYPE_UNCONNECTED>;
+ };
+ phy4 {
+ phy-type = <COMPHY_TYPE_SFI0>;
+ phy-speed = <COMPHY_SPEED_10_3125G>;
+ };
+ phy5 {
+ phy-type = <COMPHY_TYPE_SGMII2>;
+ phy-speed = <COMPHY_SPEED_1_25G>;
+ };
+};
+
+/* CP0 Ethernet - only eth2 (MAC3) is active via SGMII */
+&cp0_ethernet {
+ status = "okay";
+};
+
+&cp0_eth2 {
+ status = "okay";
+ phy = <&nbx_phy0>;
+ phy-mode = "sgmii";
+};
+
+/* CP0 UTMI PHY for USB */
+&cp0_utmi {
+ status = "okay";
+};
+
+&cp0_utmi0 {
+ status = "okay";
+};
+
+&cp0_utmi1 {
+ status = "okay";
+};
+
+/* CP0 USB3 Host controllers */
+&cp0_usb3_0 {
+ status = "okay";
+};
+
+&cp0_usb3_1 {
+ status = "okay";
+};
+
+/* CP1 pinctrl */
+&cp1_pinctl {
+ /*
+ * MPP Bus:
+ * [0-26] = Unconfigured
+ * [27-28] GE_MDIO/MDC
+ * [29-30] MSS I2C
+ * [31] = Unconfigured
+ * [32-62] = 0xff: Keep default CP1_shared_pins
+ */
+ /* 0 1 2 3 4 5 6 7 8 9 */
+ pin-func = < 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
+ 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
+ 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x8 0x8 0x8
+ 0x8 0x0 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
+ 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
+ 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
+ 0xff 0xff 0xff>;
+
+ cp1_mss_i2c_pins: cp1-mss-i2c-pins {
+ marvell,pins = <29 30>;
+ marvell,function = <8>;
+ };
+};
+
+/* CP1 MSS I2C0 - Management SubSystem I2C (pins 29-30, func 8) */
+&cp1_mss_i2c0 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&cp1_mss_i2c_pins>;
+};
+
+/* CP1 ComPhy - SerDes configuration */
+&cp1_comphy {
+ /*
+ * CP1 Serdes Configuration:
+ * Lane 0: PCIe x1
+ * Lane 1: USB3 Host
+ * Lane 2-3: Unconnected
+ * Lane 4: SFI (10G Ethernet)
+ * Lane 5: Unconnected
+ */
+ phy0 {
+ phy-type = <COMPHY_TYPE_PEX0>;
+ };
+ phy1 {
+ phy-type = <COMPHY_TYPE_USB3_HOST0>;
+ };
+ phy2 {
+ phy-type = <COMPHY_TYPE_UNCONNECTED>;
+ };
+ phy3 {
+ phy-type = <COMPHY_TYPE_UNCONNECTED>;
+ };
+ phy4 {
+ phy-type = <COMPHY_TYPE_SFI0>;
+ phy-speed = <COMPHY_SPEED_10_3125G>;
+ };
+ phy5 {
+ phy-type = <COMPHY_TYPE_UNCONNECTED>;
+ };
+};
+
+/* CP1 PCIe x1 on lane 0 */
+&cp1_pcie0 {
+ status = "okay";
+};
+
+/* CP1 USB3 Host on lane 1 */
+&cp1_usb3_0 {
+ status = "okay";
+};
+
+/* CP1 UTMI PHY for USB */
+&cp1_utmi {
+ status = "okay";
+};
+
+&cp1_utmi0 {
+ status = "okay";
+};
diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig
index b76510ab452..66e5f1788d6 100644
--- a/arch/arm/mach-mvebu/Kconfig
+++ b/arch/arm/mach-mvebu/Kconfig
@@ -166,6 +166,14 @@ config TARGET_MVEBU_ARMADA_8K
select BOARD_LATE_INIT
imply SCSI
+config TARGET_NBX10G
+ bool "Support Freebox Nodebox 10G"
+ select ARMADA_8K
+ select BOARD_LATE_INIT
+ help
+ Enable support for the Freebox Nodebox 10G board based on the
+ Marvell Armada 8040 SoC with dual CP110 companion chips.
+
config TARGET_MVEBU_ALLEYCAT5
bool "Support AlleyCat 5 platforms"
select ALLEYCAT_5
@@ -508,5 +516,6 @@ config ARMADA_32BIT_SYSCON_SYSRESET
source "board/solidrun/clearfog/Kconfig"
source "board/kobol/helios4/Kconfig"
+source "board/freebox/nbx10g/Kconfig"
endif
diff --git a/board/freebox/nbx10g/Kconfig b/board/freebox/nbx10g/Kconfig
new file mode 100644
index 00000000000..18a169761b7
--- /dev/null
+++ b/board/freebox/nbx10g/Kconfig
@@ -0,0 +1,12 @@
+if TARGET_NBX10G
+
+config SYS_BOARD
+ default "nbx10g"
+
+config SYS_VENDOR
+ default "freebox"
+
+config SYS_CONFIG_NAME
+ default "nbx10g"
+
+endif
diff --git a/board/freebox/nbx10g/MAINTAINERS b/board/freebox/nbx10g/MAINTAINERS
new file mode 100644
index 00000000000..4753a9a13bb
--- /dev/null
+++ b/board/freebox/nbx10g/MAINTAINERS
@@ -0,0 +1,13 @@
+NBX10G BOARD
+M: Vincent Jardin <vjardin@free.fr>
+S: Maintained
+F: board/freebox/nbx10g/
+F: configs/mvebu_nbx_88f8040_defconfig
+F: arch/arm/dts/armada-8040-nbx.dts
+F: arch/arm/dts/armada-8040-nbx-u-boot.dtsi
+F: include/configs/nbx10g.h
+F: cmd/mvebu/mvebu_emmcboot.c
+F: cmd/mvebu/nbx_fbxserial.c
+F: include/mvebu_imagetag.h
+F: include/mvebu_nrboot.h
+F: include/nbx_fbxserial.h
diff --git a/board/freebox/nbx10g/Makefile b/board/freebox/nbx10g/Makefile
new file mode 100644
index 00000000000..bf83bdf63ee
--- /dev/null
+++ b/board/freebox/nbx10g/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-y := board.o
diff --git a/board/freebox/nbx10g/board.c b/board/freebox/nbx10g/board.c
new file mode 100644
index 00000000000..7080e386c38
--- /dev/null
+++ b/board/freebox/nbx10g/board.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017-2018 Freebox SA
+ * Copyright (C) 2026 Free Mobile, Vincent Jardin <vjardin@free.fr>
+ *
+ * Freebox Nodebox 10G board support
+ */
+
+#include <config.h>
+#include <dm.h>
+#include <init.h>
+#include <asm/global_data.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/soc.h>
+#include <linux/delay.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Management PHY reset GPIO */
+#define NBX_PHY_RESET_GPIO 83
+
+/* Nodebox 10G ASCII art logo */
+static const char * const nbx_logo =
+ " _ _ _ _ __ ___ _____\n"
+ " | \\ | | | | | | /_ |/ _ \\ / ____|\n"
+ " | \\| | ___ __| | ___| |__ _____ __ | | | | | | __\n"
+ " | . ` |/ _ \\ / _` |/ _ \\ '_ \\ / _ \\ \\/ / | | | | | | |_ |\n"
+ " | |\\ | (_) | (_| | __/ |_) | (_) > < | | |_| | |__| |\n"
+ " |_| \\_|\\___/ \\__,_|\\___|_.__/ \\___/_/\\_\\ |_|\\___/ \\_____|\n";
+
+int checkboard(void)
+{
+ printf("%s\n", nbx_logo);
+ return 0;
+}
+
+int board_init(void)
+{
+ gd->bd->bi_boot_params = CFG_SYS_SDRAM_BASE + 0x100;
+ return 0;
+}
+
+int board_late_init(void)
+{
+ int ret;
+
+ /* Reset the management PHY */
+ ret = gpio_request(NBX_PHY_RESET_GPIO, "phy-reset");
+ if (ret) {
+ printf("Failed to request PHY reset GPIO: %d\n", ret);
+ return 0;
+ }
+
+ gpio_direction_output(NBX_PHY_RESET_GPIO, 0);
+ mdelay(100);
+ gpio_set_value(NBX_PHY_RESET_GPIO, 1);
+ mdelay(100);
+ gpio_free(NBX_PHY_RESET_GPIO);
+
+ return 0;
+}
diff --git a/configs/mvebu_nbx_88f8040_defconfig b/configs/mvebu_nbx_88f8040_defconfig
new file mode 100644
index 00000000000..975c53e9a92
--- /dev/null
+++ b/configs/mvebu_nbx_88f8040_defconfig
@@ -0,0 +1,73 @@
+CONFIG_ARM=y
+CONFIG_ARCH_CPU_INIT=y
+CONFIG_ARCH_MVEBU=y
+CONFIG_TEXT_BASE=0x00000000
+CONFIG_NR_DRAM_BANKS=2
+CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
+CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0xff0000
+CONFIG_TARGET_NBX10G=y
+CONFIG_ENV_SIZE=0x10000
+CONFIG_ENV_OFFSET=0x180000
+CONFIG_DM_GPIO=y
+CONFIG_DEFAULT_DEVICE_TREE="armada-8040-nbx"
+CONFIG_FIT=y
+CONFIG_SYS_BOOTM_LEN=0x1000000
+CONFIG_SYS_LOAD_ADDR=0x800000
+CONFIG_PCI=y
+# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+# CONFIG_EFI_LOADER is not set
+CONFIG_USE_PREBOOT=y
+CONFIG_PREBOOT="echo (CRC warning is normal: no env saved yet)"
+CONFIG_SYS_PBSIZE=1048
+CONFIG_SYS_CONSOLE_INFO_QUIET=y
+CONFIG_DISPLAY_CPUINFO=y
+CONFIG_SYS_PROMPT="nodebox10G>> "
+CONFIG_SYS_MAXARGS=32
+CONFIG_CMD_BOOTZ=y
+# CONFIG_BOOTM_VXWORKS is not set
+# CONFIG_CMD_ELF is not set
+CONFIG_CMD_MEMTEST=y
+CONFIG_CMD_GPIO=y
+CONFIG_CMD_I2C=y
+CONFIG_CMD_MISC=y
+CONFIG_CMD_MMC=y
+CONFIG_CMD_PCI=y
+# CONFIG_CMD_SF is not set
+CONFIG_CMD_USB=y
+CONFIG_CMD_DHCP=y
+CONFIG_CMD_MII=y
+CONFIG_CMD_PING=y
+CONFIG_CMD_CACHE=y
+CONFIG_CMD_TIME=y
+CONFIG_CMD_TIMER=y
+CONFIG_CMD_EXT2=y
+CONFIG_CMD_EXT4=y
+CONFIG_CMD_EXT4_WRITE=y
+CONFIG_CMD_FAT=y
+CONFIG_CMD_FS_GENERIC=y
+CONFIG_EFI_PARTITION=y
+CONFIG_ENV_OVERWRITE=y
+CONFIG_ENV_IS_IN_MMC=y
+CONFIG_ENV_RELOC_GD_ENV_ADDR=y
+CONFIG_ARP_TIMEOUT=200
+CONFIG_NET_RETRY_COUNT=50
+CONFIG_NET_RANDOM_ETHADDR=y
+CONFIG_DM_I2C=y
+CONFIG_SYS_I2C_MVTWSI=y
+CONFIG_I2C_MUX=y
+CONFIG_MISC=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_XENON=y
+# CONFIG_SPI_FLASH is not set
+CONFIG_PHY_MARVELL=y
+CONFIG_PHY_GIGE=y
+CONFIG_MVPP2=y
+CONFIG_PCIE_DW_MVEBU=y
+CONFIG_PHY=y
+CONFIG_MVEBU_COMPHY_SUPPORT=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_ARMADA_8K=y
+CONFIG_SYS_NS16550=y
+CONFIG_USB=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_STORAGE=y
diff --git a/include/configs/nbx10g.h b/include/configs/nbx10g.h
new file mode 100644
index 00000000000..bd083b7e7d8
--- /dev/null
+++ b/include/configs/nbx10g.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2017-2018 Freebox SA
+ * Copyright (C) 2026 Free Mobile, Vincent Jardin <vjardin@free.fr>
+ *
+ * Configuration for Freebox Nodebox 10G
+ */
+
+#ifndef _CONFIG_NBX10G_H
+#define _CONFIG_NBX10G_H
+
+#include "mvebu_armada-8k.h"
+
+/* Override environment settings for NBX */
+#undef CFG_EXTRA_ENV_SETTINGS
+#define CFG_EXTRA_ENV_SETTINGS \
+ "hostname=nodebox10G\0" \
+ "ethrotate=no\0" \
+ "image_addr=0x7000000\0" \
+ "image_name=Image.nodebox10G\0" \
+ "fdt_addr=0x6f00000\0" \
+ "fdt_name=nodebox10G.dtb\0" \
+ "console=ttyS0,115200\0" \
+ "tftpboot=setenv bootargs console=${console} bank=tftp; " \
+ "dhcp ${image_addr} ${image_name}; " \
+ "tftp ${fdt_addr} ${fdt_name}; " \
+ "booti ${image_addr} - ${fdt_addr}\0"
+
+#endif /* _CONFIG_NBX10G_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v1 3/5] arm: dts: armada-8040-nbx: add U-Boot dtsi for conditional OP-TEE
2026-03-10 14:12 [PATCH v1 0/5] Add support for Freebox Nodebox 10G (Armada 8040) Vincent Jardin
2026-03-10 14:12 ` [PATCH v1 1/5] arm: mach-mvebu: armada8k: add CPU and clock info display Vincent Jardin
2026-03-10 14:12 ` [PATCH v1 2/5] board: freebox: add Nodebox 10G board support Vincent Jardin
@ 2026-03-10 14:12 ` Vincent Jardin
2026-03-31 12:35 ` Stefan Roese
2026-03-10 14:12 ` [PATCH v1 4/5] cmd: mvebu: add emmcboot for Nodebox image format Vincent Jardin
2026-03-10 14:12 ` [PATCH v1 5/5] cmd: mvebu: add device serial and MAC address initialization Vincent Jardin
4 siblings, 1 reply; 11+ messages in thread
From: Vincent Jardin @ 2026-03-10 14:12 UTC (permalink / raw)
To: u-boot; +Cc: Stefan Roese, Tom Rini, Peng Fan, Jaehoon Chung, vjardin
Add armada-8040-nbx-u-boot.dtsi with the firmware/optee node guarded
by CONFIG_OPTEE. This allows U-Boot to probe the OP-TEE driver only
when OP-TEE support is enabled in the configuration.
Signed-off-by: Vincent Jardin <vjardin@free.fr>
---
arch/arm/dts/armada-8040-nbx-u-boot.dtsi | 15 +++++++++++++++
1 file changed, 15 insertions(+)
create mode 100644 arch/arm/dts/armada-8040-nbx-u-boot.dtsi
diff --git a/arch/arm/dts/armada-8040-nbx-u-boot.dtsi b/arch/arm/dts/armada-8040-nbx-u-boot.dtsi
new file mode 100644
index 00000000000..dec473b7156
--- /dev/null
+++ b/arch/arm/dts/armada-8040-nbx-u-boot.dtsi
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 Free Mobile, Vincent Jardin
+ */
+
+#ifdef CONFIG_OPTEE
+/ {
+ firmware {
+ optee {
+ compatible = "linaro,optee-tz";
+ method = "smc";
+ };
+ };
+};
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v1 4/5] cmd: mvebu: add emmcboot for Nodebox image format
2026-03-10 14:12 [PATCH v1 0/5] Add support for Freebox Nodebox 10G (Armada 8040) Vincent Jardin
` (2 preceding siblings ...)
2026-03-10 14:12 ` [PATCH v1 3/5] arm: dts: armada-8040-nbx: add U-Boot dtsi for conditional OP-TEE Vincent Jardin
@ 2026-03-10 14:12 ` Vincent Jardin
2026-03-31 12:46 ` Stefan Roese
2026-03-10 14:12 ` [PATCH v1 5/5] cmd: mvebu: add device serial and MAC address initialization Vincent Jardin
4 siblings, 1 reply; 11+ messages in thread
From: Vincent Jardin @ 2026-03-10 14:12 UTC (permalink / raw)
To: u-boot; +Cc: Stefan Roese, Tom Rini, Peng Fan, Jaehoon Chung, vjardin
The Nodebox 10G stores firmware in a board-specific format
(mvebu_image_tag) at fixed offsets in eMMC. Each image bundles a
kernel and device tree under a CRC32-validated tag header.
Include a board-specific rollback support based on a reboot counter
in order to track consecutive boot failures and then to boot from
the fallback image.
Signed-off-by: Vincent Jardin <vjardin@free.fr>
---
cmd/mvebu/Kconfig | 53 +++++
cmd/mvebu/Makefile | 1 +
cmd/mvebu/mvebu_emmcboot.c | 340 ++++++++++++++++++++++++++++
configs/mvebu_nbx_88f8040_defconfig | 1 +
include/mvebu_imagetag.h | 82 +++++++
include/mvebu_nrboot.h | 51 +++++
6 files changed, 528 insertions(+)
create mode 100644 cmd/mvebu/mvebu_emmcboot.c
create mode 100644 include/mvebu_imagetag.h
create mode 100644 include/mvebu_nrboot.h
diff --git a/cmd/mvebu/Kconfig b/cmd/mvebu/Kconfig
index e83a9829491..ea03f581280 100644
--- a/cmd/mvebu/Kconfig
+++ b/cmd/mvebu/Kconfig
@@ -79,4 +79,57 @@ config CMD_MVEBU_COMPHY_RX_TRAINING
help
Perform COMPHY RX training sequence
+config CMD_NBX_EMMCBOOT
+ bool "Nodebox emmcboot command"
+ depends on ARMADA_8K && MMC_SDHCI_XENON
+ help
+ Enable the emmcboot command for Nodebox 10G boards. The command
+ loads and boots firmware stored in the board-specific image format
+ (mvebu_image_tag) at fixed eMMC offsets. Each image bundles a
+ kernel and device tree under a CRC32-validated tag header.
+
+ Two image banks are supported (Bank0 stable, Bank1 newer) with
+ automatic fallback based on a reboot tracking counter (nrboot).
+
+ Requires image_addr and fdt_addr environment variables to be set.
+
+if CMD_NBX_EMMCBOOT
+
+config MVEBU_MMC_PART_NRBOOT_OFFSET
+ hex "NRBoot counter offset in eMMC"
+ default 0x802000
+ help
+ Byte offset in eMMC where the reboot tracking counter is stored.
+ Default: 0x802000 (8MB + 8KB)
+
+config MVEBU_MMC_PART_BANK0_OFFSET
+ hex "Bank0 image offset in eMMC"
+ default 0x804000
+ help
+ Byte offset in eMMC where the stable (Bank0) boot image starts.
+ Default: 0x804000 (8MB + 16KB)
+
+config MVEBU_MMC_PART_BANK0_SIZE
+ hex "Bank0 image maximum size"
+ default 0x10000000
+ help
+ Maximum size of the Bank0 boot image.
+ Default: 0x10000000 (256MB)
+
+config MVEBU_MMC_PART_BANK1_OFFSET
+ hex "Bank1 image offset in eMMC"
+ default 0x10804000
+ help
+ Byte offset in eMMC where the newer (Bank1) boot image starts.
+ Default: 0x10804000 (264MB + 16KB)
+
+config MVEBU_MMC_PART_BANK1_SIZE
+ hex "Bank1 image maximum size"
+ default 0x10000000
+ help
+ Maximum size of the Bank1 boot image.
+ Default: 0x10000000 (256MB)
+
+endif
+
endmenu
diff --git a/cmd/mvebu/Makefile b/cmd/mvebu/Makefile
index ca96ad01d91..c096f507e26 100644
--- a/cmd/mvebu/Makefile
+++ b/cmd/mvebu/Makefile
@@ -6,3 +6,4 @@
obj-$(CONFIG_CMD_MVEBU_BUBT) += bubt.o
obj-$(CONFIG_CMD_MVEBU_COMPHY_RX_TRAINING) += comphy_rx_training.o
+obj-$(CONFIG_CMD_NBX_EMMCBOOT) += mvebu_emmcboot.o
diff --git a/cmd/mvebu/mvebu_emmcboot.c b/cmd/mvebu/mvebu_emmcboot.c
new file mode 100644
index 00000000000..3d85f82670a
--- /dev/null
+++ b/cmd/mvebu/mvebu_emmcboot.c
@@ -0,0 +1,340 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * MVEBU eMMC boot command for Nodebox 10G boot image format
+ *
+ * Copyright (C) 2026 Free Mobile, Freebox
+ *
+ * The Nodebox 10G stores firmware images in a board-specific format
+ * (mvebu_image_tag) at fixed offsets in eMMC. Each image bundles a
+ * kernel, device tree, and optional rootfs under a CRC32-validated
+ * tag header.
+ *
+ * Two image banks are laid out in eMMC (Bank0 stable, Bank1 newer).
+ * A reboot counter (nrboot) selects which bank to try first.
+ */
+
+#include <command.h>
+#include <env.h>
+#include <mmc.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <vsprintf.h>
+#include <u-boot/crc.h>
+#include <u-boot/schedule.h>
+#include <asm/byteorder.h>
+#include <linux/bitops.h>
+#include <mvebu_imagetag.h>
+#include <mvebu_nrboot.h>
+
+/* Image Tag Functions */
+
+static int mvebu_imagetag_check(struct mvebu_image_tag *tag,
+ unsigned long maxsize, const char *name)
+{
+ if (be32_to_cpu(tag->magic) != MVEBU_IMAGE_TAG_MAGIC) {
+ if (name)
+ printf("%s: invalid TAG magic: %.8x\n", name,
+ be32_to_cpu(tag->magic));
+ return -1;
+ }
+
+ if (be32_to_cpu(tag->version) != MVEBU_IMAGE_TAG_VERSION) {
+ if (name)
+ printf("%s: invalid TAG version: %.8x\n", name,
+ be32_to_cpu(tag->version));
+ return -1;
+ }
+
+ if (be32_to_cpu(tag->total_size) < sizeof(*tag)) {
+ if (name)
+ printf("%s: tag size is too small!\n", name);
+ return -1;
+ }
+
+ if (be32_to_cpu(tag->total_size) > maxsize) {
+ if (name)
+ printf("%s: tag size is too big!\n", name);
+ return -1;
+ }
+
+ if (be32_to_cpu(tag->device_tree_offset) < sizeof(*tag) ||
+ be32_to_cpu(tag->device_tree_offset) +
+ be32_to_cpu(tag->device_tree_size) > maxsize) {
+ if (name)
+ printf("%s: bogus device tree offset/size!\n", name);
+ return -1;
+ }
+
+ if (be32_to_cpu(tag->kernel_offset) < sizeof(*tag) ||
+ be32_to_cpu(tag->kernel_offset) +
+ be32_to_cpu(tag->kernel_size) > maxsize) {
+ if (name)
+ printf("%s: bogus kernel offset/size!\n", name);
+ return -1;
+ }
+
+ if (be32_to_cpu(tag->rootfs_offset) < sizeof(*tag) ||
+ be32_to_cpu(tag->rootfs_offset) +
+ be32_to_cpu(tag->rootfs_size) > maxsize) {
+ if (name)
+ printf("%s: bogus rootfs offset/size!\n", name);
+ return -1;
+ }
+
+ if (name) {
+ /*
+ * Ensure null-termination within the 32-byte fields
+ * before printing to avoid displaying garbage.
+ */
+ tag->image_name[sizeof(tag->image_name) - 1] = '\0';
+ tag->build_date[sizeof(tag->build_date) - 1] = '\0';
+ tag->build_user[sizeof(tag->build_user) - 1] = '\0';
+
+ printf("%s: Found valid tag: %s / %s / %s\n", name,
+ tag->image_name, tag->build_date, tag->build_user);
+ }
+
+ return 0;
+}
+
+static int mvebu_imagetag_crc(struct mvebu_image_tag *tag, const char *name)
+{
+ u32 crc = ~0;
+
+ crc = crc32(crc, ((unsigned char *)tag) + 4,
+ be32_to_cpu(tag->total_size) - 4);
+
+ if (be32_to_cpu(tag->crc) != crc) {
+ if (name)
+ printf("%s: invalid tag CRC!\n", name);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* NRBoot (Reboot Tracking) Functions */
+
+int mvebu_check_nrboot(struct mmc *mmc, unsigned long offset)
+{
+ struct blk_desc *bd = mmc_get_blk_desc(mmc);
+ struct mvebu_nrboot *nr;
+ uint blk_start = ALIGN(offset, bd->blksz) / bd->blksz;
+ uint blk_cnt = ALIGN(sizeof(*nr), bd->blksz) / bd->blksz;
+ uint n;
+
+ ALLOC_CACHE_ALIGN_BUFFER(char, buf, blk_cnt * bd->blksz);
+ nr = (void *)buf;
+
+ n = blk_dread(bd, blk_start, blk_cnt, buf);
+ if (n != blk_cnt)
+ return 0;
+
+ printf(" - nr.nrboot = %04x\n", nr->nrboot);
+ printf(" - nr.nrsuccess = %04x\n", nr->nrsuccess);
+
+ /* Sanity check: values must be valid bit-field counters */
+ if (generic_hweight16(~nr->nrboot + 1) <= 1 &&
+ generic_hweight16(~nr->nrsuccess + 1) <= 1) {
+ int boot, success;
+
+ boot = 16 - generic_hweight16(nr->nrboot);
+ success = 16 - generic_hweight16(nr->nrsuccess);
+
+ printf(" - Nrboot: %d / Nrsuccess: %d\n", boot, success);
+
+ if (boot == 16 || boot < success ||
+ boot - success >= MVEBU_MAX_FAILURE) {
+ printf(" - Nrboot exceeded\n");
+ return 0;
+ }
+
+ /* Increment boot attempt counter */
+ boot++;
+ nr->nrboot = ~((1 << boot) - 1);
+
+ printf(" - Setting Nrboot to %d\n", boot);
+
+ n = blk_dwrite(bd, blk_start, blk_cnt, buf);
+ if (n != blk_cnt)
+ return 0;
+
+ return 1;
+ }
+
+ printf(" - Invalid NR values\n");
+
+ return 0;
+}
+
+/* emmcboot Command */
+
+static int mvebu_load_image(struct blk_desc *bd, unsigned long offset,
+ unsigned long maxsize, ulong tag_addr,
+ const char *bank)
+{
+ struct mvebu_image_tag *tag;
+ uint blk_start = ALIGN(offset, bd->blksz) / bd->blksz;
+ uint blk_cnt;
+ uint n;
+
+ ALLOC_CACHE_ALIGN_BUFFER(char, tag_buf,
+ ALIGN(sizeof(*tag), bd->blksz));
+ tag = (void *)tag_buf;
+
+ /* Load and validate tag header */
+ blk_cnt = ALIGN(sizeof(*tag), bd->blksz) / bd->blksz;
+ n = blk_dread(bd, blk_start, blk_cnt, tag_buf);
+ if (n != blk_cnt) {
+ printf("%s: failed to read tag header\n", bank);
+ return -1;
+ }
+
+ if (mvebu_imagetag_check(tag, maxsize, bank) != 0)
+ return -1;
+
+ if (tag->rootfs_size != 0) {
+ printf("%s: rootfs in tag not supported\n", bank);
+ return -1;
+ }
+
+ /* Load full image to tag_addr */
+ blk_cnt = ALIGN(mvebu_imagetag_total_size(tag), bd->blksz) / bd->blksz;
+ n = blk_dread(bd, blk_start, blk_cnt, (void *)tag_addr);
+ if (n != blk_cnt) {
+ printf("%s: failed to read full image\n", bank);
+ return -1;
+ }
+
+ if (mvebu_imagetag_crc((void *)tag_addr, bank) != 0)
+ return -1;
+
+ return 0;
+}
+
+static void mvebu_relocate_and_boot(ulong image_addr, ulong fdt_addr,
+ const char *bank)
+{
+ struct mvebu_image_tag *tag = (void *)image_addr;
+ char bootargs[256];
+ char cmd[128];
+ char *console_env;
+
+ /* Copy DTB and kernel to their final addresses */
+ memcpy((void *)fdt_addr,
+ ((void *)image_addr) + mvebu_imagetag_device_tree_offset(tag),
+ mvebu_imagetag_device_tree_size(tag));
+ memmove((void *)image_addr,
+ ((void *)image_addr) + mvebu_imagetag_kernel_offset(tag),
+ mvebu_imagetag_kernel_size(tag));
+
+ schedule();
+
+ /* Set bootargs */
+ console_env = env_get("console");
+ if (console_env)
+ snprintf(bootargs, sizeof(bootargs),
+ "console=%s bank=%s", console_env, bank);
+ else
+ snprintf(bootargs, sizeof(bootargs), "bank=%s", bank);
+
+ env_set("bootargs", bootargs);
+
+ printf("## Booting kernel from %s...\n", bank);
+ printf(" Image addr: 0x%lx\n", image_addr);
+ printf(" FDT addr: 0x%lx\n", fdt_addr);
+
+ snprintf(cmd, sizeof(cmd), "booti 0x%lx - 0x%lx",
+ image_addr, fdt_addr);
+ run_command(cmd, 0);
+}
+
+static void mvebu_try_emmcboot(struct mmc *mmc, unsigned long offset,
+ unsigned long maxsize, const char *bank)
+{
+ struct blk_desc *bd = mmc_get_blk_desc(mmc);
+ ulong image_addr;
+ ulong fdt_addr;
+
+ schedule();
+
+ printf("## Trying %s boot...\n", bank);
+
+ /* Get load addresses from environment */
+ image_addr = env_get_ulong("image_addr", 16, 0);
+ if (!image_addr) {
+ puts("emmcboot needs image_addr\n");
+ return;
+ }
+
+ fdt_addr = env_get_ulong("fdt_addr", 16, 0);
+ if (!fdt_addr) {
+ puts("emmcboot needs fdt_addr\n");
+ return;
+ }
+
+ if (mvebu_load_image(bd, offset, maxsize, image_addr, bank) != 0)
+ return;
+
+ schedule();
+
+ mvebu_relocate_and_boot(image_addr, fdt_addr, bank);
+
+ printf("## %s boot failed\n", bank);
+}
+
+static int do_emmcboot(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int dev;
+ struct mmc *mmc;
+
+ dev = 0;
+ if (argc >= 2)
+ dev = dectoul(argv[1], NULL);
+
+ mmc = find_mmc_device(dev);
+ if (!mmc) {
+ printf("No MMC device %d found\n", dev);
+ return CMD_RET_FAILURE;
+ }
+
+ if (mmc_init(mmc)) {
+ puts("MMC init failed\n");
+ return CMD_RET_FAILURE;
+ }
+
+ /* Switch to partition 0 (user data area) */
+ if (blk_select_hwpart_devnum(UCLASS_MMC, dev, 0)) {
+ puts("MMC partition switch failed\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (mvebu_check_nrboot(mmc, CONFIG_MVEBU_MMC_PART_NRBOOT_OFFSET)) {
+ /* System is healthy: try newer bank first */
+ mvebu_try_emmcboot(mmc, CONFIG_MVEBU_MMC_PART_BANK1_OFFSET,
+ CONFIG_MVEBU_MMC_PART_BANK1_SIZE, "bank1");
+ mvebu_try_emmcboot(mmc, CONFIG_MVEBU_MMC_PART_BANK0_OFFSET,
+ CONFIG_MVEBU_MMC_PART_BANK0_SIZE, "bank0");
+ } else {
+ /* System is degraded: use stable bank first */
+ mvebu_try_emmcboot(mmc, CONFIG_MVEBU_MMC_PART_BANK0_OFFSET,
+ CONFIG_MVEBU_MMC_PART_BANK0_SIZE, "bank0");
+ mvebu_try_emmcboot(mmc, CONFIG_MVEBU_MMC_PART_BANK1_OFFSET,
+ CONFIG_MVEBU_MMC_PART_BANK1_SIZE, "bank1");
+ }
+
+ puts("emmcboot: all boot attempts failed\n");
+
+ return CMD_RET_FAILURE;
+}
+
+U_BOOT_CMD(
+ emmcboot, 2, 0, do_emmcboot,
+ "boot from Nodebox eMMC image banks",
+ "[dev]\n"
+ " - Load and boot a Nodebox image from eMMC device <dev> (default 0)\n"
+ " - Requires image_addr and fdt_addr environment variables\n"
+ " - Two banks: Bank0 (stable) and Bank1 (newer)\n"
+ " - Bank order selected by reboot tracking counter (nrboot)"
+);
diff --git a/configs/mvebu_nbx_88f8040_defconfig b/configs/mvebu_nbx_88f8040_defconfig
index 975c53e9a92..2fd58e4ad64 100644
--- a/configs/mvebu_nbx_88f8040_defconfig
+++ b/configs/mvebu_nbx_88f8040_defconfig
@@ -40,6 +40,7 @@ CONFIG_CMD_PING=y
CONFIG_CMD_CACHE=y
CONFIG_CMD_TIME=y
CONFIG_CMD_TIMER=y
+CONFIG_CMD_NBX_EMMCBOOT=y
CONFIG_CMD_EXT2=y
CONFIG_CMD_EXT4=y
CONFIG_CMD_EXT4_WRITE=y
diff --git a/include/mvebu_imagetag.h b/include/mvebu_imagetag.h
new file mode 100644
index 00000000000..d513038aaf6
--- /dev/null
+++ b/include/mvebu_imagetag.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Nodebox 10G boot image tag format
+ *
+ * Copyright (C) 2026 Free Mobile, Freebox
+ *
+ * Defines the on-eMMC image layout used by Nodebox 10G boards.
+ * Each image carries a CRC32-validated tag header that describes
+ * the kernel, device tree, and rootfs components.
+ */
+
+#ifndef __MVEBU_IMAGETAG_H
+#define __MVEBU_IMAGETAG_H
+
+#include <linux/types.h>
+
+#define MVEBU_IMAGE_TAG_MAGIC 0x8d7c90bc
+#define MVEBU_IMAGE_TAG_VERSION 1
+
+/**
+ * struct mvebu_image_tag - Nodebox boot image tag stored in eMMC
+ *
+ * All multi-byte fields are stored in big-endian format.
+ */
+struct mvebu_image_tag {
+ u32 crc; /* CRC32-LE checksum (from offset 4) */
+ u32 magic; /* Magic: 0x8d7c90bc */
+ u32 version; /* Version: 1 */
+ u32 total_size; /* Total image size including tag */
+ u32 flags; /* Feature flags (reserved) */
+
+ u32 device_tree_offset; /* Offset from tag start to DTB */
+ u32 device_tree_size; /* DTB size in bytes */
+
+ u32 kernel_offset; /* Offset from tag start to kernel */
+ u32 kernel_size; /* Kernel size in bytes */
+
+ u32 rootfs_offset; /* Offset from tag start to rootfs */
+ u32 rootfs_size; /* Rootfs size (must be 0) */
+
+ char image_name[32]; /* Image name (null-terminated) */
+ char build_user[32]; /* Build user info */
+ char build_date[32]; /* Build date info */
+};
+
+/* Accessor functions for big-endian fields */
+static inline u32 mvebu_imagetag_device_tree_offset(struct mvebu_image_tag *tag)
+{
+ return be32_to_cpu(tag->device_tree_offset);
+}
+
+static inline u32 mvebu_imagetag_device_tree_size(struct mvebu_image_tag *tag)
+{
+ return be32_to_cpu(tag->device_tree_size);
+}
+
+static inline u32 mvebu_imagetag_kernel_offset(struct mvebu_image_tag *tag)
+{
+ return be32_to_cpu(tag->kernel_offset);
+}
+
+static inline u32 mvebu_imagetag_kernel_size(struct mvebu_image_tag *tag)
+{
+ return be32_to_cpu(tag->kernel_size);
+}
+
+static inline u32 mvebu_imagetag_rootfs_offset(struct mvebu_image_tag *tag)
+{
+ return be32_to_cpu(tag->rootfs_offset);
+}
+
+static inline u32 mvebu_imagetag_rootfs_size(struct mvebu_image_tag *tag)
+{
+ return be32_to_cpu(tag->rootfs_size);
+}
+
+static inline u32 mvebu_imagetag_total_size(struct mvebu_image_tag *tag)
+{
+ return be32_to_cpu(tag->total_size);
+}
+
+#endif /* __MVEBU_IMAGETAG_H */
diff --git a/include/mvebu_nrboot.h b/include/mvebu_nrboot.h
new file mode 100644
index 00000000000..11d59d7680f
--- /dev/null
+++ b/include/mvebu_nrboot.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Nodebox 10G reboot tracking counter (NRBoot)
+ *
+ * Copyright (C) 2026 Free Mobile, Freebox
+ *
+ * Part of the Nodebox eMMC image format: a small bit-field counter
+ * stored between the serial info and the image banks that tracks
+ * consecutive boot failures to select the right bank.
+ */
+
+#ifndef __MVEBU_NRBOOT_H
+#define __MVEBU_NRBOOT_H
+
+#include <linux/types.h>
+#include <mmc.h>
+
+#define MVEBU_MAX_FAILURE 4
+
+/**
+ * struct mvebu_nrboot - Reboot tracking counter stored in eMMC
+ * @nrboot: Bit-field counter of boot attempts (0-bits = attempt count)
+ * @nrsuccess: Bit-field counter of successful boots
+ */
+struct mvebu_nrboot {
+ u16 nrboot;
+ u16 nrsuccess;
+};
+
+/**
+ * mvebu_check_nrboot() - Check and update reboot tracking counter
+ * @mmc: MMC device
+ * @offset: Byte offset in MMC where nrboot data is stored
+ *
+ * Reads the reboot tracking counter, checks if the maximum number of
+ * failed boots (4) has been exceeded, and updates the counter for the
+ * current boot attempt.
+ *
+ * The counter uses a bit-field encoding for wear leveling:
+ * - nrboot: Running count of boot attempts (counted as cleared bits)
+ * - nrsuccess: Count of successful boots (counted as cleared bits)
+ *
+ * If boot - success >= MAX_FAILURE (4), the system is considered
+ * degraded and should use the fallback boot bank.
+ *
+ * Return: 1 if system is healthy (try newer bank first),
+ * 0 if system is degraded (use stable bank first)
+ */
+int mvebu_check_nrboot(struct mmc *mmc, unsigned long offset);
+
+#endif /* __MVEBU_NRBOOT_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v1 5/5] cmd: mvebu: add device serial and MAC address initialization
2026-03-10 14:12 [PATCH v1 0/5] Add support for Freebox Nodebox 10G (Armada 8040) Vincent Jardin
` (3 preceding siblings ...)
2026-03-10 14:12 ` [PATCH v1 4/5] cmd: mvebu: add emmcboot for Nodebox image format Vincent Jardin
@ 2026-03-10 14:12 ` Vincent Jardin
2026-03-31 12:49 ` Stefan Roese
4 siblings, 1 reply; 11+ messages in thread
From: Vincent Jardin @ 2026-03-10 14:12 UTC (permalink / raw)
To: u-boot; +Cc: Stefan Roese, Tom Rini, Peng Fan, Jaehoon Chung, vjardin
Read device identification data from a dedicated eMMC region. This
provides:
- Unique device serial number for identification and tracking
- Factory-programmed MAC address for network interfaces
- Bundle information for device variant identification
The serial structure includes CRC32 validation to detect corruption.
On read failure or invalid data, sensible defaults are used to ensure
the system remains bootable.
The fbxserial command provides two subcommands:
- fbxserial show: Display serial info (default)
- fbxserial init: Initialize ethaddr from serial info
Each network interface gets a unique MAC address by incrementing the
base address stored in the serial structure.
The board file hooks EVT_SETTINGS_R to automatically set MAC addresses
before network drivers probe, ensuring addresses are available without
manual intervention.
Signed-off-by: Vincent Jardin <vjardin@free.fr>
---
board/freebox/nbx10g/board.c | 16 ++
cmd/mvebu/Kconfig | 27 +++
cmd/mvebu/Makefile | 1 +
cmd/mvebu/nbx_fbxserial.c | 289 ++++++++++++++++++++++++++++
configs/mvebu_nbx_88f8040_defconfig | 2 +
include/nbx_fbxserial.h | 134 +++++++++++++
6 files changed, 469 insertions(+)
create mode 100644 cmd/mvebu/nbx_fbxserial.c
create mode 100644 include/nbx_fbxserial.h
diff --git a/board/freebox/nbx10g/board.c b/board/freebox/nbx10g/board.c
index 7080e386c38..2cdf5f68be5 100644
--- a/board/freebox/nbx10g/board.c
+++ b/board/freebox/nbx10g/board.c
@@ -8,6 +8,7 @@
#include <config.h>
#include <dm.h>
+#include <event.h>
#include <init.h>
#include <asm/global_data.h>
#include <asm/gpio.h>
@@ -15,6 +16,7 @@
#include <asm/arch/cpu.h>
#include <asm/arch/soc.h>
#include <linux/delay.h>
+#include <nbx_fbxserial.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -61,3 +63,17 @@ int board_late_init(void)
return 0;
}
+
+/*
+ * Set MAC addresses from eMMC serial info before network driver probes.
+ * EVT_SETTINGS_R is triggered after MMC is available but before
+ * initr_net().
+ */
+static int nbx_fbx_settings_r(void)
+{
+ nbx_fbx_init_ethaddr(0, CONFIG_NBX_MMC_PART_SERIAL_OFFSET);
+
+ return 0;
+}
+
+EVENT_SPY_SIMPLE(EVT_SETTINGS_R, nbx_fbx_settings_r);
diff --git a/cmd/mvebu/Kconfig b/cmd/mvebu/Kconfig
index ea03f581280..764e2e7de94 100644
--- a/cmd/mvebu/Kconfig
+++ b/cmd/mvebu/Kconfig
@@ -132,4 +132,31 @@ config MVEBU_MMC_PART_BANK1_SIZE
endif
+config CMD_NBX_FBXSERIAL
+ bool "NBX fbxserial command"
+ depends on ARMADA_8K && MMC_SDHCI_XENON
+ help
+ Enable the NBX fbxserial command to read and display device
+ serial information from eMMC. This includes:
+ - Device serial number (type, version, manufacturer, date, number)
+ - MAC address (used to set ethaddr environment variables)
+ - Bundle information (if present)
+
+ The serial info is stored at a fixed offset in the eMMC user area.
+
+ Subcommands:
+ - fbxserial show: display serial info (default)
+ - fbxserial init: initialize ethaddr from serial info
+
+if CMD_NBX_FBXSERIAL
+
+config NBX_MMC_PART_SERIAL_OFFSET
+ hex "Serial info offset in eMMC"
+ default 0x800000
+ help
+ Byte offset in eMMC where the serial info structure is stored.
+ Default: 0x800000 (8MB)
+
+endif
+
endmenu
diff --git a/cmd/mvebu/Makefile b/cmd/mvebu/Makefile
index c096f507e26..b1dbd283c63 100644
--- a/cmd/mvebu/Makefile
+++ b/cmd/mvebu/Makefile
@@ -7,3 +7,4 @@
obj-$(CONFIG_CMD_MVEBU_BUBT) += bubt.o
obj-$(CONFIG_CMD_MVEBU_COMPHY_RX_TRAINING) += comphy_rx_training.o
obj-$(CONFIG_CMD_NBX_EMMCBOOT) += mvebu_emmcboot.o
+obj-$(CONFIG_CMD_NBX_FBXSERIAL) += nbx_fbxserial.o
diff --git a/cmd/mvebu/nbx_fbxserial.c b/cmd/mvebu/nbx_fbxserial.c
new file mode 100644
index 00000000000..baad5c56c10
--- /dev/null
+++ b/cmd/mvebu/nbx_fbxserial.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NBX Freebox Serial Info Support
+ *
+ * Copyright (C) 2026 Free Mobile, Freebox
+ *
+ * Reads device serial number and MAC address from eMMC.
+ * The serial info is stored at a fixed offset in the eMMC user area.
+ *
+ * Serial format: TTTT-VV-M-(YY)WW-NN-NNNNN / FLAGS
+ * Where:
+ * TTTT = Device type (e.g., 9018)
+ * VV = Board version
+ * M = Manufacturer code (ASCII)
+ * YY = Year (BCD)
+ * WW = Week (1-53)
+ * NNNNN = Serial number
+ * FLAGS = Feature flags
+ */
+
+#include <command.h>
+#include <env.h>
+#include <mmc.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <string.h>
+#include <vsprintf.h>
+#include <u-boot/crc.h>
+#include <asm/byteorder.h>
+#include <linux/ctype.h>
+#include <nbx_fbxserial.h>
+
+/* Default serial info (Freebox OUI, type 9018) */
+static const struct nbx_fbx_serial nbx_fbxserial_default = {
+ .crc32 = 0,
+ .magic = NBX_FBXSERIAL_MAGIC,
+ .struct_version = NBX_FBXSERIAL_VERSION,
+ .len = sizeof(struct nbx_fbx_serial),
+ .type = 9018,
+ .version = 0,
+ .manufacturer = '_',
+ .year = 0,
+ .week = 0,
+ .number = 0,
+ .flags = 0,
+ .mac_addr_base = { 0x00, 0x07, 0xCB, 0x00, 0x00, 0xFD },
+ .mac_count = 1,
+ .random_data = { 0 },
+ .last_modified = 0,
+ .extinfo_count = 0,
+};
+
+void nbx_fbxserial_set_default(struct nbx_fbx_serial *serial)
+{
+ memcpy(serial, &nbx_fbxserial_default, sizeof(*serial));
+}
+
+/*
+ * Validate serial info structure
+ */
+static int nbx_fbx_check_serial(struct nbx_fbx_serial *fs)
+{
+ unsigned int sum, len;
+
+ /* Check magic first */
+ if (be32_to_cpu(fs->magic) != NBX_FBXSERIAL_MAGIC) {
+ printf("Invalid magic for serial info (%08x != %08x)!\n",
+ be32_to_cpu(fs->magic), NBX_FBXSERIAL_MAGIC);
+ return -1;
+ }
+
+ /* Check struct version */
+ if (be32_to_cpu(fs->struct_version) > NBX_FBXSERIAL_VERSION) {
+ printf("Version too big for fbxserial info (0x%08x)!\n",
+ be32_to_cpu(fs->struct_version));
+ return -1;
+ }
+
+ /* Check for silly len */
+ len = be32_to_cpu(fs->len);
+ if (len > NBX_FBXSERIAL_MAX_SIZE) {
+ printf("Silly len for serial info (%d)\n", len);
+ return -1;
+ }
+
+ /* Validate CRC (crc32_no_comp: no one's complement) */
+ sum = crc32_no_comp(0, (void *)fs + 4, len - 4);
+ if (be32_to_cpu(fs->crc32) != sum) {
+ printf("Invalid checksum for serial info (%08x != %08x)\n",
+ sum, be32_to_cpu(fs->crc32));
+ return -1;
+ }
+
+ return 0;
+}
+
+int nbx_fbx_read_serial(int dev_num, unsigned long offset,
+ struct nbx_fbx_serial *fs)
+{
+ struct mmc *mmc;
+ struct blk_desc *bd;
+ uint blk_start, blk_cnt;
+ uint n;
+
+ ALLOC_CACHE_ALIGN_BUFFER(char, buf, ALIGN(sizeof(*fs), 512));
+ mmc = find_mmc_device(dev_num);
+ if (!mmc) {
+ printf("No MMC device %d found\n", dev_num);
+ nbx_fbxserial_set_default(fs);
+ return -1;
+ }
+
+ if (mmc_init(mmc)) {
+ puts("MMC init failed\n");
+ nbx_fbxserial_set_default(fs);
+ return -1;
+ }
+
+ /* Switch to partition 0 (user data area) */
+ if (blk_select_hwpart_devnum(UCLASS_MMC, dev_num, 0)) {
+ puts("MMC partition switch failed\n");
+ nbx_fbxserial_set_default(fs);
+ return -1;
+ }
+
+ bd = mmc_get_blk_desc(mmc);
+ if (!bd) {
+ puts("Failed to get MMC block descriptor\n");
+ nbx_fbxserial_set_default(fs);
+ return -1;
+ }
+
+ blk_start = ALIGN(offset, bd->blksz) / bd->blksz;
+ blk_cnt = ALIGN(sizeof(*fs), bd->blksz) / bd->blksz;
+
+ n = blk_dread(bd, blk_start, blk_cnt, buf);
+ if (n != blk_cnt) {
+ printf("Failed to read serial info from MMC\n");
+ nbx_fbxserial_set_default(fs);
+ return -1;
+ }
+
+ memcpy(fs, buf, sizeof(*fs));
+
+ if (nbx_fbx_check_serial(fs) != 0) {
+ nbx_fbxserial_set_default(fs);
+ return -1;
+ }
+
+ return 0;
+}
+
+void nbx_fbx_dump_serial(struct nbx_fbx_serial *fs)
+{
+ int i;
+
+ printf("Serial: %04u-%02u-%c-(%02u)%02u-%02u-%05u / %08x\n",
+ be16_to_cpu(fs->type),
+ fs->version,
+ isprint(fs->manufacturer) ? fs->manufacturer : '?',
+ be16_to_cpu(fs->year) / 100,
+ be16_to_cpu(fs->year) % 100,
+ fs->week,
+ be32_to_cpu(fs->number),
+ be32_to_cpu(fs->flags));
+
+ printf("Mac: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ fs->mac_addr_base[0],
+ fs->mac_addr_base[1],
+ fs->mac_addr_base[2],
+ fs->mac_addr_base[3],
+ fs->mac_addr_base[4],
+ fs->mac_addr_base[5]);
+
+ /* Show bundle info */
+ for (i = 0; i < be32_to_cpu(fs->extinfo_count); i++) {
+ struct nbx_serial_extinfo *p;
+
+ if (i >= NBX_EXTINFO_MAX_COUNT)
+ break;
+
+ p = &fs->extinfos[i];
+ if (be32_to_cpu(p->type) == NBX_EXTINFO_TYPE_EXTDEV &&
+ be32_to_cpu(p->u.extdev.type) == NBX_EXTDEV_TYPE_BUNDLE) {
+ /* Ensure null termination */
+ p->u.extdev.serial[sizeof(p->u.extdev.serial) - 1] = 0;
+ printf("Bundle: %s\n", p->u.extdev.serial);
+ }
+ }
+
+ printf("\n");
+}
+
+int nbx_fbx_init_ethaddr(int dev_num, unsigned long offset)
+{
+ struct nbx_fbx_serial fs;
+ char mac[32];
+ int ret;
+
+ ret = nbx_fbx_read_serial(dev_num, offset, &fs);
+
+ nbx_fbx_dump_serial(&fs);
+
+ /* Even on error, fs has default values set */
+ snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
+ fs.mac_addr_base[0], fs.mac_addr_base[1],
+ fs.mac_addr_base[2], fs.mac_addr_base[3],
+ fs.mac_addr_base[4], fs.mac_addr_base[5]);
+
+ env_set("ethaddr", mac);
+ env_set("eth1addr", mac);
+ env_set("eth2addr", mac);
+
+ return ret;
+}
+
+/*
+ * fbxserial show - display serial info from eMMC
+ */
+static int do_fbxserial_show(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct nbx_fbx_serial fs;
+ int dev = 0;
+ unsigned long offset = CONFIG_NBX_MMC_PART_SERIAL_OFFSET;
+
+ if (argc >= 1)
+ dev = dectoul(argv[0], NULL);
+
+ if (argc >= 2)
+ offset = hextoul(argv[1], NULL);
+
+ if (nbx_fbx_read_serial(dev, offset, &fs) != 0)
+ printf("Warning: Using default serial info\n");
+
+ nbx_fbx_dump_serial(&fs);
+
+ return CMD_RET_SUCCESS;
+}
+
+/*
+ * fbxserial init - initialize ethaddr from serial info
+ */
+static int do_fbxserial_init(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int dev = 0;
+ unsigned long offset = CONFIG_NBX_MMC_PART_SERIAL_OFFSET;
+
+ if (argc >= 1)
+ dev = dectoul(argv[0], NULL);
+
+ if (argc >= 2)
+ offset = hextoul(argv[1], NULL);
+
+ return nbx_fbx_init_ethaddr(dev, offset);
+}
+
+static struct cmd_tbl cmd_fbxserial_sub[] = {
+ U_BOOT_CMD_MKENT(show, 3, 0, do_fbxserial_show, "", ""),
+ U_BOOT_CMD_MKENT(init, 3, 0, do_fbxserial_init, "", ""),
+};
+
+static int do_fbxserial(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *cp;
+
+ /* Default to 'show' if no subcommand */
+ if (argc < 2)
+ return do_fbxserial_show(cmdtp, flag, 0, NULL);
+
+ cp = find_cmd_tbl(argv[1], cmd_fbxserial_sub,
+ ARRAY_SIZE(cmd_fbxserial_sub));
+
+ if (!cp)
+ return CMD_RET_USAGE;
+
+ return cp->cmd(cmdtp, flag, argc - 2, argv + 2);
+}
+
+U_BOOT_CMD(
+ fbxserial, 5, 0, do_fbxserial,
+ "NBX serial info and MAC address initialization",
+ "show [dev] [offset] - display serial info from eMMC\n"
+ "fbxserial init [dev] [offset] - initialize ethaddr from serial info\n"
+ " dev - MMC device number (default 0)\n"
+ " offset - offset in eMMC in hex (default from Kconfig)"
+);
diff --git a/configs/mvebu_nbx_88f8040_defconfig b/configs/mvebu_nbx_88f8040_defconfig
index 2fd58e4ad64..c295094536e 100644
--- a/configs/mvebu_nbx_88f8040_defconfig
+++ b/configs/mvebu_nbx_88f8040_defconfig
@@ -21,6 +21,7 @@ CONFIG_PREBOOT="echo (CRC warning is normal: no env saved yet)"
CONFIG_SYS_PBSIZE=1048
CONFIG_SYS_CONSOLE_INFO_QUIET=y
CONFIG_DISPLAY_CPUINFO=y
+CONFIG_LAST_STAGE_INIT=y
CONFIG_SYS_PROMPT="nodebox10G>> "
CONFIG_SYS_MAXARGS=32
CONFIG_CMD_BOOTZ=y
@@ -41,6 +42,7 @@ CONFIG_CMD_CACHE=y
CONFIG_CMD_TIME=y
CONFIG_CMD_TIMER=y
CONFIG_CMD_NBX_EMMCBOOT=y
+CONFIG_CMD_NBX_FBXSERIAL=y
CONFIG_CMD_EXT2=y
CONFIG_CMD_EXT4=y
CONFIG_CMD_EXT4_WRITE=y
diff --git a/include/nbx_fbxserial.h b/include/nbx_fbxserial.h
new file mode 100644
index 00000000000..7628e658f5e
--- /dev/null
+++ b/include/nbx_fbxserial.h
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * NBX Freebox Serial Info Support
+ *
+ * Copyright (C) 2026 Free Mobile, Freebox
+ *
+ * Reads device serial number and MAC address from eMMC.
+ * Used to identify the board and set network MAC addresses.
+ */
+
+#ifndef NBX_FBXSERIAL_H
+#define NBX_FBXSERIAL_H
+
+#include <linux/types.h>
+
+/*
+ * Extended info structure - variable data depending on type
+ */
+#define NBX_EXTINFO_SIZE 128
+#define NBX_EXTINFO_MAX_COUNT 16
+
+/* Extended info types */
+#define NBX_EXTINFO_TYPE_EXTDEV 1
+
+/* Extended device types */
+#define NBX_EXTDEV_TYPE_BUNDLE 1
+#define NBX_EXTDEV_TYPE_MAX 2
+
+struct nbx_serial_extinfo {
+ u32 type;
+
+ union {
+ /* extdev */
+ struct {
+ u32 type;
+ u32 model;
+ char serial[64];
+ } extdev;
+
+ /* raw access */
+ unsigned char data[NBX_EXTINFO_SIZE];
+ } u;
+} __packed;
+
+/*
+ * Master serial structure
+ */
+#define NBX_FBXSERIAL_VERSION 1
+#define NBX_FBXSERIAL_MAGIC 0x2d9521ab
+
+#define NBX_MAC_ADDR_SIZE 6
+#define NBX_RANDOM_DATA_SIZE 32
+
+/* Maximum size for CRC validation */
+#define NBX_FBXSERIAL_MAX_SIZE 8192
+
+struct nbx_fbx_serial {
+ u32 crc32;
+ u32 magic;
+ u32 struct_version;
+ u32 len;
+
+ /* Board serial */
+ u16 type;
+ u8 version;
+ u8 manufacturer;
+ u16 year;
+ u8 week;
+ u32 number;
+ u32 flags;
+
+ /* MAC address base */
+ u8 mac_addr_base[NBX_MAC_ADDR_SIZE];
+
+ /* MAC address count */
+ u8 mac_count;
+
+ /* Random data used to derive keys */
+ u8 random_data[NBX_RANDOM_DATA_SIZE];
+
+ /* Last update of data (seconds since epoch) */
+ u32 last_modified;
+
+ /* Count of following extinfo tags */
+ u32 extinfo_count;
+
+ /* Beginning of extended info */
+ struct nbx_serial_extinfo extinfos[NBX_EXTINFO_MAX_COUNT];
+} __packed;
+
+/**
+ * nbx_fbxserial_set_default() - Initialize serial structure with defaults
+ * @serial: Pointer to serial structure to initialize
+ *
+ * Sets the serial structure to default values (Freebox OUI, type 9018).
+ * Used as fallback when serial info cannot be read from eMMC.
+ */
+void nbx_fbxserial_set_default(struct nbx_fbx_serial *serial);
+
+/**
+ * nbx_fbx_read_serial() - Read serial info from eMMC
+ * @dev_num: MMC device number
+ * @offset: Byte offset in eMMC where serial info is stored
+ * @fs: Pointer to serial structure to fill
+ *
+ * Reads and validates the serial info from eMMC. On failure,
+ * the structure is filled with default values.
+ *
+ * Return: 0 on success, negative on error (defaults still set)
+ */
+int nbx_fbx_read_serial(int dev_num, unsigned long offset,
+ struct nbx_fbx_serial *fs);
+
+/**
+ * nbx_fbx_dump_serial() - Print serial info to console
+ * @fs: Pointer to serial structure to display
+ *
+ * Prints the serial number, MAC address, and bundle info (if present).
+ */
+void nbx_fbx_dump_serial(struct nbx_fbx_serial *fs);
+
+/**
+ * nbx_fbx_init_ethaddr() - Initialize Ethernet addresses from serial info
+ * @dev_num: MMC device number
+ * @offset: Byte offset in eMMC where serial info is stored
+ *
+ * Reads serial info and sets ethaddr, eth1addr, eth2addr environment
+ * variables from the base MAC address in the serial structure.
+ *
+ * Return: 0 on success, negative on error
+ */
+int nbx_fbx_init_ethaddr(int dev_num, unsigned long offset);
+
+#endif /* NBX_FBXSERIAL_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v1 1/5] arm: mach-mvebu: armada8k: add CPU and clock info display
2026-03-10 14:12 ` [PATCH v1 1/5] arm: mach-mvebu: armada8k: add CPU and clock info display Vincent Jardin
@ 2026-03-31 12:18 ` Stefan Roese
0 siblings, 0 replies; 11+ messages in thread
From: Stefan Roese @ 2026-03-31 12:18 UTC (permalink / raw)
To: Vincent Jardin, u-boot; +Cc: Tom Rini, Peng Fan, Jaehoon Chung
On 3/10/26 15:12, Vincent Jardin wrote:
> Add CPU information display for Armada 8040 platforms.
>
> The soc_info.c reads the AP806 Sample-At-Reset (SAR) register to
> determine the PLL clock configuration and converts it to actual
> CPU, DDR, and Fabric frequencies using the PLL frequency table.
> It also reports LLC (Last Level Cache) status.
>
> Based on soc_info.c from Marvell's U-Boot 2015.01 vendor tree.
>
> Signed-off-by: Vincent Jardin <vjardin@free.fr>
Since this is based on the original Marvell source, I won't start to
nitpick on minor coding issues. So:
Reviewed-by: Stefan Roese <stefan.roese@mailbox.org>
Thanks,
Stefan
> ---
> arch/arm/mach-mvebu/armada8k/Makefile | 2 +-
> arch/arm/mach-mvebu/armada8k/cpu.c | 10 ++
> arch/arm/mach-mvebu/armada8k/soc_info.c | 192 ++++++++++++++++++++++++
> arch/arm/mach-mvebu/armada8k/soc_info.h | 14 ++
> 4 files changed, 217 insertions(+), 1 deletion(-)
> create mode 100644 arch/arm/mach-mvebu/armada8k/soc_info.c
> create mode 100644 arch/arm/mach-mvebu/armada8k/soc_info.h
>
> diff --git a/arch/arm/mach-mvebu/armada8k/Makefile b/arch/arm/mach-mvebu/armada8k/Makefile
> index 0a4756717a3..723239d9894 100644
> --- a/arch/arm/mach-mvebu/armada8k/Makefile
> +++ b/arch/arm/mach-mvebu/armada8k/Makefile
> @@ -2,4 +2,4 @@
> #
> # Copyright (C) 2016 Stefan Roese <sr@denx.de>
>
> -obj-y = cpu.o cache_llc.o dram.o
> +obj-y = cpu.o cache_llc.o dram.o soc_info.o
> diff --git a/arch/arm/mach-mvebu/armada8k/cpu.c b/arch/arm/mach-mvebu/armada8k/cpu.c
> index 3eb93c82387..22767f143c2 100644
> --- a/arch/arm/mach-mvebu/armada8k/cpu.c
> +++ b/arch/arm/mach-mvebu/armada8k/cpu.c
> @@ -15,6 +15,8 @@
> #include <asm/armv8/mmu.h>
> #include <mach/fw_info.h>
>
> +#include "soc_info.h"
> +
> /* Armada 7k/8k */
> #define MVEBU_RFU_BASE (MVEBU_REGISTER(0x6f0000))
> #define RFU_GLOBAL_SW_RST (MVEBU_RFU_BASE + 0x84)
> @@ -111,3 +113,11 @@ int mmc_get_env_dev(void)
>
> return CONFIG_ENV_MMC_DEVICE_INDEX;
> }
> +
> +int print_cpuinfo(void)
> +{
> + soc_print_clock_info();
> + soc_print_soc_info();
> +
> + return 0;
> +}
> diff --git a/arch/arm/mach-mvebu/armada8k/soc_info.c b/arch/arm/mach-mvebu/armada8k/soc_info.c
> new file mode 100644
> index 00000000000..a5c3b17cb7d
> --- /dev/null
> +++ b/arch/arm/mach-mvebu/armada8k/soc_info.c
> @@ -0,0 +1,192 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2015 Marvell International Ltd.
> + *
> + * Marvell Armada 8K SoC info: SAR, Clock frequencies, LLC status
> + * Ported from Marvell U-Boot 2015.01 to mainline U-Boot.
> + */
> +
> +#include <config.h>
> +#include <linux/types.h>
> +#include <linux/kernel.h>
> +#include <vsprintf.h>
> +#include <asm/io.h>
> +#include <asm/arch/soc.h>
> +
> +/* Clock frequency unit */
> +#define MHz 1000000
> +
> +/* AP806 SAR (Sample-At-Reset) register */
> +#define AP806_SAR_REG_BASE (SOC_REGS_PHY_BASE + 0x6F4400)
> +#define SAR_CLOCK_FREQ_MODE_OFFSET 0
> +#define SAR_CLOCK_FREQ_MODE_MASK (0x1f << SAR_CLOCK_FREQ_MODE_OFFSET)
> +
> +/* LLC (Last Level Cache) registers */
> +#define LLC_BASE (SOC_REGS_PHY_BASE + 0x8000)
> +#define LLC_CTRL 0x100
> +#define LLC_CTRL_EN 0x1
> +#define LLC_EXCLUSIVE_EN 0x100
> +
> +/* MSS clock is fixed at 200MHz on AP806 */
> +#define AP806_MSS_CLOCK (200 * MHz)
> +
> +/* Clock ID indices in PLL frequency table */
> +#define CPU_CLOCK_ID 0
> +#define DDR_CLOCK_ID 1
> +#define RING_CLOCK_ID 2
> +
> +/* Clocking options (SAR field values) */
> +enum clocking_options {
> + CPU_2000_DDR_1200_RCLK_1200 = 0x0,
> + CPU_2000_DDR_1050_RCLK_1050 = 0x1,
> + CPU_1600_DDR_800_RCLK_800 = 0x4,
> + CPU_1800_DDR_1200_RCLK_1200 = 0x6,
> + CPU_1800_DDR_1050_RCLK_1050 = 0x7,
> + CPU_1600_DDR_900_RCLK_900 = 0x0b,
> + CPU_1600_DDR_1050_RCLK_1050 = 0x0d,
> + CPU_1600_DDR_900_RCLK_900_2 = 0x0e,
> + CPU_1000_DDR_650_RCLK_650 = 0x13,
> + CPU_1300_DDR_800_RCLK_800 = 0x14,
> + CPU_1300_DDR_650_RCLK_650 = 0x17,
> + CPU_1200_DDR_800_RCLK_800 = 0x19,
> + CPU_1400_DDR_800_RCLK_800 = 0x1a,
> + CPU_600_DDR_800_RCLK_800 = 0x1b,
> + CPU_800_DDR_800_RCLK_800 = 0x1c,
> + CPU_1000_DDR_800_RCLK_800 = 0x1d,
> +};
> +
> +/*
> + * PLL frequency table: maps SAR clock mode to actual frequencies.
> + * Format: { CPU_freq, DDR_freq, RING_freq, SAR_value }
> + */
> +static const u32 pll_freq_tbl[16][4] = {
> + /* CPU */ /* DDR */ /* Ring */
> + {2000 * MHz, 1200 * MHz, 1200 * MHz, CPU_2000_DDR_1200_RCLK_1200},
> + {2000 * MHz, 1050 * MHz, 1050 * MHz, CPU_2000_DDR_1050_RCLK_1050},
> + {1800 * MHz, 1200 * MHz, 1200 * MHz, CPU_1800_DDR_1200_RCLK_1200},
> + {1800 * MHz, 1050 * MHz, 1050 * MHz, CPU_1800_DDR_1050_RCLK_1050},
> + {1600 * MHz, 1050 * MHz, 1050 * MHz, CPU_1600_DDR_1050_RCLK_1050},
> + {1600 * MHz, 900 * MHz, 900 * MHz, CPU_1600_DDR_900_RCLK_900_2},
> + {1300 * MHz, 800 * MHz, 800 * MHz, CPU_1300_DDR_800_RCLK_800},
> + {1300 * MHz, 650 * MHz, 650 * MHz, CPU_1300_DDR_650_RCLK_650},
> + {1600 * MHz, 800 * MHz, 800 * MHz, CPU_1600_DDR_800_RCLK_800},
> + {1600 * MHz, 900 * MHz, 900 * MHz, CPU_1600_DDR_900_RCLK_900},
> + {1000 * MHz, 650 * MHz, 650 * MHz, CPU_1000_DDR_650_RCLK_650},
> + {1200 * MHz, 800 * MHz, 800 * MHz, CPU_1200_DDR_800_RCLK_800},
> + {1400 * MHz, 800 * MHz, 800 * MHz, CPU_1400_DDR_800_RCLK_800},
> + {600 * MHz, 800 * MHz, 800 * MHz, CPU_600_DDR_800_RCLK_800},
> + {800 * MHz, 800 * MHz, 800 * MHz, CPU_800_DDR_800_RCLK_800},
> + {1000 * MHz, 800 * MHz, 800 * MHz, CPU_1000_DDR_800_RCLK_800}
> +};
> +
> +/*
> + * Get the clock frequency mode index from SAR register.
> + * Returns index into pll_freq_tbl, or -1 if not found.
> + */
> +static int sar_get_clock_freq_mode(void)
> +{
> + u32 i;
> + u32 clock_freq;
> +
> + clock_freq = (readl(AP806_SAR_REG_BASE) & SAR_CLOCK_FREQ_MODE_MASK)
> + >> SAR_CLOCK_FREQ_MODE_OFFSET;
> +
> + for (i = 0; i < ARRAY_SIZE(pll_freq_tbl); i++) {
> + if (pll_freq_tbl[i][3] == clock_freq)
> + return i;
> + }
> +
> + pr_err("SAR: unsupported clock freq mode %d\n", clock_freq);
> + return -1;
> +}
> +
> +/*
> + * Get CPU clock frequency in Hz.
> + */
> +static u32 soc_cpu_clk_get(void)
> +{
> + int mode = sar_get_clock_freq_mode();
> +
> + if (mode < 0)
> + return 0;
> + return pll_freq_tbl[mode][CPU_CLOCK_ID];
> +}
> +
> +/*
> + * Get DDR clock frequency in Hz.
> + */
> +static u32 soc_ddr_clk_get(void)
> +{
> + int mode = sar_get_clock_freq_mode();
> +
> + if (mode < 0)
> + return 0;
> + return pll_freq_tbl[mode][DDR_CLOCK_ID];
> +}
> +
> +/*
> + * Get Ring (Fabric) clock frequency in Hz.
> + */
> +static u32 soc_ring_clk_get(void)
> +{
> + int mode = sar_get_clock_freq_mode();
> +
> + if (mode < 0)
> + return 0;
> + return pll_freq_tbl[mode][RING_CLOCK_ID];
> +}
> +
> +/*
> + * Get MSS clock frequency in Hz.
> + */
> +static u32 soc_mss_clk_get(void)
> +{
> + return AP806_MSS_CLOCK;
> +}
> +
> +/*
> + * Get LLC status and mode.
> + * Returns 1 if LLC is enabled, 0 otherwise.
> + * If excl_mode is not NULL, sets it to 1 if exclusive mode is enabled.
> + */
> +static int llc_mode_get(int *excl_mode)
> +{
> + u32 val;
> + int ret = 0, excl = 0;
> +
> + val = readl(LLC_BASE + LLC_CTRL);
> + if (val & LLC_CTRL_EN) {
> + ret = 1;
> + if (val & LLC_EXCLUSIVE_EN)
> + excl = 1;
> + }
> + if (excl_mode)
> + *excl_mode = excl;
> +
> + return ret;
> +}
> +
> +/*
> + * Print SoC clock information.
> + */
> +void soc_print_clock_info(void)
> +{
> + printf("Clock: CPU %-4d [MHz]\n", soc_cpu_clk_get() / MHz);
> + printf("\tDDR %-4d [MHz]\n", soc_ddr_clk_get() / MHz);
> + printf("\tFABRIC %-4d [MHz]\n", soc_ring_clk_get() / MHz);
> + printf("\tMSS %-4d [MHz]\n", soc_mss_clk_get() / MHz);
> +}
> +
> +/*
> + * Print SoC-specific information: DDR width and LLC status.
> + */
> +void soc_print_soc_info(void)
> +{
> + int llc_en, llc_excl_mode;
> +
> + printf("\tDDR 64 Bit width\n");
> +
> + llc_en = llc_mode_get(&llc_excl_mode);
> + printf("\tLLC %s%s\n", llc_en ? "Enabled" : "Disabled",
> + llc_excl_mode ? " (Exclusive Mode)" : "");
> +}
> diff --git a/arch/arm/mach-mvebu/armada8k/soc_info.h b/arch/arm/mach-mvebu/armada8k/soc_info.h
> new file mode 100644
> index 00000000000..41afe7a2508
> --- /dev/null
> +++ b/arch/arm/mach-mvebu/armada8k/soc_info.h
> @@ -0,0 +1,14 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C) 2015 Marvell International Ltd.
> + *
> + * Marvell Armada 8K SoC info functions
> + */
> +
> +#ifndef _ARMADA8K_SOC_INFO_H_
> +#define _ARMADA8K_SOC_INFO_H_
> +
> +void soc_print_clock_info(void);
> +void soc_print_soc_info(void);
> +
> +#endif /* _ARMADA8K_SOC_INFO_H_ */
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v1 2/5] board: freebox: add Nodebox 10G board support
2026-03-10 14:12 ` [PATCH v1 2/5] board: freebox: add Nodebox 10G board support Vincent Jardin
@ 2026-03-31 12:35 ` Stefan Roese
0 siblings, 0 replies; 11+ messages in thread
From: Stefan Roese @ 2026-03-31 12:35 UTC (permalink / raw)
To: Vincent Jardin, u-boot; +Cc: Tom Rini, Peng Fan, Jaehoon Chung
On 3/10/26 15:12, Vincent Jardin wrote:
> Add board support for the Freebox Nodebox 10G based on the Marvell
> Armada 8040 SoC. This board features:
>
> - Quad-core ARMv8 AP806 with dual CP110 companions
> - eMMC storage via Xenon SDHCI controller
> - 1G SGMII and 10G SFI Ethernet on CP0 and CP1
> - I2C buses for peripheral access
> - NS16550 UART console at 115200 baud
>
> The implementation includes:
> - Device tree for the Nodebox 10G hardware
> - Dedicated board directory (board/freebox/nbx10g/)
> - Board-specific Kconfig and defconfig
> - Management PHY reset via GPIO in board_late_init()
>
> Signed-off-by: Vincent Jardin <vjardin@free.fr>
> ---
> arch/arm/dts/Makefile | 1 +
> arch/arm/dts/armada-8040-nbx.dts | 259 ++++++++++++++++++++++++++++
Just curious:
Is this available in mainline Linux as well? Or are there plans to do
upstream it there?
Ah, thinking about this. All this serdes / commphy device-tree stuff is
most likely not compatible between U-Boot & Linux, correct?
> arch/arm/mach-mvebu/Kconfig | 9 +
> board/freebox/nbx10g/Kconfig | 12 ++
> board/freebox/nbx10g/MAINTAINERS | 13 ++
> board/freebox/nbx10g/Makefile | 3 +
> board/freebox/nbx10g/board.c | 63 +++++++
> configs/mvebu_nbx_88f8040_defconfig | 73 ++++++++
> include/configs/nbx10g.h | 29 ++++
> 9 files changed, 462 insertions(+)
> create mode 100644 arch/arm/dts/armada-8040-nbx.dts
> create mode 100644 board/freebox/nbx10g/Kconfig
> create mode 100644 board/freebox/nbx10g/MAINTAINERS
> create mode 100644 board/freebox/nbx10g/Makefile
> create mode 100644 board/freebox/nbx10g/board.c
> create mode 100644 configs/mvebu_nbx_88f8040_defconfig
> create mode 100644 include/configs/nbx10g.h
>
> diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
> index 82ad3035308..1638d19e44d 100644
> --- a/arch/arm/dts/Makefile
> +++ b/arch/arm/dts/Makefile
> @@ -180,6 +180,7 @@ dtb-$(CONFIG_ARCH_MVEBU) += \
> armada-8040-clearfog-gt-8k.dtb \
> armada-8040-db.dtb \
> armada-8040-mcbin.dtb \
> + armada-8040-nbx.dtb \
> armada-8040-puzzle-m801.dtb \
> cn9130-db-A.dtb \
> cn9130-db-B.dtb \
> diff --git a/arch/arm/dts/armada-8040-nbx.dts b/arch/arm/dts/armada-8040-nbx.dts
> new file mode 100644
> index 00000000000..bfa8927c5e5
> --- /dev/null
> +++ b/arch/arm/dts/armada-8040-nbx.dts
> @@ -0,0 +1,259 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Device Tree file for NBX board (Freebox Nodebox10G)
> + * Based on Marvell Armada 8040 SoC
> + *
> + * Copyright (C) 2024 Freebox SA
> + */
> +
> +#include "armada-8040.dtsi"
> +
> +/ {
> + model = "NBX Armada 8040";
> + compatible = "nbx,armada8040", "marvell,armada8040";
> +
> + chosen {
> + stdout-path = "serial0:115200n8";
> + };
> +
> + aliases {
> + i2c0 = &cp0_i2c0;
> + i2c1 = &cp0_i2c1;
> + gpio0 = &ap_gpio0;
> + gpio1 = &cp0_gpio0;
> + gpio2 = &cp0_gpio1;
> + };
> +
> + memory@00000000 {
> + device_type = "memory";
> + reg = <0x0 0x0 0x0 0x80000000>; /* 2GB */
> + };
> +};
> +
> +/* AP806 UART - active */
> +&uart0 {
> + status = "okay";
> +};
> +
> +/* AP806 pinctrl */
> +&ap_pinctl {
> + /*
> + * MPP Bus:
> + * eMMC [0-10]
> + * UART0 [11,19]
> + */
> + /* 0 1 2 3 4 5 6 7 8 9 */
> + pin-func = < 1 1 1 1 1 1 1 1 1 1
> + 1 3 0 0 0 0 0 0 0 3 >;
> +};
> +
> +/* AP806 on-board eMMC */
> +&ap_sdhci0 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&ap_emmc_pins>;
> + bus-width = <8>;
> + non-removable;
> + status = "okay";
> +};
> +
> +/* CP0 pinctrl */
> +&cp0_pinctl {
> + /*
> + * MPP Bus:
> + * [0-31] = 0xff: Keep default CP0_shared_pins
> + * [32,34] GE_MDIO/MDC
> + * [35-36] I2C1
> + * [37-38] I2C0
> + * [57-58] MSS I2C
> + */
> + /* 0 1 2 3 4 5 6 7 8 9 */
> + pin-func = < 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
> + 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
> + 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
> + 0xff 0xff 7 0 7 2 2 2 2 0
> + 0 0 0 0 0 0 0 0 0 0
> + 0 0 0 0 0 0 0 2 2 0
> + 0 0 0 >;
> +
> + cp0_smi_pins: cp0-smi-pins {
> + marvell,pins = <32 34>;
> + marvell,function = <7>;
> + };
> +};
> +
> +/* CP0 I2C0 */
> +&cp0_i2c0 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&cp0_i2c0_pins>;
> + status = "okay";
> + clock-frequency = <100000>;
> +};
> +
> +/* CP0 I2C1 */
> +&cp0_i2c1 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&cp0_i2c1_pins>;
> + status = "okay";
> + clock-frequency = <100000>;
> +};
> +
> +/* CP0 MSS I2C0 - Management SubSystem I2C (pins 57-58, func 2) */
> +&cp0_mss_i2c0 {
> + status = "okay";
> +};
> +
> +/* CP0 MDIO for PHY */
> +&cp0_mdio {
> + status = "okay";
> + pinctrl-names = "default";
> + pinctrl-0 = <&cp0_smi_pins>;
> +
> + nbx_phy0: ethernet-phy@0 {
> + reg = <0>;
> + };
> +};
> +
> +/* CP0 ComPhy - SerDes configuration */
> +&cp0_comphy {
> + /*
> + * CP0 Serdes Configuration:
> + * Lane 0-3: Unconnected
> + * Lane 4: SFI (10G Ethernet)
> + * Lane 5: SGMII2 (1G Ethernet)
> + */
> + phy0 {
> + phy-type = <COMPHY_TYPE_UNCONNECTED>;
> + };
> + phy1 {
> + phy-type = <COMPHY_TYPE_UNCONNECTED>;
> + };
> + phy2 {
> + phy-type = <COMPHY_TYPE_UNCONNECTED>;
> + };
> + phy3 {
> + phy-type = <COMPHY_TYPE_UNCONNECTED>;
> + };
> + phy4 {
> + phy-type = <COMPHY_TYPE_SFI0>;
> + phy-speed = <COMPHY_SPEED_10_3125G>;
> + };
> + phy5 {
> + phy-type = <COMPHY_TYPE_SGMII2>;
> + phy-speed = <COMPHY_SPEED_1_25G>;
> + };
> +};
> +
> +/* CP0 Ethernet - only eth2 (MAC3) is active via SGMII */
> +&cp0_ethernet {
> + status = "okay";
> +};
> +
> +&cp0_eth2 {
> + status = "okay";
> + phy = <&nbx_phy0>;
> + phy-mode = "sgmii";
> +};
> +
> +/* CP0 UTMI PHY for USB */
> +&cp0_utmi {
> + status = "okay";
> +};
> +
> +&cp0_utmi0 {
> + status = "okay";
> +};
> +
> +&cp0_utmi1 {
> + status = "okay";
> +};
> +
> +/* CP0 USB3 Host controllers */
> +&cp0_usb3_0 {
> + status = "okay";
> +};
> +
> +&cp0_usb3_1 {
> + status = "okay";
> +};
> +
> +/* CP1 pinctrl */
> +&cp1_pinctl {
> + /*
> + * MPP Bus:
> + * [0-26] = Unconfigured
> + * [27-28] GE_MDIO/MDC
> + * [29-30] MSS I2C
> + * [31] = Unconfigured
> + * [32-62] = 0xff: Keep default CP1_shared_pins
> + */
> + /* 0 1 2 3 4 5 6 7 8 9 */
> + pin-func = < 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
> + 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
> + 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x8 0x8 0x8
> + 0x8 0x0 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
> + 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
> + 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
> + 0xff 0xff 0xff>;
> +
> + cp1_mss_i2c_pins: cp1-mss-i2c-pins {
> + marvell,pins = <29 30>;
> + marvell,function = <8>;
> + };
> +};
> +
> +/* CP1 MSS I2C0 - Management SubSystem I2C (pins 29-30, func 8) */
> +&cp1_mss_i2c0 {
> + status = "okay";
> + pinctrl-names = "default";
> + pinctrl-0 = <&cp1_mss_i2c_pins>;
> +};
> +
> +/* CP1 ComPhy - SerDes configuration */
> +&cp1_comphy {
> + /*
> + * CP1 Serdes Configuration:
> + * Lane 0: PCIe x1
> + * Lane 1: USB3 Host
> + * Lane 2-3: Unconnected
> + * Lane 4: SFI (10G Ethernet)
> + * Lane 5: Unconnected
> + */
> + phy0 {
> + phy-type = <COMPHY_TYPE_PEX0>;
> + };
> + phy1 {
> + phy-type = <COMPHY_TYPE_USB3_HOST0>;
> + };
> + phy2 {
> + phy-type = <COMPHY_TYPE_UNCONNECTED>;
> + };
> + phy3 {
> + phy-type = <COMPHY_TYPE_UNCONNECTED>;
> + };
> + phy4 {
> + phy-type = <COMPHY_TYPE_SFI0>;
> + phy-speed = <COMPHY_SPEED_10_3125G>;
> + };
> + phy5 {
> + phy-type = <COMPHY_TYPE_UNCONNECTED>;
> + };
> +};
> +
> +/* CP1 PCIe x1 on lane 0 */
> +&cp1_pcie0 {
> + status = "okay";
> +};
> +
> +/* CP1 USB3 Host on lane 1 */
> +&cp1_usb3_0 {
> + status = "okay";
> +};
> +
> +/* CP1 UTMI PHY for USB */
> +&cp1_utmi {
> + status = "okay";
> +};
> +
> +&cp1_utmi0 {
> + status = "okay";
> +};
> diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig
> index b76510ab452..66e5f1788d6 100644
> --- a/arch/arm/mach-mvebu/Kconfig
> +++ b/arch/arm/mach-mvebu/Kconfig
> @@ -166,6 +166,14 @@ config TARGET_MVEBU_ARMADA_8K
> select BOARD_LATE_INIT
> imply SCSI
>
> +config TARGET_NBX10G
> + bool "Support Freebox Nodebox 10G"
> + select ARMADA_8K
> + select BOARD_LATE_INIT
> + help
> + Enable support for the Freebox Nodebox 10G board based on the
> + Marvell Armada 8040 SoC with dual CP110 companion chips.
> +
> config TARGET_MVEBU_ALLEYCAT5
> bool "Support AlleyCat 5 platforms"
> select ALLEYCAT_5
> @@ -508,5 +516,6 @@ config ARMADA_32BIT_SYSCON_SYSRESET
>
> source "board/solidrun/clearfog/Kconfig"
> source "board/kobol/helios4/Kconfig"
> +source "board/freebox/nbx10g/Kconfig"
>
> endif
> diff --git a/board/freebox/nbx10g/Kconfig b/board/freebox/nbx10g/Kconfig
> new file mode 100644
> index 00000000000..18a169761b7
> --- /dev/null
> +++ b/board/freebox/nbx10g/Kconfig
> @@ -0,0 +1,12 @@
> +if TARGET_NBX10G
> +
> +config SYS_BOARD
> + default "nbx10g"
> +
> +config SYS_VENDOR
> + default "freebox"
> +
> +config SYS_CONFIG_NAME
> + default "nbx10g"
> +
> +endif
> diff --git a/board/freebox/nbx10g/MAINTAINERS b/board/freebox/nbx10g/MAINTAINERS
> new file mode 100644
> index 00000000000..4753a9a13bb
> --- /dev/null
> +++ b/board/freebox/nbx10g/MAINTAINERS
> @@ -0,0 +1,13 @@
> +NBX10G BOARD
> +M: Vincent Jardin <vjardin@free.fr>
> +S: Maintained
> +F: board/freebox/nbx10g/
> +F: configs/mvebu_nbx_88f8040_defconfig
> +F: arch/arm/dts/armada-8040-nbx.dts
> +F: arch/arm/dts/armada-8040-nbx-u-boot.dtsi
> +F: include/configs/nbx10g.h
> +F: cmd/mvebu/mvebu_emmcboot.c
> +F: cmd/mvebu/nbx_fbxserial.c
> +F: include/mvebu_imagetag.h
> +F: include/mvebu_nrboot.h
> +F: include/nbx_fbxserial.h
> diff --git a/board/freebox/nbx10g/Makefile b/board/freebox/nbx10g/Makefile
> new file mode 100644
> index 00000000000..bf83bdf63ee
> --- /dev/null
> +++ b/board/freebox/nbx10g/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0+
> +
> +obj-y := board.o
> diff --git a/board/freebox/nbx10g/board.c b/board/freebox/nbx10g/board.c
> new file mode 100644
> index 00000000000..7080e386c38
> --- /dev/null
> +++ b/board/freebox/nbx10g/board.c
> @@ -0,0 +1,63 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2017-2018 Freebox SA
> + * Copyright (C) 2026 Free Mobile, Vincent Jardin <vjardin@free.fr>
> + *
> + * Freebox Nodebox 10G board support
> + */
> +
> +#include <config.h>
> +#include <dm.h>
> +#include <init.h>
> +#include <asm/global_data.h>
> +#include <asm/gpio.h>
> +#include <asm/io.h>
> +#include <asm/arch/cpu.h>
> +#include <asm/arch/soc.h>
> +#include <linux/delay.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +/* Management PHY reset GPIO */
> +#define NBX_PHY_RESET_GPIO 83
> +
> +/* Nodebox 10G ASCII art logo */
> +static const char * const nbx_logo =
> + " _ _ _ _ __ ___ _____\n"
> + " | \\ | | | | | | /_ |/ _ \\ / ____|\n"
> + " | \\| | ___ __| | ___| |__ _____ __ | | | | | | __\n"
> + " | . ` |/ _ \\ / _` |/ _ \\ '_ \\ / _ \\ \\/ / | | | | | | |_ |\n"
> + " | |\\ | (_) | (_| | __/ |_) | (_) > < | | |_| | |__| |\n"
> + " |_| \\_|\\___/ \\__,_|\\___|_.__/ \\___/_/\\_\\ |_|\\___/ \\_____|\n";
> +
> +int checkboard(void)
> +{
> + printf("%s\n", nbx_logo);
> + return 0;
> +}
> +
> +int board_init(void)
> +{
> + gd->bd->bi_boot_params = CFG_SYS_SDRAM_BASE + 0x100;
Is this really needed? If not, this function can be dropped most likely.
Other than this looks good:
Reviewed-by: Stefan Roese <stefan.roese@mailbox.org>
Thanks,
Stefan
> + return 0;
> +}
> +
> +int board_late_init(void)
> +{
> + int ret;
> +
> + /* Reset the management PHY */
> + ret = gpio_request(NBX_PHY_RESET_GPIO, "phy-reset");
> + if (ret) {
> + printf("Failed to request PHY reset GPIO: %d\n", ret);
> + return 0;
> + }
> +
> + gpio_direction_output(NBX_PHY_RESET_GPIO, 0);
> + mdelay(100);
> + gpio_set_value(NBX_PHY_RESET_GPIO, 1);
> + mdelay(100);
> + gpio_free(NBX_PHY_RESET_GPIO);
> +
> + return 0;
> +}
> diff --git a/configs/mvebu_nbx_88f8040_defconfig b/configs/mvebu_nbx_88f8040_defconfig
> new file mode 100644
> index 00000000000..975c53e9a92
> --- /dev/null
> +++ b/configs/mvebu_nbx_88f8040_defconfig
> @@ -0,0 +1,73 @@
> +CONFIG_ARM=y
> +CONFIG_ARCH_CPU_INIT=y
> +CONFIG_ARCH_MVEBU=y
> +CONFIG_TEXT_BASE=0x00000000
> +CONFIG_NR_DRAM_BANKS=2
> +CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
> +CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0xff0000
> +CONFIG_TARGET_NBX10G=y
> +CONFIG_ENV_SIZE=0x10000
> +CONFIG_ENV_OFFSET=0x180000
> +CONFIG_DM_GPIO=y
> +CONFIG_DEFAULT_DEVICE_TREE="armada-8040-nbx"
> +CONFIG_FIT=y
> +CONFIG_SYS_BOOTM_LEN=0x1000000
> +CONFIG_SYS_LOAD_ADDR=0x800000
> +CONFIG_PCI=y
> +# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
> +# CONFIG_EFI_LOADER is not set
> +CONFIG_USE_PREBOOT=y
> +CONFIG_PREBOOT="echo (CRC warning is normal: no env saved yet)"
> +CONFIG_SYS_PBSIZE=1048
> +CONFIG_SYS_CONSOLE_INFO_QUIET=y
> +CONFIG_DISPLAY_CPUINFO=y
> +CONFIG_SYS_PROMPT="nodebox10G>> "
> +CONFIG_SYS_MAXARGS=32
> +CONFIG_CMD_BOOTZ=y
> +# CONFIG_BOOTM_VXWORKS is not set
> +# CONFIG_CMD_ELF is not set
> +CONFIG_CMD_MEMTEST=y
> +CONFIG_CMD_GPIO=y
> +CONFIG_CMD_I2C=y
> +CONFIG_CMD_MISC=y
> +CONFIG_CMD_MMC=y
> +CONFIG_CMD_PCI=y
> +# CONFIG_CMD_SF is not set
> +CONFIG_CMD_USB=y
> +CONFIG_CMD_DHCP=y
> +CONFIG_CMD_MII=y
> +CONFIG_CMD_PING=y
> +CONFIG_CMD_CACHE=y
> +CONFIG_CMD_TIME=y
> +CONFIG_CMD_TIMER=y
> +CONFIG_CMD_EXT2=y
> +CONFIG_CMD_EXT4=y
> +CONFIG_CMD_EXT4_WRITE=y
> +CONFIG_CMD_FAT=y
> +CONFIG_CMD_FS_GENERIC=y
> +CONFIG_EFI_PARTITION=y
> +CONFIG_ENV_OVERWRITE=y
> +CONFIG_ENV_IS_IN_MMC=y
> +CONFIG_ENV_RELOC_GD_ENV_ADDR=y
> +CONFIG_ARP_TIMEOUT=200
> +CONFIG_NET_RETRY_COUNT=50
> +CONFIG_NET_RANDOM_ETHADDR=y
> +CONFIG_DM_I2C=y
> +CONFIG_SYS_I2C_MVTWSI=y
> +CONFIG_I2C_MUX=y
> +CONFIG_MISC=y
> +CONFIG_MMC_SDHCI=y
> +CONFIG_MMC_SDHCI_XENON=y
> +# CONFIG_SPI_FLASH is not set
> +CONFIG_PHY_MARVELL=y
> +CONFIG_PHY_GIGE=y
> +CONFIG_MVPP2=y
> +CONFIG_PCIE_DW_MVEBU=y
> +CONFIG_PHY=y
> +CONFIG_MVEBU_COMPHY_SUPPORT=y
> +CONFIG_PINCTRL=y
> +CONFIG_PINCTRL_ARMADA_8K=y
> +CONFIG_SYS_NS16550=y
> +CONFIG_USB=y
> +CONFIG_USB_XHCI_HCD=y
> +CONFIG_USB_STORAGE=y
> diff --git a/include/configs/nbx10g.h b/include/configs/nbx10g.h
> new file mode 100644
> index 00000000000..bd083b7e7d8
> --- /dev/null
> +++ b/include/configs/nbx10g.h
> @@ -0,0 +1,29 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C) 2017-2018 Freebox SA
> + * Copyright (C) 2026 Free Mobile, Vincent Jardin <vjardin@free.fr>
> + *
> + * Configuration for Freebox Nodebox 10G
> + */
> +
> +#ifndef _CONFIG_NBX10G_H
> +#define _CONFIG_NBX10G_H
> +
> +#include "mvebu_armada-8k.h"
> +
> +/* Override environment settings for NBX */
> +#undef CFG_EXTRA_ENV_SETTINGS
> +#define CFG_EXTRA_ENV_SETTINGS \
> + "hostname=nodebox10G\0" \
> + "ethrotate=no\0" \
> + "image_addr=0x7000000\0" \
> + "image_name=Image.nodebox10G\0" \
> + "fdt_addr=0x6f00000\0" \
> + "fdt_name=nodebox10G.dtb\0" \
> + "console=ttyS0,115200\0" \
> + "tftpboot=setenv bootargs console=${console} bank=tftp; " \
> + "dhcp ${image_addr} ${image_name}; " \
> + "tftp ${fdt_addr} ${fdt_name}; " \
> + "booti ${image_addr} - ${fdt_addr}\0"
> +
> +#endif /* _CONFIG_NBX10G_H */
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v1 3/5] arm: dts: armada-8040-nbx: add U-Boot dtsi for conditional OP-TEE
2026-03-10 14:12 ` [PATCH v1 3/5] arm: dts: armada-8040-nbx: add U-Boot dtsi for conditional OP-TEE Vincent Jardin
@ 2026-03-31 12:35 ` Stefan Roese
0 siblings, 0 replies; 11+ messages in thread
From: Stefan Roese @ 2026-03-31 12:35 UTC (permalink / raw)
To: Vincent Jardin, u-boot; +Cc: Tom Rini, Peng Fan, Jaehoon Chung
On 3/10/26 15:12, Vincent Jardin wrote:
> Add armada-8040-nbx-u-boot.dtsi with the firmware/optee node guarded
> by CONFIG_OPTEE. This allows U-Boot to probe the OP-TEE driver only
> when OP-TEE support is enabled in the configuration.
>
> Signed-off-by: Vincent Jardin <vjardin@free.fr>
Reviewed-by: Stefan Roese <stefan.roese@mailbox.org>
Thanks,
Stefan
> ---
> arch/arm/dts/armada-8040-nbx-u-boot.dtsi | 15 +++++++++++++++
> 1 file changed, 15 insertions(+)
> create mode 100644 arch/arm/dts/armada-8040-nbx-u-boot.dtsi
>
> diff --git a/arch/arm/dts/armada-8040-nbx-u-boot.dtsi b/arch/arm/dts/armada-8040-nbx-u-boot.dtsi
> new file mode 100644
> index 00000000000..dec473b7156
> --- /dev/null
> +++ b/arch/arm/dts/armada-8040-nbx-u-boot.dtsi
> @@ -0,0 +1,15 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2026 Free Mobile, Vincent Jardin
> + */
> +
> +#ifdef CONFIG_OPTEE
> +/ {
> + firmware {
> + optee {
> + compatible = "linaro,optee-tz";
> + method = "smc";
> + };
> + };
> +};
> +#endif
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v1 4/5] cmd: mvebu: add emmcboot for Nodebox image format
2026-03-10 14:12 ` [PATCH v1 4/5] cmd: mvebu: add emmcboot for Nodebox image format Vincent Jardin
@ 2026-03-31 12:46 ` Stefan Roese
0 siblings, 0 replies; 11+ messages in thread
From: Stefan Roese @ 2026-03-31 12:46 UTC (permalink / raw)
To: Vincent Jardin, u-boot; +Cc: Tom Rini, Peng Fan, Jaehoon Chung
On 3/10/26 15:12, Vincent Jardin wrote:
> The Nodebox 10G stores firmware in a board-specific format
> (mvebu_image_tag) at fixed offsets in eMMC. Each image bundles a
> kernel and device tree under a CRC32-validated tag header.
>
> Include a board-specific rollback support based on a reboot counter
> in order to track consecutive boot failures and then to boot from
> the fallback image.
So is all this board-specific or platform specific? If it's board-
specific, wouldn't this command better reside in the board directory?
Thinking about this a bit more, is there something board-specific or
even platform-specific in this emmc boot cmd and/or format? Could other
targets perhaps use this as well? Seems to be a pretty "lightweight"
failsafe boot method, even though I did not look too deep into it.
Another thought / question:
Not sure if we need new methods though. What is the reasoning for
introducing this emmc boot method?
> Signed-off-by: Vincent Jardin <vjardin@free.fr>
> ---
> cmd/mvebu/Kconfig | 53 +++++
> cmd/mvebu/Makefile | 1 +
> cmd/mvebu/mvebu_emmcboot.c | 340 ++++++++++++++++++++++++++++
> configs/mvebu_nbx_88f8040_defconfig | 1 +
> include/mvebu_imagetag.h | 82 +++++++
> include/mvebu_nrboot.h | 51 +++++
> 6 files changed, 528 insertions(+)
> create mode 100644 cmd/mvebu/mvebu_emmcboot.c
> create mode 100644 include/mvebu_imagetag.h
> create mode 100644 include/mvebu_nrboot.h
>
> diff --git a/cmd/mvebu/Kconfig b/cmd/mvebu/Kconfig
> index e83a9829491..ea03f581280 100644
> --- a/cmd/mvebu/Kconfig
> +++ b/cmd/mvebu/Kconfig
> @@ -79,4 +79,57 @@ config CMD_MVEBU_COMPHY_RX_TRAINING
> help
> Perform COMPHY RX training sequence
>
> +config CMD_NBX_EMMCBOOT
> + bool "Nodebox emmcboot command"
> + depends on ARMADA_8K && MMC_SDHCI_XENON
> + help
> + Enable the emmcboot command for Nodebox 10G boards. The command
> + loads and boots firmware stored in the board-specific image format
> + (mvebu_image_tag) at fixed eMMC offsets. Each image bundles a
> + kernel and device tree under a CRC32-validated tag header.
> +
> + Two image banks are supported (Bank0 stable, Bank1 newer) with
> + automatic fallback based on a reboot tracking counter (nrboot).
> +
> + Requires image_addr and fdt_addr environment variables to be set.
> +
> +if CMD_NBX_EMMCBOOT
> +
> +config MVEBU_MMC_PART_NRBOOT_OFFSET
> + hex "NRBoot counter offset in eMMC"
> + default 0x802000
> + help
> + Byte offset in eMMC where the reboot tracking counter is stored.
> + Default: 0x802000 (8MB + 8KB)
> +
> +config MVEBU_MMC_PART_BANK0_OFFSET
> + hex "Bank0 image offset in eMMC"
> + default 0x804000
> + help
> + Byte offset in eMMC where the stable (Bank0) boot image starts.
> + Default: 0x804000 (8MB + 16KB)
> +
> +config MVEBU_MMC_PART_BANK0_SIZE
> + hex "Bank0 image maximum size"
> + default 0x10000000
> + help
> + Maximum size of the Bank0 boot image.
> + Default: 0x10000000 (256MB)
> +
> +config MVEBU_MMC_PART_BANK1_OFFSET
> + hex "Bank1 image offset in eMMC"
> + default 0x10804000
> + help
> + Byte offset in eMMC where the newer (Bank1) boot image starts.
> + Default: 0x10804000 (264MB + 16KB)
> +
> +config MVEBU_MMC_PART_BANK1_SIZE
> + hex "Bank1 image maximum size"
> + default 0x10000000
> + help
> + Maximum size of the Bank1 boot image.
> + Default: 0x10000000 (256MB)
> +
> +endif
> +
> endmenu
> diff --git a/cmd/mvebu/Makefile b/cmd/mvebu/Makefile
> index ca96ad01d91..c096f507e26 100644
> --- a/cmd/mvebu/Makefile
> +++ b/cmd/mvebu/Makefile
> @@ -6,3 +6,4 @@
>
> obj-$(CONFIG_CMD_MVEBU_BUBT) += bubt.o
> obj-$(CONFIG_CMD_MVEBU_COMPHY_RX_TRAINING) += comphy_rx_training.o
> +obj-$(CONFIG_CMD_NBX_EMMCBOOT) += mvebu_emmcboot.o
> diff --git a/cmd/mvebu/mvebu_emmcboot.c b/cmd/mvebu/mvebu_emmcboot.c
> new file mode 100644
> index 00000000000..3d85f82670a
> --- /dev/null
> +++ b/cmd/mvebu/mvebu_emmcboot.c
> @@ -0,0 +1,340 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * MVEBU eMMC boot command for Nodebox 10G boot image format
> + *
> + * Copyright (C) 2026 Free Mobile, Freebox
> + *
> + * The Nodebox 10G stores firmware images in a board-specific format
> + * (mvebu_image_tag) at fixed offsets in eMMC. Each image bundles a
> + * kernel, device tree, and optional rootfs under a CRC32-validated
> + * tag header.
> + *
> + * Two image banks are laid out in eMMC (Bank0 stable, Bank1 newer).
> + * A reboot counter (nrboot) selects which bank to try first.
> + */
> +
> +#include <command.h>
> +#include <env.h>
> +#include <mmc.h>
> +#include <malloc.h>
> +#include <memalign.h>
> +#include <vsprintf.h>
> +#include <u-boot/crc.h>
> +#include <u-boot/schedule.h>
> +#include <asm/byteorder.h>
> +#include <linux/bitops.h>
> +#include <mvebu_imagetag.h>
> +#include <mvebu_nrboot.h>
> +
> +/* Image Tag Functions */
> +
> +static int mvebu_imagetag_check(struct mvebu_image_tag *tag,
> + unsigned long maxsize, const char *name)
> +{
> + if (be32_to_cpu(tag->magic) != MVEBU_IMAGE_TAG_MAGIC) {
> + if (name)
> + printf("%s: invalid TAG magic: %.8x\n", name,
> + be32_to_cpu(tag->magic));
> + return -1;
Could you use an actuall errno code instead? And in other places as
well?
> + }
> +
> + if (be32_to_cpu(tag->version) != MVEBU_IMAGE_TAG_VERSION) {
> + if (name)
> + printf("%s: invalid TAG version: %.8x\n", name,
> + be32_to_cpu(tag->version));
> + return -1;
> + }
> +
> + if (be32_to_cpu(tag->total_size) < sizeof(*tag)) {
> + if (name)
> + printf("%s: tag size is too small!\n", name);
> + return -1;
> + }
> +
> + if (be32_to_cpu(tag->total_size) > maxsize) {
> + if (name)
> + printf("%s: tag size is too big!\n", name);
> + return -1;
> + }
> +
> + if (be32_to_cpu(tag->device_tree_offset) < sizeof(*tag) ||
> + be32_to_cpu(tag->device_tree_offset) +
> + be32_to_cpu(tag->device_tree_size) > maxsize) {
> + if (name)
> + printf("%s: bogus device tree offset/size!\n", name);
> + return -1;
> + }
> +
> + if (be32_to_cpu(tag->kernel_offset) < sizeof(*tag) ||
> + be32_to_cpu(tag->kernel_offset) +
> + be32_to_cpu(tag->kernel_size) > maxsize) {
> + if (name)
> + printf("%s: bogus kernel offset/size!\n", name);
> + return -1;
> + }
> +
> + if (be32_to_cpu(tag->rootfs_offset) < sizeof(*tag) ||
> + be32_to_cpu(tag->rootfs_offset) +
> + be32_to_cpu(tag->rootfs_size) > maxsize) {
> + if (name)
> + printf("%s: bogus rootfs offset/size!\n", name);
> + return -1;
> + }
> +
> + if (name) {
> + /*
> + * Ensure null-termination within the 32-byte fields
> + * before printing to avoid displaying garbage.
> + */
> + tag->image_name[sizeof(tag->image_name) - 1] = '\0';
> + tag->build_date[sizeof(tag->build_date) - 1] = '\0';
> + tag->build_user[sizeof(tag->build_user) - 1] = '\0';
> +
> + printf("%s: Found valid tag: %s / %s / %s\n", name,
> + tag->image_name, tag->build_date, tag->build_user);
> + }
> +
> + return 0;
> +}
> +
> +static int mvebu_imagetag_crc(struct mvebu_image_tag *tag, const char *name)
> +{
> + u32 crc = ~0;
> +
> + crc = crc32(crc, ((unsigned char *)tag) + 4,
> + be32_to_cpu(tag->total_size) - 4);
> +
> + if (be32_to_cpu(tag->crc) != crc) {
> + if (name)
> + printf("%s: invalid tag CRC!\n", name);
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +/* NRBoot (Reboot Tracking) Functions */
> +
> +int mvebu_check_nrboot(struct mmc *mmc, unsigned long offset)
> +{
> + struct blk_desc *bd = mmc_get_blk_desc(mmc);
> + struct mvebu_nrboot *nr;
> + uint blk_start = ALIGN(offset, bd->blksz) / bd->blksz;
> + uint blk_cnt = ALIGN(sizeof(*nr), bd->blksz) / bd->blksz;
> + uint n;
> +
> + ALLOC_CACHE_ALIGN_BUFFER(char, buf, blk_cnt * bd->blksz);
> + nr = (void *)buf;
> +
> + n = blk_dread(bd, blk_start, blk_cnt, buf);
> + if (n != blk_cnt)
> + return 0;
> +
> + printf(" - nr.nrboot = %04x\n", nr->nrboot);
> + printf(" - nr.nrsuccess = %04x\n", nr->nrsuccess);
> +
> + /* Sanity check: values must be valid bit-field counters */
> + if (generic_hweight16(~nr->nrboot + 1) <= 1 &&
> + generic_hweight16(~nr->nrsuccess + 1) <= 1) {
> + int boot, success;
> +
> + boot = 16 - generic_hweight16(nr->nrboot);
> + success = 16 - generic_hweight16(nr->nrsuccess);
> +
> + printf(" - Nrboot: %d / Nrsuccess: %d\n", boot, success);
> +
> + if (boot == 16 || boot < success ||
> + boot - success >= MVEBU_MAX_FAILURE) {
> + printf(" - Nrboot exceeded\n");
> + return 0;
> + }
> +
> + /* Increment boot attempt counter */
> + boot++;
> + nr->nrboot = ~((1 << boot) - 1);
> +
> + printf(" - Setting Nrboot to %d\n", boot);
> +
> + n = blk_dwrite(bd, blk_start, blk_cnt, buf);
> + if (n != blk_cnt)
> + return 0;
> +
> + return 1;
> + }
> +
> + printf(" - Invalid NR values\n");
> +
> + return 0;
> +}
> +
> +/* emmcboot Command */
> +
> +static int mvebu_load_image(struct blk_desc *bd, unsigned long offset,
> + unsigned long maxsize, ulong tag_addr,
> + const char *bank)
> +{
> + struct mvebu_image_tag *tag;
> + uint blk_start = ALIGN(offset, bd->blksz) / bd->blksz;
> + uint blk_cnt;
> + uint n;
> +
> + ALLOC_CACHE_ALIGN_BUFFER(char, tag_buf,
> + ALIGN(sizeof(*tag), bd->blksz));
> + tag = (void *)tag_buf;
> +
> + /* Load and validate tag header */
> + blk_cnt = ALIGN(sizeof(*tag), bd->blksz) / bd->blksz;
> + n = blk_dread(bd, blk_start, blk_cnt, tag_buf);
> + if (n != blk_cnt) {
> + printf("%s: failed to read tag header\n", bank);
> + return -1;
> + }
> +
> + if (mvebu_imagetag_check(tag, maxsize, bank) != 0)
> + return -1;
> +
> + if (tag->rootfs_size != 0) {
> + printf("%s: rootfs in tag not supported\n", bank);
> + return -1;
> + }
> +
> + /* Load full image to tag_addr */
> + blk_cnt = ALIGN(mvebu_imagetag_total_size(tag), bd->blksz) / bd->blksz;
> + n = blk_dread(bd, blk_start, blk_cnt, (void *)tag_addr);
> + if (n != blk_cnt) {
> + printf("%s: failed to read full image\n", bank);
> + return -1;
> + }
> +
> + if (mvebu_imagetag_crc((void *)tag_addr, bank) != 0)
> + return -1;
> +
> + return 0;
> +}
> +
> +static void mvebu_relocate_and_boot(ulong image_addr, ulong fdt_addr,
> + const char *bank)
> +{
> + struct mvebu_image_tag *tag = (void *)image_addr;
> + char bootargs[256];
> + char cmd[128];
> + char *console_env;
> +
> + /* Copy DTB and kernel to their final addresses */
> + memcpy((void *)fdt_addr,
> + ((void *)image_addr) + mvebu_imagetag_device_tree_offset(tag),
> + mvebu_imagetag_device_tree_size(tag));
> + memmove((void *)image_addr,
> + ((void *)image_addr) + mvebu_imagetag_kernel_offset(tag),
> + mvebu_imagetag_kernel_size(tag));
> +
> + schedule();
> +
> + /* Set bootargs */
> + console_env = env_get("console");
> + if (console_env)
> + snprintf(bootargs, sizeof(bootargs),
> + "console=%s bank=%s", console_env, bank);
> + else
> + snprintf(bootargs, sizeof(bootargs), "bank=%s", bank);
> +
> + env_set("bootargs", bootargs);
> +
> + printf("## Booting kernel from %s...\n", bank);
> + printf(" Image addr: 0x%lx\n", image_addr);
> + printf(" FDT addr: 0x%lx\n", fdt_addr);
> +
> + snprintf(cmd, sizeof(cmd), "booti 0x%lx - 0x%lx",
> + image_addr, fdt_addr);
> + run_command(cmd, 0);
> +}
> +
> +static void mvebu_try_emmcboot(struct mmc *mmc, unsigned long offset,
> + unsigned long maxsize, const char *bank)
> +{
> + struct blk_desc *bd = mmc_get_blk_desc(mmc);
> + ulong image_addr;
> + ulong fdt_addr;
> +
> + schedule();
> +
> + printf("## Trying %s boot...\n", bank);
> +
> + /* Get load addresses from environment */
> + image_addr = env_get_ulong("image_addr", 16, 0);
> + if (!image_addr) {
> + puts("emmcboot needs image_addr\n");
> + return;
> + }
> +
> + fdt_addr = env_get_ulong("fdt_addr", 16, 0);
> + if (!fdt_addr) {
> + puts("emmcboot needs fdt_addr\n");
> + return;
> + }
> +
> + if (mvebu_load_image(bd, offset, maxsize, image_addr, bank) != 0)
> + return;
> +
> + schedule();
> +
> + mvebu_relocate_and_boot(image_addr, fdt_addr, bank);
> +
> + printf("## %s boot failed\n", bank);
> +}
> +
> +static int do_emmcboot(struct cmd_tbl *cmdtp, int flag, int argc,
> + char *const argv[])
> +{
> + int dev;
> + struct mmc *mmc;
> +
> + dev = 0;
> + if (argc >= 2)
> + dev = dectoul(argv[1], NULL);
> +
> + mmc = find_mmc_device(dev);
> + if (!mmc) {
> + printf("No MMC device %d found\n", dev);
> + return CMD_RET_FAILURE;
> + }
> +
> + if (mmc_init(mmc)) {
> + puts("MMC init failed\n");
> + return CMD_RET_FAILURE;
> + }
> +
> + /* Switch to partition 0 (user data area) */
> + if (blk_select_hwpart_devnum(UCLASS_MMC, dev, 0)) {
> + puts("MMC partition switch failed\n");
> + return CMD_RET_FAILURE;
> + }
> +
> + if (mvebu_check_nrboot(mmc, CONFIG_MVEBU_MMC_PART_NRBOOT_OFFSET)) {
> + /* System is healthy: try newer bank first */
> + mvebu_try_emmcboot(mmc, CONFIG_MVEBU_MMC_PART_BANK1_OFFSET,
> + CONFIG_MVEBU_MMC_PART_BANK1_SIZE, "bank1");
> + mvebu_try_emmcboot(mmc, CONFIG_MVEBU_MMC_PART_BANK0_OFFSET,
> + CONFIG_MVEBU_MMC_PART_BANK0_SIZE, "bank0");
> + } else {
> + /* System is degraded: use stable bank first */
> + mvebu_try_emmcboot(mmc, CONFIG_MVEBU_MMC_PART_BANK0_OFFSET,
> + CONFIG_MVEBU_MMC_PART_BANK0_SIZE, "bank0");
> + mvebu_try_emmcboot(mmc, CONFIG_MVEBU_MMC_PART_BANK1_OFFSET,
> + CONFIG_MVEBU_MMC_PART_BANK1_SIZE, "bank1");
> + }
> +
> + puts("emmcboot: all boot attempts failed\n");
> +
> + return CMD_RET_FAILURE;
> +}
> +
> +U_BOOT_CMD(
> + emmcboot, 2, 0, do_emmcboot,
> + "boot from Nodebox eMMC image banks",
> + "[dev]\n"
> + " - Load and boot a Nodebox image from eMMC device <dev> (default 0)\n"
> + " - Requires image_addr and fdt_addr environment variables\n"
> + " - Two banks: Bank0 (stable) and Bank1 (newer)\n"
> + " - Bank order selected by reboot tracking counter (nrboot)"
> +);
> diff --git a/configs/mvebu_nbx_88f8040_defconfig b/configs/mvebu_nbx_88f8040_defconfig
> index 975c53e9a92..2fd58e4ad64 100644
> --- a/configs/mvebu_nbx_88f8040_defconfig
> +++ b/configs/mvebu_nbx_88f8040_defconfig
> @@ -40,6 +40,7 @@ CONFIG_CMD_PING=y
> CONFIG_CMD_CACHE=y
> CONFIG_CMD_TIME=y
> CONFIG_CMD_TIMER=y
> +CONFIG_CMD_NBX_EMMCBOOT=y
> CONFIG_CMD_EXT2=y
> CONFIG_CMD_EXT4=y
> CONFIG_CMD_EXT4_WRITE=y
> diff --git a/include/mvebu_imagetag.h b/include/mvebu_imagetag.h
> new file mode 100644
> index 00000000000..d513038aaf6
> --- /dev/null
> +++ b/include/mvebu_imagetag.h
> @@ -0,0 +1,82 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Nodebox 10G boot image tag format
> + *
> + * Copyright (C) 2026 Free Mobile, Freebox
> + *
> + * Defines the on-eMMC image layout used by Nodebox 10G boards.
> + * Each image carries a CRC32-validated tag header that describes
> + * the kernel, device tree, and rootfs components.
> + */
> +
> +#ifndef __MVEBU_IMAGETAG_H
> +#define __MVEBU_IMAGETAG_H
> +
> +#include <linux/types.h>
> +
> +#define MVEBU_IMAGE_TAG_MAGIC 0x8d7c90bc
> +#define MVEBU_IMAGE_TAG_VERSION 1
> +
> +/**
> + * struct mvebu_image_tag - Nodebox boot image tag stored in eMMC
> + *
> + * All multi-byte fields are stored in big-endian format.
> + */
> +struct mvebu_image_tag {
> + u32 crc; /* CRC32-LE checksum (from offset 4) */
> + u32 magic; /* Magic: 0x8d7c90bc */
> + u32 version; /* Version: 1 */
> + u32 total_size; /* Total image size including tag */
> + u32 flags; /* Feature flags (reserved) */
> +
> + u32 device_tree_offset; /* Offset from tag start to DTB */
> + u32 device_tree_size; /* DTB size in bytes */
> +
> + u32 kernel_offset; /* Offset from tag start to kernel */
> + u32 kernel_size; /* Kernel size in bytes */
> +
> + u32 rootfs_offset; /* Offset from tag start to rootfs */
> + u32 rootfs_size; /* Rootfs size (must be 0) */
> +
> + char image_name[32]; /* Image name (null-terminated) */
> + char build_user[32]; /* Build user info */
> + char build_date[32]; /* Build date info */
> +};
> +
> +/* Accessor functions for big-endian fields */
> +static inline u32 mvebu_imagetag_device_tree_offset(struct mvebu_image_tag *tag)
> +{
> + return be32_to_cpu(tag->device_tree_offset);
> +}
> +
> +static inline u32 mvebu_imagetag_device_tree_size(struct mvebu_image_tag *tag)
> +{
> + return be32_to_cpu(tag->device_tree_size);
> +}
> +
> +static inline u32 mvebu_imagetag_kernel_offset(struct mvebu_image_tag *tag)
> +{
> + return be32_to_cpu(tag->kernel_offset);
> +}
> +
> +static inline u32 mvebu_imagetag_kernel_size(struct mvebu_image_tag *tag)
> +{
> + return be32_to_cpu(tag->kernel_size);
> +}
> +
> +static inline u32 mvebu_imagetag_rootfs_offset(struct mvebu_image_tag *tag)
> +{
> + return be32_to_cpu(tag->rootfs_offset);
> +}
> +
> +static inline u32 mvebu_imagetag_rootfs_size(struct mvebu_image_tag *tag)
> +{
> + return be32_to_cpu(tag->rootfs_size);
> +}
> +
> +static inline u32 mvebu_imagetag_total_size(struct mvebu_image_tag *tag)
> +{
> + return be32_to_cpu(tag->total_size);
> +}
> +
> +#endif /* __MVEBU_IMAGETAG_H */
> diff --git a/include/mvebu_nrboot.h b/include/mvebu_nrboot.h
> new file mode 100644
> index 00000000000..11d59d7680f
> --- /dev/null
> +++ b/include/mvebu_nrboot.h
> @@ -0,0 +1,51 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Nodebox 10G reboot tracking counter (NRBoot)
> + *
> + * Copyright (C) 2026 Free Mobile, Freebox
> + *
> + * Part of the Nodebox eMMC image format: a small bit-field counter
> + * stored between the serial info and the image banks that tracks
> + * consecutive boot failures to select the right bank.
> + */
> +
> +#ifndef __MVEBU_NRBOOT_H
> +#define __MVEBU_NRBOOT_H
> +
> +#include <linux/types.h>
> +#include <mmc.h>
> +
> +#define MVEBU_MAX_FAILURE 4
> +
> +/**
> + * struct mvebu_nrboot - Reboot tracking counter stored in eMMC
> + * @nrboot: Bit-field counter of boot attempts (0-bits = attempt count)
> + * @nrsuccess: Bit-field counter of successful boots
> + */
> +struct mvebu_nrboot {
> + u16 nrboot;
> + u16 nrsuccess;
> +};
> +
> +/**
> + * mvebu_check_nrboot() - Check and update reboot tracking counter
> + * @mmc: MMC device
> + * @offset: Byte offset in MMC where nrboot data is stored
> + *
> + * Reads the reboot tracking counter, checks if the maximum number of
> + * failed boots (4) has been exceeded, and updates the counter for the
> + * current boot attempt.
> + *
> + * The counter uses a bit-field encoding for wear leveling:
> + * - nrboot: Running count of boot attempts (counted as cleared bits)
> + * - nrsuccess: Count of successful boots (counted as cleared bits)
> + *
> + * If boot - success >= MAX_FAILURE (4), the system is considered
> + * degraded and should use the fallback boot bank.
> + *
> + * Return: 1 if system is healthy (try newer bank first),
> + * 0 if system is degraded (use stable bank first)
> + */
> +int mvebu_check_nrboot(struct mmc *mmc, unsigned long offset);
> +
> +#endif /* __MVEBU_NRBOOT_H */
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v1 5/5] cmd: mvebu: add device serial and MAC address initialization
2026-03-10 14:12 ` [PATCH v1 5/5] cmd: mvebu: add device serial and MAC address initialization Vincent Jardin
@ 2026-03-31 12:49 ` Stefan Roese
0 siblings, 0 replies; 11+ messages in thread
From: Stefan Roese @ 2026-03-31 12:49 UTC (permalink / raw)
To: Vincent Jardin, u-boot; +Cc: Tom Rini, Peng Fan, Jaehoon Chung
On 3/10/26 15:12, Vincent Jardin wrote:
> Read device identification data from a dedicated eMMC region. This
> provides:
>
> - Unique device serial number for identification and tracking
> - Factory-programmed MAC address for network interfaces
> - Bundle information for device variant identification
>
> The serial structure includes CRC32 validation to detect corruption.
> On read failure or invalid data, sensible defaults are used to ensure
> the system remains bootable.
>
> The fbxserial command provides two subcommands:
> - fbxserial show: Display serial info (default)
> - fbxserial init: Initialize ethaddr from serial info
>
> Each network interface gets a unique MAC address by incrementing the
> base address stored in the serial structure.
>
> The board file hooks EVT_SETTINGS_R to automatically set MAC addresses
> before network drivers probe, ensuring addresses are available without
> manual intervention.
Again my question, if this is mvebu specific? If not, it should perhaps
be moved to the board directory. Or is useful for other boards (non
mvebu) then perhaps even to cmd?
Thanks,
Stefan
> Signed-off-by: Vincent Jardin <vjardin@free.fr>
> ---
> board/freebox/nbx10g/board.c | 16 ++
> cmd/mvebu/Kconfig | 27 +++
> cmd/mvebu/Makefile | 1 +
> cmd/mvebu/nbx_fbxserial.c | 289 ++++++++++++++++++++++++++++
> configs/mvebu_nbx_88f8040_defconfig | 2 +
> include/nbx_fbxserial.h | 134 +++++++++++++
> 6 files changed, 469 insertions(+)
> create mode 100644 cmd/mvebu/nbx_fbxserial.c
> create mode 100644 include/nbx_fbxserial.h
>
> diff --git a/board/freebox/nbx10g/board.c b/board/freebox/nbx10g/board.c
> index 7080e386c38..2cdf5f68be5 100644
> --- a/board/freebox/nbx10g/board.c
> +++ b/board/freebox/nbx10g/board.c
> @@ -8,6 +8,7 @@
>
> #include <config.h>
> #include <dm.h>
> +#include <event.h>
> #include <init.h>
> #include <asm/global_data.h>
> #include <asm/gpio.h>
> @@ -15,6 +16,7 @@
> #include <asm/arch/cpu.h>
> #include <asm/arch/soc.h>
> #include <linux/delay.h>
> +#include <nbx_fbxserial.h>
>
> DECLARE_GLOBAL_DATA_PTR;
>
> @@ -61,3 +63,17 @@ int board_late_init(void)
>
> return 0;
> }
> +
> +/*
> + * Set MAC addresses from eMMC serial info before network driver probes.
> + * EVT_SETTINGS_R is triggered after MMC is available but before
> + * initr_net().
> + */
> +static int nbx_fbx_settings_r(void)
> +{
> + nbx_fbx_init_ethaddr(0, CONFIG_NBX_MMC_PART_SERIAL_OFFSET);
> +
> + return 0;
> +}
> +
> +EVENT_SPY_SIMPLE(EVT_SETTINGS_R, nbx_fbx_settings_r);
> diff --git a/cmd/mvebu/Kconfig b/cmd/mvebu/Kconfig
> index ea03f581280..764e2e7de94 100644
> --- a/cmd/mvebu/Kconfig
> +++ b/cmd/mvebu/Kconfig
> @@ -132,4 +132,31 @@ config MVEBU_MMC_PART_BANK1_SIZE
>
> endif
>
> +config CMD_NBX_FBXSERIAL
> + bool "NBX fbxserial command"
> + depends on ARMADA_8K && MMC_SDHCI_XENON
> + help
> + Enable the NBX fbxserial command to read and display device
> + serial information from eMMC. This includes:
> + - Device serial number (type, version, manufacturer, date, number)
> + - MAC address (used to set ethaddr environment variables)
> + - Bundle information (if present)
> +
> + The serial info is stored at a fixed offset in the eMMC user area.
> +
> + Subcommands:
> + - fbxserial show: display serial info (default)
> + - fbxserial init: initialize ethaddr from serial info
> +
> +if CMD_NBX_FBXSERIAL
> +
> +config NBX_MMC_PART_SERIAL_OFFSET
> + hex "Serial info offset in eMMC"
> + default 0x800000
> + help
> + Byte offset in eMMC where the serial info structure is stored.
> + Default: 0x800000 (8MB)
> +
> +endif
> +
> endmenu
> diff --git a/cmd/mvebu/Makefile b/cmd/mvebu/Makefile
> index c096f507e26..b1dbd283c63 100644
> --- a/cmd/mvebu/Makefile
> +++ b/cmd/mvebu/Makefile
> @@ -7,3 +7,4 @@
> obj-$(CONFIG_CMD_MVEBU_BUBT) += bubt.o
> obj-$(CONFIG_CMD_MVEBU_COMPHY_RX_TRAINING) += comphy_rx_training.o
> obj-$(CONFIG_CMD_NBX_EMMCBOOT) += mvebu_emmcboot.o
> +obj-$(CONFIG_CMD_NBX_FBXSERIAL) += nbx_fbxserial.o
> diff --git a/cmd/mvebu/nbx_fbxserial.c b/cmd/mvebu/nbx_fbxserial.c
> new file mode 100644
> index 00000000000..baad5c56c10
> --- /dev/null
> +++ b/cmd/mvebu/nbx_fbxserial.c
> @@ -0,0 +1,289 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * NBX Freebox Serial Info Support
> + *
> + * Copyright (C) 2026 Free Mobile, Freebox
> + *
> + * Reads device serial number and MAC address from eMMC.
> + * The serial info is stored at a fixed offset in the eMMC user area.
> + *
> + * Serial format: TTTT-VV-M-(YY)WW-NN-NNNNN / FLAGS
> + * Where:
> + * TTTT = Device type (e.g., 9018)
> + * VV = Board version
> + * M = Manufacturer code (ASCII)
> + * YY = Year (BCD)
> + * WW = Week (1-53)
> + * NNNNN = Serial number
> + * FLAGS = Feature flags
> + */
> +
> +#include <command.h>
> +#include <env.h>
> +#include <mmc.h>
> +#include <malloc.h>
> +#include <memalign.h>
> +#include <string.h>
> +#include <vsprintf.h>
> +#include <u-boot/crc.h>
> +#include <asm/byteorder.h>
> +#include <linux/ctype.h>
> +#include <nbx_fbxserial.h>
> +
> +/* Default serial info (Freebox OUI, type 9018) */
> +static const struct nbx_fbx_serial nbx_fbxserial_default = {
> + .crc32 = 0,
> + .magic = NBX_FBXSERIAL_MAGIC,
> + .struct_version = NBX_FBXSERIAL_VERSION,
> + .len = sizeof(struct nbx_fbx_serial),
> + .type = 9018,
> + .version = 0,
> + .manufacturer = '_',
> + .year = 0,
> + .week = 0,
> + .number = 0,
> + .flags = 0,
> + .mac_addr_base = { 0x00, 0x07, 0xCB, 0x00, 0x00, 0xFD },
> + .mac_count = 1,
> + .random_data = { 0 },
> + .last_modified = 0,
> + .extinfo_count = 0,
> +};
> +
> +void nbx_fbxserial_set_default(struct nbx_fbx_serial *serial)
> +{
> + memcpy(serial, &nbx_fbxserial_default, sizeof(*serial));
> +}
> +
> +/*
> + * Validate serial info structure
> + */
> +static int nbx_fbx_check_serial(struct nbx_fbx_serial *fs)
> +{
> + unsigned int sum, len;
> +
> + /* Check magic first */
> + if (be32_to_cpu(fs->magic) != NBX_FBXSERIAL_MAGIC) {
> + printf("Invalid magic for serial info (%08x != %08x)!\n",
> + be32_to_cpu(fs->magic), NBX_FBXSERIAL_MAGIC);
> + return -1;
> + }
> +
> + /* Check struct version */
> + if (be32_to_cpu(fs->struct_version) > NBX_FBXSERIAL_VERSION) {
> + printf("Version too big for fbxserial info (0x%08x)!\n",
> + be32_to_cpu(fs->struct_version));
> + return -1;
> + }
> +
> + /* Check for silly len */
> + len = be32_to_cpu(fs->len);
> + if (len > NBX_FBXSERIAL_MAX_SIZE) {
> + printf("Silly len for serial info (%d)\n", len);
> + return -1;
> + }
> +
> + /* Validate CRC (crc32_no_comp: no one's complement) */
> + sum = crc32_no_comp(0, (void *)fs + 4, len - 4);
> + if (be32_to_cpu(fs->crc32) != sum) {
> + printf("Invalid checksum for serial info (%08x != %08x)\n",
> + sum, be32_to_cpu(fs->crc32));
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +int nbx_fbx_read_serial(int dev_num, unsigned long offset,
> + struct nbx_fbx_serial *fs)
> +{
> + struct mmc *mmc;
> + struct blk_desc *bd;
> + uint blk_start, blk_cnt;
> + uint n;
> +
> + ALLOC_CACHE_ALIGN_BUFFER(char, buf, ALIGN(sizeof(*fs), 512));
> + mmc = find_mmc_device(dev_num);
> + if (!mmc) {
> + printf("No MMC device %d found\n", dev_num);
> + nbx_fbxserial_set_default(fs);
> + return -1;
> + }
> +
> + if (mmc_init(mmc)) {
> + puts("MMC init failed\n");
> + nbx_fbxserial_set_default(fs);
> + return -1;
> + }
> +
> + /* Switch to partition 0 (user data area) */
> + if (blk_select_hwpart_devnum(UCLASS_MMC, dev_num, 0)) {
> + puts("MMC partition switch failed\n");
> + nbx_fbxserial_set_default(fs);
> + return -1;
> + }
> +
> + bd = mmc_get_blk_desc(mmc);
> + if (!bd) {
> + puts("Failed to get MMC block descriptor\n");
> + nbx_fbxserial_set_default(fs);
> + return -1;
> + }
> +
> + blk_start = ALIGN(offset, bd->blksz) / bd->blksz;
> + blk_cnt = ALIGN(sizeof(*fs), bd->blksz) / bd->blksz;
> +
> + n = blk_dread(bd, blk_start, blk_cnt, buf);
> + if (n != blk_cnt) {
> + printf("Failed to read serial info from MMC\n");
> + nbx_fbxserial_set_default(fs);
> + return -1;
> + }
> +
> + memcpy(fs, buf, sizeof(*fs));
> +
> + if (nbx_fbx_check_serial(fs) != 0) {
> + nbx_fbxserial_set_default(fs);
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +void nbx_fbx_dump_serial(struct nbx_fbx_serial *fs)
> +{
> + int i;
> +
> + printf("Serial: %04u-%02u-%c-(%02u)%02u-%02u-%05u / %08x\n",
> + be16_to_cpu(fs->type),
> + fs->version,
> + isprint(fs->manufacturer) ? fs->manufacturer : '?',
> + be16_to_cpu(fs->year) / 100,
> + be16_to_cpu(fs->year) % 100,
> + fs->week,
> + be32_to_cpu(fs->number),
> + be32_to_cpu(fs->flags));
> +
> + printf("Mac: %02X:%02X:%02X:%02X:%02X:%02X\n",
> + fs->mac_addr_base[0],
> + fs->mac_addr_base[1],
> + fs->mac_addr_base[2],
> + fs->mac_addr_base[3],
> + fs->mac_addr_base[4],
> + fs->mac_addr_base[5]);
> +
> + /* Show bundle info */
> + for (i = 0; i < be32_to_cpu(fs->extinfo_count); i++) {
> + struct nbx_serial_extinfo *p;
> +
> + if (i >= NBX_EXTINFO_MAX_COUNT)
> + break;
> +
> + p = &fs->extinfos[i];
> + if (be32_to_cpu(p->type) == NBX_EXTINFO_TYPE_EXTDEV &&
> + be32_to_cpu(p->u.extdev.type) == NBX_EXTDEV_TYPE_BUNDLE) {
> + /* Ensure null termination */
> + p->u.extdev.serial[sizeof(p->u.extdev.serial) - 1] = 0;
> + printf("Bundle: %s\n", p->u.extdev.serial);
> + }
> + }
> +
> + printf("\n");
> +}
> +
> +int nbx_fbx_init_ethaddr(int dev_num, unsigned long offset)
> +{
> + struct nbx_fbx_serial fs;
> + char mac[32];
> + int ret;
> +
> + ret = nbx_fbx_read_serial(dev_num, offset, &fs);
> +
> + nbx_fbx_dump_serial(&fs);
> +
> + /* Even on error, fs has default values set */
> + snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
> + fs.mac_addr_base[0], fs.mac_addr_base[1],
> + fs.mac_addr_base[2], fs.mac_addr_base[3],
> + fs.mac_addr_base[4], fs.mac_addr_base[5]);
> +
> + env_set("ethaddr", mac);
> + env_set("eth1addr", mac);
> + env_set("eth2addr", mac);
> +
> + return ret;
> +}
> +
> +/*
> + * fbxserial show - display serial info from eMMC
> + */
> +static int do_fbxserial_show(struct cmd_tbl *cmdtp, int flag, int argc,
> + char *const argv[])
> +{
> + struct nbx_fbx_serial fs;
> + int dev = 0;
> + unsigned long offset = CONFIG_NBX_MMC_PART_SERIAL_OFFSET;
> +
> + if (argc >= 1)
> + dev = dectoul(argv[0], NULL);
> +
> + if (argc >= 2)
> + offset = hextoul(argv[1], NULL);
> +
> + if (nbx_fbx_read_serial(dev, offset, &fs) != 0)
> + printf("Warning: Using default serial info\n");
> +
> + nbx_fbx_dump_serial(&fs);
> +
> + return CMD_RET_SUCCESS;
> +}
> +
> +/*
> + * fbxserial init - initialize ethaddr from serial info
> + */
> +static int do_fbxserial_init(struct cmd_tbl *cmdtp, int flag, int argc,
> + char *const argv[])
> +{
> + int dev = 0;
> + unsigned long offset = CONFIG_NBX_MMC_PART_SERIAL_OFFSET;
> +
> + if (argc >= 1)
> + dev = dectoul(argv[0], NULL);
> +
> + if (argc >= 2)
> + offset = hextoul(argv[1], NULL);
> +
> + return nbx_fbx_init_ethaddr(dev, offset);
> +}
> +
> +static struct cmd_tbl cmd_fbxserial_sub[] = {
> + U_BOOT_CMD_MKENT(show, 3, 0, do_fbxserial_show, "", ""),
> + U_BOOT_CMD_MKENT(init, 3, 0, do_fbxserial_init, "", ""),
> +};
> +
> +static int do_fbxserial(struct cmd_tbl *cmdtp, int flag, int argc,
> + char *const argv[])
> +{
> + struct cmd_tbl *cp;
> +
> + /* Default to 'show' if no subcommand */
> + if (argc < 2)
> + return do_fbxserial_show(cmdtp, flag, 0, NULL);
> +
> + cp = find_cmd_tbl(argv[1], cmd_fbxserial_sub,
> + ARRAY_SIZE(cmd_fbxserial_sub));
> +
> + if (!cp)
> + return CMD_RET_USAGE;
> +
> + return cp->cmd(cmdtp, flag, argc - 2, argv + 2);
> +}
> +
> +U_BOOT_CMD(
> + fbxserial, 5, 0, do_fbxserial,
> + "NBX serial info and MAC address initialization",
> + "show [dev] [offset] - display serial info from eMMC\n"
> + "fbxserial init [dev] [offset] - initialize ethaddr from serial info\n"
> + " dev - MMC device number (default 0)\n"
> + " offset - offset in eMMC in hex (default from Kconfig)"
> +);
> diff --git a/configs/mvebu_nbx_88f8040_defconfig b/configs/mvebu_nbx_88f8040_defconfig
> index 2fd58e4ad64..c295094536e 100644
> --- a/configs/mvebu_nbx_88f8040_defconfig
> +++ b/configs/mvebu_nbx_88f8040_defconfig
> @@ -21,6 +21,7 @@ CONFIG_PREBOOT="echo (CRC warning is normal: no env saved yet)"
> CONFIG_SYS_PBSIZE=1048
> CONFIG_SYS_CONSOLE_INFO_QUIET=y
> CONFIG_DISPLAY_CPUINFO=y
> +CONFIG_LAST_STAGE_INIT=y
> CONFIG_SYS_PROMPT="nodebox10G>> "
> CONFIG_SYS_MAXARGS=32
> CONFIG_CMD_BOOTZ=y
> @@ -41,6 +42,7 @@ CONFIG_CMD_CACHE=y
> CONFIG_CMD_TIME=y
> CONFIG_CMD_TIMER=y
> CONFIG_CMD_NBX_EMMCBOOT=y
> +CONFIG_CMD_NBX_FBXSERIAL=y
> CONFIG_CMD_EXT2=y
> CONFIG_CMD_EXT4=y
> CONFIG_CMD_EXT4_WRITE=y
> diff --git a/include/nbx_fbxserial.h b/include/nbx_fbxserial.h
> new file mode 100644
> index 00000000000..7628e658f5e
> --- /dev/null
> +++ b/include/nbx_fbxserial.h
> @@ -0,0 +1,134 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * NBX Freebox Serial Info Support
> + *
> + * Copyright (C) 2026 Free Mobile, Freebox
> + *
> + * Reads device serial number and MAC address from eMMC.
> + * Used to identify the board and set network MAC addresses.
> + */
> +
> +#ifndef NBX_FBXSERIAL_H
> +#define NBX_FBXSERIAL_H
> +
> +#include <linux/types.h>
> +
> +/*
> + * Extended info structure - variable data depending on type
> + */
> +#define NBX_EXTINFO_SIZE 128
> +#define NBX_EXTINFO_MAX_COUNT 16
> +
> +/* Extended info types */
> +#define NBX_EXTINFO_TYPE_EXTDEV 1
> +
> +/* Extended device types */
> +#define NBX_EXTDEV_TYPE_BUNDLE 1
> +#define NBX_EXTDEV_TYPE_MAX 2
> +
> +struct nbx_serial_extinfo {
> + u32 type;
> +
> + union {
> + /* extdev */
> + struct {
> + u32 type;
> + u32 model;
> + char serial[64];
> + } extdev;
> +
> + /* raw access */
> + unsigned char data[NBX_EXTINFO_SIZE];
> + } u;
> +} __packed;
> +
> +/*
> + * Master serial structure
> + */
> +#define NBX_FBXSERIAL_VERSION 1
> +#define NBX_FBXSERIAL_MAGIC 0x2d9521ab
> +
> +#define NBX_MAC_ADDR_SIZE 6
> +#define NBX_RANDOM_DATA_SIZE 32
> +
> +/* Maximum size for CRC validation */
> +#define NBX_FBXSERIAL_MAX_SIZE 8192
> +
> +struct nbx_fbx_serial {
> + u32 crc32;
> + u32 magic;
> + u32 struct_version;
> + u32 len;
> +
> + /* Board serial */
> + u16 type;
> + u8 version;
> + u8 manufacturer;
> + u16 year;
> + u8 week;
> + u32 number;
> + u32 flags;
> +
> + /* MAC address base */
> + u8 mac_addr_base[NBX_MAC_ADDR_SIZE];
> +
> + /* MAC address count */
> + u8 mac_count;
> +
> + /* Random data used to derive keys */
> + u8 random_data[NBX_RANDOM_DATA_SIZE];
> +
> + /* Last update of data (seconds since epoch) */
> + u32 last_modified;
> +
> + /* Count of following extinfo tags */
> + u32 extinfo_count;
> +
> + /* Beginning of extended info */
> + struct nbx_serial_extinfo extinfos[NBX_EXTINFO_MAX_COUNT];
> +} __packed;
> +
> +/**
> + * nbx_fbxserial_set_default() - Initialize serial structure with defaults
> + * @serial: Pointer to serial structure to initialize
> + *
> + * Sets the serial structure to default values (Freebox OUI, type 9018).
> + * Used as fallback when serial info cannot be read from eMMC.
> + */
> +void nbx_fbxserial_set_default(struct nbx_fbx_serial *serial);
> +
> +/**
> + * nbx_fbx_read_serial() - Read serial info from eMMC
> + * @dev_num: MMC device number
> + * @offset: Byte offset in eMMC where serial info is stored
> + * @fs: Pointer to serial structure to fill
> + *
> + * Reads and validates the serial info from eMMC. On failure,
> + * the structure is filled with default values.
> + *
> + * Return: 0 on success, negative on error (defaults still set)
> + */
> +int nbx_fbx_read_serial(int dev_num, unsigned long offset,
> + struct nbx_fbx_serial *fs);
> +
> +/**
> + * nbx_fbx_dump_serial() - Print serial info to console
> + * @fs: Pointer to serial structure to display
> + *
> + * Prints the serial number, MAC address, and bundle info (if present).
> + */
> +void nbx_fbx_dump_serial(struct nbx_fbx_serial *fs);
> +
> +/**
> + * nbx_fbx_init_ethaddr() - Initialize Ethernet addresses from serial info
> + * @dev_num: MMC device number
> + * @offset: Byte offset in eMMC where serial info is stored
> + *
> + * Reads serial info and sets ethaddr, eth1addr, eth2addr environment
> + * variables from the base MAC address in the serial structure.
> + *
> + * Return: 0 on success, negative on error
> + */
> +int nbx_fbx_init_ethaddr(int dev_num, unsigned long offset);
> +
> +#endif /* NBX_FBXSERIAL_H */
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-03-31 12:49 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-10 14:12 [PATCH v1 0/5] Add support for Freebox Nodebox 10G (Armada 8040) Vincent Jardin
2026-03-10 14:12 ` [PATCH v1 1/5] arm: mach-mvebu: armada8k: add CPU and clock info display Vincent Jardin
2026-03-31 12:18 ` Stefan Roese
2026-03-10 14:12 ` [PATCH v1 2/5] board: freebox: add Nodebox 10G board support Vincent Jardin
2026-03-31 12:35 ` Stefan Roese
2026-03-10 14:12 ` [PATCH v1 3/5] arm: dts: armada-8040-nbx: add U-Boot dtsi for conditional OP-TEE Vincent Jardin
2026-03-31 12:35 ` Stefan Roese
2026-03-10 14:12 ` [PATCH v1 4/5] cmd: mvebu: add emmcboot for Nodebox image format Vincent Jardin
2026-03-31 12:46 ` Stefan Roese
2026-03-10 14:12 ` [PATCH v1 5/5] cmd: mvebu: add device serial and MAC address initialization Vincent Jardin
2026-03-31 12:49 ` Stefan Roese
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox