summaryrefslogtreecommitdiff
path: root/board
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2026-06-10 09:18:15 -0600
committerTom Rini <[email protected]>2026-06-10 09:18:15 -0600
commit953653936cfc9e12f52df1165080fa6c7cbae1bc (patch)
tree407487ab8999b482dfe367b0561347c0f22f261a /board
parent45cfba58dba911685700d6574b01f39329dce99c (diff)
parenteab66c3a9085e07b227847b0c442ee8d2efb241d (diff)
Merge tag 'u-boot-marvell-next-20260610' of https://source.denx.de/u-boot/custodians/u-boot-marvell into next
CI: https://source.denx.de/u-boot/custodians/u-boot-marvell/-/pipelines/30414 u-boot-marvell changes 2026-06-10: - arm: kirkwood: Remove unnecessary watchdog GPIO for ZyXEL NSA325 board (Tony) - net: mvpp2: fix NULL pointer dereference in mvpp2_phy_connect (Vincent) - mtd: nand: pxa3xx: Pass valid dev to dev_err() (Chris) - arm: mvebu: db-xc3-24g4xg: Remove marvell, nand-keep-config (Chris) - arm: mvebu: Add Allied Telesis x220 (Chris) - board: Synology: legacy.c: Include asm/io.h (Phil) - board: Synology: common: Fix typo in Makefile (Phil) - serial: serial_octeon_bootcmd.c: use correct Kconfig symbol (Heinrich) - arm: mvebu: Drop unnecessary BOARD_EARLY_INIT_F usage (Tom) - pinctrl: armada-38x: Staticize and constify driver ops (Marek) - arm: mach-mvebu: armada8k: cpuinfo and SAR (Vincent) - board: freebox: add Nodebox 10G board support (Vincent) - board: freebox: nbx10g: add emmcboot for dual-bank eMMC boot (Vincent) - board: freebox: nbx10g: add device serial and MAC address initialization (Vincent) - arm: dts: armada-8040-nbx: add U-Boot dtsi for conditional OP-TEE (Vincent) - timer: orion: Use dev_remap_addr_index() (Peng)
Diffstat (limited to 'board')
-rw-r--r--board/Marvell/mvebu_armada-37xx/board.c5
-rw-r--r--board/Synology/common/Makefile2
-rw-r--r--board/Synology/common/legacy.c1
-rw-r--r--board/alliedtelesis/x220/.gitattributes1
-rw-r--r--board/alliedtelesis/x220/.gitignore1
-rw-r--r--board/alliedtelesis/x220/MAINTAINERS8
-rw-r--r--board/alliedtelesis/x220/Makefile14
-rw-r--r--board/alliedtelesis/x220/binary.011
-rw-r--r--board/alliedtelesis/x220/kwbimage.cfg.in12
-rw-r--r--board/alliedtelesis/x220/x220.c67
-rw-r--r--board/freebox/nbx10g/Kconfig94
-rw-r--r--board/freebox/nbx10g/MAINTAINERS6
-rw-r--r--board/freebox/nbx10g/Makefile5
-rw-r--r--board/freebox/nbx10g/board.c53
-rw-r--r--board/freebox/nbx10g/nbx_emmcboot.c357
-rw-r--r--board/freebox/nbx10g/nbx_fbxserial.c286
-rw-r--r--board/freebox/nbx10g/nbx_fbxserial.h156
-rw-r--r--board/freebox/nbx10g/nbx_imagetag.h78
-rw-r--r--board/freebox/nbx10g/nbx_nrboot.h34
-rw-r--r--board/zyxel/nsa325/nsa325.c7
20 files changed, 1188 insertions, 10 deletions
diff --git a/board/Marvell/mvebu_armada-37xx/board.c b/board/Marvell/mvebu_armada-37xx/board.c
index e44b713f96d..c30fca6cffd 100644
--- a/board/Marvell/mvebu_armada-37xx/board.c
+++ b/board/Marvell/mvebu_armada-37xx/board.c
@@ -102,11 +102,6 @@ static bool is_edpu_plus(void)
return false;
}
-int board_early_init_f(void)
-{
- return 0;
-}
-
int board_init(void)
{
/* adress of boot parameters */
diff --git a/board/Synology/common/Makefile b/board/Synology/common/Makefile
index f688b549063..87be53321ee 100644
--- a/board/Synology/common/Makefile
+++ b/board/Synology/common/Makefile
@@ -2,4 +2,4 @@
#
# Copyright (C) 2021 Phil Sutter <[email protected]>
-obj-$(SUPPORT_PASSING_ATAGS) += legacy.o
+obj-$(CONFIG_SUPPORT_PASSING_ATAGS) += legacy.o
diff --git a/board/Synology/common/legacy.c b/board/Synology/common/legacy.c
index 2e3aa660eaa..5b7d07bc2ee 100644
--- a/board/Synology/common/legacy.c
+++ b/board/Synology/common/legacy.c
@@ -10,6 +10,7 @@
#include <vsprintf.h>
#include <env.h>
#include <net.h>
+#include <asm/io.h>
#include <asm/setup.h>
#include "legacy.h"
diff --git a/board/alliedtelesis/x220/.gitattributes b/board/alliedtelesis/x220/.gitattributes
new file mode 100644
index 00000000000..2aeb4eee641
--- /dev/null
+++ b/board/alliedtelesis/x220/.gitattributes
@@ -0,0 +1 @@
+binary.0 binary
diff --git a/board/alliedtelesis/x220/.gitignore b/board/alliedtelesis/x220/.gitignore
new file mode 100644
index 00000000000..775b9346b85
--- /dev/null
+++ b/board/alliedtelesis/x220/.gitignore
@@ -0,0 +1 @@
+kwbimage.cfg
diff --git a/board/alliedtelesis/x220/MAINTAINERS b/board/alliedtelesis/x220/MAINTAINERS
new file mode 100644
index 00000000000..63da2725f71
--- /dev/null
+++ b/board/alliedtelesis/x220/MAINTAINERS
@@ -0,0 +1,8 @@
+x220 BOARD
+M: Chris Packham <[email protected]>
+S: Maintained
+F: board/alliedtelesis/x220
+F: include/configs/x220.h
+F: configs/x220_defconfig
+F: arch/arm/dts/armada-xp-atl-x220.dts
+F: doc/board/alliedtelesis/x220.rst
diff --git a/board/alliedtelesis/x220/Makefile b/board/alliedtelesis/x220/Makefile
new file mode 100644
index 00000000000..a74f0a76948
--- /dev/null
+++ b/board/alliedtelesis/x220/Makefile
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2025 Allied Telesis Labs
+
+obj-y := x220.o
+extra-y := kwbimage.cfg
+
+quiet_cmd_sed = SED $@
+ cmd_sed = sed $(SEDFLAGS_$(@F)) $< >$(dir $@)$(@F)
+
+SEDFLAGS_kwbimage.cfg =-e "s|^BINARY.*|BINARY $(srctree)/$(@D)/binary.0 0000005b 00000068|"
+$(obj)/kwbimage.cfg: $(src)/kwbimage.cfg.in include/autoconf.mk \
+ include/config/auto.conf
+ $(call if_changed,sed)
diff --git a/board/alliedtelesis/x220/binary.0 b/board/alliedtelesis/x220/binary.0
new file mode 100644
index 00000000000..8dd687286a0
--- /dev/null
+++ b/board/alliedtelesis/x220/binary.0
@@ -0,0 +1,11 @@
+--------
+WARNING:
+--------
+This file should contain the bin_hdr generated by the original Marvell
+U-Boot implementation. As this is currently not included in this
+U-Boot version, we have added this placeholder, so that the U-Boot
+image can be generated without errors.
+
+If you have a known to be working bin_hdr for your board, then you
+just need to replace this text file here with the binary header
+and recompile U-Boot.
diff --git a/board/alliedtelesis/x220/kwbimage.cfg.in b/board/alliedtelesis/x220/kwbimage.cfg.in
new file mode 100644
index 00000000000..8beda907ba4
--- /dev/null
+++ b/board/alliedtelesis/x220/kwbimage.cfg.in
@@ -0,0 +1,12 @@
+#
+# Copyright (C) 2025 Allied Telesis Labs
+#
+
+# Armada XP uses version 1 image format
+VERSION 1
+
+# Boot Media configurations
+BOOT_FROM spi
+
+# Binary Header (bin_hdr) with DDR3 training code
+BINARY board/alliedtelesis/x220/binary.0 0000005b 00000068
diff --git a/board/alliedtelesis/x220/x220.c b/board/alliedtelesis/x220/x220.c
new file mode 100644
index 00000000000..7c9a73de9a2
--- /dev/null
+++ b/board/alliedtelesis/x220/x220.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Allied Telesis Labs
+ */
+
+#include <i2c.h>
+#include <init.h>
+#include <asm/global_data.h>
+#include <asm/gpio.h>
+#include <linux/bitops.h>
+#include <linux/mbus.h>
+#include <linux/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/soc.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define X220_GPP_OUT_ENA_LOW (~(BIT(12) | BIT(17) | BIT(18) | BIT(31)))
+#define X220_GPP_OUT_ENA_MID (~(0))
+#define X220_GPP_OUT_VAL_LOW (BIT(12) | BIT(18))
+#define X220_GPP_OUT_VAL_MID 0x0
+#define X220_GPP_POL_LOW 0x0
+#define X220_GPP_POL_MID 0x0
+
+int board_early_init_f(void)
+{
+ /* Configure MPP */
+ writel(0x44042222, MVEBU_MPP_BASE + 0x00);
+ writel(0x11000004, MVEBU_MPP_BASE + 0x04);
+ writel(0x44444004, MVEBU_MPP_BASE + 0x08);
+ writel(0x04444444, MVEBU_MPP_BASE + 0x0c);
+ writel(0x00000004, MVEBU_MPP_BASE + 0x10);
+
+ /* Set GPP Out value */
+ writel(X220_GPP_OUT_VAL_LOW, MVEBU_GPIO0_BASE + 0x00);
+ writel(X220_GPP_OUT_VAL_MID, MVEBU_GPIO1_BASE + 0x00);
+
+ /* Set GPP Polarity */
+ writel(X220_GPP_POL_LOW, MVEBU_GPIO0_BASE + 0x0c);
+ writel(X220_GPP_POL_MID, MVEBU_GPIO1_BASE + 0x0c);
+
+ /* Set GPP Out Enable */
+ writel(X220_GPP_OUT_ENA_LOW, MVEBU_GPIO0_BASE + 0x04);
+ writel(X220_GPP_OUT_ENA_MID, MVEBU_GPIO1_BASE + 0x04);
+
+ return 0;
+}
+
+int board_init(void)
+{
+ /* address of boot parameters */
+ gd->bd->bi_boot_params = mvebu_sdram_bar(0) + 0x100;
+
+ /* Disable MBUS Err Prop - in order to avoid data aborts */
+ clrbits_le32(MVEBU_CPU_WIN_BASE + 0x200, (1 << 8));
+
+ return 0;
+}
+
+#ifdef CONFIG_DISPLAY_BOARDINFO
+int checkboard(void)
+{
+ puts("Board: Allied Telesis x220\n");
+
+ return 0;
+}
+#endif
diff --git a/board/freebox/nbx10g/Kconfig b/board/freebox/nbx10g/Kconfig
new file mode 100644
index 00000000000..958c8fdd4c3
--- /dev/null
+++ b/board/freebox/nbx10g/Kconfig
@@ -0,0 +1,94 @@
+if TARGET_NBX10G
+
+config SYS_BOARD
+ default "nbx10g"
+
+config SYS_VENDOR
+ default "freebox"
+
+config SYS_CONFIG_NAME
+ default "nbx10g"
+
+config CMD_NBX_EMMCBOOT
+ bool "emmcboot command"
+ depends on MMC_SDHCI_XENON
+ help
+ Enable the emmcboot command for dual-bank boot from eMMC.
+ This is a legacy boot format used on this board for many years.
+ It implements a boot system with two image banks and automatic
+ fallback on boot failures. The boot order depends on a reboot
+ tracking counter (nrboot):
+ - If healthy: try Bank1 (newer) first, then Bank0 (stable)
+ - If degraded (>= 4 failures): try Bank0 first, then Bank1
+
+ Requires image_addr and fdt_addr environment variables to be set.
+
+if CMD_NBX_EMMCBOOT
+
+config NBX_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 NBX_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 NBX_MMC_PART_BANK0_SIZE
+ hex "Bank0 image maximum size"
+ default 0x10000000
+ help
+ Maximum size of the Bank0 boot image.
+ Default: 0x10000000 (256MB)
+
+config NBX_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 NBX_MMC_PART_BANK1_SIZE
+ hex "Bank1 image maximum size"
+ default 0x10000000
+ help
+ Maximum size of the Bank1 boot image.
+ Default: 0x10000000 (256MB)
+
+endif
+
+config CMD_NBX_FBXSERIAL
+ bool "fbxserial command"
+ depends on MMC_SDHCI_XENON
+ help
+ Enable the 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
+
+ Use CONFIG_PREBOOT="fbxserial init" to auto-initialize at boot.
+
+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
+
+endif
diff --git a/board/freebox/nbx10g/MAINTAINERS b/board/freebox/nbx10g/MAINTAINERS
new file mode 100644
index 00000000000..2e31eed45b9
--- /dev/null
+++ b/board/freebox/nbx10g/MAINTAINERS
@@ -0,0 +1,6 @@
+NBX10G BOARD
+M: Vincent Jardin <[email protected]>
+S: Maintained
+F: board/freebox/nbx10g/
+F: configs/mvebu_nbx_88f8040_defconfig
+F: arch/arm/dts/armada-8040-nbx*
diff --git a/board/freebox/nbx10g/Makefile b/board/freebox/nbx10g/Makefile
new file mode 100644
index 00000000000..4b70d94e14d
--- /dev/null
+++ b/board/freebox/nbx10g/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-y := board.o
+obj-$(CONFIG_CMD_NBX_EMMCBOOT) += nbx_emmcboot.o
+obj-$(CONFIG_CMD_NBX_FBXSERIAL) += nbx_fbxserial.o
diff --git a/board/freebox/nbx10g/board.c b/board/freebox/nbx10g/board.c
new file mode 100644
index 00000000000..7d16010ec7e
--- /dev/null
+++ b/board/freebox/nbx10g/board.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017-2018 Freebox SA
+ * Copyright (C) 2026 Free Mobile, Vincent Jardin <[email protected]>
+ *
+ * Freebox Nodebox 10G board support
+ */
+
+#include <init.h>
+#include <asm/gpio.h>
+#include <linux/delay.h>
+
+/* 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)
+{
+ 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);
+
+ return 0;
+}
diff --git a/board/freebox/nbx10g/nbx_emmcboot.c b/board/freebox/nbx10g/nbx_emmcboot.c
new file mode 100644
index 00000000000..0bea96fadd9
--- /dev/null
+++ b/board/freebox/nbx10g/nbx_emmcboot.c
@@ -0,0 +1,357 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Nodebox 10G dual-bank eMMC boot command with automatic fallback
+ *
+ * Copyright (C) 2026 Free Mobile, Freebox
+ *
+ * This implements a dual-bank boot system with automatic fallback:
+ * - Bank0: Stable/fallback boot image
+ * - Bank1: Newer/test boot image
+ *
+ * The boot order depends on the reboot tracking counter (nrboot):
+ * - If healthy: try Bank1 first, then Bank0
+ * - If degraded (>= 4 failures): try Bank0 first, then Bank1
+ */
+
+#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/errno.h>
+#include "nbx_imagetag.h"
+#include "nbx_nrboot.h"
+
+/* Partition offsets defined in Kconfig (CONFIG_NBX_MMC_PART_*) */
+
+/* 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 -EINVAL;
+ }
+
+ 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 -EINVAL;
+ }
+
+ if (be32_to_cpu(tag->total_size) < sizeof(*tag)) {
+ if (name)
+ printf("%s: tag size is too small!\n", name);
+ return -EINVAL;
+ }
+
+ if (be32_to_cpu(tag->total_size) > maxsize) {
+ if (name)
+ printf("%s: tag size is too big!\n", name);
+ return -EINVAL;
+ }
+
+ 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 -EINVAL;
+ }
+
+ 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 -EINVAL;
+ }
+
+ 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 -EINVAL;
+ }
+
+ 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 -EINVAL;
+ }
+
+ return 0;
+}
+
+/* NRBoot (Reboot Tracking) Functions */
+
+struct mvebu_nrboot {
+ u16 nrboot;
+ u16 nrsuccess;
+};
+
+#define MVEBU_MAX_FAILURE 4
+
+static int mvebu_count_bits(u16 val)
+{
+ int i, found = 0;
+
+ for (i = 0; i < 16; i++) {
+ if (val & (1 << i))
+ found++;
+ }
+ return found;
+}
+
+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 on values */
+ if (mvebu_count_bits(~nr->nrboot + 1) <= 1 &&
+ mvebu_count_bits(~nr->nrsuccess + 1) <= 1) {
+ int boot, success;
+
+ boot = 16 - mvebu_count_bits(nr->nrboot);
+ success = 16 - mvebu_count_bits(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 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);
+ struct mvebu_image_tag *tag;
+ ulong image_addr = 0;
+ ulong fdt_addr = 0;
+ ulong tag_addr;
+ uint tag_blk_start = ALIGN(offset, bd->blksz) / bd->blksz;
+ uint tag_blk_cnt = ALIGN(sizeof(*tag), bd->blksz) / bd->blksz;
+ uint n;
+
+ ALLOC_CACHE_ALIGN_BUFFER(char, tag_buf, tag_blk_cnt * bd->blksz);
+ tag = (void *)tag_buf;
+
+ schedule();
+
+ printf("## Trying %s boot...\n", bank);
+
+ /* Load tag header */
+ n = blk_dread(bd, tag_blk_start, tag_blk_cnt, tag_buf);
+ if (n != tag_blk_cnt) {
+ printf("%s: failed to read tag header\n", bank);
+ return;
+ }
+
+ if (mvebu_imagetag_check(tag, maxsize, bank) != 0)
+ return;
+
+ if (tag->rootfs_size != 0) {
+ printf("%s: rootfs in tag not supported\n", bank);
+ return;
+ }
+
+ /* Get image and device tree 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;
+ }
+
+ tag_addr = image_addr;
+
+ /* Load full image, temporarily reuse image_addr for this */
+ {
+ uint data_blk_start = ALIGN(offset, bd->blksz) / bd->blksz;
+ uint data_blk_cnt = ALIGN(mvebu_imagetag_total_size(tag),
+ bd->blksz) / bd->blksz;
+
+ n = blk_dread(bd, data_blk_start, data_blk_cnt, (void *)tag_addr);
+ if (n != data_blk_cnt) {
+ printf("%s: failed to read full image\n", bank);
+ return;
+ }
+
+ if (mvebu_imagetag_crc((void *)tag_addr, bank) != 0)
+ return;
+ }
+
+ schedule();
+
+ /* Copy image and device tree to the right addresses */
+ /* We assume that image_addr + tag_size < fdt_addr */
+ {
+ tag = (void *)tag_addr;
+ memcpy((void *)fdt_addr,
+ ((void *)tag_addr) + mvebu_imagetag_device_tree_offset(tag),
+ mvebu_imagetag_device_tree_size(tag));
+ memmove((void *)image_addr,
+ ((void *)tag_addr) + mvebu_imagetag_kernel_offset(tag),
+ mvebu_imagetag_kernel_size(tag));
+ }
+
+ schedule();
+
+ /* Set bootargs and boot */
+ {
+ char bootargs[256];
+ char *console_env;
+
+ console_env = env_get("console");
+ if (console_env)
+ snprintf(bootargs, sizeof(bootargs), "%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);
+
+ /* Build and run booti command */
+ {
+ char cmd[128];
+
+ snprintf(cmd, sizeof(cmd), "booti 0x%lx - 0x%lx",
+ image_addr, fdt_addr);
+ run_command(cmd, 0);
+ }
+ }
+
+ 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_NBX_MMC_PART_NRBOOT_OFFSET)) {
+ /* System is healthy: try newer bank first */
+ mvebu_try_emmcboot(mmc, CONFIG_NBX_MMC_PART_BANK1_OFFSET,
+ CONFIG_NBX_MMC_PART_BANK1_SIZE, "bank1");
+ mvebu_try_emmcboot(mmc, CONFIG_NBX_MMC_PART_BANK0_OFFSET,
+ CONFIG_NBX_MMC_PART_BANK0_SIZE, "bank0");
+ } else {
+ /* System is degraded: use stable bank first */
+ mvebu_try_emmcboot(mmc, CONFIG_NBX_MMC_PART_BANK0_OFFSET,
+ CONFIG_NBX_MMC_PART_BANK0_SIZE, "bank0");
+ mvebu_try_emmcboot(mmc, CONFIG_NBX_MMC_PART_BANK1_OFFSET,
+ CONFIG_NBX_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 MVEBU eMMC image banks",
+ "[dev]\n"
+ " - Boot from eMMC device <dev> (default 0)\n"
+ " - Requires image_addr and fdt_addr environment variables\n"
+ " - Uses dual-bank boot with automatic fallback\n"
+ " - Bank selection based on reboot tracking (nrboot)"
+);
diff --git a/board/freebox/nbx10g/nbx_fbxserial.c b/board/freebox/nbx10g/nbx_fbxserial.c
new file mode 100644
index 00000000000..088133a9496
--- /dev/null
+++ b/board/freebox/nbx10g/nbx_fbxserial.c
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NBX Freebox Serial Info Support
+ *
+ * Copyright (C) 2025 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 <dm/device.h>
+#include <env.h>
+#include <event.h>
+#include <mmc.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <vsprintf.h>
+#include <u-boot/crc.h>
+#include <asm/byteorder.h>
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include "nbx_fbxserial.h"
+
+/* Partition offset defined in Kconfig (CONFIG_NBX_MMC_PART_SERIAL_OFFSET) */
+
+/*
+ * 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 -EINVAL;
+ }
+
+ /* 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 -EINVAL;
+ }
+
+ /* 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 -EINVAL;
+ }
+
+ /* 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 -EINVAL;
+ }
+
+ 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 -ENODEV;
+ }
+
+ if (mmc_init(mmc)) {
+ puts("MMC init failed\n");
+ nbx_fbxserial_set_default(fs);
+ return -EIO;
+ }
+
+ /* 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 -EIO;
+ }
+
+ bd = mmc_get_blk_desc(mmc);
+ if (!bd) {
+ puts("Failed to get MMC block descriptor\n");
+ nbx_fbxserial_set_default(fs);
+ return -EIO;
+ }
+
+ blk_start = ALIGN(offset, bd->blksz) / bd->blksz;
+ blk_cnt = ALIGN(sizeof(*fs), bd->blksz) / bd->blksz;
+
+ memset(fs, 0x42, sizeof(*fs));
+
+ 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 -EIO;
+ }
+
+ memcpy(fs, buf, sizeof(*fs));
+
+ if (nbx_fbx_check_serial(fs) != 0) {
+ nbx_fbxserial_set_default(fs);
+ return -EINVAL;
+ }
+
+ 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",
+ ntohs(fs->type),
+ fs->version,
+ isprint(fs->manufacturer) ? fs->manufacturer : '?',
+ ntohs(fs->year) / 100,
+ ntohs(fs->year) % 100,
+ fs->week,
+ ntohl(fs->number),
+ ntohl(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);
+
+ /* 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]);
+
+ nbx_fbx_dump_serial(&fs);
+
+ 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)"
+);
+
+/*
+ * Early init hook: Set MAC address 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)
+{
+ if (!of_machine_is_compatible("nbx,armada8040"))
+ return 0;
+
+ 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/board/freebox/nbx10g/nbx_fbxserial.h b/board/freebox/nbx10g/nbx_fbxserial.h
new file mode 100644
index 00000000000..7bcaef09fe3
--- /dev/null
+++ b/board/freebox/nbx10g/nbx_fbxserial.h
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * NBX Freebox Serial Info Support
+ *
+ * Copyright (C) 2025 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.
+ */
+static inline void nbx_fbxserial_set_default(struct nbx_fbx_serial *serial)
+{
+ static const struct nbx_fbx_serial def = {
+ .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,
+ };
+
+ memcpy(serial, &def, sizeof(def));
+}
+
+/**
+ * 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 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 */
diff --git a/board/freebox/nbx10g/nbx_imagetag.h b/board/freebox/nbx10g/nbx_imagetag.h
new file mode 100644
index 00000000000..999293dd58a
--- /dev/null
+++ b/board/freebox/nbx10g/nbx_imagetag.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * MVEBU Image Tag header
+ *
+ * Copyright (C) 2026 Free Mobile, Freebox
+ */
+
+#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 - MVEBU boot image tag structure
+ *
+ * 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/board/freebox/nbx10g/nbx_nrboot.h b/board/freebox/nbx10g/nbx_nrboot.h
new file mode 100644
index 00000000000..91c9fb2e57b
--- /dev/null
+++ b/board/freebox/nbx10g/nbx_nrboot.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * MVEBU NRBoot (Number of Reboots) tracking header
+ *
+ * Copyright (C) 2026 Free Mobile, Freebox
+ */
+
+#ifndef __MVEBU_NRBOOT_H
+#define __MVEBU_NRBOOT_H
+
+#include <mmc.h>
+
+/**
+ * mvebu_check_nrboot() - Check and update reboot tracking counter
+ * @mmc: MMC device
+ * @offset: Byte offset in MMC where nrboot data is stored
+ *
+ * This function reads the reboot tracking counter, checks if we've
+ * exceeded the maximum number of failed boots (4), and updates the
+ * counter for the current boot attempt.
+ *
+ * The counter uses a bit-field encoding:
+ * - nrboot: Running count of boot attempts
+ * - nrsuccess: Count of successful boots
+ *
+ * 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 */
diff --git a/board/zyxel/nsa325/nsa325.c b/board/zyxel/nsa325/nsa325.c
index 38340b33c8b..894c2ef293c 100644
--- a/board/zyxel/nsa325/nsa325.c
+++ b/board/zyxel/nsa325/nsa325.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright (C) 2014-2023 Tony Dinh <[email protected]>
+ * Copyright (C) 2014-2025 Tony Dinh <[email protected]>
*
* Based on
* Copyright (C) 2014 Jason Plum <[email protected]>
@@ -51,11 +51,10 @@ DECLARE_GLOBAL_DATA_PTR;
#define HDD1_GREEN_LED BIT(9)
#define HDD1_RED_LED BIT(10)
#define HDD2_POWER BIT(15)
-#define WATCHDOG_SIGNAL BIT(14)
#define NSA325_OE_HIGH (~(COPY_GREEN_LED | COPY_RED_LED | \
- HDD1_GREEN_LED | HDD1_RED_LED | HDD2_POWER | WATCHDOG_SIGNAL))
-#define NSA325_VAL_HIGH (WATCHDOG_SIGNAL | HDD2_POWER)
+ HDD1_GREEN_LED | HDD1_RED_LED | HDD2_POWER))
+#define NSA325_VAL_HIGH (HDD2_POWER)
#define BTN_POWER 46
#define BTN_RESET 36