Merge tag 'u-boot-at91-fixes-2023.01-b' of https://source.denx.de/u-boot/custodians/u-boot-at91

Second set of u-boot-at91 fixes for the 2023.01 cycle:

This is a single tiny fix that allows the correct name for one pin on
sama7g5 device. People with DT coming from Linux will have build errors
without this if they add NAND device.
diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml
index bda7624..d02c663 100644
--- a/.azure-pipelines.yml
+++ b/.azure-pipelines.yml
@@ -362,6 +362,7 @@
           cat << "EOF" >> test.sh
           # the below corresponds to .gitlab-ci.yml "before_script"
           cd ${WORK_DIR}
+          git config --global --add safe.directory ${WORK_DIR}
           git clone --depth=1 https://source.denx.de/u-boot/u-boot-test-hooks /tmp/uboot-test-hooks
           ln -s travis-ci /tmp/uboot-test-hooks/bin/`hostname`
           ln -s travis-ci /tmp/uboot-test-hooks/py/`hostname`
@@ -549,11 +550,12 @@
           cd ${WORK_DIR}
           # make environment variables available as tests are running inside a container
           export BUILDMAN="${BUILDMAN}"
+          git config --global --add safe.directory ${WORK_DIR}
           EOF
           cat << "EOF" >> build.sh
           if [[ "${BUILDMAN}" != "" ]]; then
               ret=0;
-              tools/buildman/buildman -o /tmp -P -E -W ${BUILDMAN} ${OVERRIDE} || ret=$?;
+              tools/buildman/buildman -o /tmp -PEWM ${BUILDMAN} ${OVERRIDE} || ret=$?;
               if [[ $ret -ne 0 ]]; then
                   tools/buildman/buildman -o /tmp -seP ${BUILDMAN};
                   exit $ret;
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 6f4c34f..91d5e0c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -14,6 +14,7 @@
   stage: test.py
   before_script:
     # Clone uboot-test-hooks
+    - git config --global --add safe.directory "${CI_PROJECT_DIR}"
     - git clone --depth=1 https://source.denx.de/u-boot/u-boot-test-hooks /tmp/uboot-test-hooks
     - ln -s travis-ci /tmp/uboot-test-hooks/bin/`hostname`
     - ln -s travis-ci /tmp/uboot-test-hooks/py/`hostname`
@@ -81,7 +82,8 @@
   stage: world build
   script:
     - ret=0;
-      ./tools/buildman/buildman -o /tmp -P -E -W arm -x aarch64 || ret=$?;
+      git config --global --add safe.directory "${CI_PROJECT_DIR}";
+      ./tools/buildman/buildman -o /tmp -PEWM arm -x aarch64 || ret=$?;
       if [[ $ret -ne 0 ]]; then
         ./tools/buildman/buildman -o /tmp -seP;
         exit $ret;
@@ -93,7 +95,8 @@
     - virtualenv -p /usr/bin/python3 /tmp/venv
     - . /tmp/venv/bin/activate
     - ret=0;
-      ./tools/buildman/buildman -o /tmp -P -E -W aarch64 || ret=$?;
+      git config --global --add safe.directory "${CI_PROJECT_DIR}";
+      ./tools/buildman/buildman -o /tmp -PEWM aarch64 || ret=$?;
       if [[ $ret -ne 0 ]]; then
         ./tools/buildman/buildman -o /tmp -seP;
         exit $ret;
@@ -103,6 +106,7 @@
   stage: world build
   script:
     - ret=0;
+      git config --global --add safe.directory "${CI_PROJECT_DIR}";
       ./tools/buildman/buildman -o /tmp -P -E -W powerpc || ret=$?;
       if [[ $ret -ne 0 ]]; then
         ./tools/buildman/buildman -o /tmp -seP;
@@ -113,7 +117,8 @@
   stage: world build
   script:
     - ret=0;
-      ./tools/buildman/buildman -o /tmp -P -E -W -x arm,powerpc || ret=$?;
+      git config --global --add safe.directory "${CI_PROJECT_DIR}";
+      ./tools/buildman/buildman -o /tmp -PEWM -x arm,powerpc || ret=$?;
       if [[ $ret -ne 0 ]]; then
         ./tools/buildman/buildman -o /tmp -seP;
         exit $ret;
diff --git a/MAINTAINERS b/MAINTAINERS
index 97b2f69..75b27bc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -192,6 +192,7 @@
 
 ARM BROADCOM BCM283X / BCM27XX
 M:	Matthias Brugger <mbrugger@suse.com>
+M:	Peter Robinson <pbrobinson@gmail.com>
 S:	Maintained
 F:	arch/arm/dts/bcm283*
 F:	arch/arm/mach-bcm283x/
@@ -494,6 +495,12 @@
 F:	arch/arm/mach-s5pc1xx/
 F:	arch/arm/cpu/armv7/s5p-common/
 
+ARM SANCLOUD
+M:	Paul Barker <paul.barker@sancloud.com>
+R:	Marc Murphy <marc.murphy@sancloud.com>
+S:	Supported
+F:	arch/arm/dts/am335x-sancloud*
+
 ARM SNAPDRAGON
 M:	Ramon Fried <rfried.dev@gmail.com>
 S:	Maintained
@@ -664,6 +671,7 @@
 S:	Maintained
 T:	git https://source.denx.de/u-boot/custodians/u-boot-microblaze.git
 F:	arch/arm/mach-versal-net/
+F:	drivers/soc/soc_xilinx_versal_net.c
 N:	(?<!uni)versal-net
 
 ARM VERSAL
@@ -853,7 +861,6 @@
 F:	cmd/usb_*.c
 F:	common/dfu.c
 F:	common/update.c
-F:	common/usb_storage.c
 F:	doc/api/dfu.rst
 F:	doc/usage/dfu.rst
 F:	drivers/dfu/
@@ -1481,6 +1488,7 @@
 F:	drivers/usb/
 F:	common/usb.c
 F:	common/usb_kbd.c
+F:	common/usb_storage.c
 F:	include/usb.h
 
 USB xHCI
diff --git a/Makefile b/Makefile
index 47dbdcc..de57463 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
 VERSION = 2023
 PATCHLEVEL = 01
 SUBLEVEL =
-EXTRAVERSION = -rc2
+EXTRAVERSION = -rc3
 NAME =
 
 # *DOCUMENTATION*
@@ -1108,18 +1108,15 @@
 
 endef
 
-PHONY += inputs
-inputs: $(INPUTS-y)
-
-all: .binman_stamp inputs
+# Timestamp file to make sure that binman always runs
+.binman_stamp: $(INPUTS-y) FORCE
 ifeq ($(CONFIG_BINMAN),y)
 	$(call if_changed,binman)
 endif
-
-# Timestamp file to make sure that binman always runs
-.binman_stamp: FORCE
 	@touch $@
 
+all: .binman_stamp
+
 ifeq ($(CONFIG_DEPRECATED),y)
 	$(warning "You have deprecated configuration options enabled in your .config! Please check your configuration.")
 endif
@@ -1336,8 +1333,8 @@
 		$(foreach f,$(BINMAN_TOOLPATHS),--toolpath $(f)) \
                 --toolpath $(objtree)/tools \
 		$(if $(BINMAN_VERBOSE),-v$(BINMAN_VERBOSE)) \
-		build -u -d u-boot.dtb -O . -m --allow-missing \
-		--fake-ext-blobs \
+		build -u -d u-boot.dtb -O . -m \
+		$(if $(BINMAN_ALLOW_MISSING),--allow-missing --fake-ext-blobs) \
 		-I . -I $(srctree) -I $(srctree)/board/$(BOARDDIR) \
 		-I arch/$(ARCH)/dts -a of-list=$(CONFIG_OF_LIST) \
 		$(foreach f,$(BINMAN_INDIRS),-I $(f)) \
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 7866e8f..3f68d09 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1230,7 +1230,7 @@
 	imply ENV_VARS_UBOOT_RUNTIME_CONFIG
 
 config ARCH_VERSAL_NET
-	bool "Support Xilinx Keystone Platform"
+	bool "Support Xilinx Versal NET Platform"
 	select ARM64
 	select CLK
 	select DM
@@ -1302,7 +1302,7 @@
 	select DM
 	select DEBUG_UART_BOARD_INIT if SPL && DEBUG_UART
 	select DM_ETH if NET
-	select DM_MAILBOX
+	imply DM_MAILBOX
 	select DM_MMC if MMC
 	select DM_SERIAL
 	select DM_SPI if SPI
@@ -1319,7 +1319,7 @@
 	imply SPL_FIRMWARE if SPL
 	select SPL_SEPARATE_BSS if SPL
 	select SUPPORT_SPL
-	select ZYNQMP_IPI
+	imply ZYNQMP_IPI if DM_MAILBOX
 	select SOC_DEVICE
 	imply BOARD_LATE_INIT
 	imply CMD_DM
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index e89598a..43951a7 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -383,6 +383,8 @@
 	versal-mini.dtb \
 	versal-mini-emmc0.dtb \
 	versal-mini-emmc1.dtb \
+	versal-mini-ospi-single.dtb \
+	versal-mini-qspi-single.dtb \
 	xilinx-versal-virt.dtb
 dtb-$(CONFIG_ARCH_VERSAL_NET) += \
 	versal-net-mini.dtb \
diff --git a/arch/arm/dts/am335x-evm-u-boot.dtsi b/arch/arm/dts/am335x-evm-u-boot.dtsi
index 4cf5f99..8fc65df 100644
--- a/arch/arm/dts/am335x-evm-u-boot.dtsi
+++ b/arch/arm/dts/am335x-evm-u-boot.dtsi
@@ -6,9 +6,9 @@
 #include "am33xx-u-boot.dtsi"
 
 &l4_per {
-
+	u-boot,dm-pre-reloc;
 	segment@300000 {
-
+		u-boot,dm-pre-reloc;
 		target-module@e000 {
 			u-boot,dm-pre-reloc;
 
@@ -26,3 +26,29 @@
 &usb0 {
 	dr_mode = "peripheral";
 };
+
+&i2c0 {
+	u-boot,dm-pre-reloc;
+};
+
+&l4_wkup {
+	u-boot,dm-pre-reloc;
+	segment@200000 {
+		u-boot,dm-pre-reloc;
+		target-module@9000 {
+			u-boot,dm-pre-reloc;
+		};
+	};
+};
+
+&uart0 {
+	u-boot,dm-pre-reloc;
+};
+
+&mmc1 {
+	u-boot,dm-pre-reloc;
+};
+
+&mmc2 {
+	u-boot,dm-pre-reloc;
+};
diff --git a/arch/arm/dts/am335x-sancloud-bbe-lite-u-boot.dtsi b/arch/arm/dts/am335x-sancloud-bbe-lite-u-boot.dtsi
new file mode 100644
index 0000000..01c105e
--- /dev/null
+++ b/arch/arm/dts/am335x-sancloud-bbe-lite-u-boot.dtsi
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2021 SanCloud Ltd
+ */
+
+#include "am335x-sancloud-bbe-u-boot.dtsi"
+
+&l4_wkup {
+	segment@200000 {
+		target-module@0 {
+			u-boot,dm-pre-reloc;
+		};
+	};
+};
+
+&prcm {
+	u-boot,dm-pre-reloc;
+};
+
+&per_cm {
+	u-boot,dm-pre-reloc;
+};
+
+&l4ls_clkctrl {
+	u-boot,dm-pre-reloc;
+};
+
+&l4_per {
+	u-boot,dm-pre-reloc;
+	segment@0 {
+		u-boot,dm-pre-reloc;
+		target-module@30000 {
+			u-boot,dm-pre-reloc;
+		};
+	};
+};
+
+&spi0 {
+	u-boot,dm-pre-reloc;
+	channel@0 {
+		u-boot,dm-pre-reloc;
+	};
+};
diff --git a/arch/arm/dts/am335x-sancloud-bbe-lite.dts b/arch/arm/dts/am335x-sancloud-bbe-lite.dts
index d6ef193..8ffbc72 100644
--- a/arch/arm/dts/am335x-sancloud-bbe-lite.dts
+++ b/arch/arm/dts/am335x-sancloud-bbe-lite.dts
@@ -41,7 +41,7 @@
 		#address-cells = <1>;
 		#size-cells = <0>;
 
-		compatible = "micron,spi-authenta";
+		compatible = "micron,spi-authenta", "jedec,spi-nor";
 
 		reg = <0>;
 		spi-max-frequency = <16000000>;
diff --git a/arch/arm/dts/am335x-sancloud-bbe-u-boot.dtsi b/arch/arm/dts/am335x-sancloud-bbe-u-boot.dtsi
new file mode 100644
index 0000000..06e7554
--- /dev/null
+++ b/arch/arm/dts/am335x-sancloud-bbe-u-boot.dtsi
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2022 SanCloud Ltd
+ */
+
+#include "am335x-evm-u-boot.dtsi"
diff --git a/arch/arm/dts/meson-gxbb-nanopi-k2-u-boot.dtsi b/arch/arm/dts/meson-gxbb-nanopi-k2-u-boot.dtsi
index c35158d..2a245bb 100644
--- a/arch/arm/dts/meson-gxbb-nanopi-k2-u-boot.dtsi
+++ b/arch/arm/dts/meson-gxbb-nanopi-k2-u-boot.dtsi
@@ -5,3 +5,10 @@
  */
 
 #include "meson-gx-u-boot.dtsi"
+
+&ethmac {
+	snps,reset-gpio = <&gpio GPIOZ_14 0>;
+	snps,reset-delays-us = <0 10000 1000000>;
+	snps,reset-active-low;
+};
+
diff --git a/arch/arm/dts/versal-mini-ospi-single.dts b/arch/arm/dts/versal-mini-ospi-single.dts
new file mode 100644
index 0000000..23f6e47
--- /dev/null
+++ b/arch/arm/dts/versal-mini-ospi-single.dts
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Versal QSPI single DTS
+ *
+ * Copyright (C) 2018-2020 Xilinx, Inc.
+ */
+
+#include "versal-mini-ospi.dtsi"
+
+/ {
+	model = "Xilinx Versal MINI OSPI SINGLE";
+};
+
+&flash0 {
+	spi-rx-bus-width = <8>;
+};
diff --git a/arch/arm/dts/versal-mini-ospi.dtsi b/arch/arm/dts/versal-mini-ospi.dtsi
new file mode 100644
index 0000000..a4b76e2
--- /dev/null
+++ b/arch/arm/dts/versal-mini-ospi.dtsi
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dts file for Xilinx Versal Mini OSPI Configuration
+ *
+ * (C) Copyright 2018-2019, Xilinx, Inc.
+ *
+ * Siva Durga Prasad <siva.durga.paladugu@xilinx.com>
+ * Michal Simek <michal.simek@xilinx.com>
+ */
+
+/dts-v1/;
+
+/ {
+	compatible = "xlnx,versal";
+	#address-cells = <2>;
+	#size-cells = <2>;
+	model = "Xilinx Versal MINI OSPI";
+
+	clk125: clk125 {
+		compatible = "fixed-clock";
+		#clock-cells = <0x0>;
+		clock-frequency = <125000000>;
+	};
+
+	dcc: dcc {
+		compatible = "arm,dcc";
+		status = "okay";
+		u-boot,dm-pre-reloc;
+	};
+
+	amba: amba {
+		u-boot,dm-pre-reloc;
+		compatible = "simple-bus";
+		#address-cells = <0x2>;
+		#size-cells = <0x2>;
+		ranges;
+
+		ospi: spi@f1010000 {
+			compatible = "cadence,qspi", "cdns,qspi-nor";
+			status = "okay";
+			reg = <0 0xf1010000 0 0x10000 0 0xc0000000 0 0x20000000>;
+			clock-names = "ref_clk", "pclk";
+			clocks = <&clk125 &clk125>;
+			bus-num = <2>;
+			num-cs = <1>;
+			cdns,fifo-depth = <256>;
+			cdns,fifo-width = <4>;
+			cdns,is-dma = <1>;
+			cdns,trigger-address = <0xc0000000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			flash0: flash@0 {
+				compatible = "n25q512a", "micron,m25p80",
+					     "jedec,spi-nor";
+				reg = <0x0>;
+				spi-tx-bus-width = <8>;
+				spi-rx-bus-width = <8>;
+				spi-max-frequency = <20000000>;
+			};
+		};
+	};
+
+	aliases {
+		serial0 = &dcc;
+		spi0 = &ospi;
+	};
+
+	chosen {
+		stdout-path = "serial0:115200";
+	};
+
+	memory@fffc0000 {
+		device_type = "memory";
+		reg = <0x0 0xfffc0000 0x0 0x40000>;
+	};
+};
diff --git a/arch/arm/dts/versal-mini-qspi-single.dts b/arch/arm/dts/versal-mini-qspi-single.dts
new file mode 100644
index 0000000..ee518d5
--- /dev/null
+++ b/arch/arm/dts/versal-mini-qspi-single.dts
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Versal QSPI single DTS
+ *
+ * Copyright (C) 2018-2019 Xilinx, Inc.
+ */
+
+#include "versal-mini-qspi.dtsi"
+
+/ {
+	model = "Xilinx Versal MINI QSPI SINGLE";
+};
+
+&flash0 {
+	spi-rx-bus-width = <4>;
+};
diff --git a/arch/arm/dts/versal-mini-qspi.dtsi b/arch/arm/dts/versal-mini-qspi.dtsi
new file mode 100644
index 0000000..71d0ba5
--- /dev/null
+++ b/arch/arm/dts/versal-mini-qspi.dtsi
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dts file for Xilinx Versal Mini QSPI Configuration
+ *
+ * (C) Copyright 2018-2019, Xilinx, Inc.
+ *
+ * Siva Durga Prasad <siva.durga.paladugu@xilinx.com>
+ * Michal Simek <michal.simek@xilinx.com>
+ */
+
+/dts-v1/;
+
+/ {
+	compatible = "xlnx,versal";
+	#address-cells = <2>;
+	#size-cells = <2>;
+	model = "Xilinx Versal MINI QSPI";
+
+	clk150: clk150 {
+		compatible = "fixed-clock";
+		#clock-cells = <0x0>;
+		clock-frequency = <150000000>;
+	};
+
+	dcc: dcc {
+		compatible = "arm,dcc";
+		status = "okay";
+		u-boot,dm-pre-reloc;
+	};
+
+	amba: amba {
+		u-boot,dm-pre-reloc;
+		compatible = "simple-bus";
+		#address-cells = <0x2>;
+		#size-cells = <0x2>;
+		ranges;
+
+		qspi: spi@f1030000 {
+			compatible = "xlnx,versal-qspi-1.0";
+			status = "okay";
+			clock-names = "ref_clk", "pclk";
+			num-cs = <0x1>;
+			reg = <0x0 0xf1030000 0x0 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			clocks = <&clk150 &clk150>;
+
+			flash0: flash@0 {
+				compatible = "n25q512a", "micron,m25p80",
+					     "jedec,spi-nor";
+				reg = <0x0>;
+				spi-tx-bus-width = <4>;
+				spi-rx-bus-width = <4>;
+				spi-max-frequency = <20000000>;
+			};
+		};
+	};
+
+	aliases {
+		serial0 = &dcc;
+		spi0 = &qspi;
+	};
+
+	chosen {
+		stdout-path = "serial0:115200";
+	};
+
+	memory@fffc0000 {
+		device_type = "memory";
+		reg = <0x0 0xfffc0000 0x0 0x40000>;
+	};
+};
diff --git a/arch/arm/dts/zynq-7000.dtsi b/arch/arm/dts/zynq-7000.dtsi
index edc147d..f72ef52 100644
--- a/arch/arm/dts/zynq-7000.dtsi
+++ b/arch/arm/dts/zynq-7000.dtsi
@@ -340,7 +340,7 @@
 				u-boot,dm-pre-reloc;
 				#clock-cells = <1>;
 				compatible = "xlnx,ps7-clkc";
-				fclk-enable = <0>;
+				fclk-enable = <0xf>;
 				clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x",
 						"cpu_3or2x", "cpu_2x", "cpu_1x", "ddr2x", "ddr3x",
 						"dci", "lqspi", "smc", "pcap", "gem0", "gem1",
diff --git a/arch/arm/dts/zynq-cse-nand.dts b/arch/arm/dts/zynq-cse-nand.dts
index 32cb3bf..27adfb9 100644
--- a/arch/arm/dts/zynq-cse-nand.dts
+++ b/arch/arm/dts/zynq-cse-nand.dts
@@ -86,6 +86,13 @@
 				reg = <0x100 0x100>;
 			};
 		};
+
+		scutimer: timer@f8f00600 {
+			u-boot,dm-pre-reloc;
+			compatible = "arm,cortex-a9-twd-timer";
+			reg = <0xf8f00600 0x20>;
+			clock-frequency = <333333333>;
+		};
 	};
 };
 
diff --git a/arch/arm/dts/zynq-cse-nor.dts b/arch/arm/dts/zynq-cse-nor.dts
index 197fbd7..f22a149 100644
--- a/arch/arm/dts/zynq-cse-nor.dts
+++ b/arch/arm/dts/zynq-cse-nor.dts
@@ -85,6 +85,13 @@
 			#address-cells = <1>;
 			#size-cells = <1>;
 		};
+
+		scutimer: timer@f8f00600 {
+			u-boot,dm-pre-reloc;
+			compatible = "arm,cortex-a9-twd-timer";
+			reg = <0xf8f00600 0x20>;
+			clock-frequency = <333333333>;
+		};
 	};
 };
 
diff --git a/arch/arm/dts/zynq-cse-qspi.dtsi b/arch/arm/dts/zynq-cse-qspi.dtsi
index 38410ee..f7ac92b 100644
--- a/arch/arm/dts/zynq-cse-qspi.dtsi
+++ b/arch/arm/dts/zynq-cse-qspi.dtsi
@@ -116,6 +116,13 @@
 				reg = <0x100 0x100>;
 			};
 		};
+
+		scutimer: timer@f8f00600 {
+			u-boot,dm-pre-reloc;
+			compatible = "arm,cortex-a9-twd-timer";
+			reg = <0xf8f00600 0x20>;
+			clock-frequency = <333333333>;
+		};
 	};
 
 };
diff --git a/arch/arm/dts/zynqmp-zcu106-revA.dts b/arch/arm/dts/zynqmp-zcu106-revA.dts
index 3e13767..4858b4d 100644
--- a/arch/arm/dts/zynqmp-zcu106-revA.dts
+++ b/arch/arm/dts/zynqmp-zcu106-revA.dts
@@ -200,12 +200,19 @@
 	phy-mode = "rgmii-id";
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_gem3_default>;
-	phy0: ethernet-phy@c {
-		reg = <0xc>;
-		ti,rx-internal-delay = <0x8>;
-		ti,tx-internal-delay = <0xa>;
-		ti,fifo-depth = <0x1>;
-		ti,dp83867-rxctrl-strap-quirk;
+	mdio: mdio {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		phy0: ethernet-phy@c {
+			#phy-cells = <1>;
+			reg = <0xc>;
+			compatible = "ethernet-phy-id2000.a231";
+			ti,rx-internal-delay = <0x8>;
+			ti,tx-internal-delay = <0xa>;
+			ti,fifo-depth = <0x1>;
+			ti,dp83867-rxctrl-strap-quirk;
+			reset-gpios = <&tca6416_u97 6 GPIO_ACTIVE_LOW>;
+		};
 	};
 };
 
diff --git a/arch/arm/include/asm/arch-aspeed/sdram_ast2600.h b/arch/arm/include/asm/arch-aspeed/sdram_ast2600.h
index d2408c0..b0a91ae 100644
--- a/arch/arm/include/asm/arch-aspeed/sdram_ast2600.h
+++ b/arch/arm/include/asm/arch-aspeed/sdram_ast2600.h
@@ -104,6 +104,10 @@
 #define SDRAM_FORCE_PRECHARGE_EN	BIT(4)
 #define SDRAM_REFRESH_EN		BIT(0)
 
+/* MCR14 */
+#define SDRAM_WL_SETTING		GENMASK(23, 20)
+#define SDRAM_CL_SETTING		GENMASK(19, 16)
+
 #define SDRAM_TEST_LEN_SHIFT		4
 #define SDRAM_TEST_LEN_MASK		0xfffff
 #define SDRAM_TEST_START_ADDR_SHIFT	24
diff --git a/arch/arm/mach-k3/am625_init.c b/arch/arm/mach-k3/am625_init.c
index 8c4b349..da2229d 100644
--- a/arch/arm/mach-k3/am625_init.c
+++ b/arch/arm/mach-k3/am625_init.c
@@ -26,7 +26,7 @@
 static void store_boot_info_from_rom(void)
 {
 	bootindex = *(u32 *)(CONFIG_SYS_K3_BOOT_PARAM_TABLE_INDEX);
-	memcpy(&bootdata, (uintptr_t *)ROM_ENTENDED_BOOT_DATA_INFO,
+	memcpy(&bootdata, (uintptr_t *)ROM_EXTENDED_BOOT_DATA_INFO,
 	       sizeof(struct rom_extended_boot_data));
 }
 
diff --git a/arch/arm/mach-k3/am642_init.c b/arch/arm/mach-k3/am642_init.c
index 0e30cc4..96f292e 100644
--- a/arch/arm/mach-k3/am642_init.c
+++ b/arch/arm/mach-k3/am642_init.c
@@ -61,7 +61,7 @@
 static void store_boot_info_from_rom(void)
 {
 	bootindex = *(u32 *)(CONFIG_SYS_K3_BOOT_PARAM_TABLE_INDEX);
-	memcpy(&bootdata, (uintptr_t *)ROM_ENTENDED_BOOT_DATA_INFO,
+	memcpy(&bootdata, (uintptr_t *)ROM_EXTENDED_BOOT_DATA_INFO,
 	       sizeof(struct rom_extended_boot_data));
 }
 
diff --git a/arch/arm/mach-k3/include/mach/am62_hardware.h b/arch/arm/mach-k3/include/mach/am62_hardware.h
index 278beb5..17d3228 100644
--- a/arch/arm/mach-k3/include/mach/am62_hardware.h
+++ b/arch/arm/mach-k3/include/mach/am62_hardware.h
@@ -53,7 +53,7 @@
 
 #define CTRLMMR_MCU_RST_CTRL			(MCU_CTRL_MMR0_BASE + 0x18170)
 
-#define ROM_ENTENDED_BOOT_DATA_INFO		0x43c3f1e0
+#define ROM_EXTENDED_BOOT_DATA_INFO		0x43c3f1e0
 
 /* Use Last 2K as Scratch pad */
 #define TI_SRAM_SCRATCH_BOARD_EEPROM_START		0x70000000
diff --git a/arch/arm/mach-k3/include/mach/am64_hardware.h b/arch/arm/mach-k3/include/mach/am64_hardware.h
index 6c9332e..207ef95 100644
--- a/arch/arm/mach-k3/include/mach/am64_hardware.h
+++ b/arch/arm/mach-k3/include/mach/am64_hardware.h
@@ -36,7 +36,7 @@
 
 #define MAIN_DEVSTAT_BACKUP_USB_MODE_MASK		0x01
 
-#define ROM_ENTENDED_BOOT_DATA_INFO			0x701beb00
+#define ROM_EXTENDED_BOOT_DATA_INFO			0x701beb00
 
 /* Use Last 2K as Scratch pad */
 #define TI_SRAM_SCRATCH_BOARD_EEPROM_START		0x7019f800
diff --git a/arch/arm/mach-k3/include/mach/j721e_hardware.h b/arch/arm/mach-k3/include/mach/j721e_hardware.h
index 032cb26..247dee9 100644
--- a/arch/arm/mach-k3/include/mach/j721e_hardware.h
+++ b/arch/arm/mach-k3/include/mach/j721e_hardware.h
@@ -33,7 +33,7 @@
 #define WKUP_DEVSTAT_MCU_ONLY_SHIFT		6
 
 /* ROM HANDOFF Structure location */
-#define ROM_ENTENDED_BOOT_DATA_INFO			0x41cffb00
+#define ROM_EXTENDED_BOOT_DATA_INFO			0x41cffb00
 
 /* MCU SCRATCHPAD usage */
 #define TI_SRAM_SCRATCH_BOARD_EEPROM_START	CONFIG_SYS_K3_MCU_SCRATCHPAD_BASE
diff --git a/arch/arm/mach-k3/include/mach/j721s2_hardware.h b/arch/arm/mach-k3/include/mach/j721s2_hardware.h
index e47f40e..2e155ed 100644
--- a/arch/arm/mach-k3/include/mach/j721s2_hardware.h
+++ b/arch/arm/mach-k3/include/mach/j721s2_hardware.h
@@ -33,7 +33,7 @@
 #define WKUP_DEVSTAT_MCU_ONLY_SHIFT			6
 
 /* ROM HANDOFF Structure location */
-#define ROM_ENTENDED_BOOT_DATA_INFO			0x41cfdb00
+#define ROM_EXTENDED_BOOT_DATA_INFO			0x41cfdb00
 
 /* MCU SCRATCHPAD usage */
 #define TI_SRAM_SCRATCH_BOARD_EEPROM_START	CONFIG_SYS_K3_MCU_SCRATCHPAD_BASE
diff --git a/arch/arm/mach-k3/j721e_init.c b/arch/arm/mach-k3/j721e_init.c
index 1bea05a..276cbb5 100644
--- a/arch/arm/mach-k3/j721e_init.c
+++ b/arch/arm/mach-k3/j721e_init.c
@@ -130,7 +130,7 @@
 static void store_boot_info_from_rom(void)
 {
 	bootindex = *(u32 *)(CONFIG_SYS_K3_BOOT_PARAM_TABLE_INDEX);
-	memcpy(&bootdata, (uintptr_t *)ROM_ENTENDED_BOOT_DATA_INFO,
+	memcpy(&bootdata, (uintptr_t *)ROM_EXTENDED_BOOT_DATA_INFO,
 	       sizeof(struct rom_extended_boot_data));
 }
 
diff --git a/arch/arm/mach-k3/j721s2_init.c b/arch/arm/mach-k3/j721s2_init.c
index dd0c7ba..0206b01 100644
--- a/arch/arm/mach-k3/j721s2_init.c
+++ b/arch/arm/mach-k3/j721s2_init.c
@@ -89,7 +89,7 @@
 static void store_boot_info_from_rom(void)
 {
 	bootindex = *(u32 *)(CONFIG_SYS_K3_BOOT_PARAM_TABLE_INDEX);
-	memcpy(&bootdata, (uintptr_t *)ROM_ENTENDED_BOOT_DATA_INFO,
+	memcpy(&bootdata, (uintptr_t *)ROM_EXTENDED_BOOT_DATA_INFO,
 	       sizeof(struct rom_extended_boot_data));
 }
 
diff --git a/arch/arm/mach-versal-net/cpu.c b/arch/arm/mach-versal-net/cpu.c
index 4c9b154..a82741e 100644
--- a/arch/arm/mach-versal-net/cpu.c
+++ b/arch/arm/mach-versal-net/cpu.c
@@ -15,6 +15,7 @@
 #include <asm/arch/hardware.h>
 #include <asm/arch/sys_proto.h>
 #include <asm/cache.h>
+#include <dm/platdata.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -87,3 +88,7 @@
 {
 	return 0x14000;
 }
+
+U_BOOT_DRVINFO(soc_xilinx_versal_net) = {
+	.name = "soc_xilinx_versal_net",
+};
diff --git a/arch/arm/mach-zynq/include/mach/sys_proto.h b/arch/arm/mach-zynq/include/mach/sys_proto.h
index 1dc16d4..268ec50 100644
--- a/arch/arm/mach-zynq/include/mach/sys_proto.h
+++ b/arch/arm/mach-zynq/include/mach/sys_proto.h
@@ -17,6 +17,4 @@
 extern void zynq_ddrc_init(void);
 extern unsigned int zynq_get_silicon_version(void);
 
-int zynq_board_read_rom_ethaddr(unsigned char *ethaddr);
-
 #endif /* _SYS_PROTO_H_ */
diff --git a/arch/arm/mach-zynqmp/Kconfig b/arch/arm/mach-zynqmp/Kconfig
index 6604506..fd6f077 100644
--- a/arch/arm/mach-zynqmp/Kconfig
+++ b/arch/arm/mach-zynqmp/Kconfig
@@ -142,7 +142,14 @@
 	bool "Include psu_init"
 	select BOARD_EARLY_INIT_F
 	help
-	  Include psu_init to full u-boot. SPL include psu_init by default.
+	  Include psu_init to full u-boot.
+
+config SPL_ZYNQMP_PSU_INIT_ENABLED
+	bool "Include psu_init in SPL"
+	default y if SPL
+	select BOARD_EARLY_INIT_F
+	help
+	  Include psu_init by default in SPL.
 
 config SPL_ZYNQMP_ALT_BOOTMODE_ENABLED
 	bool "Overwrite SPL bootmode"
diff --git a/arch/arm/mach-zynqmp/Makefile b/arch/arm/mach-zynqmp/Makefile
index 4f9f6b5..bb1830c 100644
--- a/arch/arm/mach-zynqmp/Makefile
+++ b/arch/arm/mach-zynqmp/Makefile
@@ -8,4 +8,4 @@
 obj-$(CONFIG_MP)	+= mp.o
 obj-$(CONFIG_SPL_BUILD) += spl.o handoff.o psu_spl_init.o
 obj-$(CONFIG_SPL_ZYNQMP_DRAM_ECC_INIT) += ecc_spl_init.o
-obj-$(CONFIG_ZYNQMP_PSU_INIT_ENABLED)	+= psu_spl_init.o
+obj-$(CONFIG_$(SPL_)ZYNQMP_PSU_INIT_ENABLED)	+= psu_spl_init.o
diff --git a/arch/arm/mach-zynqmp/include/mach/hardware.h b/arch/arm/mach-zynqmp/include/mach/hardware.h
index a70d6d6..70221e0 100644
--- a/arch/arm/mach-zynqmp/include/mach/hardware.h
+++ b/arch/arm/mach-zynqmp/include/mach/hardware.h
@@ -175,7 +175,9 @@
 #define ZYNQMP_PMU_BASEADDR	0xFFD80000
 
 struct pmu_regs {
-	u32 reserved[18];
+	u32 reserved0[16];
+	u32 gen_storage4; /* 0x40 */
+	u32 reserved1[1];
 	u32 gen_storage6; /* 0x48 */
 };
 
diff --git a/arch/arm/mach-zynqmp/include/mach/sys_proto.h b/arch/arm/mach-zynqmp/include/mach/sys_proto.h
index 9fffb4e..c6733ed 100644
--- a/arch/arm/mach-zynqmp/include/mach/sys_proto.h
+++ b/arch/arm/mach-zynqmp/include/mach/sys_proto.h
@@ -46,7 +46,6 @@
 	TCM_SPLIT,
 };
 
-int zynq_board_read_rom_ethaddr(unsigned char *ethaddr);
 unsigned int zynqmp_get_silicon_version(void);
 
 int zynqmp_mmio_write(const u32 address, const u32 mask, const u32 value);
diff --git a/arch/arm/mach-zynqmp/mp.c b/arch/arm/mach-zynqmp/mp.c
index 949456d..2891878 100644
--- a/arch/arm/mach-zynqmp/mp.c
+++ b/arch/arm/mach-zynqmp/mp.c
@@ -42,6 +42,9 @@
 
 #define ZYNQMP_MAX_CORES	6
 
+#define ZYNQMP_RPU0_USE_MASK BIT(1)
+#define ZYNQMP_RPU1_USE_MASK BIT(2)
+
 int is_core_valid(unsigned int core)
 {
 	if (core < ZYNQMP_MAX_CORES)
@@ -248,6 +251,27 @@
 		enable_clock_r5();
 		release_r5_reset(ZYNQMP_CORE_RPU1, SPLIT);
 	}
+}
+
+static void mark_r5_used(u32 nr, u8 mode)
+{
+	u32 mask = 0;
+
+	if (mode == LOCK) {
+		mask = ZYNQMP_RPU0_USE_MASK | ZYNQMP_RPU1_USE_MASK;
+	} else {
+		switch (nr) {
+		case ZYNQMP_CORE_RPU0:
+			mask = ZYNQMP_RPU0_USE_MASK;
+			break;
+		case ZYNQMP_CORE_RPU1:
+			mask = ZYNQMP_RPU1_USE_MASK;
+			break;
+		default:
+			return;
+		}
+	}
+	zynqmp_mmio_write((ulong)&pmu_base->gen_storage4, mask, mask);
 }
 
 int cpu_release(u32 nr, int argc, char *const argv[])
@@ -305,6 +329,7 @@
 			write_tcm_boot_trampoline(boot_addr_uniq);
 			dcache_enable();
 			set_r5_halt_mode(nr, RELEASE, LOCK);
+			mark_r5_used(nr, LOCK);
 		} else if (!strncmp(argv[1], "split", 5)) {
 			printf("R5 split mode\n");
 			set_r5_reset(nr, SPLIT);
@@ -317,6 +342,7 @@
 			write_tcm_boot_trampoline(boot_addr_uniq);
 			dcache_enable();
 			set_r5_halt_mode(nr, RELEASE, SPLIT);
+			mark_r5_used(nr, SPLIT);
 		} else {
 			printf("Unsupported mode\n");
 			return 1;
diff --git a/arch/sandbox/cpu/sdl.c b/arch/sandbox/cpu/sdl.c
index f4ca36b..2c570ed 100644
--- a/arch/sandbox/cpu/sdl.c
+++ b/arch/sandbox/cpu/sdl.c
@@ -441,7 +441,6 @@
 {
 	struct buf_info *buf;
 	int avail;
-	bool have_data = false;
 	int i;
 
 	for (i = 0; i < 2; i++) {
@@ -453,10 +452,9 @@
 		}
 		if (avail > len)
 			avail = len;
-		have_data = true;
 
-		SDL_MixAudio(stream, buf->data + buf->pos, avail,
-			     SDL_MIX_MAXVOLUME);
+		memcpy(stream, buf->data + buf->pos, avail);
+		stream += avail;
 		buf->pos += avail;
 		len -= avail;
 
@@ -466,7 +464,8 @@
 		else
 			break;
 	}
-	sdl.stopping = !have_data;
+	memset(stream, 0, len);
+	sdl.stopping = !!len;
 }
 
 int sandbox_sdl_sound_init(int rate, int channels)
@@ -484,7 +483,7 @@
 	wanted.freq = rate;
 	wanted.format = AUDIO_S16;
 	wanted.channels = channels;
-	wanted.samples = 1024;  /* Good low-latency value for callback */
+	wanted.samples = 960;  /* Good low-latency value for callback */
 	wanted.callback = sandbox_sdl_fill_audio;
 	wanted.userdata = NULL;
 
diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h
index 0406085..568738c 100644
--- a/arch/sandbox/include/asm/test.h
+++ b/arch/sandbox/include/asm/test.h
@@ -189,6 +189,16 @@
 int sandbox_get_sound_active(struct udevice *dev);
 
 /**
+ * sandbox_get_sound_count() - Read back the count of the sound data so far
+ *
+ * This data is provided to the sandbox driver by the sound play() method.
+ *
+ * @dev: Device to check
+ * Return: count of audio data
+ */
+int sandbox_get_sound_count(struct udevice *dev);
+
+/**
  * sandbox_get_sound_sum() - Read back the sum of the sound data so far
  *
  * This data is provided to the sandbox driver by the sound play() method.
diff --git a/board/raspberrypi/rpi/MAINTAINERS b/board/raspberrypi/rpi/MAINTAINERS
index 4f1b23e..9893511 100644
--- a/board/raspberrypi/rpi/MAINTAINERS
+++ b/board/raspberrypi/rpi/MAINTAINERS
@@ -1,5 +1,6 @@
 RPI BOARD
 M:	Matthias Brugger <mbrugger@suse.com>
+M:	Peter Robinson <pbrobinson@gmail.com>
 S:	Maintained
 F:	board/raspberrypi/rpi/
 F:	include/configs/rpi.h
diff --git a/board/sandbox/sandbox.c b/board/sandbox/sandbox.c
index 4d89f9b..4c655df 100644
--- a/board/sandbox/sandbox.c
+++ b/board/sandbox/sandbox.c
@@ -30,6 +30,19 @@
 gd_t *gd;
 
 #if CONFIG_IS_ENABLED(EFI_HAVE_CAPSULE_SUPPORT)
+/* GUIDs for capsule updatable firmware images */
+#define SANDBOX_UBOOT_IMAGE_GUID \
+	EFI_GUID(0x09d7cf52, 0x0720, 0x4710, 0x91, 0xd1, \
+		 0x08, 0x46, 0x9b, 0x7f, 0xe9, 0xc8)
+
+#define SANDBOX_UBOOT_ENV_IMAGE_GUID \
+	EFI_GUID(0x5a7021f5, 0xfef2, 0x48b4, 0xaa, 0xba, \
+		 0x83, 0x2e, 0x77, 0x74, 0x18, 0xc0)
+
+#define SANDBOX_FIT_IMAGE_GUID \
+	EFI_GUID(0x3673b45d, 0x6a7c, 0x46f3, 0x9e, 0x60, \
+		 0xad, 0xab, 0xb0, 0x3f, 0x79, 0x37)
+
 struct efi_fw_image fw_images[] = {
 #if defined(CONFIG_EFI_CAPSULE_FIRMWARE_RAW)
 	{
diff --git a/board/xilinx/Kconfig b/board/xilinx/Kconfig
index 746a233..4f0776e 100644
--- a/board/xilinx/Kconfig
+++ b/board/xilinx/Kconfig
@@ -58,23 +58,6 @@
 	help
 	   Specifies distro boot script offset in NAND/QSPI/NOR flash.
 
-config ZYNQ_MAC_IN_EEPROM
-	bool "Reading MAC address from EEPROM"
-	help
-	  Enable this option if your MAC address is saved in eeprom and
-	  xlnx,eeprom DT property in chosen node points to it.
-
-if ZYNQ_MAC_IN_EEPROM
-
-config ZYNQ_GEM_I2C_MAC_OFFSET
-	hex "Set the I2C MAC offset"
-	default 0x0
-	depends on DM_I2C
-	help
-	  Set the MAC offset for i2C.
-
-endif
-
 config CMD_FRU
 	bool "FRU information for product"
 	help
@@ -82,3 +65,12 @@
 	  information present in the device. The FRU Information is used
 	  to primarily to provide "inventory" information about the boards
 	  that the FRU Information Device is located on.
+
+config FRU_SC
+	bool "FRU system controller decoding"
+	help
+	  Xilinx System Controller (SC) FRU format is describing boards from two
+	  angles. One from DUT and then from SC. DUT is default option for
+	  the main CPU. SC behaves more or less as slave and have different ID.
+	  If you build U-Boot for SC you should enable this option to get proper
+	  MAC address.
diff --git a/board/xilinx/common/board.c b/board/xilinx/common/board.c
index 99fdbac..59d87f2 100644
--- a/board/xilinx/common/board.c
+++ b/board/xilinx/common/board.c
@@ -54,34 +54,6 @@
 u8 num_image_type_guids = ARRAY_SIZE(fw_images);
 #endif /* EFI_HAVE_CAPSULE_SUPPORT */
 
-#if defined(CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET)
-int zynq_board_read_rom_ethaddr(unsigned char *ethaddr)
-{
-	int ret = -EINVAL;
-	struct udevice *dev;
-	ofnode eeprom;
-
-	eeprom = ofnode_get_chosen_node("xlnx,eeprom");
-	if (!ofnode_valid(eeprom))
-		return -ENODEV;
-
-	debug("%s: Path to EEPROM %s\n", __func__,
-	      ofnode_read_chosen_string("xlnx,eeprom"));
-
-	ret = uclass_get_device_by_ofnode(UCLASS_I2C_EEPROM, eeprom, &dev);
-	if (ret)
-		return ret;
-
-	ret = dm_i2c_read(dev, CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET, ethaddr, 6);
-	if (ret)
-		debug("%s: I2C EEPROM MAC address read failed\n", __func__);
-	else
-		debug("%s: I2C EEPROM MAC %pM\n", __func__, ethaddr);
-
-	return ret;
-}
-#endif
-
 #define EEPROM_HEADER_MAGIC		0xdaaddeed
 #define EEPROM_HDR_MANUFACTURER_LEN	16
 #define EEPROM_HDR_NAME_LEN		16
diff --git a/board/xilinx/common/fru.h b/board/xilinx/common/fru.h
index 59f6b72..586c41b 100644
--- a/board/xilinx/common/fru.h
+++ b/board/xilinx/common/fru.h
@@ -90,6 +90,7 @@
 #define FRU_MULTIREC_MAC_OFFSET		4
 #define FRU_LAST_REC			BIT(7)
 #define FRU_DUT_MACID			0x31
+#define FRU_SC_MACID			0x11
 
 /* This should be minimum of fields */
 #define FRU_BOARD_AREA_TOTAL_FIELDS	5
diff --git a/board/xilinx/common/fru_ops.c b/board/xilinx/common/fru_ops.c
index 49846ae..c4f009a 100644
--- a/board/xilinx/common/fru_ops.c
+++ b/board/xilinx/common/fru_ops.c
@@ -239,8 +239,12 @@
 
 		if (mrc.rec_type == FRU_MULTIREC_TYPE_OEM) {
 			struct fru_multirec_mac *mac = (void *)addr + hdr_len;
+			u32 type = FRU_DUT_MACID;
+
+			if (CONFIG_IS_ENABLED(FRU_SC))
+				type = FRU_SC_MACID;
 
-			if (mac->ver == FRU_DUT_MACID) {
+			if (mac->ver == type) {
 				mac_len = mrc.len - FRU_MULTIREC_MAC_OFFSET;
 				memcpy(&fru_data.mac.macid, mac->macid, mac_len);
 			}
diff --git a/board/xilinx/zynqmp/MAINTAINERS b/board/xilinx/zynqmp/MAINTAINERS
index 17a2766..07b91b8 100644
--- a/board/xilinx/zynqmp/MAINTAINERS
+++ b/board/xilinx/zynqmp/MAINTAINERS
@@ -11,7 +11,7 @@
 F:	configs/avnet_ultra96_rev1_defconfig
 
 ARM ZYNQMP AVNET ULTRAZED EV BOARD
-M:	Luca Ceresoli <luca@lucaceresoli.net>
+M:	Luca Ceresoli <luca.ceresoli@bootlin.com>
 S:	Maintained
 F:	arch/arm/dts/avnet-ultrazedev-*
 F:	configs/avnet_ultrazedev_cc_v1_0_ultrazedev_som_v1_0_defconfig
diff --git a/board/xilinx/zynqmp/Makefile b/board/xilinx/zynqmp/Makefile
index a914028..732f909 100644
--- a/board/xilinx/zynqmp/Makefile
+++ b/board/xilinx/zynqmp/Makefile
@@ -31,11 +31,7 @@
 endif
 endif
 
-ifdef_any_of = $(filter-out undefined,$(foreach v,$(1),$(origin $(v))))
-
-ifneq ($(call ifdef_any_of, CONFIG_ZYNQMP_PSU_INIT_ENABLED CONFIG_SPL_BUILD),)
-obj-y += $(init-objs)
-endif
+obj-$(CONFIG_$(SPL_)ZYNQMP_PSU_INIT_ENABLED) += $(init-objs)
 
 ifdef CONFIG_SPL_BUILD
 ifneq ($(CONFIG_ZYNQMP_SPL_PM_CFG_OBJ_FILE),"")
diff --git a/board/xilinx/zynqmp/zynqmp-e-a2197-00-revB b/board/xilinx/zynqmp/zynqmp-e-a2197-00-revB
new file mode 120000
index 0000000..15ccce3
--- /dev/null
+++ b/board/xilinx/zynqmp/zynqmp-e-a2197-00-revB
@@ -0,0 +1 @@
+zynqmp-e-a2197-00-revA
\ No newline at end of file
diff --git a/board/xilinx/zynqmp/zynqmp.c b/board/xilinx/zynqmp/zynqmp.c
index 5fe0873..579708d 100644
--- a/board/xilinx/zynqmp/zynqmp.c
+++ b/board/xilinx/zynqmp/zynqmp.c
@@ -611,8 +611,7 @@
 
 void set_dfu_alt_info(char *interface, char *devstr)
 {
-	int multiboot;
-	int bootseq = 0;
+	int multiboot, bootseq = 0, len = 0;
 
 	ALLOC_CACHE_ALIGN_BUFFER(char, buf, DFU_ALT_BUF_LEN);
 
@@ -634,29 +633,33 @@
 	case SD1_LSHFT_MODE:
 	case SD_MODE1:
 		bootseq = mmc_get_env_dev();
-		if (!multiboot)
-			snprintf(buf, DFU_ALT_BUF_LEN,
-				 "mmc %d=boot.bin fat %d 1;"
-				 "%s fat %d 1",
-				 bootseq, bootseq,
-				 CONFIG_SPL_FS_LOAD_PAYLOAD_NAME, bootseq);
-		else
-			snprintf(buf, DFU_ALT_BUF_LEN,
-				 "mmc %d=boot%04d.bin fat %d 1;"
-				 "%s fat %d 1",
-				 bootseq, multiboot, bootseq,
-				 CONFIG_SPL_FS_LOAD_PAYLOAD_NAME, bootseq);
+
+		len += snprintf(buf + len, DFU_ALT_BUF_LEN, "mmc %d=boot",
+			       bootseq);
+
+		if (multiboot)
+			len += snprintf(buf + len, DFU_ALT_BUF_LEN,
+				       "%04d", multiboot);
+
+		len += snprintf(buf + len, DFU_ALT_BUF_LEN, ".bin fat %d 1",
+			       bootseq);
+#if defined(CONFIG_SPL_FS_LOAD_PAYLOAD_NAME)
+		len += snprintf(buf + len, DFU_ALT_BUF_LEN, ";%s fat %d 1",
+			       CONFIG_SPL_FS_LOAD_PAYLOAD_NAME, bootseq);
+#endif
 		break;
-#if defined(CONFIG_SYS_SPI_U_BOOT_OFFS)
 	case QSPI_MODE_24BIT:
 	case QSPI_MODE_32BIT:
-		snprintf(buf, DFU_ALT_BUF_LEN,
-			 "sf 0:0=boot.bin raw %x 0x1500000;"
-			 "%s raw 0x%x 0x500000",
-			 multiboot * SZ_32K, CONFIG_SPL_FS_LOAD_PAYLOAD_NAME,
-			 CONFIG_SYS_SPI_U_BOOT_OFFS);
-		break;
+		len += snprintf(buf + len, DFU_ALT_BUF_LEN,
+			       "sf 0:0=boot.bin raw %x 0x1500000",
+			       multiboot * SZ_32K);
+#if defined(CONFIG_SPL_FS_LOAD_PAYLOAD_NAME) && defined(CONFIG_SYS_SPI_U_BOOT_OFFS)
+		len += snprintf(buf + len, DFU_ALT_BUF_LEN,
+			       ";%s raw 0x%x 0x500000",
+			       CONFIG_SPL_FS_LOAD_PAYLOAD_NAME,
+			       CONFIG_SYS_SPI_U_BOOT_OFFS);
 #endif
+		break;
 	default:
 		return;
 	}
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 1092fb9..d93731f 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1812,6 +1812,13 @@
 	  is complete.  Enable this option to disable this behavior and instead
 	  require files to be loaded over the network by subsequent commands.
 
+config CMD_WGET
+	bool "wget"
+	select TCP
+	help
+	  wget is a simple command to download kernel, or other files,
+	  from a http server over TCP.
+
 config CMD_MII
 	bool "mii"
 	imply CMD_MDIO
@@ -1840,6 +1847,13 @@
 	help
 	  Send ICMP ECHO_REQUEST to network host
 
+config CMD_PING6
+	bool "ping6"
+	depends on IPV6
+	default y if (CMD_PING && IPV6)
+	help
+	  Send ICMPv6 ECHO_REQUEST to network host
+
 config CMD_CDP
 	bool "cdp"
 	help
@@ -1959,7 +1973,7 @@
 
 config CMD_CLS
 	bool "Enable clear screen command 'cls'"
-	default y if LCD || DM_VIDEO
+	default y if LCD || VIDEO
 	help
 	  Enable the 'cls' command which clears the screen contents
 	  on video frame buffer.
diff --git a/cmd/Makefile b/cmd/Makefile
index 2444d11..0b6a96c 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -66,6 +66,11 @@
 obj-$(CONFIG_EFI) += efi.o
 obj-$(CONFIG_CMD_EFIDEBUG) += efidebug.o
 obj-$(CONFIG_CMD_EFICONFIG) += eficonfig.o
+ifdef CONFIG_CMD_EFICONFIG
+ifdef CONFIG_EFI_MM_COMM_TEE
+obj-$(CONFIG_EFI_SECURE_BOOT) += eficonfig_sbkey.o
+endif
+endif
 obj-$(CONFIG_CMD_ELF) += elf.o
 obj-$(CONFIG_CMD_EROFS) += erofs.o
 obj-$(CONFIG_HUSH_PARSER) += exit.o
diff --git a/cmd/eficonfig.c b/cmd/eficonfig.c
index 2b14389..394ae67 100644
--- a/cmd/eficonfig.c
+++ b/cmd/eficonfig.c
@@ -93,20 +93,14 @@
 };
 
 /**
- * struct eficonfig_boot_order - structure to be used to update BootOrder variable
+ * struct eficonfig_boot_order_data - structure to be used to update BootOrder variable
  *
- * @num:		index in the menu entry
- * @description:	pointer to the description string
  * @boot_index:		boot option index
  * @active:		flag to include the boot option into BootOrder variable
- * @list:		list structure
  */
-struct eficonfig_boot_order {
-	u32 num;
-	u16 *description;
+struct eficonfig_boot_order_data {
 	u32 boot_index;
 	bool active;
-	struct list_head list;
 };
 
 /**
@@ -263,7 +257,7 @@
 }
 
 /**
- * append_entry() - append menu item
+ * eficonfig_append_menu_entry() - append menu item
  *
  * @efi_menu:	pointer to the efimenu structure
  * @title:	pointer to the entry title
@@ -271,8 +265,9 @@
  * @data:	pointer to the data to be passed to each entry callback
  * Return:	status code
  */
-static efi_status_t append_entry(struct efimenu *efi_menu,
-				 char *title, eficonfig_entry_func func, void *data)
+efi_status_t eficonfig_append_menu_entry(struct efimenu *efi_menu,
+					 char *title, eficonfig_entry_func func,
+					 void *data)
 {
 	struct eficonfig_entry *entry;
 
@@ -295,12 +290,12 @@
 }
 
 /**
- * append_quit_entry() - append quit entry
+ * eficonfig_append_quit_entry() - append quit entry
  *
  * @efi_menu:	pointer to the efimenu structure
  * Return:	status code
  */
-static efi_status_t append_quit_entry(struct efimenu *efi_menu)
+efi_status_t eficonfig_append_quit_entry(struct efimenu *efi_menu)
 {
 	char *title;
 	efi_status_t ret;
@@ -309,7 +304,7 @@
 	if (!title)
 		return EFI_OUT_OF_RESOURCES;
 
-	ret = append_entry(efi_menu, title, eficonfig_process_quit, NULL);
+	ret = eficonfig_append_menu_entry(efi_menu, title, eficonfig_process_quit, NULL);
 	if (ret != EFI_SUCCESS)
 		free(title);
 
@@ -341,7 +336,7 @@
 		if (!title)
 			goto out;
 
-		ret = append_entry(efi_menu, title, iter->func, iter->data);
+		ret = eficonfig_append_menu_entry(efi_menu, title, iter->func, iter->data);
 		if (ret != EFI_SUCCESS) {
 			free(title);
 			goto out;
@@ -441,14 +436,15 @@
 }
 
 /**
- * create_selected_device_path() - create device path
+ * eficonfig_create_device_path() - create device path
  *
- * @file_info:	pointer to the selected file information
+ * @dp_volume:	pointer to the volume
+ * @current_path: pointer to the file path u16 string
  * Return:
  * device path or NULL. Caller must free the returned value
  */
-static
-struct efi_device_path *create_selected_device_path(struct eficonfig_select_file_info *file_info)
+struct efi_device_path *eficonfig_create_device_path(struct efi_device_path *dp_volume,
+						     u16 *current_path)
 {
 	char *p;
 	void *buf;
@@ -456,8 +452,7 @@
 	struct efi_device_path *dp;
 	struct efi_device_path_file_path *fp;
 
-	fp_size = sizeof(struct efi_device_path) +
-		  ((u16_strlen(file_info->current_path) + 1) * sizeof(u16));
+	fp_size = sizeof(struct efi_device_path) + u16_strsize(current_path);
 	buf = calloc(1, fp_size + sizeof(END));
 	if (!buf)
 		return NULL;
@@ -466,13 +461,13 @@
 	fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE,
 	fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH,
 	fp->dp.length = (u16)fp_size;
-	u16_strcpy(fp->str, file_info->current_path);
+	u16_strcpy(fp->str, current_path);
 
 	p = buf;
 	p += fp_size;
 	*((struct efi_device_path *)p) = END;
 
-	dp = efi_dp_append(file_info->dp_volume, (struct efi_device_path *)buf);
+	dp = efi_dp_append(dp_volume, (struct efi_device_path *)buf);
 	free(buf);
 
 	return dp;
@@ -492,7 +487,7 @@
 	if (!info)
 		return EFI_INVALID_PARAMETER;
 
-	if (!strcmp(info->file_name, "..")) {
+	if (!strcmp(info->file_name, "..\\")) {
 		struct eficonfig_filepath_info *iter;
 		struct list_head *pos, *n;
 		int is_last;
@@ -634,14 +629,15 @@
 		info->v = v;
 		info->dp = device_path;
 		info->file_info = file_info;
-		ret = append_entry(efi_menu, devname, eficonfig_volume_selected, info);
+		ret = eficonfig_append_menu_entry(efi_menu, devname, eficonfig_volume_selected,
+						  info);
 		if (ret != EFI_SUCCESS) {
 			free(info);
 			goto out;
 		}
 	}
 
-	ret = append_quit_entry(efi_menu);
+	ret = eficonfig_append_quit_entry(efi_menu);
 	if (ret != EFI_SUCCESS)
 		goto out;
 
@@ -699,14 +695,14 @@
 	u32 i, entry_num = 0;
 	struct eficonfig_file_entry_data *info;
 
-	efi_file_setpos_int(f, 0);
+	EFI_CALL(f->setpos(f, 0));
 	/* Read directory and construct menu structure */
 	for (i = 0; i < count; i++) {
 		if (entry_num >= EFICONFIG_ENTRY_NUM_MAX - 1)
 			break;
 
 		len = sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE;
-		ret = efi_file_read_int(f, &len, buf);
+		ret = EFI_CALL(f->read(f, &len, buf));
 		if (ret != EFI_SUCCESS || len == 0)
 			break;
 
@@ -745,8 +741,8 @@
 	      (int (*)(const void *, const void *))sort_file);
 
 	for (i = 0; i < entry_num; i++) {
-		ret = append_entry(efi_menu, tmp_infos[i]->file_name,
-				   eficonfig_file_selected, tmp_infos[i]);
+		ret = eficonfig_append_menu_entry(efi_menu, tmp_infos[i]->file_name,
+						  eficonfig_file_selected, tmp_infos[i]);
 		if (ret != EFI_SUCCESS)
 			goto out;
 	}
@@ -756,14 +752,14 @@
 }
 
 /**
- * eficonfig_select_file() - construct the file selection menu
+ * eficonfig_show_file_selection() - construct the file selection menu
  *
  * @file_info:	pointer to the file selection structure
  * @root:	pointer to the file handle
  * Return:	status code
  */
-static efi_status_t eficonfig_select_file(struct eficonfig_select_file_info *file_info,
-					  struct efi_file_handle *root)
+static efi_status_t eficonfig_show_file_selection(struct eficonfig_select_file_info *file_info,
+						  struct efi_file_handle *root)
 {
 	u32 count = 0, i;
 	efi_uintn_t len;
@@ -785,7 +781,8 @@
 		}
 		INIT_LIST_HEAD(&efi_menu->list);
 
-		ret = efi_file_open_int(root, &f, file_info->current_path, EFI_FILE_MODE_READ, 0);
+		ret = EFI_CALL(root->open(root, &f, file_info->current_path,
+					  EFI_FILE_MODE_READ, 0));
 		if (ret != EFI_SUCCESS) {
 			eficonfig_print_msg("Reading volume failed!");
 			free(efi_menu);
@@ -796,7 +793,7 @@
 		/* Count the number of directory entries */
 		for (;;) {
 			len = sizeof(struct efi_file_info) + EFICONFIG_FILE_PATH_BUF_SIZE;
-			ret = efi_file_read_int(f, &len, buf);
+			ret = EFI_CALL(f->read(f, &len, buf));
 			if (ret != EFI_SUCCESS || len == 0)
 				break;
 
@@ -815,13 +812,13 @@
 		if (ret != EFI_SUCCESS)
 			goto err;
 
-		ret = append_quit_entry(efi_menu);
+		ret = eficonfig_append_quit_entry(efi_menu);
 		if (ret != EFI_SUCCESS)
 			goto err;
 
 		ret = eficonfig_process_common(efi_menu, "  ** Select File **");
 err:
-		efi_file_close_int(f);
+		EFI_CALL(f->close(f));
 		eficonfig_destroy(efi_menu);
 
 		if (tmp_infos) {
@@ -939,17 +936,6 @@
 }
 
 /**
- * eficonfig_process_select_file() - callback function for "Select File" entry
- *
- * @data:	pointer to the data
- * Return:	status code
- */
-efi_status_t eficonfig_process_select_file(void *data)
-{
-	return EFI_SUCCESS;
-}
-
-/**
  * eficonfig_process_clear_file_selection() - callback function for "Clear" entry
  *
  * @data:	pointer to the data
@@ -973,19 +959,19 @@
 	{"Quit", eficonfig_process_quit},
 };
 
-
 /**
- * eficonfig_display_select_file_option() - display select file option
+ * eficonfig_process_show_file_option() - display select file option
  *
  * @file_info:	pointer to the file information structure
  * Return:	status code
  */
-efi_status_t eficonfig_display_select_file_option(struct eficonfig_select_file_info *file_info)
+efi_status_t eficonfig_process_show_file_option(void *data)
 {
 	efi_status_t ret;
 	struct efimenu *efi_menu;
 
-	select_file_menu_items[1].data = file_info;
+	select_file_menu_items[0].data = data;
+	select_file_menu_items[1].data = data;
 	efi_menu = eficonfig_create_fixed_menu(select_file_menu_items,
 					       ARRAY_SIZE(select_file_menu_items));
 	if (!efi_menu)
@@ -1001,12 +987,12 @@
 }
 
 /**
- * eficonfig_select_file_handler() - handle user file selection
+ * eficonfig_process_select_file() - handle user file selection
  *
  * @data:	pointer to the data
  * Return:	status code
  */
-efi_status_t eficonfig_select_file_handler(void *data)
+efi_status_t eficonfig_process_select_file(void *data)
 {
 	size_t len;
 	efi_status_t ret;
@@ -1016,10 +1002,6 @@
 	struct eficonfig_select_file_info *tmp = NULL;
 	struct eficonfig_select_file_info *file_info = data;
 
-	ret = eficonfig_display_select_file_option(file_info);
-	if (ret != EFI_SUCCESS)
-		return ret;
-
 	tmp = calloc(1, sizeof(struct eficonfig_select_file_info));
 	if (!tmp)
 		return EFI_OUT_OF_RESOURCES;
@@ -1042,11 +1024,11 @@
 		if (!tmp->current_volume)
 			return EFI_INVALID_PARAMETER;
 
-		ret = efi_open_volume_int(tmp->current_volume, &root);
+		ret = EFI_CALL(tmp->current_volume->open_volume(tmp->current_volume, &root));
 		if (ret != EFI_SUCCESS)
 			goto out;
 
-		ret = eficonfig_select_file(tmp, root);
+		ret = eficonfig_show_file_selection(tmp, root);
 		if (ret == EFI_ABORTED)
 			continue;
 		if (ret != EFI_SUCCESS)
@@ -1221,7 +1203,7 @@
 		utf16_utf8_strcpy(&p, val);
 	}
 
-	return append_entry(efi_menu, buf, func, data);
+	return eficonfig_append_menu_entry(efi_menu, buf, func, data);
 }
 
 /**
@@ -1284,7 +1266,7 @@
 	utf8_utf16_strcpy(&p, devname);
 	u16_strlcat(file_name, file_info->current_path, len);
 	ret = create_boot_option_entry(efi_menu, title, file_name,
-				       eficonfig_select_file_handler, file_info);
+				       eficonfig_process_show_file_option, file_info);
 out:
 	free(devname);
 	free(file_name);
@@ -1491,7 +1473,8 @@
 	}
 
 	if (bo->initrd_info.dp_volume) {
-		dp = create_selected_device_path(&bo->initrd_info);
+		dp = eficonfig_create_device_path(bo->initrd_info.dp_volume,
+						 bo->initrd_info.current_path);
 		if (!dp) {
 			ret = EFI_OUT_OF_RESOURCES;
 			goto out;
@@ -1500,7 +1483,7 @@
 		efi_free_pool(dp);
 	}
 
-	dp = create_selected_device_path(&bo->file_info);
+	dp = eficonfig_create_device_path(bo->file_info.dp_volume, bo->file_info.current_path);
 	if (!dp) {
 		ret = EFI_OUT_OF_RESOURCES;
 		goto out;
@@ -1678,7 +1661,7 @@
 	utf16_utf8_strcpy(&p, lo.label);
 	info->boot_index = boot_index;
 	info->selected = selected;
-	ret = append_entry(efi_menu, buf, eficonfig_process_boot_selected, info);
+	ret = eficonfig_append_menu_entry(efi_menu, buf, eficonfig_process_boot_selected, info);
 	if (ret != EFI_SUCCESS) {
 		free(load_option);
 		free(info);
@@ -1700,7 +1683,8 @@
 	u32 i;
 	u16 *bootorder;
 	efi_status_t ret;
-	efi_uintn_t num, size;
+	u16 *var_name16 = NULL, *p;
+	efi_uintn_t num, size, buf_size;
 	struct efimenu *efi_menu;
 	struct list_head *pos, *n;
 	struct eficonfig_entry *entry;
@@ -1724,20 +1708,49 @@
 	}
 
 	/* list the remaining load option not included in the BootOrder */
-	for (i = 0; i <= 0xFFFF; i++) {
-		/* If the index is included in the BootOrder, skip it */
-		if (search_bootorder(bootorder, num, i, NULL))
-			continue;
+	buf_size = 128;
+	var_name16 = malloc(buf_size);
+	if (!var_name16)
+		return EFI_OUT_OF_RESOURCES;
 
-		ret = eficonfig_add_boot_selection_entry(efi_menu, i, selected);
-		if (ret != EFI_SUCCESS)
-			goto out;
+	var_name16[0] = 0;
+	for (;;) {
+		int index;
+		efi_guid_t guid;
+
+		size = buf_size;
+		ret = efi_get_next_variable_name_int(&size, var_name16, &guid);
+		if (ret == EFI_NOT_FOUND)
+			break;
+		if (ret == EFI_BUFFER_TOO_SMALL) {
+			buf_size = size;
+			p = realloc(var_name16, buf_size);
+			if (!p) {
+				free(var_name16);
+				return EFI_OUT_OF_RESOURCES;
+			}
+			var_name16 = p;
+			ret = efi_get_next_variable_name_int(&size, var_name16, &guid);
+		}
+		if (ret != EFI_SUCCESS) {
+			free(var_name16);
+			return ret;
+		}
+		if (efi_varname_is_load_option(var_name16, &index)) {
+			/* If the index is included in the BootOrder, skip it */
+			if (search_bootorder(bootorder, num, index, NULL))
+				continue;
+
+			ret = eficonfig_add_boot_selection_entry(efi_menu, index, selected);
+			if (ret != EFI_SUCCESS)
+				goto out;
+		}
 
 		if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 1)
 			break;
 	}
 
-	ret = append_quit_entry(efi_menu);
+	ret = eficonfig_append_quit_entry(efi_menu);
 	if (ret != EFI_SUCCESS)
 		goto out;
 
@@ -1749,6 +1762,8 @@
 	}
 	eficonfig_destroy(efi_menu);
 
+	free(var_name16);
+
 	return ret;
 }
 
@@ -1813,7 +1828,7 @@
 {
 	bool reverse;
 	struct list_head *pos, *n;
-	struct eficonfig_boot_order *entry;
+	struct eficonfig_entry *entry;
 
 	printf(ANSI_CLEAR_CONSOLE ANSI_CURSOR_POSITION
 	       "\n  ** Change Boot Order **\n"
@@ -1829,7 +1844,7 @@
 
 	/* draw boot option list */
 	list_for_each_safe(pos, n, &efi_menu->list) {
-		entry = list_entry(pos, struct eficonfig_boot_order, list);
+		entry = list_entry(pos, struct eficonfig_entry, list);
 		reverse = (entry->num == efi_menu->active);
 
 		printf(ANSI_CURSOR_POSITION, entry->num + 4, 7);
@@ -1838,13 +1853,13 @@
 			puts(ANSI_COLOR_REVERSE);
 
 		if (entry->num < efi_menu->count - 2) {
-			if (entry->active)
+			if (((struct eficonfig_boot_order_data *)entry->data)->active)
 				printf("[*]  ");
 			else
 				printf("[ ]  ");
 		}
 
-		printf("%ls", entry->description);
+		printf("%s", entry->title);
 
 		if (reverse)
 			puts(ANSI_COLOR_RESET);
@@ -1861,9 +1876,8 @@
 {
 	int esc = 0;
 	struct list_head *pos, *n;
-	struct eficonfig_boot_order *tmp;
 	enum bootmenu_key key = KEY_NONE;
-	struct eficonfig_boot_order *entry;
+	struct eficonfig_entry *entry, *tmp;
 
 	while (1) {
 		bootmenu_loop(NULL, &key, &esc);
@@ -1872,11 +1886,11 @@
 		case KEY_PLUS:
 			if (efi_menu->active > 0) {
 				list_for_each_safe(pos, n, &efi_menu->list) {
-					entry = list_entry(pos, struct eficonfig_boot_order, list);
+					entry = list_entry(pos, struct eficonfig_entry, list);
 					if (entry->num == efi_menu->active)
 						break;
 				}
-				tmp = list_entry(pos->prev, struct eficonfig_boot_order, list);
+				tmp = list_entry(pos->prev, struct eficonfig_entry, list);
 				entry->num--;
 				tmp->num++;
 				list_del(&tmp->list);
@@ -1890,11 +1904,11 @@
 		case KEY_MINUS:
 			if (efi_menu->active < efi_menu->count - 3) {
 				list_for_each_safe(pos, n, &efi_menu->list) {
-					entry = list_entry(pos, struct eficonfig_boot_order, list);
+					entry = list_entry(pos, struct eficonfig_entry, list);
 					if (entry->num == efi_menu->active)
 						break;
 				}
-				tmp = list_entry(pos->next, struct eficonfig_boot_order, list);
+				tmp = list_entry(pos->next, struct eficonfig_entry, list);
 				entry->num++;
 				tmp->num--;
 				list_del(&entry->list);
@@ -1920,9 +1934,11 @@
 		case KEY_SPACE:
 			if (efi_menu->active < efi_menu->count - 2) {
 				list_for_each_safe(pos, n, &efi_menu->list) {
-					entry = list_entry(pos, struct eficonfig_boot_order, list);
+					entry = list_entry(pos, struct eficonfig_entry, list);
 					if (entry->num == efi_menu->active) {
-						entry->active = entry->active ? false : true;
+						struct eficonfig_boot_order_data *data = entry->data;
+
+						data->active = !data->active;
 						return EFI_NOT_READY;
 					}
 				}
@@ -1948,12 +1964,13 @@
 static efi_status_t eficonfig_add_change_boot_order_entry(struct efimenu *efi_menu,
 							  u32 boot_index, bool active)
 {
+	char *title, *p;
 	efi_status_t ret;
 	efi_uintn_t size;
 	void *load_option;
 	struct efi_load_option lo;
 	u16 varname[] = u"Boot####";
-	struct eficonfig_boot_order *entry;
+	struct eficonfig_boot_order_data *data;
 
 	efi_create_indexed_name(varname, sizeof(varname), "Boot", boot_index);
 	load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
@@ -1961,31 +1978,38 @@
 		return EFI_SUCCESS;
 
 	ret = efi_deserialize_load_option(&lo, load_option, &size);
-	if (ret != EFI_SUCCESS) {
-		free(load_option);
-		return ret;
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	data = calloc(1, sizeof(*data));
+	if (!data) {
+		ret = EFI_OUT_OF_RESOURCES;
+		goto out;
 	}
 
-	entry = calloc(1, sizeof(struct eficonfig_boot_order));
-	if (!entry) {
-		free(load_option);
-		return EFI_OUT_OF_RESOURCES;
+	title = calloc(1, utf16_utf8_strlen(lo.label) + 1);
+	if (!title) {
+		free(data);
+		ret = EFI_OUT_OF_RESOURCES;
+		goto out;
 	}
+	p = title;
+	utf16_utf8_strcpy(&p, lo.label);
 
-	entry->description = u16_strdup(lo.label);
-	if (!entry->description) {
-		free(load_option);
-		free(entry);
-		return EFI_OUT_OF_RESOURCES;
+	data->boot_index = boot_index;
+	data->active = active;
+
+	ret = eficonfig_append_menu_entry(efi_menu, title, NULL, data);
+	if (ret != EFI_SUCCESS) {
+		free(data);
+		free(title);
+		goto out;
 	}
-	entry->num = efi_menu->count++;
-	entry->boot_index = boot_index;
-	entry->active = active;
-	list_add_tail(&entry->list, &efi_menu->list);
 
+out:
 	free(load_option);
 
-	return EFI_SUCCESS;
+	return ret;
 }
 
 /**
@@ -2000,8 +2024,10 @@
 							     u16 *bootorder, efi_uintn_t num)
 {
 	u32 i;
+	char *title;
 	efi_status_t ret;
-	struct eficonfig_boot_order *entry;
+	u16 *var_name16 = NULL, *p;
+	efi_uintn_t size, buf_size;
 
 	/* list the load option in the order of BootOrder variable */
 	for (i = 0; i < num; i++) {
@@ -2014,41 +2040,67 @@
 	}
 
 	/* list the remaining load option not included in the BootOrder */
-	for (i = 0; i < 0xFFFF; i++) {
+	buf_size = 128;
+	var_name16 = malloc(buf_size);
+	if (!var_name16)
+		return EFI_OUT_OF_RESOURCES;
+
+	var_name16[0] = 0;
+	for (;;) {
+		int index;
+		efi_guid_t guid;
+
 		if (efi_menu->count >= EFICONFIG_ENTRY_NUM_MAX - 2)
 			break;
 
-		/* If the index is included in the BootOrder, skip it */
-		if (search_bootorder(bootorder, num, i, NULL))
-			continue;
-
-		ret = eficonfig_add_change_boot_order_entry(efi_menu, i, false);
+		size = buf_size;
+		ret = efi_get_next_variable_name_int(&size, var_name16, &guid);
+		if (ret == EFI_NOT_FOUND)
+			break;
+		if (ret == EFI_BUFFER_TOO_SMALL) {
+			buf_size = size;
+			p = realloc(var_name16, buf_size);
+			if (!p) {
+				ret = EFI_OUT_OF_RESOURCES;
+				goto out;
+			}
+			var_name16 = p;
+			ret = efi_get_next_variable_name_int(&size, var_name16, &guid);
+		}
 		if (ret != EFI_SUCCESS)
 			goto out;
+
+		if (efi_varname_is_load_option(var_name16, &index)) {
+			/* If the index is included in the BootOrder, skip it */
+			if (search_bootorder(bootorder, num, index, NULL))
+				continue;
+
+			ret = eficonfig_add_change_boot_order_entry(efi_menu, index, false);
+			if (ret != EFI_SUCCESS)
+				goto out;
+		}
 	}
 
 	/* add "Save" and "Quit" entries */
-	entry = calloc(1, sizeof(struct eficonfig_boot_order));
-	if (!entry)
+	title = strdup("Save");
+	if (!title) {
+		ret = EFI_OUT_OF_RESOURCES;
 		goto out;
-
-	entry->num = efi_menu->count++;
-	entry->description = u16_strdup(u"Save");
-	list_add_tail(&entry->list, &efi_menu->list);
+	}
 
-	entry = calloc(1, sizeof(struct eficonfig_boot_order));
-	if (!entry)
+	ret = eficonfig_append_menu_entry(efi_menu, title, NULL, NULL);
+	if (ret != EFI_SUCCESS)
 		goto out;
 
-	entry->num = efi_menu->count++;
-	entry->description = u16_strdup(u"Quit");
-	list_add_tail(&entry->list, &efi_menu->list);
+	ret = eficonfig_append_quit_entry(efi_menu);
+	if (ret != EFI_SUCCESS)
+		goto out;
 
 	efi_menu->active = 0;
-
-	return EFI_SUCCESS;
 out:
-	return EFI_OUT_OF_RESOURCES;
+	free(var_name16);
+
+	return ret;
 }
 
 /**
@@ -2064,7 +2116,7 @@
 	efi_status_t ret;
 	efi_uintn_t num, size;
 	struct list_head *pos, *n;
-	struct eficonfig_boot_order *entry;
+	struct eficonfig_entry *entry;
 	struct efimenu *efi_menu;
 
 	efi_menu = calloc(1, sizeof(struct efimenu));
@@ -2095,9 +2147,16 @@
 			/* create new BootOrder  */
 			count = 0;
 			list_for_each_safe(pos, n, &efi_menu->list) {
-				entry = list_entry(pos, struct eficonfig_boot_order, list);
-				if (entry->active)
-					new_bootorder[count++] = entry->boot_index;
+				struct eficonfig_boot_order_data *data;
+
+				entry = list_entry(pos, struct eficonfig_entry, list);
+				/* exit the loop when iteration reaches "Save" */
+				if (!strncmp(entry->title, "Save", strlen("Save")))
+					break;
+
+				data = entry->data;
+				if (data->active)
+					new_bootorder[count++] = data->boot_index;
 			}
 
 			size = count * sizeof(u16);
@@ -2116,15 +2175,12 @@
 		}
 	}
 out:
+	free(bootorder);
 	list_for_each_safe(pos, n, &efi_menu->list) {
-		entry = list_entry(pos, struct eficonfig_boot_order, list);
-		list_del(&entry->list);
-		free(entry->description);
-		free(entry);
+		entry = list_entry(pos, struct eficonfig_entry, list);
+		free(entry->data);
 	}
-
-	free(bootorder);
-	free(efi_menu);
+	eficonfig_destroy(efi_menu);
 
 	/* to stay the parent menu */
 	ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
@@ -2276,17 +2332,48 @@
 efi_status_t eficonfig_delete_invalid_boot_option(struct eficonfig_media_boot_option *opt,
 						  efi_status_t count)
 {
-	u32 i, j;
+	u32 i;
 	efi_uintn_t size;
 	void *load_option;
 	struct efi_load_option lo;
+	u16 *var_name16 = NULL, *p;
 	u16 varname[] = u"Boot####";
 	efi_status_t ret = EFI_SUCCESS;
+	efi_uintn_t varname_size, buf_size;
 
-	for (i = 0; i <= 0xFFFF; i++) {
+	buf_size = 128;
+	var_name16 = malloc(buf_size);
+	if (!var_name16)
+		return EFI_OUT_OF_RESOURCES;
+
+	var_name16[0] = 0;
+	for (;;) {
+		int index;
+		efi_guid_t guid;
 		efi_uintn_t tmp;
 
+		varname_size = buf_size;
+		ret = efi_get_next_variable_name_int(&varname_size, var_name16, &guid);
+		if (ret == EFI_NOT_FOUND)
+			break;
+		if (ret == EFI_BUFFER_TOO_SMALL) {
+			buf_size = varname_size;
+			p = realloc(var_name16, buf_size);
+			if (!p) {
+				free(var_name16);
+				return EFI_OUT_OF_RESOURCES;
+			}
+			var_name16 = p;
+			ret = efi_get_next_variable_name_int(&varname_size, var_name16, &guid);
+		}
+		if (ret != EFI_SUCCESS) {
+			free(var_name16);
+			return ret;
+		}
+		if (!efi_varname_is_load_option(var_name16, &index))
+			continue;
+
-		efi_create_indexed_name(varname, sizeof(varname), "Boot", i);
+		efi_create_indexed_name(varname, sizeof(varname), "Boot", index);
 		load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
 		if (!load_option)
 			continue;
@@ -2298,15 +2385,15 @@
 
 		if (size >= sizeof(efi_guid_bootmenu_auto_generated)) {
 			if (guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated) == 0) {
-				for (j = 0; j < count; j++) {
-					if (opt[j].size == tmp &&
-					    memcmp(opt[j].lo, load_option, tmp) == 0) {
-						opt[j].exist = true;
+				for (i = 0; i < count; i++) {
+					if (opt[i].size == tmp &&
+					    memcmp(opt[i].lo, load_option, tmp) == 0) {
+						opt[i].exist = true;
 						break;
 					}
 				}
 
-				if (j == count) {
+				if (i == count) {
 					ret = delete_boot_option(i);
 					if (ret != EFI_SUCCESS) {
 						free(load_option);
@@ -2441,6 +2528,9 @@
 	{"Edit Boot Option", eficonfig_process_edit_boot_option},
 	{"Change Boot Order", eficonfig_process_change_boot_order},
 	{"Delete Boot Option", eficonfig_process_delete_boot_option},
+#if (CONFIG_IS_ENABLED(EFI_SECURE_BOOT) && CONFIG_IS_ENABLED(EFI_MM_COMM_TEE))
+	{"Secure Boot Configuration", eficonfig_process_secure_boot_config},
+#endif
 	{"Quit", eficonfig_process_quit},
 };
 
diff --git a/cmd/eficonfig_sbkey.c b/cmd/eficonfig_sbkey.c
new file mode 100644
index 0000000..6e0bebf
--- /dev/null
+++ b/cmd/eficonfig_sbkey.c
@@ -0,0 +1,498 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *  Menu-driven UEFI Secure Boot Key Maintenance
+ *
+ *  Copyright (c) 2022 Masahisa Kojima, Linaro Limited
+ */
+
+#include <ansi.h>
+#include <common.h>
+#include <charset.h>
+#include <hexdump.h>
+#include <log.h>
+#include <malloc.h>
+#include <menu.h>
+#include <efi_loader.h>
+#include <efi_config.h>
+#include <efi_variable.h>
+#include <crypto/pkcs7_parser.h>
+
+struct eficonfig_sig_data {
+	struct efi_signature_list *esl;
+	struct efi_signature_data *esd;
+	struct list_head list;
+	u16 *varname;
+};
+
+enum efi_sbkey_signature_type {
+	SIG_TYPE_X509 = 0,
+	SIG_TYPE_HASH,
+	SIG_TYPE_CRL,
+	SIG_TYPE_RSA2048,
+};
+
+struct eficonfig_sigtype_to_str {
+	efi_guid_t sig_type;
+	char *str;
+	enum efi_sbkey_signature_type type;
+};
+
+static const struct eficonfig_sigtype_to_str sigtype_to_str[] = {
+	{EFI_CERT_X509_GUID,		"X509",			SIG_TYPE_X509},
+	{EFI_CERT_SHA256_GUID,		"SHA256",		SIG_TYPE_HASH},
+	{EFI_CERT_X509_SHA256_GUID,	"X509_SHA256 CRL",	SIG_TYPE_CRL},
+	{EFI_CERT_X509_SHA384_GUID,	"X509_SHA384 CRL",	SIG_TYPE_CRL},
+	{EFI_CERT_X509_SHA512_GUID,	"X509_SHA512 CRL",	SIG_TYPE_CRL},
+	/* U-Boot does not support the following signature types */
+/*	{EFI_CERT_RSA2048_GUID,		"RSA2048",		SIG_TYPE_RSA2048}, */
+/*	{EFI_CERT_RSA2048_SHA256_GUID,	"RSA2048_SHA256",	SIG_TYPE_RSA2048}, */
+/*	{EFI_CERT_SHA1_GUID,		"SHA1",			SIG_TYPE_HASH}, */
+/*	{EFI_CERT_RSA2048_SHA_GUID,	"RSA2048_SHA",		SIG_TYPE_RSA2048 }, */
+/*	{EFI_CERT_SHA224_GUID,		"SHA224",		SIG_TYPE_HASH}, */
+/*	{EFI_CERT_SHA384_GUID,		"SHA384",		SIG_TYPE_HASH}, */
+/*	{EFI_CERT_SHA512_GUID,		"SHA512",		SIG_TYPE_HASH}, */
+};
+
+/**
+ * file_have_auth_header() - check file has EFI_VARIABLE_AUTHENTICATION_2 header
+ * @buf:	pointer to file
+ * @size:	file size
+ * Return:	true if file has auth header, false otherwise
+ */
+static bool file_have_auth_header(void *buf, efi_uintn_t size)
+{
+	struct efi_variable_authentication_2 *auth = buf;
+
+	if (auth->auth_info.hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID)
+		return false;
+
+	if (guidcmp(&auth->auth_info.cert_type, &efi_guid_cert_type_pkcs7))
+		return false;
+
+	return true;
+}
+
+/**
+ * eficonfig_process_enroll_key() - enroll key into signature database
+ *
+ * @data:	pointer to the data for each entry
+ * Return:	status code
+ */
+static efi_status_t eficonfig_process_enroll_key(void *data)
+{
+	u32 attr;
+	char *buf = NULL;
+	efi_uintn_t size;
+	efi_status_t ret;
+	struct efi_file_handle *f = NULL;
+	struct efi_device_path *full_dp = NULL;
+	struct eficonfig_select_file_info file_info;
+
+	file_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
+	if (!file_info.current_path) {
+		ret = EFI_OUT_OF_RESOURCES;
+		goto out;
+	}
+
+	ret = eficonfig_process_select_file(&file_info);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	full_dp = eficonfig_create_device_path(file_info.dp_volume, file_info.current_path);
+	if (!full_dp) {
+		ret = EFI_OUT_OF_RESOURCES;
+		goto out;
+	}
+	f = efi_file_from_path(full_dp);
+	if (!f) {
+		ret = EFI_NOT_FOUND;
+		goto out;
+	}
+
+	size = 0;
+	ret = EFI_CALL(f->getinfo(f, &efi_file_info_guid, &size, NULL));
+	if (ret != EFI_BUFFER_TOO_SMALL)
+		goto out;
+
+	buf = malloc(size);
+	if (!buf) {
+		ret = EFI_OUT_OF_RESOURCES;
+		goto out;
+	}
+	ret = EFI_CALL(f->getinfo(f, &efi_file_info_guid, &size, buf));
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	size = ((struct efi_file_info *)buf)->file_size;
+	free(buf);
+
+	if (!size) {
+		eficonfig_print_msg("ERROR! File is empty.");
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	buf = malloc(size);
+	if (!buf) {
+		ret = EFI_OUT_OF_RESOURCES;
+		goto out;
+	}
+
+	ret = EFI_CALL(f->read(f, &size, buf));
+	if (ret != EFI_SUCCESS) {
+		eficonfig_print_msg("ERROR! Failed to read file.");
+		goto out;
+	}
+	if (!file_have_auth_header(buf, size)) {
+		eficonfig_print_msg("ERROR! Invalid file format. Only .auth variables is allowed.");
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	attr = EFI_VARIABLE_NON_VOLATILE |
+	       EFI_VARIABLE_BOOTSERVICE_ACCESS |
+	       EFI_VARIABLE_RUNTIME_ACCESS |
+	       EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
+
+	/* PK can enroll only one certificate */
+	if (u16_strcmp(data, u"PK")) {
+		efi_uintn_t db_size = 0;
+
+		/* check the variable exists. If exists, add APPEND_WRITE attribute */
+		ret = efi_get_variable_int(data, efi_auth_var_get_guid(data), NULL,
+					   &db_size, NULL,  NULL);
+		if (ret == EFI_BUFFER_TOO_SMALL)
+			attr |= EFI_VARIABLE_APPEND_WRITE;
+	}
+
+	ret = efi_set_variable_int((u16 *)data, efi_auth_var_get_guid((u16 *)data),
+				   attr, size, buf, false);
+	if (ret != EFI_SUCCESS)
+		eficonfig_print_msg("ERROR! Failed to update signature database");
+
+out:
+	free(file_info.current_path);
+	free(buf);
+	efi_free_pool(full_dp);
+	if (f)
+		EFI_CALL(f->close(f));
+
+	/* return to the parent menu */
+	ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
+
+	return ret;
+}
+
+/**
+ * eficonfig_process_show_siglist() - show signature list content
+ *
+ * @data:	pointer to the data for each entry
+ * Return:	status code
+ */
+static efi_status_t eficonfig_process_show_siglist(void *data)
+{
+	u32 i;
+	struct eficonfig_sig_data *sg = data;
+
+	puts(ANSI_CURSOR_HIDE);
+	puts(ANSI_CLEAR_CONSOLE);
+	printf(ANSI_CURSOR_POSITION, 1, 1);
+
+	printf("\n  ** Show Signature Database (%ls) **\n\n"
+	       "    Owner GUID:\n"
+	       "      %pUL\n",
+	       sg->varname, sg->esd->signature_owner.b);
+
+	for (i = 0; i < ARRAY_SIZE(sigtype_to_str); i++) {
+		if (!guidcmp(&sg->esl->signature_type, &sigtype_to_str[i].sig_type)) {
+			printf("    Signature Type:\n"
+			       "      %s\n", sigtype_to_str[i].str);
+
+			switch (sigtype_to_str[i].type) {
+			case SIG_TYPE_X509:
+			{
+				struct x509_certificate *cert_tmp;
+
+				cert_tmp = x509_cert_parse(sg->esd->signature_data,
+							   sg->esl->signature_size);
+				printf("    Subject:\n"
+				       "      %s\n"
+				       "    Issuer:\n"
+				       "      %s\n",
+				       cert_tmp->subject, cert_tmp->issuer);
+				break;
+			}
+			case SIG_TYPE_CRL:
+			{
+				u32 hash_size = sg->esl->signature_size - sizeof(efi_guid_t) -
+						sizeof(struct efi_time);
+				struct efi_time *time =
+					(struct efi_time *)((u8 *)sg->esd->signature_data +
+					hash_size);
+
+				printf("    ToBeSignedHash:\n");
+				print_hex_dump("      ", DUMP_PREFIX_NONE, 16, 1,
+					       sg->esd->signature_data, hash_size, false);
+				printf("    TimeOfRevocation:\n"
+				       "      %d-%d-%d %02d:%02d:%02d\n",
+				       time->year, time->month, time->day,
+				       time->hour, time->minute, time->second);
+				break;
+			}
+			case SIG_TYPE_HASH:
+			{
+				u32 hash_size = sg->esl->signature_size - sizeof(efi_guid_t);
+
+				printf("    Hash:\n");
+				print_hex_dump("      ", DUMP_PREFIX_NONE, 16, 1,
+					       sg->esd->signature_data, hash_size, false);
+				break;
+			}
+			default:
+				eficonfig_print_msg("ERROR! Unsupported format.");
+				return EFI_INVALID_PARAMETER;
+			}
+		}
+	}
+
+	while (tstc())
+		getchar();
+
+	printf("\n\n  Press any key to continue");
+	getchar();
+
+	return EFI_SUCCESS;
+}
+
+/**
+ * prepare_signature_list_menu() - create the signature list menu entry
+ *
+ * @efimenu:	pointer to the efimenu structure
+ * @varname:	pointer to the variable name
+ * @db:		pointer to the variable raw data
+ * @db_size:	variable data size
+ * @func:	callback of each entry
+ * Return:	status code
+ */
+static efi_status_t prepare_signature_list_menu(struct efimenu *efi_menu, void *varname,
+						void *db, efi_uintn_t db_size,
+						eficonfig_entry_func func)
+{
+	u32 num = 0;
+	efi_uintn_t size;
+	struct eficonfig_sig_data *sg;
+	struct efi_signature_list *esl;
+	struct efi_signature_data *esd;
+	efi_status_t ret = EFI_SUCCESS;
+
+	INIT_LIST_HEAD(&efi_menu->list);
+
+	esl = db;
+	size = db_size;
+	while (size > 0) {
+		u32 remain;
+
+		esd = (struct efi_signature_data *)((u8 *)esl +
+						    (sizeof(struct efi_signature_list) +
+						    esl->signature_header_size));
+		remain = esl->signature_list_size - sizeof(struct efi_signature_list) -
+			 esl->signature_header_size;
+		for (; remain > 0; remain -= esl->signature_size) {
+			char buf[37];
+			char *title;
+
+			if (num >= EFICONFIG_ENTRY_NUM_MAX - 1) {
+				ret = EFI_OUT_OF_RESOURCES;
+				goto out;
+			}
+
+			sg = calloc(1, sizeof(struct eficonfig_sig_data));
+			if (!sg) {
+				ret = EFI_OUT_OF_RESOURCES;
+				goto err;
+			}
+
+			snprintf(buf, sizeof(buf), "%pUL", &esd->signature_owner);
+			title = strdup(buf);
+			if (!title) {
+				free(sg);
+				ret = EFI_OUT_OF_RESOURCES;
+				goto err;
+			}
+
+			sg->esl = esl;
+			sg->esd = esd;
+			sg->varname = varname;
+			ret = eficonfig_append_menu_entry(efi_menu, title, func, sg);
+			if (ret != EFI_SUCCESS) {
+				free(sg);
+				free(title);
+				goto err;
+			}
+			esd = (struct efi_signature_data *)((u8 *)esd + esl->signature_size);
+			num++;
+		}
+
+		size -= esl->signature_list_size;
+		esl = (struct efi_signature_list *)((u8 *)esl + esl->signature_list_size);
+	}
+out:
+	ret = eficonfig_append_quit_entry(efi_menu);
+err:
+	return ret;
+}
+
+/**
+ * enumerate_and_show_signature_database() - enumerate and show the signature database
+ *
+ * @data:	pointer to the data for each entry
+ * Return:	status code
+ */
+static efi_status_t enumerate_and_show_signature_database(void *varname)
+{
+	void *db;
+	char buf[50];
+	efi_status_t ret;
+	efi_uintn_t db_size;
+	struct efimenu *efi_menu;
+	struct list_head *pos, *n;
+	struct eficonfig_entry *entry;
+
+	db = efi_get_var(varname, efi_auth_var_get_guid(varname), &db_size);
+	if (!db) {
+		eficonfig_print_msg("There is no entry in the signature database.");
+		return EFI_NOT_FOUND;
+	}
+
+	efi_menu = calloc(1, sizeof(struct efimenu));
+	if (!efi_menu) {
+		free(db);
+		return EFI_OUT_OF_RESOURCES;
+	}
+
+	ret = prepare_signature_list_menu(efi_menu, varname, db, db_size,
+					  eficonfig_process_show_siglist);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	snprintf(buf, sizeof(buf), "  ** Show Signature Database (%ls) **", (u16 *)varname);
+	ret = eficonfig_process_common(efi_menu, buf);
+out:
+	list_for_each_safe(pos, n, &efi_menu->list) {
+		entry = list_entry(pos, struct eficonfig_entry, list);
+		free(entry->data);
+	}
+	eficonfig_destroy(efi_menu);
+	free(db);
+
+	return ret;
+}
+
+/**
+ * eficonfig_process_show_signature_database() - process show signature database
+ *
+ * @data:	pointer to the data for each entry
+ * Return:	status code
+ */
+static efi_status_t eficonfig_process_show_signature_database(void *data)
+{
+	efi_status_t ret;
+
+	while (1) {
+		ret = enumerate_and_show_signature_database(data);
+		if (ret != EFI_SUCCESS && ret != EFI_NOT_READY)
+			break;
+	}
+
+	/* return to the parent menu */
+	ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
+
+	return ret;
+}
+
+static struct eficonfig_item key_config_menu_items[] = {
+	{"Enroll New Key", eficonfig_process_enroll_key},
+	{"Show Signature Database", eficonfig_process_show_signature_database},
+	{"Quit", eficonfig_process_quit},
+};
+
+/**
+ * eficonfig_process_set_secure_boot_key() - display the key configuration menu
+ *
+ * @data:	pointer to the data for each entry
+ * Return:	status code
+ */
+static efi_status_t eficonfig_process_set_secure_boot_key(void *data)
+{
+	u32 i;
+	efi_status_t ret;
+	char header_str[32];
+	struct efimenu *efi_menu;
+
+	for (i = 0; i < ARRAY_SIZE(key_config_menu_items); i++)
+		key_config_menu_items[i].data = data;
+
+	snprintf(header_str, sizeof(header_str), "  ** Configure %ls **", (u16 *)data);
+
+	while (1) {
+		efi_menu = eficonfig_create_fixed_menu(key_config_menu_items,
+						       ARRAY_SIZE(key_config_menu_items));
+
+		ret = eficonfig_process_common(efi_menu, header_str);
+		eficonfig_destroy(efi_menu);
+
+		if (ret == EFI_ABORTED)
+			break;
+	}
+
+	/* return to the parent menu */
+	ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
+
+	return ret;
+}
+
+static const struct eficonfig_item secure_boot_menu_items[] = {
+	{"PK", eficonfig_process_set_secure_boot_key, u"PK"},
+	{"KEK", eficonfig_process_set_secure_boot_key, u"KEK"},
+	{"db", eficonfig_process_set_secure_boot_key, u"db"},
+	{"dbx", eficonfig_process_set_secure_boot_key, u"dbx"},
+	{"Quit", eficonfig_process_quit},
+};
+
+/**
+ * eficonfig_process_secure_boot_config() - display the key list menu
+ *
+ * @data:	pointer to the data for each entry
+ * Return:	status code
+ */
+efi_status_t eficonfig_process_secure_boot_config(void *data)
+{
+	efi_status_t ret;
+	struct efimenu *efi_menu;
+
+	while (1) {
+		char header_str[64];
+
+		snprintf(header_str, sizeof(header_str),
+			 "  ** UEFI Secure Boot Key Configuration (SecureBoot : %s) **",
+			 (efi_secure_boot_enabled() ? "ON" : "OFF"));
+
+		efi_menu = eficonfig_create_fixed_menu(secure_boot_menu_items,
+						       ARRAY_SIZE(secure_boot_menu_items));
+		if (!efi_menu) {
+			ret = EFI_OUT_OF_RESOURCES;
+			break;
+		}
+
+		ret = eficonfig_process_common(efi_menu, header_str);
+		eficonfig_destroy(efi_menu);
+
+		if (ret == EFI_ABORTED)
+			break;
+	}
+
+	/* return to the parent menu */
+	ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
+
+	return ret;
+}
diff --git a/cmd/efidebug.c b/cmd/efidebug.c
index ef239bb..569003a 100644
--- a/cmd/efidebug.c
+++ b/cmd/efidebug.c
@@ -600,7 +600,7 @@
 	ret = efi_get_memory_map(&map_size, memmap, NULL, NULL, NULL);
 	if (ret == EFI_BUFFER_TOO_SMALL) {
 		map_size += sizeof(struct efi_mem_desc); /* for my own */
-		ret = efi_allocate_pool(EFI_LOADER_DATA, map_size,
+		ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, map_size,
 					(void *)&memmap);
 		if (ret != EFI_SUCCESS)
 			return CMD_RET_FAILURE;
@@ -1010,17 +1010,6 @@
 	}
 }
 
-static int u16_tohex(u16 c)
-{
-	if (c >= '0' && c <= '9')
-		return c - '0';
-	if (c >= 'A' && c <= 'F')
-		return c - 'A' + 10;
-
-	/* not hexadecimal */
-	return -1;
-}
-
 /**
  * show_efi_boot_dump() - dump all UEFI load options
  *
@@ -1041,7 +1030,6 @@
 	u16 *var_name16, *p;
 	efi_uintn_t buf_size, size;
 	efi_guid_t guid;
-	int id, i, digit;
 	efi_status_t ret;
 
 	if (argc > 1)
@@ -1074,16 +1062,7 @@
 			return CMD_RET_FAILURE;
 		}
 
-		if (memcmp(var_name16, u"Boot", 8))
-			continue;
-
-		for (id = 0, i = 0; i < 4; i++) {
-			digit = u16_tohex(var_name16[4 + i]);
-			if (digit < 0)
-				break;
-			id = (id << 4) + digit;
-		}
-		if (i == 4 && !var_name16[8])
+		if (efi_varname_is_load_option(var_name16, NULL))
 			show_efi_boot_opt(var_name16);
 	}
 
diff --git a/cmd/fdt.c b/cmd/fdt.c
index 4b2dcfe..8e51a43 100644
--- a/cmd/fdt.c
+++ b/cmd/fdt.c
@@ -60,11 +60,14 @@
 		 * Iterate over all members in stringlist and find the one at
 		 * offset $index. If no such index exists, indicate failure.
 		 */
-		for (i = 0; i < len; i += strlen(nodec) + 1) {
-			if (index-- > 0)
+		for (i = 0; i < len; ) {
+			if (index-- > 0) {
+				i += strlen(nodec) + 1;
+				nodec += strlen(nodec) + 1;
 				continue;
+			}
 
-			env_set(var, nodec + i);
+			env_set(var, nodec);
 			return 0;
 		}
 
diff --git a/cmd/net.c b/cmd/net.c
index addcad3..0e9f200 100644
--- a/cmd/net.c
+++ b/cmd/net.c
@@ -14,6 +14,7 @@
 #include <env.h>
 #include <image.h>
 #include <net.h>
+#include <net6.h>
 #include <net/udp.h>
 #include <net/sntp.h>
 #include <net/ncsi.h>
@@ -45,12 +46,22 @@
 	return ret;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+U_BOOT_CMD(
+	tftpboot,	4,	1,	do_tftpb,
+	"boot image via network using TFTP protocol\n"
+	"To use IPv6 add -ipv6 parameter or use IPv6 hostIPaddr framed "
+	"with [] brackets",
+	"[loadAddress] [[hostIPaddr:]bootfilename] [" USE_IP6_CMD_PARAM "]"
+);
+#else
 U_BOOT_CMD(
 	tftpboot,	3,	1,	do_tftpb,
 	"load file via network using TFTP protocol",
 	"[loadAddress] [[hostIPaddr:]bootfilename]"
 );
 #endif
+#endif
 
 #ifdef CONFIG_CMD_TFTPPUT
 static int do_tftpput(struct cmd_tbl *cmdtp, int flag, int argc,
@@ -125,6 +136,19 @@
 );
 #endif
 
+#if defined(CONFIG_CMD_WGET)
+static int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+	return netboot_common(WGET, cmdtp, argc, argv);
+}
+
+U_BOOT_CMD(
+	wget,   3,      1,      do_wget,
+	"boot image via network using HTTP protocol",
+	"[loadAddress] [[hostIPaddr:]path and image name]"
+);
+#endif
+
 static void netboot_update_env(void)
 {
 	char tmp[22];
@@ -293,6 +317,17 @@
 	if (s != NULL)
 		image_load_addr = hextoul(s, NULL);
 
+	if (IS_ENABLED(CONFIG_IPV6)) {
+		use_ip6 = false;
+
+		/* IPv6 parameter has to be always *last* */
+		if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM)) {
+			use_ip6 = true;
+			/* It is a hack not to break switch/case code */
+			--argc;
+		}
+	}
+
 	if (parse_args(proto, argc, argv)) {
 		bootstage_error(BOOTSTAGE_ID_NET_START);
 		return CMD_RET_USAGE;
@@ -300,6 +335,19 @@
 
 	bootstage_mark(BOOTSTAGE_ID_NET_START);
 
+	if (IS_ENABLED(CONFIG_IPV6) && !use_ip6) {
+		char *s, *e;
+		size_t len;
+
+		s = strchr(net_boot_file_name, '[');
+		e = strchr(net_boot_file_name, ']');
+		if (s && e) {
+			len = e - s;
+			if (!string_to_ip6(s + 1, len - 1, &net_server_ip6))
+				use_ip6 = true;
+		}
+	}
+
 	size = net_loop(proto);
 	if (size < 0) {
 		bootstage_error(BOOTSTAGE_ID_NET_NETLOOP_OK);
@@ -355,6 +403,32 @@
 );
 #endif
 
+#if IS_ENABLED(CONFIG_CMD_PING6)
+int do_ping6(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+	if (string_to_ip6(argv[1], strlen(argv[1]), &net_ping_ip6))
+		return CMD_RET_USAGE;
+
+	use_ip6 = true;
+	if (net_loop(PING6) < 0) {
+		use_ip6 = false;
+		printf("ping6 failed; host %pI6c is not alive\n",
+		       &net_ping_ip6);
+		return 1;
+	}
+
+	use_ip6 = false;
+	printf("host %pI6c is alive\n", &net_ping_ip6);
+	return 0;
+}
+
+U_BOOT_CMD(
+	ping6,  2,      1,      do_ping6,
+	"send ICMPv6 ECHO_REQUEST to network host",
+	"pingAddress"
+);
+#endif /* CONFIG_CMD_PING6 */
+
 #if defined(CONFIG_CMD_CDP)
 
 static void cdp_update_env(void)
diff --git a/cmd/sound.c b/cmd/sound.c
index f82f2aa..20ac3f7 100644
--- a/cmd/sound.c
+++ b/cmd/sound.c
@@ -86,5 +86,5 @@
 	sound, 4, 1, do_sound,
 	"sound sub-system",
 	"init - initialise the sound driver\n"
-	"sound play [len] [freq] - play a sound for len ms at freq hz\n"
+	"sound play [len [freq]] - play a sound for len ms at freq Hz\n"
 );
diff --git a/cmd/ubifs.c b/cmd/ubifs.c
index 7a620c5..6a01d09 100644
--- a/cmd/ubifs.c
+++ b/cmd/ubifs.c
@@ -33,7 +33,7 @@
 
 	ret = uboot_ubifs_mount(vol_name);
 	if (ret)
-		return -1;
+		return CMD_RET_FAILURE;
 
 	ubifs_mounted = 1;
 
@@ -62,7 +62,7 @@
 {
 	if (ubifs_initialized == 0) {
 		printf("No UBIFS volume mounted!\n");
-		return -1;
+		return CMD_RET_FAILURE;
 	}
 
 	uboot_ubifs_umount();
@@ -89,7 +89,7 @@
 
 	if (!ubifs_mounted) {
 		printf("UBIFS not mounted, use ubifsmount to mount volume first!\n");
-		return -1;
+		return CMD_RET_FAILURE;
 	}
 
 	if (argc == 2)
@@ -116,7 +116,7 @@
 
 	if (!ubifs_mounted) {
 		printf("UBIFS not mounted, use ubifs mount to mount volume first!\n");
-		return -1;
+		return CMD_RET_FAILURE;
 	}
 
 	if (argc < 3)
diff --git a/common/console.c b/common/console.c
index 0c9bf66..10ab361 100644
--- a/common/console.c
+++ b/common/console.c
@@ -497,7 +497,7 @@
 
 int fgetc(int file)
 {
-	if (file < MAX_FILES) {
+	if ((unsigned int)file < MAX_FILES) {
 		/*
 		 * Effectively poll for input wherever it may be available.
 		 */
@@ -530,7 +530,7 @@
 
 int ftstc(int file)
 {
-	if (file < MAX_FILES)
+	if ((unsigned int)file < MAX_FILES)
 		return console_tstc(file);
 
 	return -1;
@@ -538,20 +538,20 @@
 
 void fputc(int file, const char c)
 {
-	if (file < MAX_FILES)
+	if ((unsigned int)file < MAX_FILES)
 		console_putc(file, c);
 }
 
 void fputs(int file, const char *s)
 {
-	if (file < MAX_FILES)
+	if ((unsigned int)file < MAX_FILES)
 		console_puts(file, s);
 }
 
 #ifdef CONFIG_CONSOLE_FLUSH_SUPPORT
 void fflush(int file)
 {
-	if (file < MAX_FILES)
+	if ((unsigned int)file < MAX_FILES)
 		console_flush(file);
 }
 #endif
diff --git a/configs/am335x_evm_defconfig b/configs/am335x_evm_defconfig
index f0fbe47..f73123e 100644
--- a/configs/am335x_evm_defconfig
+++ b/configs/am335x_evm_defconfig
@@ -92,6 +92,7 @@
 CONFIG_SYS_NAND_U_BOOT_OFFS=0xc0000
 CONFIG_DM_SPI_FLASH=y
 CONFIG_SF_DEFAULT_SPEED=24000000
+CONFIG_SPI_FLASH_STMICRO=y
 CONFIG_SPI_FLASH_WINBOND=y
 CONFIG_PHY_ATHEROS=y
 CONFIG_PHY_SMSC=y
diff --git a/configs/am335x_evm_spiboot_defconfig b/configs/am335x_evm_spiboot_defconfig
index 3d04e6f..7f42201 100644
--- a/configs/am335x_evm_spiboot_defconfig
+++ b/configs/am335x_evm_spiboot_defconfig
@@ -53,10 +53,14 @@
 CONFIG_VERSION_VARIABLE=y
 CONFIG_NET_RETRY_COUNT=10
 CONFIG_BOOTP_SEND_HOSTNAME=y
+CONFIG_SPL_OF_TRANSLATE=y
+CONFIG_SPL_TI_SYSC=y
 CONFIG_BOOTCOUNT_LIMIT=y
 CONFIG_SYS_BOOTCOUNT_BE=y
 CONFIG_CLK=y
+CONFIG_SPL_CLK=y
 CONFIG_CLK_CDCE9XX=y
+CONFIG_CLK_TI_CTRL=y
 CONFIG_DFU_TFTP=y
 CONFIG_DFU_MMC=y
 CONFIG_DFU_NAND=y
@@ -80,6 +84,7 @@
 CONFIG_SYS_NAND_U_BOOT_OFFS=0xc0000
 CONFIG_DM_SPI_FLASH=y
 CONFIG_SF_DEFAULT_SPEED=24000000
+CONFIG_SPI_FLASH_STMICRO=y
 CONFIG_SPI_FLASH_WINBOND=y
 CONFIG_PHY_ATHEROS=y
 CONFIG_PHY_SMSC=y
diff --git a/configs/avnet_ultrazedev_cc_v1_0_ultrazedev_som_v1_0_defconfig b/configs/avnet_ultrazedev_cc_v1_0_ultrazedev_som_v1_0_defconfig
index 943b4da..0a3d710 100644
--- a/configs/avnet_ultrazedev_cc_v1_0_ultrazedev_som_v1_0_defconfig
+++ b/configs/avnet_ultrazedev_cc_v1_0_ultrazedev_som_v1_0_defconfig
@@ -8,9 +8,6 @@
 CONFIG_SPL=y
 CONFIG_SPL_SPI_FLASH_SUPPORT=y
 CONFIG_SPL_SPI=y
-CONFIG_ZYNQ_MAC_IN_EEPROM=y
-CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET=0xfa
-CONFIG_ZYNQMP_PSU_INIT_ENABLED=y
 CONFIG_SYS_LOAD_ADDR=0x8000000
 CONFIG_DEBUG_UART=y
 CONFIG_SYS_MEMTEST_START=0x00000000
diff --git a/configs/microblaze-generic_defconfig b/configs/microblaze-generic_defconfig
index 1a23dc5..f1b9cb4 100644
--- a/configs/microblaze-generic_defconfig
+++ b/configs/microblaze-generic_defconfig
@@ -71,6 +71,7 @@
 CONFIG_SYS_FLASH_PROTECTION=y
 CONFIG_SYS_FLASH_CFI=y
 CONFIG_SYS_MAX_FLASH_SECT=2048
+CONFIG_SPI_FLASH_BAR=y
 CONFIG_SPI_FLASH_ISSI=y
 CONFIG_SPI_FLASH_MACRONIX=y
 CONFIG_SPI_FLASH_SPANSION=y
diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig
index cff166b..b69e053 100644
--- a/configs/sandbox64_defconfig
+++ b/configs/sandbox64_defconfig
@@ -258,3 +258,4 @@
 CONFIG_UNIT_TEST=y
 CONFIG_UT_TIME=y
 CONFIG_UT_DM=y
+CONFIG_IPV6=y
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 07fe0c3..404e4a3 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -333,3 +333,4 @@
 CONFIG_UNIT_TEST=y
 CONFIG_UT_TIME=y
 CONFIG_UT_DM=y
+CONFIG_IPV6=y
diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig
index 01234dc..7237efe 100644
--- a/configs/sandbox_flattree_defconfig
+++ b/configs/sandbox_flattree_defconfig
@@ -216,3 +216,4 @@
 CONFIG_UNIT_TEST=y
 CONFIG_UT_TIME=y
 CONFIG_UT_DM=y
+CONFIG_IPV6=y
diff --git a/configs/syzygy_hub_defconfig b/configs/syzygy_hub_defconfig
index acce95e..179cb52 100644
--- a/configs/syzygy_hub_defconfig
+++ b/configs/syzygy_hub_defconfig
@@ -9,8 +9,6 @@
 CONFIG_DEFAULT_DEVICE_TREE="zynq-syzygy-hub"
 CONFIG_SPL_STACK_R_ADDR=0x200000
 CONFIG_SPL=y
-CONFIG_ZYNQ_MAC_IN_EEPROM=y
-CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET=0xFA
 CONFIG_SYS_LOAD_ADDR=0x0
 CONFIG_DEBUG_UART=y
 CONFIG_DISTRO_DEFAULTS=y
diff --git a/configs/tools-only_defconfig b/configs/tools-only_defconfig
index 86464d2..de99f38 100644
--- a/configs/tools-only_defconfig
+++ b/configs/tools-only_defconfig
@@ -27,6 +27,7 @@
 CONFIG_SOUND=y
 CONFIG_SYSRESET=y
 CONFIG_TIMER=y
+CONFIG_VIDEO=y
 # CONFIG_VIRTIO_MMIO is not set
 # CONFIG_VIRTIO_PCI is not set
 # CONFIG_VIRTIO_SANDBOX is not set
diff --git a/configs/xilinx_versal_mini_ospi_defconfig b/configs/xilinx_versal_mini_ospi_defconfig
new file mode 100644
index 0000000..abcd20b
--- /dev/null
+++ b/configs/xilinx_versal_mini_ospi_defconfig
@@ -0,0 +1,73 @@
+CONFIG_ARM=y
+CONFIG_SYS_CONFIG_NAME="xilinx_versal_mini_qspi"
+CONFIG_COUNTER_FREQUENCY=100000000
+CONFIG_ARCH_VERSAL=y
+CONFIG_TEXT_BASE=0xFFFC0000
+CONFIG_SYS_MALLOC_LEN=0x2000
+CONFIG_SYS_MALLOC_F_LEN=0x500
+CONFIG_NR_DRAM_BANKS=1
+CONFIG_ENV_SIZE=0x80
+# CONFIG_DM_GPIO is not set
+CONFIG_DEFAULT_DEVICE_TREE="versal-mini-ospi-single"
+CONFIG_SYS_PROMPT="Versal> "
+CONFIG_SYS_MEM_RSVD_FOR_MMU=y
+CONFIG_VERSAL_NO_DDR=y
+# CONFIG_PSCI_RESET is not set
+CONFIG_SYS_LOAD_ADDR=0x8000000
+CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
+CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0xFFFE0000
+# CONFIG_EXPERT is not set
+CONFIG_REMAKE_ELF=y
+# CONFIG_AUTOBOOT is not set
+CONFIG_SYS_CONSOLE_INFO_QUIET=y
+# CONFIG_DISPLAY_CPUINFO is not set
+CONFIG_BOARD_EARLY_INIT_R=y
+# CONFIG_BOARD_LATE_INIT is not set
+# CONFIG_CMDLINE_EDITING is not set
+# CONFIG_AUTO_COMPLETE is not set
+# CONFIG_SYS_LONGHELP is not set
+# CONFIG_CMD_BDI is not set
+# CONFIG_CMD_CONSOLE is not set
+# CONFIG_CMD_BOOTD is not set
+# CONFIG_CMD_BOOTM is not set
+# CONFIG_CMD_BOOTI is not set
+# CONFIG_CMD_ELF is not set
+# CONFIG_CMD_FDT is not set
+# CONFIG_CMD_GO is not set
+# CONFIG_CMD_RUN is not set
+# CONFIG_CMD_IMI is not set
+# CONFIG_CMD_XIMG is not set
+# CONFIG_CMD_EXPORTENV is not set
+# CONFIG_CMD_IMPORTENV is not set
+# CONFIG_CMD_EDITENV is not set
+# CONFIG_CMD_SAVEENV is not set
+# CONFIG_CMD_ENV_EXISTS is not set
+# CONFIG_CMD_CRC32 is not set
+# CONFIG_CMD_LOADB is not set
+# CONFIG_CMD_LOADS is not set
+# CONFIG_CMD_ECHO is not set
+# CONFIG_CMD_ITEST is not set
+# CONFIG_CMD_SOURCE is not set
+# CONFIG_CMD_SETEXPR is not set
+# CONFIG_NET is not set
+# CONFIG_DM_WARN is not set
+# CONFIG_DM_DEVICE_REMOVE is not set
+# CONFIG_MMC is not set
+CONFIG_DM_SPI_FLASH=y
+CONFIG_SF_DEFAULT_SPEED=30000000
+CONFIG_SPI_FLASH_SOFT_RESET=y
+CONFIG_SPI_FLASH_SOFT_RESET_ON_BOOT=y
+CONFIG_SPI_FLASH_GIGADEVICE=y
+CONFIG_SPI_FLASH_ISSI=y
+CONFIG_SPI_FLASH_MACRONIX=y
+CONFIG_SPI_FLASH_STMICRO=y
+CONFIG_SPI_FLASH_MT35XU=y
+CONFIG_ARM_DCC=y
+CONFIG_SOC_XILINX_VERSAL=y
+CONFIG_SPI=y
+CONFIG_DM_SPI=y
+CONFIG_CADENCE_QSPI=y
+CONFIG_HAS_CQSPI_REF_CLK=y
+CONFIG_CQSPI_REF_CLK=200000000
+CONFIG_CADENCE_OSPI_VERSAL=y
+# CONFIG_LMB is not set
diff --git a/configs/xilinx_versal_mini_qspi_defconfig b/configs/xilinx_versal_mini_qspi_defconfig
new file mode 100644
index 0000000..9ca9b7e
--- /dev/null
+++ b/configs/xilinx_versal_mini_qspi_defconfig
@@ -0,0 +1,76 @@
+CONFIG_ARM=y
+CONFIG_SYS_CONFIG_NAME="xilinx_versal_mini_qspi"
+CONFIG_COUNTER_FREQUENCY=100000000
+CONFIG_ARCH_VERSAL=y
+CONFIG_TEXT_BASE=0xFFFC0000
+CONFIG_SYS_MALLOC_LEN=0x2000
+CONFIG_NR_DRAM_BANKS=1
+CONFIG_ENV_SIZE=0x80
+CONFIG_DEFAULT_DEVICE_TREE="versal-mini-qspi-single"
+CONFIG_SYS_PROMPT="Versal> "
+CONFIG_SYS_MEM_RSVD_FOR_MMU=y
+CONFIG_VERSAL_NO_DDR=y
+# CONFIG_PSCI_RESET is not set
+CONFIG_SYS_LOAD_ADDR=0x8000000
+CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
+CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0xFFFE0000
+# CONFIG_EXPERT is not set
+CONFIG_REMAKE_ELF=y
+# CONFIG_ARCH_FIXUP_FDT_MEMORY is not set
+# CONFIG_AUTOBOOT is not set
+CONFIG_LOGLEVEL=0
+CONFIG_SYS_CONSOLE_INFO_QUIET=y
+# CONFIG_DISPLAY_CPUINFO is not set
+CONFIG_BOARD_EARLY_INIT_R=y
+# CONFIG_BOARD_LATE_INIT is not set
+# CONFIG_CMDLINE_EDITING is not set
+# CONFIG_AUTO_COMPLETE is not set
+# CONFIG_SYS_LONGHELP is not set
+# CONFIG_SYS_XTRACE is not set
+# CONFIG_CMD_BDI is not set
+# CONFIG_CMD_CONSOLE is not set
+# CONFIG_CMD_BOOTD is not set
+# CONFIG_CMD_BOOTM is not set
+# CONFIG_CMD_BOOTI is not set
+# CONFIG_CMD_ELF is not set
+# CONFIG_CMD_FDT is not set
+# CONFIG_CMD_GO is not set
+# CONFIG_CMD_RUN is not set
+# CONFIG_CMD_IMI is not set
+# CONFIG_CMD_XIMG is not set
+# CONFIG_CMD_EXPORTENV is not set
+# CONFIG_CMD_IMPORTENV is not set
+# CONFIG_CMD_EDITENV is not set
+# CONFIG_CMD_SAVEENV is not set
+# CONFIG_CMD_ENV_EXISTS is not set
+# CONFIG_CMD_CRC32 is not set
+# CONFIG_CMD_LOADB is not set
+# CONFIG_CMD_LOADS is not set
+# CONFIG_CMD_ECHO is not set
+# CONFIG_CMD_ITEST is not set
+# CONFIG_CMD_SOURCE is not set
+# CONFIG_CMD_SETEXPR is not set
+# CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG is not set
+# CONFIG_NET is not set
+# CONFIG_DM_WARN is not set
+# CONFIG_DM_DEVICE_REMOVE is not set
+# CONFIG_GPIO is not set
+# CONFIG_I2C is not set
+# CONFIG_INPUT is not set
+# CONFIG_MMC is not set
+CONFIG_DM_SPI_FLASH=y
+CONFIG_SF_DEFAULT_SPEED=30000000
+# CONFIG_SPI_FLASH_SMART_HWCAPS is not set
+# CONFIG_SPI_FLASH_UNLOCK_ALL is not set
+CONFIG_SPI_FLASH_ISSI=y
+CONFIG_SPI_FLASH_MACRONIX=y
+CONFIG_SPI_FLASH_SPANSION=y
+CONFIG_SPI_FLASH_STMICRO=y
+CONFIG_SPI_FLASH_WINBOND=y
+# CONFIG_SPI_FLASH_USE_4K_SECTORS is not set
+# CONFIG_POWER is not set
+CONFIG_ARM_DCC=y
+CONFIG_SPI=y
+CONFIG_DM_SPI=y
+CONFIG_ZYNQMP_GQSPI=y
+# CONFIG_LMB is not set
diff --git a/configs/xilinx_versal_net_mini_defconfig b/configs/xilinx_versal_net_mini_defconfig
index e3aa45c..c5fa431 100644
--- a/configs/xilinx_versal_net_mini_defconfig
+++ b/configs/xilinx_versal_net_mini_defconfig
@@ -13,7 +13,7 @@
 CONFIG_SYS_PROMPT="Versal NET> "
 CONFIG_SYS_MEM_RSVD_FOR_MMU=y
 # CONFIG_PSCI_RESET is not set
-CONFIG_SYS_LOAD_ADDR=0x8000000
+CONFIG_SYS_LOAD_ADDR=0xBBF00000
 CONFIG_SYS_MEMTEST_START=0x00000000
 CONFIG_SYS_MEMTEST_END=0x00001000
 CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
@@ -70,3 +70,4 @@
 CONFIG_ARM_DCC=y
 CONFIG_PL01X_SERIAL=y
 # CONFIG_GZIP is not set
+# CONFIG_LMB is not set
diff --git a/configs/xilinx_versal_net_virt_defconfig b/configs/xilinx_versal_net_virt_defconfig
index 8a53490..2fdf99f 100644
--- a/configs/xilinx_versal_net_virt_defconfig
+++ b/configs/xilinx_versal_net_virt_defconfig
@@ -87,6 +87,7 @@
 CONFIG_SPI_FLASH_MACRONIX=y
 CONFIG_SPI_FLASH_SPANSION=y
 CONFIG_SPI_FLASH_STMICRO=y
+CONFIG_SPI_FLASH_MT35XU=y
 CONFIG_SPI_FLASH_SST=y
 CONFIG_SPI_FLASH_WINBOND=y
 # CONFIG_SPI_FLASH_USE_4K_SECTORS is not set
@@ -108,6 +109,8 @@
 CONFIG_ARM_DCC=y
 CONFIG_PL01X_SERIAL=y
 CONFIG_XILINX_UARTLITE=y
+CONFIG_SOC_DEVICE=y
+CONFIG_SOC_XILINX_VERSAL_NET=y
 CONFIG_SPI=y
 CONFIG_DM_SPI=y
 CONFIG_CADENCE_QSPI=y
diff --git a/configs/xilinx_zynqmp_mini_defconfig b/configs/xilinx_zynqmp_mini_defconfig
index 245b6a4..d8b3aab 100644
--- a/configs/xilinx_zynqmp_mini_defconfig
+++ b/configs/xilinx_zynqmp_mini_defconfig
@@ -7,7 +7,6 @@
 CONFIG_ENV_SIZE=0x80
 CONFIG_DEFAULT_DEVICE_TREE="zynqmp-mini"
 CONFIG_SYS_MEM_RSVD_FOR_MMU=y
-CONFIG_ZYNQMP_PSU_INIT_ENABLED=y
 # CONFIG_CMD_ZYNQMP is not set
 CONFIG_SYS_LOAD_ADDR=0x8000000
 CONFIG_SYS_MEMTEST_START=0x00000000
@@ -59,6 +58,7 @@
 # CONFIG_NET is not set
 # CONFIG_DM_WARN is not set
 # CONFIG_DM_DEVICE_REMOVE is not set
+# CONFIG_DM_MAILBOX is not set
 # CONFIG_MMC is not set
 CONFIG_ARM_DCC=y
 CONFIG_PANIC_HANG=y
diff --git a/configs/xilinx_zynqmp_mini_emmc0_defconfig b/configs/xilinx_zynqmp_mini_emmc0_defconfig
index adf1dae..a1ee98d 100644
--- a/configs/xilinx_zynqmp_mini_emmc0_defconfig
+++ b/configs/xilinx_zynqmp_mini_emmc0_defconfig
@@ -10,7 +10,6 @@
 CONFIG_DEFAULT_DEVICE_TREE="zynqmp-mini-emmc0"
 CONFIG_SPL_SYS_MALLOC_F_LEN=0x600
 CONFIG_SPL=y
-CONFIG_ZYNQMP_PSU_INIT_ENABLED=y
 # CONFIG_CMD_ZYNQMP is not set
 CONFIG_SYS_LOAD_ADDR=0x8000000
 CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
@@ -71,6 +70,7 @@
 # CONFIG_DM_WARN is not set
 # CONFIG_DM_DEVICE_REMOVE is not set
 CONFIG_SPL_DM_SEQ_ALIAS=y
+# CONFIG_DM_MAILBOX is not set
 CONFIG_SUPPORT_EMMC_BOOT=y
 CONFIG_MMC_SDHCI=y
 CONFIG_MMC_SDHCI_ZYNQ=y
diff --git a/configs/xilinx_zynqmp_mini_emmc1_defconfig b/configs/xilinx_zynqmp_mini_emmc1_defconfig
index 9d799ad..88c95d4 100644
--- a/configs/xilinx_zynqmp_mini_emmc1_defconfig
+++ b/configs/xilinx_zynqmp_mini_emmc1_defconfig
@@ -10,7 +10,6 @@
 CONFIG_DEFAULT_DEVICE_TREE="zynqmp-mini-emmc1"
 CONFIG_SPL_SYS_MALLOC_F_LEN=0x600
 CONFIG_SPL=y
-CONFIG_ZYNQMP_PSU_INIT_ENABLED=y
 # CONFIG_CMD_ZYNQMP is not set
 CONFIG_SYS_LOAD_ADDR=0x8000000
 CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
@@ -71,6 +70,7 @@
 # CONFIG_DM_WARN is not set
 # CONFIG_DM_DEVICE_REMOVE is not set
 CONFIG_SPL_DM_SEQ_ALIAS=y
+# CONFIG_DM_MAILBOX is not set
 CONFIG_SUPPORT_EMMC_BOOT=y
 CONFIG_MMC_SDHCI=y
 CONFIG_MMC_SDHCI_ZYNQ=y
diff --git a/configs/xilinx_zynqmp_mini_nand_defconfig b/configs/xilinx_zynqmp_mini_nand_defconfig
index 29040a3..0e03534 100644
--- a/configs/xilinx_zynqmp_mini_nand_defconfig
+++ b/configs/xilinx_zynqmp_mini_nand_defconfig
@@ -7,7 +7,6 @@
 CONFIG_NR_DRAM_BANKS=1
 CONFIG_ENV_SIZE=0x80
 CONFIG_DEFAULT_DEVICE_TREE="zynqmp-mini-nand"
-CONFIG_ZYNQMP_PSU_INIT_ENABLED=y
 # CONFIG_CMD_ZYNQMP is not set
 CONFIG_SYS_LOAD_ADDR=0x8000000
 CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
@@ -55,6 +54,7 @@
 # CONFIG_NET is not set
 # CONFIG_DM_WARN is not set
 # CONFIG_DM_DEVICE_REMOVE is not set
+# CONFIG_DM_MAILBOX is not set
 # CONFIG_MMC is not set
 CONFIG_MTD=y
 CONFIG_DM_MTD=y
diff --git a/configs/xilinx_zynqmp_mini_nand_single_defconfig b/configs/xilinx_zynqmp_mini_nand_single_defconfig
index 7c17c06..5255419 100644
--- a/configs/xilinx_zynqmp_mini_nand_single_defconfig
+++ b/configs/xilinx_zynqmp_mini_nand_single_defconfig
@@ -7,7 +7,6 @@
 CONFIG_NR_DRAM_BANKS=1
 CONFIG_ENV_SIZE=0x80
 CONFIG_DEFAULT_DEVICE_TREE="zynqmp-mini-nand"
-CONFIG_ZYNQMP_PSU_INIT_ENABLED=y
 # CONFIG_CMD_ZYNQMP is not set
 CONFIG_SYS_LOAD_ADDR=0x8000000
 CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
@@ -55,6 +54,7 @@
 # CONFIG_NET is not set
 # CONFIG_DM_WARN is not set
 # CONFIG_DM_DEVICE_REMOVE is not set
+# CONFIG_DM_MAILBOX is not set
 # CONFIG_MMC is not set
 CONFIG_MTD=y
 CONFIG_DM_MTD=y
diff --git a/configs/xilinx_zynqmp_mini_qspi_defconfig b/configs/xilinx_zynqmp_mini_qspi_defconfig
index 513b519..6861f73 100644
--- a/configs/xilinx_zynqmp_mini_qspi_defconfig
+++ b/configs/xilinx_zynqmp_mini_qspi_defconfig
@@ -10,7 +10,6 @@
 CONFIG_SPL=y
 CONFIG_SYS_MEM_RSVD_FOR_MMU=y
 CONFIG_ZYNQMP_NO_DDR=y
-CONFIG_ZYNQMP_PSU_INIT_ENABLED=y
 # CONFIG_CMD_ZYNQMP is not set
 # CONFIG_PSCI_RESET is not set
 CONFIG_SYS_LOAD_ADDR=0x8000000
@@ -76,6 +75,7 @@
 # CONFIG_GPIO is not set
 # CONFIG_I2C is not set
 # CONFIG_INPUT is not set
+# CONFIG_DM_MAILBOX is not set
 # CONFIG_MMC is not set
 # CONFIG_SPI_FLASH_SMART_HWCAPS is not set
 # CONFIG_SPI_FLASH_UNLOCK_ALL is not set
diff --git a/configs/xilinx_zynqmp_virt_defconfig b/configs/xilinx_zynqmp_virt_defconfig
index ea11127..4732c39 100644
--- a/configs/xilinx_zynqmp_virt_defconfig
+++ b/configs/xilinx_zynqmp_virt_defconfig
@@ -13,8 +13,6 @@
 CONFIG_ENV_OFFSET_REDUND=0x1E80000
 CONFIG_SPL_SPI_FLASH_SUPPORT=y
 CONFIG_SPL_SPI=y
-CONFIG_ZYNQ_MAC_IN_EEPROM=y
-CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET=0x20
 CONFIG_CMD_FRU=y
 CONFIG_ZYNQMP_USB=y
 CONFIG_SYS_LOAD_ADDR=0x8000000
@@ -81,7 +79,6 @@
 CONFIG_CMD_SPI=y
 CONFIG_CMD_USB=y
 CONFIG_CMD_USB_MASS_STORAGE=y
-CONFIG_CMD_WDT=y
 CONFIG_BOOTP_MAY_FAIL=y
 CONFIG_BOOTP_BOOTFILESIZE=y
 CONFIG_CMD_TFTPPUT=y
@@ -229,7 +226,6 @@
 CONFIG_BMP_16BPP=y
 CONFIG_BMP_24BPP=y
 CONFIG_BMP_32BPP=y
-CONFIG_WDT=y
 CONFIG_PANIC_HANG=y
 CONFIG_TPM=y
 CONFIG_SPL_GZIP=y
diff --git a/disk/part.c b/disk/part.c
index 2eb30eb..5ee60a7 100644
--- a/disk/part.c
+++ b/disk/part.c
@@ -433,25 +433,17 @@
 	int part;
 	struct disk_partition tmpinfo;
 
+	*dev_desc = NULL;
+	memset(info, 0, sizeof(*info));
+
 #if IS_ENABLED(CONFIG_SANDBOX) || IS_ENABLED(CONFIG_SEMIHOSTING)
 	/*
 	 * Special-case a pseudo block device "hostfs", to allow access to the
 	 * host's own filesystem.
 	 */
-	if (0 == strcmp(ifname, "hostfs")) {
-		*dev_desc = NULL;
-		info->start = 0;
-		info->size = 0;
-		info->blksz = 0;
-		info->bootable = 0;
+	if (!strcmp(ifname, "hostfs")) {
 		strcpy((char *)info->type, BOOT_PART_TYPE);
 		strcpy((char *)info->name, "Host filesystem");
-#if CONFIG_IS_ENABLED(PARTITION_UUIDS)
-		info->uuid[0] = 0;
-#endif
-#ifdef CONFIG_PARTITION_TYPE_GUID
-		info->type_guid[0] = 0;
-#endif
 
 		return 0;
 	}
@@ -462,19 +454,14 @@
 	 * Special-case ubi, ubi goes through a mtd, rather than through
 	 * a regular block device.
 	 */
-	if (0 == strcmp(ifname, "ubi")) {
+	if (!strcmp(ifname, "ubi")) {
 		if (!ubifs_is_mounted()) {
 			printf("UBIFS not mounted, use ubifsmount to mount volume first!\n");
 			return -EINVAL;
 		}
 
-		*dev_desc = NULL;
-		memset(info, 0, sizeof(*info));
 		strcpy((char *)info->type, BOOT_PART_TYPE);
 		strcpy((char *)info->name, "UBI");
-#if CONFIG_IS_ENABLED(PARTITION_UUIDS)
-		info->uuid[0] = 0;
-#endif
 		return 0;
 	}
 #endif
diff --git a/doc/board/microchip/mpfs_icicle.rst b/doc/board/microchip/mpfs_icicle.rst
index a4b10c6..09c2c6a 100644
--- a/doc/board/microchip/mpfs_icicle.rst
+++ b/doc/board/microchip/mpfs_icicle.rst
@@ -209,7 +209,7 @@
 ~~~~~~~~~
 
 The HSS always picks up HSS payload from a GPT partition with
-GIUD type "21686148-6449-6E6F-744E-656564454649" or sector '0' of the eMMC if no
+GUID type "21686148-6449-6E6F-744E-656564454649" or sector '0' of the eMMC if no
 GPT partition.
 
 Booting
@@ -460,7 +460,7 @@
 ~~~~~~~~~
 
 The HSS always picks up the HSS payload from a GPT partition with
-GIUD type "21686148-6449-6E6F-744E-656564454649" or sector '0' of the eMMC if no
+GUID type "21686148-6449-6E6F-744E-656564454649" or sector '0' of the eMMC if no
 GPT partition.
 
 Sample boot log from MPFS Icicle Kit
diff --git a/doc/board/ti/j721e_evm.rst b/doc/board/ti/j721e_evm.rst
index 44dc316..ad70f15 100644
--- a/doc/board/ti/j721e_evm.rst
+++ b/doc/board/ti/j721e_evm.rst
@@ -142,7 +142,11 @@
 	Tree: https://github.com/OP-TEE/optee_os.git
 	Branch: master
 
-4. U-Boot:
+4. DM Firmware:
+	Tree: git://git.ti.com/processor-firmware/ti-linux-firmware.git
+	Branch: ti-linux-firmware
+
+5. U-Boot:
 	Tree: https://source.denx.de/u-boot/u-boot
 	Branch: master
 
@@ -150,37 +154,37 @@
 ----------------
 1. SYSFW:
 
-.. code-block:: text
+.. code-block:: bash
 
- $ make CROSS_COMPILE=arm-linux-gnueabihf-
+    make CROSS_COMPILE=arm-linux-gnueabihf- SOC=j721e
 
 2. ATF:
 
-.. code-block:: text
+.. code-block:: bash
 
- $ make CROSS_COMPILE=aarch64-linux-gnu- ARCH=aarch64 PLAT=k3 TARGET_BOARD=generic SPD=opteed
+    make CROSS_COMPILE=aarch64-linux-gnu- ARCH=aarch64 PLAT=k3 TARGET_BOARD=generic SPD=opteed
 
 3. OPTEE:
 
-.. code-block:: text
+.. code-block:: bash
 
- $ make PLATFORM=k3-j721e CFG_ARM64_core=y
+    make PLATFORM=k3-j721e CFG_ARM64_core=y
 
 4. U-Boot:
 
 * 4.1 R5:
 
-.. code-block:: text
+.. code-block:: bash
 
- $ make CROSS_COMPILE=arm-linux-gnueabihf- j721e_evm_r5_defconfig O=/tmp/r5
- $ make CROSS_COMPILE=arm-linux-gnueabihf- O=/tmp/r5
+    make CROSS_COMPILE=arm-linux-gnueabihf- j721e_evm_r5_defconfig O=build/r5
+    make CROSS_COMPILE=arm-linux-gnueabihf- O=build/r5
 
 * 4.2 A72:
 
-.. code-block:: text
+.. code-block:: bash
 
- $ make CROSS_COMPILE=aarch64-linux-gnu- j721e_evm_a72_defconfig O=/tmp/a72
- $ make CROSS_COMPILE=aarch64-linux-gnu- ATF=<path to ATF dir>/build/k3/generic/release/bl31.bin TEE=<path to OPTEE OS dir>/out/arm-plat-k3/core/tee-pager_v2.bin DM=<path to DM firmware image> O=/tmp/a72
+    make CROSS_COMPILE=aarch64-linux-gnu- j721e_evm_a72_defconfig O=build/a72
+    make CROSS_COMPILE=aarch64-linux-gnu- ATF=<ATF dir>/build/k3/generic/release/bl31.bin TEE=<OPTEE OS dir>/out/arm-plat-k3/core/tee-pager_v2.bin DM=<DM firmware>/ti-dm/j721e/ipc_echo_testb_mcu1_0_release_strip.xer5f O=build/a72
 
 Target Images
 --------------
diff --git a/doc/build/buildman.rst b/doc/build/buildman.rst
new file mode 120000
index 0000000..beeaa42
--- /dev/null
+++ b/doc/build/buildman.rst
@@ -0,0 +1 @@
+../../tools/buildman/buildman.rst
\ No newline at end of file
diff --git a/doc/build/index.rst b/doc/build/index.rst
index 69952f9..9a8105d 100644
--- a/doc/build/index.rst
+++ b/doc/build/index.rst
@@ -11,3 +11,4 @@
    clang
    docker
    tools
+   buildman
diff --git a/doc/develop/release_cycle.rst b/doc/develop/release_cycle.rst
index 2bd037f..6ee67ef 100644
--- a/doc/develop/release_cycle.rst
+++ b/doc/develop/release_cycle.rst
@@ -67,7 +67,7 @@
 
 * U-Boot v2023.01-rc2 was released on Mon 21 November 2022.
 
-.. * U-Boot v2023.01-rc3 was released on Mon 21 November 2022.
+* U-Boot v2023.01-rc3 was released on Mon 05 December 2022.
 
 .. * U-Boot v2023.01-rc4 was released on Mon 05 December 2022.
 
diff --git a/doc/usage/cmd/cmp.rst b/doc/usage/cmd/cmp.rst
index 241320d..8d196ee 100644
--- a/doc/usage/cmd/cmp.rst
+++ b/doc/usage/cmd/cmp.rst
@@ -14,20 +14,20 @@
 -----------
 
 The cmp command is used to compare two memory areas. By default it works on
-four byte tuples. By appending .b, .w, .l, .q the size of the tuples is
-controlled:
+four byte (32-bit) values. By appending .b, .w, .l, .q the size of the
+values is controlled:
 
 cmp.b
-    compare 1 byte tuples
+    compare 1 byte (8-bit) values
 
 cmp.w
-    compare 2 byte tuples
+    compare 2 byte (16-bit) values
 
 cmp.l
-    compare 4 byte tuples
+    compare 4 byte (32-bit) values
 
 cmp.q
-    compare 8 byte tuples
+    compare 8 byte (64-bit) values
 
 The parameters are used as follows:
 
diff --git a/doc/usage/cmd/eficonfig.rst b/doc/usage/cmd/eficonfig.rst
index 340ebc8..30eb72b 100644
--- a/doc/usage/cmd/eficonfig.rst
+++ b/doc/usage/cmd/eficonfig.rst
@@ -13,49 +13,43 @@
 Description
 -----------
 
-The "eficonfig" command uses U-Boot menu interface and provides
-a menu-driven UEFI variable maintenance feature.
-The "eficonfig" has the following menu entries.
+The "eficonfig" command uses the U-Boot menu interface to provide a
+menu-driven UEFI variable maintenance feature. These are the top level menu
+entries:
 
 Add Boot Option
-    Add new UEFI Boot Option.
-    User can edit description, file path, and optional_data.
+    Add a new UEFI Boot Option.
+    The user can edit description, file path, and optional_data.
+    The new boot opiton is appended to the boot order in the *BootOrder*
+    variable. The user may want to update the boot order using the
+    *Change Boot Order* menu entry.
 
 Edit Boot Option
-    Edit the existing UEFI Boot Option
-    User can edit description, file path, and optional_data.
+    Edit an existing UEFI Boot Option.
+    The User can edit description, file path, and optional_data.
 
 Change Boot Order
-    Change the order of UEFI BootOrder variable.
+    Change the boot order updating the UEFI BootOrder variable.
 
 Delete Boot Option
-    Delete the UEFI Boot Option
+    Delete a UEFI Boot Option
 
-Configuration
--------------
+Secure Boot Configuration
+    Edit the UEFI Secure Boot Configuration
 
-The "eficonfig" command is enabled by::
+How to boot the system with a newly added UEFI Boot Option
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
 
-    CONFIG_CMD_EFICONFIG=y
-
-If CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE is enabled, user can not enter
-U-Boot console. In this case, bootmenu can be used to invoke "eficonfig"::
-
-    CONFIG_USE_PREBOOT=y
-    CONFIG_PREBOOT="setenv bootmenu_0 UEFI Maintenance Menu=eficonfig"
+The "eficonfig" command is used to set the UEFI boot options which are stored
+in the UEFI variable Boot#### where #### is a hexadecimal number.
 
-How to boot the system with newly added UEFI Boot Option
-''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+The command *bootefi bootmgr* can be used to boot by trying in sequence all
+boot options selected by the variable *BootOrder*.
 
-"eficonfig" command is responsible for configuring the UEFI variables,
-not directly handle the system boot.
-The new Boot Option added by "eficonfig" is appended at the last entry
-of UEFI BootOrder variable, user may want to change the boot order
-through "Change Boot Order".
 If the bootmenu is enabled, CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE is enabled,
 and "eficonfig" is configured as preboot command, the newly added Boot Options
-are enumerated in the bootmenu when user exits from the eficonfig menu.
-User may select the entry in the bootmenu to boot the system, or follow
+are enumerated in the bootmenu when the user exits from the eficonfig menu.
+The user may select the entry in the bootmenu to boot the system, or follow
 the U-Boot configuration the system already has.
 
 Auto boot with the UEFI Boot Option
@@ -66,6 +60,44 @@
 
     CONFIG_PREBOOT="setenv bootmenu_0 UEFI Boot Manager=bootefi bootmgr; setenv bootmenu_1 UEFI Maintenance Menu=eficonfig"
 
+UEFI Secure Boot Configuration
+''''''''''''''''''''''''''''''
+
+The user can enroll the variables PK, KEK, db and dbx by selecting a file.
+The "eficonfig" command only accepts signed EFI Signature List(s) with an
+authenticated header, typically a ".auth" file.
+
+To clear the PK, KEK, db and dbx, the user needs to enroll a null value
+signed by PK or KEK.
+
+Configuration
+-------------
+
+The "eficonfig" command is enabled by::
+
+    CONFIG_CMD_EFICONFIG=y
+
+If CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE is enabled, the user can not enter
+U-Boot console. In this case, the bootmenu can be used to invoke "eficonfig"::
+
+    CONFIG_USE_PREBOOT=y
+    CONFIG_PREBOOT="setenv bootmenu_0 UEFI Maintenance Menu=eficonfig"
+
+The only way U-Boot can currently store EFI variables on a tamper
+resistant medium is via OP-TEE. The Kconfig option that enables that is::
+
+    CONFIG_EFI_MM_COMM_TEE=y.
+
+It enables storing EFI variables on the RPMB partition of an eMMC device.
+
+The UEFI Secure Boot Configuration menu entry is only available if the following
+options are enabled::
+
+    CONFIG_EFI_SECURE_BOOT=y
+    CONFIG_EFI_MM_COMM_TEE=y
+
 See also
 --------
-* :doc:`bootmenu<bootmenu>` provides a simple mechanism for creating menus with different boot items
+
+* :doc:`bootmenu<bootmenu>` provides a simple mechanism for creating menus with
+  different boot items
diff --git a/doc/usage/cmd/sound.rst b/doc/usage/cmd/sound.rst
new file mode 100644
index 0000000..d3fac24
--- /dev/null
+++ b/doc/usage/cmd/sound.rst
@@ -0,0 +1,41 @@
+.. SPDX-License-Identifier: GPL-2.0+
+.. Copyright 2022, Heinrich Schuchardt <xypron.glpk@gmx.de>
+
+sound command
+=============
+
+Synopsis
+--------
+
+::
+
+    sound init
+    sound play [len [freq]]
+
+Description
+-----------
+
+The *sound* command is used to play a beep sound.
+
+sound init
+    initializes the sound driver.
+
+sound play
+    plays a square wave sound. It does not depend on previously calling
+    *sound init*.
+
+len
+    duration of the sound in ms, defaults to 1000 ms
+
+freq
+    frequency of the sound in Hz, defaults to 400 Hz
+
+Configuration
+-------------
+
+The sound command is enabled by CONFIG_CMD_SOUND=y.
+
+Return value
+------------
+
+The return value $? is 0 (true) if the command succeeds, 1 (false) otherwise.
diff --git a/doc/usage/cmd/wget.rst b/doc/usage/cmd/wget.rst
new file mode 100644
index 0000000..4fcfa03
--- /dev/null
+++ b/doc/usage/cmd/wget.rst
@@ -0,0 +1,61 @@
+.. SPDX-License-Identifier: GPL-2.0+:
+
+wget command
+============
+
+Synopsis
+--------
+
+::
+    wget address [[hostIPaddr:]path]
+
+Description
+-----------
+
+The wget command is used to download a file from an HTTP server.
+
+wget command will use HTTP over TCP to download files from an HTTP server.
+Currently it can only download image from an HTTP server hosted on port 80.
+
+address
+    memory address for the data downloaded
+
+hostIPaddr
+    IP address of the HTTP server, defaults to the value of environment
+    variable *serverip*
+
+path
+    path of the file to be downloaded.
+
+Example
+-------
+
+In the example the following steps are executed:
+
+* setup client network address
+* download a file from the HTTP server
+
+::
+
+    => setenv autoload no
+    => dhcp
+    BOOTP broadcast 1
+    *** Unhandled DHCP Option in OFFER/ACK: 23
+    *** Unhandled DHCP Option in OFFER/ACK: 23
+    DHCP client bound to address 192.168.1.105 (210 ms)
+    => wget ${loadaddr} 192.168.1.254:/index.html
+    HTTP/1.0 302 Found
+    Packets received 4, Transfer Successful
+
+Configuration
+-------------
+
+The command is only available if CONFIG_CMD_WGET=y.
+
+CONFIG_PROT_TCP_SACK can be turned on for the TCP SACK options. This will
+help increasing the downloading speed.
+
+Return value
+------------
+
+The return value $? is 0 (true) on success and 1 (false) otherwise.
diff --git a/doc/usage/index.rst b/doc/usage/index.rst
index d6283fa..bbd40a6 100644
--- a/doc/usage/index.rst
+++ b/doc/usage/index.rst
@@ -73,12 +73,14 @@
    cmd/scp03
    cmd/setexpr
    cmd/size
+   cmd/sound
    cmd/temperature
    cmd/tftpput
    cmd/true
    cmd/ums
    cmd/ut
    cmd/wdt
+   cmd/wget
    cmd/xxd
 
 Booting OS
diff --git a/drivers/Makefile b/drivers/Makefile
index ac2d83a..6f1de58 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -37,6 +37,7 @@
 obj-$(CONFIG_$(SPL_TPL_)TPM) += tpm/
 obj-$(CONFIG_XEN) += xen/
 obj-$(CONFIG_$(SPL_)FPGA) += fpga/
+obj-y += bus/
 
 ifndef CONFIG_TPL_BUILD
 ifndef CONFIG_VPL_BUILD
@@ -77,7 +78,6 @@
 
 obj-y += adc/
 obj-y += ata/
-obj-y += bus/
 obj-$(CONFIG_DM_DEMO) += demo/
 obj-$(CONFIG_BIOSEMU) += bios_emulator/
 obj-y += block/
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index d742ed3..e60aa72 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -13,7 +13,14 @@
 
 config TI_SYSC
 	bool "TI sysc interconnect target module driver"
-	depends on ARCH_OMAP2PLUS
+	depends on DM && ARCH_OMAP2PLUS
+	help
+	  Generic driver for Texas Instruments interconnect target module
+	  found on many TI SoCs.
+
+config SPL_TI_SYSC
+	bool "Support TI sysc interconnect in SPL"
+	depends on SPL_DM && TI_SYSC
 	help
 	  Generic driver for Texas Instruments interconnect target module
 	  found on many TI SoCs.
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index a2e71c7..0802b96 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -3,6 +3,9 @@
 # Makefile for the bus drivers.
 #
 
+ifeq ($(CONFIG_SPL_BUILD)$(CONFIG_TPL_BUILD),)
 obj-$(CONFIG_TI_PWMSS)	+= ti-pwmss.o
-obj-$(CONFIG_TI_SYSC)	+= ti-sysc.o
 obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o
+endif
+
+obj-$(CONFIG_$(SPL_)TI_SYSC)	+= ti-sysc.o
diff --git a/drivers/core/lists.c b/drivers/core/lists.c
index 3878957..8034a8f 100644
--- a/drivers/core/lists.c
+++ b/drivers/core/lists.c
@@ -120,10 +120,10 @@
 		int ret;
 
 		ret = bind_drivers_pass(parent, pre_reloc_only);
-		if (!ret)
-			break;
-		if (ret != -EAGAIN && !result)
+		if (!result || result == -EAGAIN)
 			result = ret;
+		if (ret != -EAGAIN)
+			break;
 	}
 
 	return result;
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
index 14bbfe7..4d56b1a 100644
--- a/drivers/core/ofnode.c
+++ b/drivers/core/ofnode.c
@@ -1197,12 +1197,12 @@
 	while (list < end) {
 		len = strlen(list);
 
-		if (len >= strlen("ethernet-phy-idVVVV,DDDD")) {
+		if (len >= strlen("ethernet-phy-idVVVV.DDDD")) {
 			char *s = strstr(list, "ethernet-phy-id");
 
 			/*
 			 * check if the string is something like
-			 * ethernet-phy-idVVVV,DDDD
+			 * ethernet-phy-idVVVV.DDDD
 			 */
 			if (s && s[19] == '.') {
 				s += strlen("ethernet-phy-id");
diff --git a/drivers/i2c/npcm-i2c.c b/drivers/i2c/npcm_i2c.c
similarity index 100%
rename from drivers/i2c/npcm-i2c.c
rename to drivers/i2c/npcm_i2c.c
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 78de3c5..1ea8363 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -1600,7 +1600,7 @@
 	ofs -= ofs & (SZ_64K - 1);
 	len = len & (SZ_64K - 1) ? (len & ~(SZ_64K - 1)) + SZ_64K : len;
 
-	return sst26_lock_ctl(nor, ofs, len, SST26_CTL_CHECK);
+	return !sst26_lock_ctl(nor, ofs, len, SST26_CTL_CHECK);
 }
 
 static int sst_write_byteprogram(struct spi_nor *nor, loff_t to, size_t len,
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 8df3dce..029bf38 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -438,6 +438,11 @@
 	  This driver implements a DSA switch driver for the KSZ9477 family
 	  of GbE switches using the I2C interface.
 
+config LITEETH
+	bool "LiteX LiteEth Ethernet MAC"
+	help
+	 Driver for the LiteEth Ethernet MAC from LiteX.
+
 config MVGBE
 	bool "Marvell Orion5x/Kirkwood network interface support"
 	depends on ARCH_KIRKWOOD || ARCH_ORION5X
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 96b7678..d3fc6b7 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -47,6 +47,7 @@
 obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o
 obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o
 obj-$(CONFIG_KSZ9477) += ksz9477.o
+obj-$(CONFIG_LITEETH) += liteeth.o
 obj-$(CONFIG_MACB) += macb.o
 obj-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o
 obj-$(CONFIG_MDIO_IPQ4019) += mdio-ipq4019.o
diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
index 001b028..afc47b5 100644
--- a/drivers/net/dwc_eth_qos.c
+++ b/drivers/net/dwc_eth_qos.c
@@ -75,10 +75,7 @@
  */
 static void *eqos_alloc_descs(struct eqos_priv *eqos, unsigned int num)
 {
-	eqos->desc_size = ALIGN(sizeof(struct eqos_desc),
-				(unsigned int)ARCH_DMA_MINALIGN);
-
-	return memalign(eqos->desc_size, num * eqos->desc_size);
+	return memalign(ARCH_DMA_MINALIGN, num * eqos->desc_size);
 }
 
 static void eqos_free_descs(void *descs)
@@ -89,13 +86,13 @@
 static struct eqos_desc *eqos_get_desc(struct eqos_priv *eqos,
 				       unsigned int num, bool rx)
 {
-	return eqos->descs +
-		((rx ? EQOS_DESCRIPTORS_TX : 0) + num) * eqos->desc_size;
+	return (rx ? eqos->rx_descs : eqos->tx_descs) +
+	       (num * eqos->desc_size);
 }
 
 void eqos_inval_desc_generic(void *desc)
 {
-	unsigned long start = (unsigned long)desc;
+	unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1);
 	unsigned long end = ALIGN(start + sizeof(struct eqos_desc),
 				  ARCH_DMA_MINALIGN);
 
@@ -104,7 +101,7 @@
 
 void eqos_flush_desc_generic(void *desc)
 {
-	unsigned long start = (unsigned long)desc;
+	unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1);
 	unsigned long end = ALIGN(start + sizeof(struct eqos_desc),
 				  ARCH_DMA_MINALIGN);
 
@@ -1001,7 +998,8 @@
 
 	/* Set up descriptors */
 
-	memset(eqos->descs, 0, eqos->desc_size * EQOS_DESCRIPTORS_NUM);
+	memset(eqos->tx_descs, 0, eqos->desc_size * EQOS_DESCRIPTORS_TX);
+	memset(eqos->rx_descs, 0, eqos->desc_size * EQOS_DESCRIPTORS_RX);
 
 	for (i = 0; i < EQOS_DESCRIPTORS_TX; i++) {
 		struct eqos_desc *tx_desc = eqos_get_desc(eqos, i, false);
@@ -1187,6 +1185,7 @@
 static int eqos_free_pkt(struct udevice *dev, uchar *packet, int length)
 {
 	struct eqos_priv *eqos = dev_get_priv(dev);
+	u32 idx, idx_mask = eqos->desc_per_cacheline - 1;
 	uchar *packet_expected;
 	struct eqos_desc *rx_desc;
 
@@ -1200,26 +1199,32 @@
 		return -EINVAL;
 	}
 
-	eqos->config->ops->eqos_inval_buffer(packet, length);
-
-	rx_desc = eqos_get_desc(eqos, eqos->rx_desc_idx, true);
-
-	rx_desc->des0 = 0;
-	mb();
-	eqos->config->ops->eqos_flush_desc(rx_desc);
 	eqos->config->ops->eqos_inval_buffer(packet, length);
-	rx_desc->des0 = (u32)(ulong)packet;
-	rx_desc->des1 = 0;
-	rx_desc->des2 = 0;
-	/*
-	 * Make sure that if HW sees the _OWN write below, it will see all the
-	 * writes to the rest of the descriptor too.
-	 */
-	mb();
-	rx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_BUF1V;
-	eqos->config->ops->eqos_flush_desc(rx_desc);
 
-	writel((ulong)rx_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer);
+	if ((eqos->rx_desc_idx & idx_mask) == idx_mask) {
+		for (idx = eqos->rx_desc_idx - idx_mask;
+		     idx <= eqos->rx_desc_idx;
+		     idx++) {
+			rx_desc = eqos_get_desc(eqos, idx, true);
+			rx_desc->des0 = 0;
+			mb();
+			eqos->config->ops->eqos_flush_desc(rx_desc);
+			eqos->config->ops->eqos_inval_buffer(packet, length);
+			rx_desc->des0 = (u32)(ulong)(eqos->rx_dma_buf +
+					     (idx * EQOS_MAX_PACKET_SIZE));
+			rx_desc->des1 = 0;
+			rx_desc->des2 = 0;
+			/*
+			 * Make sure that if HW sees the _OWN write below,
+			 * it will see all the writes to the rest of the
+			 * descriptor too.
+			 */
+			mb();
+			rx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_BUF1V;
+			eqos->config->ops->eqos_flush_desc(rx_desc);
+		}
+		writel((ulong)rx_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer);
+	}
 
 	eqos->rx_desc_idx++;
 	eqos->rx_desc_idx %= EQOS_DESCRIPTORS_RX;
@@ -1230,17 +1235,41 @@
 static int eqos_probe_resources_core(struct udevice *dev)
 {
 	struct eqos_priv *eqos = dev_get_priv(dev);
+	unsigned int desc_step;
 	int ret;
 
 	debug("%s(dev=%p):\n", __func__, dev);
 
-	eqos->descs = eqos_alloc_descs(eqos, EQOS_DESCRIPTORS_NUM);
-	if (!eqos->descs) {
-		debug("%s: eqos_alloc_descs() failed\n", __func__);
+	/* Maximum distance between neighboring descriptors, in Bytes. */
+	desc_step = sizeof(struct eqos_desc) +
+		    EQOS_DMA_CH0_CONTROL_DSL_MASK * eqos->config->axi_bus_width;
+	if (desc_step < ARCH_DMA_MINALIGN) {
+		/*
+		 * The EQoS hardware implementation cannot place one descriptor
+		 * per cacheline, it is necessary to place multiple descriptors
+		 * per cacheline in memory and do cache management carefully.
+		 */
+		eqos->desc_size = BIT(fls(desc_step) - 1);
+	} else {
+		eqos->desc_size = ALIGN(sizeof(struct eqos_desc),
+					(unsigned int)ARCH_DMA_MINALIGN);
+	}
+	eqos->desc_per_cacheline = ARCH_DMA_MINALIGN / eqos->desc_size;
+
+	eqos->tx_descs = eqos_alloc_descs(eqos, EQOS_DESCRIPTORS_TX);
+	if (!eqos->tx_descs) {
+		debug("%s: eqos_alloc_descs(tx) failed\n", __func__);
 		ret = -ENOMEM;
 		goto err;
 	}
 
+	eqos->rx_descs = eqos_alloc_descs(eqos, EQOS_DESCRIPTORS_RX);
+	if (!eqos->rx_descs) {
+		debug("%s: eqos_alloc_descs(rx) failed\n", __func__);
+		ret = -ENOMEM;
+		goto err_free_tx_descs;
+	}
+
 	eqos->tx_dma_buf = memalign(EQOS_BUFFER_ALIGN, EQOS_MAX_PACKET_SIZE);
 	if (!eqos->tx_dma_buf) {
 		debug("%s: memalign(tx_dma_buf) failed\n", __func__);
@@ -1276,7 +1305,9 @@
 err_free_tx_dma_buf:
 	free(eqos->tx_dma_buf);
 err_free_descs:
-	eqos_free_descs(eqos->descs);
+	eqos_free_descs(eqos->rx_descs);
+err_free_tx_descs:
+	eqos_free_descs(eqos->tx_descs);
 err:
 
 	debug("%s: returns %d\n", __func__, ret);
@@ -1292,7 +1323,8 @@
 	free(eqos->rx_pkt);
 	free(eqos->rx_dma_buf);
 	free(eqos->tx_dma_buf);
-	eqos_free_descs(eqos->descs);
+	eqos_free_descs(eqos->rx_descs);
+	eqos_free_descs(eqos->tx_descs);
 
 	debug("%s: OK\n", __func__);
 	return 0;
diff --git a/drivers/net/dwc_eth_qos.h b/drivers/net/dwc_eth_qos.h
index b35e774..8fccd6f 100644
--- a/drivers/net/dwc_eth_qos.h
+++ b/drivers/net/dwc_eth_qos.h
@@ -162,6 +162,7 @@
 #define EQOS_DMA_SYSBUS_MODE_BLEN4			BIT(1)
 
 #define EQOS_DMA_CH0_CONTROL_DSL_SHIFT			18
+#define EQOS_DMA_CH0_CONTROL_DSL_MASK			0x7
 #define EQOS_DMA_CH0_CONTROL_PBLX8			BIT(16)
 
 #define EQOS_DMA_CH0_TX_CONTROL_TXPBL_SHIFT		16
@@ -264,9 +265,11 @@
 	struct phy_device *phy;
 	ofnode phy_of_node;
 	u32 max_speed;
-	void *descs;
+	void *tx_descs;
+	void *rx_descs;
 	int tx_desc_idx, rx_desc_idx;
 	unsigned int desc_size;
+	unsigned int desc_per_cacheline;
 	void *tx_dma_buf;
 	void *rx_dma_buf;
 	void *rx_pkt;
diff --git a/drivers/net/liteeth.c b/drivers/net/liteeth.c
new file mode 100644
index 0000000..84d3852
--- /dev/null
+++ b/drivers/net/liteeth.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * LiteX Liteeth Ethernet
+ *
+ * Copyright 2021 Joel Stanley <joel@jms.id.au>, IBM Corp.
+ */
+
+#include <linux/litex.h>
+
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <net.h>
+
+#define LITEETH_WRITER_SLOT       0x00
+#define LITEETH_WRITER_LENGTH     0x04
+#define LITEETH_WRITER_ERRORS     0x08
+#define LITEETH_WRITER_EV_STATUS  0x0C
+#define LITEETH_WRITER_EV_PENDING 0x10
+#define LITEETH_WRITER_EV_ENABLE  0x14
+#define LITEETH_READER_START      0x18
+#define LITEETH_READER_READY      0x1C
+#define LITEETH_READER_LEVEL      0x20
+#define LITEETH_READER_SLOT       0x24
+#define LITEETH_READER_LENGTH     0x28
+#define LITEETH_READER_EV_STATUS  0x2C
+#define LITEETH_READER_EV_PENDING 0x30
+#define LITEETH_READER_EV_ENABLE  0x34
+#define LITEETH_PREAMBLE_CRC      0x38
+#define LITEETH_PREAMBLE_ERRORS   0x3C
+#define LITEETH_CRC_ERRORS        0x40
+
+struct liteeth {
+	struct udevice *dev;
+
+	void __iomem *base;
+	u32 slot_size;
+
+	/* Tx */
+	u32 tx_slot;
+	u32 num_tx_slots;
+	void __iomem *tx_base;
+
+	/* Rx */
+	u32 rx_slot;
+	u32 num_rx_slots;
+	void __iomem *rx_base;
+};
+
+static int liteeth_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+	struct liteeth *priv = dev_get_priv(dev);
+	u8 rx_slot;
+	int len;
+
+	if (!litex_read8(priv->base + LITEETH_WRITER_EV_PENDING)) {
+		debug("liteeth: No packet ready\n");
+		return -EAGAIN;
+	}
+
+	rx_slot = litex_read8(priv->base + LITEETH_WRITER_SLOT);
+	len = litex_read32(priv->base + LITEETH_WRITER_LENGTH);
+
+	debug("%s: slot %d len 0x%x\n", __func__, rx_slot, len);
+
+	*packetp = priv->rx_base + rx_slot * priv->slot_size;
+
+	return len;
+}
+
+static int liteeth_free_pkt(struct udevice *dev, uchar *packet, int length)
+{
+	struct liteeth *priv = dev_get_priv(dev);
+
+	litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, 1);
+
+	return 0;
+}
+
+static int liteeth_start(struct udevice *dev)
+{
+	struct liteeth *priv = dev_get_priv(dev);
+
+	/* Clear pending events */
+	litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, 1);
+	litex_write8(priv->base + LITEETH_READER_EV_PENDING, 1);
+
+	/* Enable events */
+	litex_write8(priv->base + LITEETH_WRITER_EV_ENABLE, 1);
+	litex_write8(priv->base + LITEETH_READER_EV_ENABLE, 1);
+
+	return 0;
+}
+
+static void liteeth_stop(struct udevice *dev)
+{
+	struct liteeth *priv = dev_get_priv(dev);
+
+	litex_write8(priv->base + LITEETH_WRITER_EV_ENABLE, 0);
+	litex_write8(priv->base + LITEETH_READER_EV_ENABLE, 0);
+}
+
+static int liteeth_send(struct udevice *dev, void *packet, int len)
+{
+	struct liteeth *priv = dev_get_priv(dev);
+	void __iomem *txbuffer;
+
+	if (!litex_read8(priv->base + LITEETH_READER_READY)) {
+		printf("liteeth: reader not ready\n");
+		return -EAGAIN;
+	}
+
+	/* Reject oversize packets */
+	if (unlikely(len > priv->slot_size))
+		return -EMSGSIZE;
+
+	txbuffer = priv->tx_base + priv->tx_slot * priv->slot_size;
+	memcpy_toio(txbuffer, packet, len);
+	litex_write8(priv->base + LITEETH_READER_SLOT, priv->tx_slot);
+	litex_write16(priv->base + LITEETH_READER_LENGTH, len);
+	litex_write8(priv->base + LITEETH_READER_START, 1);
+
+	priv->tx_slot = (priv->tx_slot + 1) % priv->num_tx_slots;
+
+	return 0;
+}
+
+static void liteeth_setup_slots(struct liteeth *priv)
+{
+	int err;
+
+	err = ofnode_read_u32(dev_ofnode(priv->dev), "litex,rx-slots", &priv->num_rx_slots);
+	if (err) {
+		dev_dbg(priv->dev, "unable to get litex,rx-slots, using 2\n");
+		priv->num_rx_slots = 2;
+	}
+
+	err = ofnode_read_u32(dev_ofnode(priv->dev), "litex,tx-slots", &priv->num_tx_slots);
+	if (err) {
+		dev_dbg(priv->dev, "unable to get litex,tx-slots, using 2\n");
+		priv->num_tx_slots = 2;
+	}
+
+	err = ofnode_read_u32(dev_ofnode(priv->dev), "litex,slot-size", &priv->slot_size);
+	if (err) {
+		dev_dbg(priv->dev, "unable to get litex,slot-size, using 0x800\n");
+		priv->slot_size = 0x800;
+	}
+}
+
+static int liteeth_remove(struct udevice *dev)
+{
+	liteeth_stop(dev);
+
+	return 0;
+}
+
+static const struct eth_ops liteeth_ops = {
+	.start = liteeth_start,
+	.stop = liteeth_stop,
+	.send = liteeth_send,
+	.recv = liteeth_recv,
+	.free_pkt = liteeth_free_pkt,
+};
+
+static int liteeth_of_to_plat(struct udevice *dev)
+{
+	struct eth_pdata *pdata = dev_get_plat(dev);
+	struct liteeth *priv = dev_get_priv(dev);
+	void __iomem *buf_base;
+
+	pdata->iobase = dev_read_addr(dev);
+
+	priv->dev = dev;
+
+	priv->base = dev_remap_addr_name(dev, "mac");
+	if (!priv->base) {
+		dev_err(dev, "failed to map registers\n");
+		return -EINVAL;
+	}
+
+	buf_base = dev_remap_addr_name(dev, "buffer");
+	if (!buf_base) {
+		dev_err(dev, "failed to map buffer\n");
+		return -EINVAL;
+	}
+
+	liteeth_setup_slots(priv);
+
+	/* Rx slots */
+	priv->rx_base = buf_base;
+	priv->rx_slot = 0;
+
+	/* Tx slots come after Rx slots */
+	priv->tx_base = buf_base + priv->num_rx_slots * priv->slot_size;
+	priv->tx_slot = 0;
+
+	return 0;
+}
+
+static const struct udevice_id liteeth_ids[] = {
+	{ .compatible = "litex,liteeth" },
+	{}
+};
+
+U_BOOT_DRIVER(liteeth) = {
+	.name = "liteeth",
+	.id = UCLASS_ETH,
+	.of_match = liteeth_ids,
+	.of_to_plat = liteeth_of_to_plat,
+	.plat_auto = sizeof(struct eth_pdata),
+	.remove = liteeth_remove,
+	.ops = &liteeth_ops,
+	.priv_auto = sizeof(struct liteeth),
+};
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index e02a57b..65ec1f2 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -98,6 +98,9 @@
 #define MACB_RX_DMA_DESC_SIZE	(DMA_DESC_BYTES(MACB_RX_RING_SIZE))
 #define MACB_TX_DUMMY_DMA_DESC_SIZE	(DMA_DESC_BYTES(1))
 
+#define DESC_PER_CACHELINE_32	(ARCH_DMA_MINALIGN/sizeof(struct macb_dma_desc))
+#define DESC_PER_CACHELINE_64	(ARCH_DMA_MINALIGN/DMA_DESC_SIZE)
+
 #define RXBUF_FRMLEN_MASK	0x00000fff
 #define TXBUF_FRMLEN_MASK	0x000007ff
 
@@ -401,32 +404,56 @@
 	return 0;
 }
 
+static void reclaim_rx_buffer(struct macb_device *macb,
+			      unsigned int idx)
+{
+	unsigned int mask;
+	unsigned int shift;
+	unsigned int i;
+
+	/*
+	 * There may be multiple descriptors per CPU cacheline,
+	 * so a cache flush would flush the whole line, meaning the content of other descriptors
+	 * in the cacheline would also flush. If one of the other descriptors had been
+	 * written to by the controller, the flush would cause those changes to be lost.
+	 *
+	 * To circumvent this issue, we do the actual freeing only when we need to free
+	 * the last descriptor in the current cacheline. When the current descriptor is the
+	 * last in the cacheline, we free all the descriptors that belong to that cacheline.
+	 */
+	if (macb->config->hw_dma_cap & HW_DMA_CAP_64B) {
+		mask = DESC_PER_CACHELINE_64 - 1;
+		shift = 1;
+	} else {
+		mask = DESC_PER_CACHELINE_32 - 1;
+		shift = 0;
+	}
+
+	/* we exit without freeing if idx is not the last descriptor in the cacheline */
+	if ((idx & mask) != mask)
+		return;
+
+	for (i = idx & (~mask); i <= idx; i++)
+		macb->rx_ring[i << shift].addr &= ~MACB_BIT(RX_USED);
+}
+
 static void reclaim_rx_buffers(struct macb_device *macb,
 			       unsigned int new_tail)
 {
 	unsigned int i;
-	unsigned int count;
 
 	i = macb->rx_tail;
 
 	macb_invalidate_ring_desc(macb, RX);
 	while (i > new_tail) {
-		if (macb->config->hw_dma_cap & HW_DMA_CAP_64B)
-			count = i * 2;
-		else
-			count = i;
-		macb->rx_ring[count].addr &= ~MACB_BIT(RX_USED);
+		reclaim_rx_buffer(macb, i);
 		i++;
-		if (i > MACB_RX_RING_SIZE)
+		if (i >= MACB_RX_RING_SIZE)
 			i = 0;
 	}
 
 	while (i < new_tail) {
-		if (macb->config->hw_dma_cap & HW_DMA_CAP_64B)
-			count = i * 2;
-		else
-			count = i;
-		macb->rx_ring[count].addr &= ~MACB_BIT(RX_USED);
+		reclaim_rx_buffer(macb, i);
 		i++;
 	}
 
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 52ce08b..86e6981 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -321,6 +321,11 @@
 	  as bridge between MAC connected over GMII and external phy that
 	  is connected over RGMII interface.
 
+config PHY_XWAY
+	bool "Intel XWAY PHY support"
+	help
+	  This adds support for the Intel XWAY (formerly Lantiq) Gbe PHYs.
+
 config PHY_ETHERNET_ID
 	bool "Read ethernet PHY id"
 	depends on DM_GPIO
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 9d87eb2..d38e99e 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -34,6 +34,7 @@
 obj-$(CONFIG_PHY_TI_DP83869) += dp83869.o
 obj-$(CONFIG_PHY_XILINX) += xilinx_phy.o
 obj-$(CONFIG_PHY_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o
+obj-$(CONFIG_PHY_XWAY) += intel_xway.o
 obj-$(CONFIG_PHY_ETHERNET_ID) += ethernet_id.o
 obj-$(CONFIG_PHY_VITESSE) += vitesse.o
 obj-$(CONFIG_PHY_MSCC) += mscc.o
diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c
index 7e950fe..79fbc11 100644
--- a/drivers/net/phy/aquantia.c
+++ b/drivers/net/phy/aquantia.c
@@ -136,7 +136,7 @@
 
 	*fw_addr = NULL;
 	*fw_length = 0;
-	debug("Loading Acquantia microcode from %s %s\n",
+	debug("Loading Aquantia microcode from %s %s\n",
 	      CONFIG_PHY_AQUANTIA_FW_PART, CONFIG_PHY_AQUANTIA_FW_NAME);
 	ret = fs_set_blk_dev("mmc", CONFIG_PHY_AQUANTIA_FW_PART, FS_TYPE_ANY);
 	if (ret < 0)
@@ -163,7 +163,7 @@
 
 	*fw_addr = addr;
 	*fw_length = length;
-	debug("Found Acquantia microcode.\n");
+	debug("Found Aquantia microcode.\n");
 
 cleanup:
 	if (ret < 0) {
@@ -257,7 +257,7 @@
 
 	strlcpy(version, (char *)&addr[dram_offset + VERSION_STRING_OFFSET],
 		VERSION_STRING_SIZE);
-	printf("%s loading firmare version '%s'\n", phydev->dev->name, version);
+	printf("%s loading firmware version '%s'\n", phydev->dev->name, version);
 
 	/* stall the microcprocessor */
 	phy_write(phydev, MDIO_MMD_VEND1, UP_CONTROL,
@@ -288,7 +288,7 @@
 
 	phy_write(phydev, MDIO_MMD_VEND1, UP_CONTROL, UP_RUN_STALL_OVERRIDE);
 
-	printf("%s firmare loading done.\n", phydev->dev->name);
+	printf("%s firmware loading done.\n", phydev->dev->name);
 done:
 	free(addr);
 	return ret;
diff --git a/drivers/net/phy/intel_xway.c b/drivers/net/phy/intel_xway.c
new file mode 100644
index 0000000..dfce3f8
--- /dev/null
+++ b/drivers/net/phy/intel_xway.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <common.h>
+#include <phy.h>
+#include <linux/bitfield.h>
+
+#define XWAY_MDIO_MIICTRL		0x17	/* mii control */
+
+#define XWAY_MDIO_MIICTRL_RXSKEW_MASK	GENMASK(14, 12)
+#define XWAY_MDIO_MIICTRL_TXSKEW_MASK	GENMASK(10, 8)
+
+static int xway_config(struct phy_device *phydev)
+{
+	ofnode node = phy_get_ofnode(phydev);
+	u32 val = 0;
+
+	if (ofnode_valid(node)) {
+		u32 rx_delay, tx_delay;
+
+		rx_delay = ofnode_read_u32_default(node, "rx-internal-delay-ps", 2000);
+		tx_delay = ofnode_read_u32_default(node, "tx-internal-delay-ps", 2000);
+		val |= FIELD_PREP(XWAY_MDIO_MIICTRL_TXSKEW_MASK, rx_delay / 500);
+		val |= FIELD_PREP(XWAY_MDIO_MIICTRL_RXSKEW_MASK, tx_delay / 500);
+		phy_modify(phydev, MDIO_DEVAD_NONE, XWAY_MDIO_MIICTRL,
+			   XWAY_MDIO_MIICTRL_TXSKEW_MASK |
+			   XWAY_MDIO_MIICTRL_RXSKEW_MASK, val);
+	}
+
+	genphy_config_aneg(phydev);
+
+	return 0;
+}
+
+static struct phy_driver XWAY_driver = {
+	.name = "XWAY",
+	.uid = 0xD565A400,
+	.mask = 0xffffff00,
+	.features = PHY_GBIT_FEATURES,
+	.config = xway_config,
+	.startup = genphy_startup,
+	.shutdown = genphy_shutdown,
+};
+
+int phy_xway_init(void)
+{
+	phy_register(&XWAY_driver);
+
+	return 0;
+}
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 9087663..92143cf 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -556,6 +556,9 @@
 #ifdef CONFIG_PHY_XILINX
 	phy_xilinx_init();
 #endif
+#ifdef CONFIG_PHY_XWAY
+	phy_xway_init();
+#endif
 #ifdef CONFIG_PHY_MSCC
 	phy_mscc_init();
 #endif
diff --git a/drivers/net/xilinx_axi_emac.c b/drivers/net/xilinx_axi_emac.c
index 5f5bc65..3e99199 100644
--- a/drivers/net/xilinx_axi_emac.c
+++ b/drivers/net/xilinx_axi_emac.c
@@ -109,6 +109,7 @@
 	struct eth_pdata eth_pdata;
 	struct axidma_reg *dmatx;
 	struct axidma_reg *dmarx;
+	int pcsaddr;
 	int phyaddr;
 	u8 eth_hasnobuf;
 	int phy_of_handle;
@@ -119,6 +120,7 @@
 struct axidma_priv {
 	struct axidma_reg *dmatx;
 	struct axidma_reg *dmarx;
+	int pcsaddr;
 	int phyaddr;
 	struct axi_regs *iobase;
 	phy_interface_t interface;
@@ -301,6 +303,13 @@
 	if (IS_ENABLED(CONFIG_DM_ETH_PHY))
 		priv->phyaddr = eth_phy_get_addr(dev);
 
+	/*
+	 * Set address of PCS/PMA PHY to the one pointed by phy-handle for
+	 * backward compatibility.
+	 */
+	if (priv->phyaddr != -1 && priv->pcsaddr == 0)
+		priv->pcsaddr = priv->phyaddr;
+
 	if (priv->phyaddr == -1) {
 		/* Detect the PHY address */
 		for (i = 31; i >= 0; i--) {
@@ -333,6 +342,45 @@
 	return 0;
 }
 
+static int pcs_pma_startup(struct axidma_priv *priv)
+{
+	u32 rc, retry_cnt = 0;
+	u16 mii_reg;
+
+	rc = phyread(priv, priv->pcsaddr, MII_BMCR, &mii_reg);
+	if (rc)
+		goto failed_mdio;
+
+	if (!(mii_reg & BMCR_ANENABLE)) {
+		mii_reg |= BMCR_ANENABLE;
+		if (phywrite(priv, priv->pcsaddr, MII_BMCR, mii_reg))
+			goto failed_mdio;
+	}
+
+	/*
+	 * Check the internal PHY status and warn user if the link between it
+	 * and the external PHY is not obtained.
+	 */
+	debug("axiemac: waiting for link status of the PCS/PMA PHY");
+	while (retry_cnt * 10 < PHY_ANEG_TIMEOUT) {
+		rc = phyread(priv, priv->pcsaddr, MII_BMSR, &mii_reg);
+		if ((mii_reg & BMSR_LSTATUS) && mii_reg != 0xffff && !rc) {
+			debug(".Done\n");
+			return 0;
+		}
+		if ((retry_cnt++ % 10) == 0)
+			debug(".");
+		mdelay(10);
+	}
+	debug("\n");
+	printf("axiemac: Warning, PCS/PMA PHY@%d is not ready, link is down\n",
+	       priv->pcsaddr);
+	return 1;
+failed_mdio:
+	printf("axiemac: MDIO to the PCS/PMA PHY has failed\n");
+	return 1;
+}
+
 /* Setting axi emac and phy to proper setting */
 static int setup_phy(struct udevice *dev)
 {
@@ -348,12 +396,12 @@
 		 * after DMA and ethernet resets and hence
 		 * check and clear if set.
 		 */
-		ret = phyread(priv, priv->phyaddr, MII_BMCR, &temp);
+		ret = phyread(priv, priv->pcsaddr, MII_BMCR, &temp);
 		if (ret)
 			return 0;
 		if (temp & BMCR_ISOLATE) {
 			temp &= ~BMCR_ISOLATE;
-			ret = phywrite(priv, priv->phyaddr, MII_BMCR, temp);
+			ret = phywrite(priv, priv->pcsaddr, MII_BMCR, temp);
 			if (ret)
 				return 0;
 		}
@@ -364,6 +412,11 @@
 		       phydev->dev->name);
 		return 0;
 	}
+	if (priv->interface == PHY_INTERFACE_MODE_SGMII ||
+	    priv->interface == PHY_INTERFACE_MODE_1000BASEX) {
+		if (pcs_pma_startup(priv))
+			return 0;
+	}
 	if (!phydev->link) {
 		printf("%s: No link.\n", phydev->dev->name);
 		return 0;
@@ -784,6 +837,7 @@
 
 	if (priv->mactype == EMAC_1G) {
 		priv->eth_hasnobuf = plat->eth_hasnobuf;
+		priv->pcsaddr = plat->pcsaddr;
 		priv->phyaddr = plat->phyaddr;
 		priv->phy_of_handle = plat->phy_of_handle;
 		priv->interface = pdata->phy_interface;
@@ -861,6 +915,8 @@
 
 	if (plat->mactype == EMAC_1G) {
 		plat->phyaddr = -1;
+		/* PHYAD 0 always redirects to the PCS/PMA PHY */
+		plat->pcsaddr = 0;
 
 		offset = fdtdec_lookup_phandle(gd->fdt_blob, node,
 					       "phy-handle");
@@ -878,6 +934,16 @@
 
 		plat->eth_hasnobuf = fdtdec_get_bool(gd->fdt_blob, node,
 						     "xlnx,eth-hasnobuf");
+
+		if (pdata->phy_interface == PHY_INTERFACE_MODE_SGMII ||
+		    pdata->phy_interface == PHY_INTERFACE_MODE_1000BASEX) {
+			offset = fdtdec_lookup_phandle(gd->fdt_blob, node,
+						       "pcs-handle");
+			if (offset > 0) {
+				plat->pcsaddr = fdtdec_get_int(gd->fdt_blob,
+							       offset, "reg", -1);
+			}
+		}
 	}
 
 	return 0;
diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c
index 3f4357e..507b19b 100644
--- a/drivers/net/zynq_gem.c
+++ b/drivers/net/zynq_gem.c
@@ -662,21 +662,6 @@
 						ZYNQ_GEM_NWCTRL_TXEN_MASK, 0);
 }
 
-__weak int zynq_board_read_rom_ethaddr(unsigned char *ethaddr)
-{
-	return -ENOSYS;
-}
-
-static int zynq_gem_read_rom_mac(struct udevice *dev)
-{
-	struct eth_pdata *pdata = dev_get_plat(dev);
-
-	if (!pdata)
-		return -ENOSYS;
-
-	return zynq_board_read_rom_ethaddr(pdata->enetaddr);
-}
-
 static int zynq_gem_miiphy_read(struct mii_dev *bus, int addr,
 				int devad, int reg)
 {
@@ -884,7 +869,6 @@
 	.free_pkt		= zynq_gem_free_pkt,
 	.stop			= zynq_gem_halt,
 	.write_hwaddr		= zynq_gem_setup_mac,
-	.read_rom_hwaddr	= zynq_gem_read_rom_mac,
 };
 
 static int zynq_gem_of_to_plat(struct udevice *dev)
diff --git a/drivers/pinctrl/pinctrl-zynqmp.c b/drivers/pinctrl/pinctrl-zynqmp.c
index 52d428f..7c5a02d 100644
--- a/drivers/pinctrl/pinctrl-zynqmp.c
+++ b/drivers/pinctrl/pinctrl-zynqmp.c
@@ -467,10 +467,6 @@
 				 pin);
 		break;
 	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
-		param = PM_PINCTRL_CONFIG_TRI_STATE;
-		arg = PM_PINCTRL_TRI_STATE_ENABLE;
-		ret = zynqmp_pm_pinctrl_set_config(pin, param, arg);
-		break;
 	case PIN_CONFIG_LOW_POWER_MODE:
 		/*
 		 * This cases are mentioned in dts but configurable
@@ -479,11 +475,6 @@
 		 */
 		ret = 0;
 		break;
-	case PIN_CONFIG_OUTPUT_ENABLE:
-		param = PM_PINCTRL_CONFIG_TRI_STATE;
-		arg = PM_PINCTRL_TRI_STATE_DISABLE;
-		ret = zynqmp_pm_pinctrl_set_config(pin, param, arg);
-		break;
 	default:
 		dev_warn(dev, "unsupported configuration parameter '%u'\n",
 			 param);
diff --git a/drivers/ram/aspeed/sdram_ast2600.c b/drivers/ram/aspeed/sdram_ast2600.c
index 9ad398d..5d42608 100644
--- a/drivers/ram/aspeed/sdram_ast2600.c
+++ b/drivers/ram/aspeed/sdram_ast2600.c
@@ -15,11 +15,125 @@
 #include <asm/global_data.h>
 #include <linux/err.h>
 #include <linux/kernel.h>
+#include <linux/bitfield.h>
 #include <dt-bindings/clock/ast2600-clock.h>
 
 #define DDR_PHY_TBL_CHG_ADDR            0xaeeddeea
 #define DDR_PHY_TBL_END                 0xaeededed
 
+/**
+ * phyr030[18:16] - Ron PU (PHY side)
+ * phyr030[14:12] - Ron PD (PHY side)
+ *   b'000 : disable
+ *   b'001 : 240 ohm
+ *   b'010 : 120 ohm
+ *   b'011 : 80 ohm
+ *   b'100 : 60 ohm
+ *   b'101 : 48 ohm
+ *   b'110 : 40 ohm
+ *   b'111 : 34 ohm (default)
+ */
+#define PHY_RON				((0x7 << 16) | (0x7 << 12))
+
+/**
+ * phyr030[10:8] - ODT configuration (PHY side)
+ *   b'000 : ODT disabled
+ *   b'001 : 240 ohm
+ *   b'010 : 120 ohm
+ *   b'011 : 80 ohm (default)
+ *   b'100 : 60 ohm
+ *   b'101 : 48 ohm
+ *   b'110 : 40 ohm
+ *   b'111 : 34 ohm
+ */
+#define PHY_ODT				(0x3 << 8)
+
+/**
+ * MR1[2:1] output driver impedance
+ *   b'00 : 34 ohm (default)
+ *   b'01 : 48 ohm
+ */
+#define DRAM_RON			(0x0 << 1)
+
+/**
+ * DRAM ODT - synchronous ODT mode
+ *   RTT_WR: disable
+ *   RTT_NOM = RTT_PARK
+ *
+ * MR1[10:8] RTT_NOM
+ *   b'000 : RTT_NOM disable
+ *   b'001 : 60 ohm
+ *   b'010 : 120 ohm
+ *   b'011 : 40 ohm
+ *   b'100 : 240 ohm
+ *   b'101 : 48 ohm  (default)
+ *   b'110 : 80 ohm
+ *   b'111 : 34 ohm
+ *
+ * MR5[8:6] RTT_PARK
+ *   b'000 : RTT_PARK disable
+ *   b'001 : 60 ohm
+ *   b'010 : 120 ohm
+ *   b'011 : 40 ohm
+ *   b'100 : 240 ohm
+ *   b'101 : 48 ohm  (default)
+ *   b'110 : 80 ohm
+ *   b'111 : 34 ohm
+ *
+ * MR2[11:9] RTT_WR
+ *   b'000 : Dynamic ODT off  (default)
+ *   b'001 : 120 ohm
+ *   b'010 : 240 ohm
+ *   b'011 : Hi-Z
+ *   b'100 : 80 ohm
+ */
+#define RTT_WR				(0x0 << 9)
+#define RTT_NOM				(0x5 << 8)
+#define RTT_PARK			(0x5 << 6)
+
+/**
+ * MR6[6] VrefDQ training range
+ *   b'0 : range 1
+ *   b'1 : range 2 (default)
+ */
+#define VREFDQ_RANGE_2			BIT(6)
+
+/**
+ * Latency setting:
+ * AL = PL = 0 (hardware fixed setting)
+ * -> WL = AL + CWL + PL = CWL
+ * -> RL = AL + CL + PL = CL
+ */
+#define CONFIG_WL			9
+#define CONFIG_RL			12
+#define T_RDDATA_EN			((CONFIG_RL - 2) << 8)
+#define T_PHY_WRLAT			(CONFIG_WL - 2)
+
+/* MR0 */
+#define MR0_CL_12			(BIT(4) | BIT(2))
+#define MR0_WR12_RTP6			BIT(9)
+#define MR0_DLL_RESET			BIT(8)
+#define MR0_VAL				(MR0_CL_12 | MR0_WR12_RTP6 | MR0_DLL_RESET)
+
+/* MR1 */
+#define MR1_VAL				(0x0001 | RTT_NOM | DRAM_RON)
+
+/* MR2 */
+#define MR2_CWL_9			0
+#define MR2_VAL				(0x0000 | RTT_WR | MR2_CWL_9)
+
+/* MR3 ~ MR6 */
+#define MR3_VAL				0x0000
+#define MR4_VAL				0x0000
+#define MR5_VAL				(0x0400 | RTT_PARK)
+#define MR6_VAL				0x0400
+
+/**
+ * The offset value applied to the DDR PHY write data eye training result
+ * to fine-tune the write DQ/DQS alignment
+ */
+#define WR_DATA_EYE_OFFSET		(0x10 << 8)
+
 #if defined(CONFIG_ASPEED_DDR4_800)
 u32 ast2600_sdramphy_config[165] = {
 	0x1e6e0100,	// start address
@@ -35,7 +149,7 @@
 	0x20000000,	// phyr024
 	0x00000008,	// phyr028
 	0x00000000,	// phyr02c
-	0x00077600,	// phyr030
+	(PHY_RON | PHY_ODT),	/* phyr030 */
 	0x00000000,	// phyr034
 	0x00000000,	// phyr038
 	0x20000000,	// phyr03c
@@ -44,18 +158,18 @@
 	0x00002f07,	// phyr048
 	0x00003080,	// phyr04c
 	0x04000000,	// phyr050
-	0x00000200,	// phyr054
-	0x03140201,	// phyr058
-	0x04800000,	// phyr05c
-	0x0800044e,	// phyr060
+	((MR3_VAL << 16) | MR2_VAL),	/* phyr054 */
+	((MR0_VAL << 16) | MR1_VAL),	/* phyr058 */
+	((MR5_VAL << 16) | MR4_VAL),	/* phyr05c */
+	((0x0800 << 16) | MR6_VAL | VREFDQ_RANGE_2 | 0xe), /* phyr060 */
 	0x00000000,	// phyr064
 	0x00180008,	// phyr068
 	0x00e00400,	// phyr06c
 	0x00140206,	// phyr070
 	0x1d4c0000,	// phyr074
-	0x493e0107,	// phyr078
+	(0x493e0100 | T_PHY_WRLAT),	/* phyr078 */
 	0x08060404,	// phyr07c
-	0x90000a00,	// phyr080
+	(0x90000000 | T_RDDATA_EN),	/* phyr080 */
 	0x06420618,	// phyr084
 	0x00001002,	// phyr088
 	0x05701016,	// phyr08c
@@ -94,7 +208,7 @@
 	0x20202020,	// phyr09c
 	0x20202020,	// phyr0a0
 	0x00002020,	// phyr0a4
-	0x80000000,	// phyr0a8
+	0x00000000,	/* phyr0a8 */
 	0x00000001,	// phyr0ac
 	0xaeeddeea,	// change address
 	0x1e6e0318,	// new address
@@ -154,7 +268,7 @@
 	0x20202020,	// phyr170
 	0xaeeddeea,	// change address
 	0x1e6e0298,	// new address
-	0x20200800,	// phyr198
+	0x20200000,	/* phyr198 */
 	0x20202020,	// phyr19c
 	0x20202020,	// phyr1a0
 	0x20202020,	// phyr1a4
@@ -177,7 +291,7 @@
 	0x00002020,	// phyr1e8
 	0xaeeddeea,	// change address
 	0x1e6e0304,	// new address
-	0x00000800,	// phyr204
+	(0x00000001 | WR_DATA_EYE_OFFSET), /* phyr204 */
 	0xaeeddeea,	// change address
 	0x1e6e027c,	// new address
 	0x4e400000,	// phyr17c
@@ -203,7 +317,7 @@
 	0x20000000,	// phyr024
 	0x00000008,	// phyr028
 	0x00000000,	// phyr02c
-	0x00077600,	// phyr030
+	(PHY_RON | PHY_ODT),	/* phyr030 */
 	0x00000000,	// phyr034
 	0x00000000,	// phyr038
 	0x20000000,	// phyr03c
@@ -212,18 +326,18 @@
 	0x00002f07,	// phyr048
 	0x00003080,	// phyr04c
 	0x04000000,	// phyr050
-	0x00000200,	// phyr054
-	0x03140501,	// phyr058-rtt:40
-	0x04800000,	// phyr05c
-	0x0800044e,	// phyr060
+	((MR3_VAL << 16) | MR2_VAL),	/* phyr054 */
+	((MR0_VAL << 16) | MR1_VAL),	/* phyr058 */
+	((MR5_VAL << 16) | MR4_VAL),	/* phyr05c */
+	((0x0800 << 16) | MR6_VAL | VREFDQ_RANGE_2 | 0xe), /* phyr060 */
 	0x00000000,	// phyr064
 	0x00180008,	// phyr068
 	0x00e00400,	// phyr06c
 	0x00140206,	// phyr070
 	0x1d4c0000,	// phyr074
-	0x493e0107,	// phyr078
+	(0x493e0100 | T_PHY_WRLAT),	/* phyr078 */
 	0x08060404,	// phyr07c
-	0x90000a00,	// phyr080
+	(0x90000000 | T_RDDATA_EN),	/* phyr080 */
 	0x06420c30,	// phyr084
 	0x00001002,	// phyr088
 	0x05701016,	// phyr08c
@@ -256,13 +370,13 @@
 	0x00000000,	// phyr200
 	0xaeeddeea,	// change address
 	0x1e6e0194,	// new address
-	0x801112e0,	// phyr094 - bit12=1,15=0,- write window is ok
+	0x801112e0,	// phyr094
 	0xaeeddeea,	// change address
 	0x1e6e019c,	// new address
 	0x20202020,	// phyr09c
 	0x20202020,	// phyr0a0
 	0x00002020,	// phyr0a4
-	0x80000000,	// phyr0a8
+	0x00000000,	/* phyr0a8 */
 	0x00000001,	// phyr0ac
 	0xaeeddeea,	// change address
 	0x1e6e0318,	// new address
@@ -322,7 +436,7 @@
 	0x20202020,	// phyr170
 	0xaeeddeea,	// change address
 	0x1e6e0298,	// new address
-	0x20200800,	// phyr198
+	0x20200000,	/* phyr198 */
 	0x20202020,	// phyr19c
 	0x20202020,	// phyr1a0
 	0x20202020,	// phyr1a4
@@ -345,7 +459,7 @@
 	0x00002020,	// phyr1e8
 	0xaeeddeea,	// change address
 	0x1e6e0304,	// new address
-	0x00000800,	// phyr204
+	(0x00000001 | WR_DATA_EYE_OFFSET), /* phyr204 */
 	0xaeeddeea,	// change address
 	0x1e6e027c,	// new address
 	0x4e400000,	// phyr17c
@@ -388,10 +502,10 @@
  * AC timing and SDRAM mode register setting
  * for real chip are derived from the model GDDR4-1600
  */
-#define DDR4_MR01_MODE	0x03010510
-#define DDR4_MR23_MODE	0x00000000
-#define DDR4_MR45_MODE	0x04000000
-#define DDR4_MR6_MODE	0x00000400
+#define DDR4_MR01_MODE	((MR1_VAL << 16) | MR0_VAL)
+#define DDR4_MR23_MODE	((MR3_VAL << 16) | MR2_VAL)
+#define DDR4_MR45_MODE	((MR5_VAL << 16) | MR4_VAL)
+#define DDR4_MR6_MODE	MR6_VAL
 #define DDR4_TRFC_1600	0x467299f1
 #define DDR4_TRFC_1333	0x3a5f80c9
 #define DDR4_TRFC_800	0x23394c78
@@ -449,7 +563,7 @@
 
 	while (1) {
 		data = readl(&regs->phy_ctrl[0]) & SDRAM_PHYCTRL0_INIT;
-		if (~data)
+		if (data == 0)
 			break;
 	}
 }
@@ -822,6 +936,7 @@
 static void ast2600_sdrammc_common_init(struct ast2600_sdrammc_regs *regs)
 {
 	int i;
+	u32 reg;
 
 	writel(MCR34_MREQI_DIS | MCR34_RESETN_DIS, &regs->power_ctrl);
 	writel(SDRAM_VIDEO_UNLOCK_KEY, &regs->gm_protection_key);
@@ -856,6 +971,13 @@
 	for (i = 0; i < ARRAY_SIZE(ddr4_ac_timing); ++i)
 		writel(ddr4_ac_timing[i], &regs->ac_timing[i]);
 
+	/* update CL and WL */
+	reg = readl(&regs->ac_timing[1]);
+	reg &= ~(SDRAM_WL_SETTING | SDRAM_CL_SETTING);
+	reg |= FIELD_PREP(SDRAM_WL_SETTING, CONFIG_WL - 5) |
+	       FIELD_PREP(SDRAM_CL_SETTING, CONFIG_RL - 5);
+	writel(reg, &regs->ac_timing[1]);
+
 	writel(DDR4_MR01_MODE, &regs->mr01_mode_setting);
 	writel(DDR4_MR23_MODE, &regs->mr23_mode_setting);
 	writel(DDR4_MR45_MODE, &regs->mr45_mode_setting);
@@ -984,11 +1106,6 @@
 L_ast2600_sdramphy_train:
 	ast2600_sdrammc_init_ddr4(priv);
 
-	/* make sure DDR-PHY is ready before access */
-	do {
-		reg = readl(priv->phy_status) & BIT(1);
-	} while (reg == 0);
-
 	if (ast2600_sdramphy_check_status(priv) != 0) {
 		printf("DDR4 PHY training fail, retrain\n");
 		goto L_ast2600_sdramphy_train;
diff --git a/drivers/serial/serial_meson.c b/drivers/serial/serial_meson.c
index c5ed3ed..d026f5a 100644
--- a/drivers/serial/serial_meson.c
+++ b/drivers/serial/serial_meson.c
@@ -7,9 +7,11 @@
 #include <dm.h>
 #include <errno.h>
 #include <fdtdec.h>
+#include <linux/kernel.h>
 #include <linux/bitops.h>
 #include <linux/compiler.h>
 #include <serial.h>
+#include <clk.h>
 
 struct meson_uart {
 	u32 wfifo;
@@ -17,6 +19,7 @@
 	u32 control;
 	u32 status;
 	u32 misc;
+	u32 reg5; /* New baud control register */
 };
 
 struct meson_serial_plat {
@@ -42,6 +45,35 @@
 #define AML_UART_RX_RST			BIT(23)
 #define AML_UART_CLR_ERR		BIT(24)
 
+/* AML_UART_REG5 bits */
+#define AML_UART_REG5_XTAL_DIV2		BIT(27)
+#define AML_UART_REG5_XTAL_CLK_SEL	BIT(26) /* default 0 (div by 3), 1 for no div */
+#define AML_UART_REG5_USE_XTAL_CLK	BIT(24) /* default 1 (use crystal as clock source) */
+#define AML_UART_REG5_USE_NEW_BAUD	BIT(23) /* default 1 (use new baud rate register) */
+#define AML_UART_REG5_BAUD_MASK		0x7fffff
+
+static u32 meson_calc_baud_divisor(ulong src_rate, u32 baud)
+{
+	/*
+	 * Usually src_rate is 24 MHz (from crystal) as clock source for serial
+	 * device. Since 8 Mb/s is the maximum supported baud rate, use div by 3
+	 * to derive baud rate. This choice is used also in meson_serial_setbrg.
+	 */
+	return DIV_ROUND_CLOSEST(src_rate / 3, baud) - 1;
+}
+
+static void meson_serial_set_baud(struct meson_uart *uart, ulong src_rate, u32 baud)
+{
+	/*
+	 * Set crystal divided by 3 (regardless of device tree clock property)
+	 * as clock source and the corresponding divisor to approximate baud
+	 */
+	u32 divisor = meson_calc_baud_divisor(src_rate, baud);
+	u32 val = AML_UART_REG5_USE_XTAL_CLK | AML_UART_REG5_USE_NEW_BAUD |
+		(divisor & AML_UART_REG5_BAUD_MASK);
+	writel(val, &uart->reg5);
+}
+
 static void meson_serial_init(struct meson_uart *uart)
 {
 	u32 val;
@@ -59,7 +91,14 @@
 {
 	struct meson_serial_plat *plat = dev_get_plat(dev);
 	struct meson_uart *const uart = plat->reg;
+	struct clk per_clk;
+	int ret = clk_get_by_name(dev, "baud", &per_clk);
 
+	if (ret)
+		return ret;
+	ulong rate = clk_get_rate(&per_clk);
+
+	meson_serial_set_baud(uart, rate, CONFIG_BAUDRATE);
 	meson_serial_init(uart);
 
 	return 0;
@@ -111,6 +150,36 @@
 	return 0;
 }
 
+static int meson_serial_setbrg(struct udevice *dev, const int baud)
+{
+	/*
+	 * Change device baud rate if baud is reasonable (considering a 23 bit
+	 * counter with an 8 MHz clock input) and the actual baud
+	 * rate is within 2% of the requested value (2% is arbitrary).
+	 */
+	if (baud < 1 || baud > 8000000)
+		return -EINVAL;
+
+	struct meson_serial_plat *const plat = dev_get_plat(dev);
+	struct meson_uart *const uart = plat->reg;
+	struct clk per_clk;
+	int ret = clk_get_by_name(dev, "baud", &per_clk);
+
+	if (ret)
+		return ret;
+	ulong rate = clk_get_rate(&per_clk);
+	u32 divisor = meson_calc_baud_divisor(rate, baud);
+	u32 calc_baud = (rate / 3) / (divisor + 1);
+	u32 calc_err = baud > calc_baud ? baud - calc_baud : calc_baud - baud;
+
+	if (((calc_err * 100) / baud) > 2)
+		return -EINVAL;
+
+	meson_serial_set_baud(uart, rate, baud);
+
+	return 0;
+}
+
 static int meson_serial_pending(struct udevice *dev, bool input)
 {
 	struct meson_serial_plat *plat = dev_get_plat(dev);
@@ -154,6 +223,7 @@
 	.putc = meson_serial_putc,
 	.pending = meson_serial_pending,
 	.getc = meson_serial_getc,
+	.setbrg = meson_serial_setbrg,
 };
 
 static const struct udevice_id meson_serial_ids[] = {
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index 292dc41..acf555b 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -32,6 +32,14 @@
 	  This allows other drivers to verify the SoC familiy & revision using
 	  matching SoC attributes.
 
+config SOC_XILINX_VERSAL_NET
+	bool "Enable SoC Device ID driver for Xilinx Versal NET"
+	depends on SOC_DEVICE && ARCH_VERSAL_NET
+	help
+	  Enable this option to select SoC device id driver for Xilinx Versal NET.
+	  This allows other drivers to verify the SoC familiy & revision using
+	  matching SoC attributes.
+
 source "drivers/soc/ti/Kconfig"
 
 endmenu
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 031fa76..8438565 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -8,3 +8,4 @@
 obj-$(CONFIG_SANDBOX) += soc_sandbox.o
 obj-$(CONFIG_SOC_XILINX_ZYNQMP) += soc_xilinx_zynqmp.o
 obj-$(CONFIG_SOC_XILINX_VERSAL) += soc_xilinx_versal.o
+obj-$(CONFIG_SOC_XILINX_VERSAL_NET) += soc_xilinx_versal_net.o
diff --git a/drivers/soc/soc_xilinx_versal_net.c b/drivers/soc/soc_xilinx_versal_net.c
new file mode 100644
index 0000000..146d068
--- /dev/null
+++ b/drivers/soc/soc_xilinx_versal_net.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Versal NET SOC driver
+ *
+ * Copyright (C) 2022, Advanced Micro Devices, Inc.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <soc.h>
+#include <zynqmp_firmware.h>
+#include <asm/io.h>
+#include <asm/arch/hardware.h>
+
+#include <linux/bitfield.h>
+
+/*
+ * v1 -> 0x10 - ES1
+ * v2 -> 0x20 - Production
+ */
+static const char versal_family[] = "Versal NET";
+
+struct soc_xilinx_versal_net_priv {
+	const char *family;
+	char revision;
+};
+
+static int soc_xilinx_versal_net_get_family(struct udevice *dev, char *buf, int size)
+{
+	struct soc_xilinx_versal_net_priv *priv = dev_get_priv(dev);
+
+	return snprintf(buf, size, "%s", priv->family);
+}
+
+static int soc_xilinx_versal_net_get_revision(struct udevice *dev, char *buf, int size)
+{
+	struct soc_xilinx_versal_net_priv *priv = dev_get_priv(dev);
+
+	return snprintf(buf, size, "v%d", priv->revision);
+}
+
+static const struct soc_ops soc_xilinx_versal_net_ops = {
+	.get_family = soc_xilinx_versal_net_get_family,
+	.get_revision = soc_xilinx_versal_net_get_revision,
+};
+
+static int soc_xilinx_versal_net_probe(struct udevice *dev)
+{
+	struct soc_xilinx_versal_net_priv *priv = dev_get_priv(dev);
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+	int ret;
+
+	priv->family = versal_family;
+
+	if (IS_ENABLED(CONFIG_ZYNQMP_FIRMWARE)) {
+		ret = xilinx_pm_request(PM_GET_CHIPID, 0, 0, 0, 0,
+					ret_payload);
+		if (ret)
+			return ret;
+	} else {
+		ret_payload[2] = readl(PMC_TAP_VERSION);
+		if (!ret_payload[2])
+			return -EINVAL;
+	}
+
+	priv->revision = FIELD_GET(PS_VERSION_MASK, ret_payload[2]);
+
+	return 0;
+}
+
+U_BOOT_DRIVER(soc_xilinx_versal_net) = {
+	.name		= "soc_xilinx_versal_net",
+	.id		= UCLASS_SOC,
+	.ops		= &soc_xilinx_versal_net_ops,
+	.probe		= soc_xilinx_versal_net_probe,
+	.priv_auto	= sizeof(struct soc_xilinx_versal_net_priv),
+	.flags		= DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/sound/sandbox.c b/drivers/sound/sandbox.c
index 4a2c87a..c6cbd81 100644
--- a/drivers/sound/sandbox.c
+++ b/drivers/sound/sandbox.c
@@ -29,6 +29,7 @@
 struct sandbox_sound_priv {
 	int setup_called;	/* Incremented when setup() method is called */
 	bool active;		/* TX data is being sent */
+	int count;		/* Use to count the provided audio data */
 	int sum;		/* Use to sum the provided audio data */
 	bool allow_beep;	/* true to allow the start_beep() interface */
 	int frequency_hz;	/* Beep frequency if active, else 0 */
@@ -68,6 +69,13 @@
 	return priv->active;
 }
 
+int sandbox_get_sound_count(struct udevice *dev)
+{
+	struct sandbox_sound_priv *priv = dev_get_priv(dev);
+
+	return priv->count;
+}
+
 int sandbox_get_sound_sum(struct udevice *dev)
 {
 	struct sandbox_sound_priv *priv = dev_get_priv(dev);
@@ -168,6 +176,7 @@
 
 	for (i = 0; i < data_size; i++)
 		priv->sum += ((uint8_t *)data)[i];
+	priv->count += data_size;
 
 	return i2s_tx_data(uc_priv->i2s, data, data_size);
 }
diff --git a/drivers/sound/sound.c b/drivers/sound/sound.c
index 041dfdc..c0fc50c 100644
--- a/drivers/sound/sound.c
+++ b/drivers/sound/sound.c
@@ -15,7 +15,10 @@
 	const int period = freq ? sample_rate / freq : 0;
 	const int half = period / 2;
 
-	assert(freq);
+	if (!half) {
+		memset(data, 0, size);
+		return;
+	}
 
 	/* Make sure we don't overflow our buffer */
 	if (size % 2)
diff --git a/drivers/spi/cadence_ospi_versal.c b/drivers/spi/cadence_ospi_versal.c
index a9547a8..e0d5e6b 100644
--- a/drivers/spi/cadence_ospi_versal.c
+++ b/drivers/spi/cadence_ospi_versal.c
@@ -182,11 +182,11 @@
 
 	/* set direction as output */
 	writel((readl(BOOT_MODE_DIR) | BIT(FLASH_RESET_GPIO)),
-	       BOOT_MODE_POR_0);
+	       BOOT_MODE_DIR);
 
 	/* Data output enable */
 	writel((readl(BOOT_MODE_OUT) | BIT(FLASH_RESET_GPIO)),
-	       BOOT_MODE_POR_1);
+	       BOOT_MODE_OUT);
 
 	/* IOU SLCR write enable */
 	writel(0, WPROT_PMC_MIO);
diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c
index cfae5dc..d1f8913 100644
--- a/drivers/spi/cadence_qspi_apb.c
+++ b/drivers/spi/cadence_qspi_apb.c
@@ -735,8 +735,7 @@
 	void *buf = op->data.buf.in;
 	size_t len = op->data.nbytes;
 
-	if (CONFIG_IS_ENABLED(ARCH_VERSAL))
-		cadence_qspi_apb_enable_linear_mode(true);
+	cadence_qspi_apb_enable_linear_mode(true);
 
 	if (priv->use_dac_mode && (from + len < priv->ahbsize)) {
 		if (len < 256 ||
@@ -905,9 +904,6 @@
 	const void *buf = op->data.buf.out;
 	size_t len = op->data.nbytes;
 
-	if (CONFIG_IS_ENABLED(ARCH_VERSAL))
-		cadence_qspi_apb_enable_linear_mode(true);
-
 	/*
 	 * Some flashes like the Cypress Semper flash expect a dummy 4-byte
 	 * address (all 0s) with the read status register command in DTR mode.
diff --git a/drivers/spi/zynqmp_gqspi.c b/drivers/spi/zynqmp_gqspi.c
index 49facc4..335b458 100644
--- a/drivers/spi/zynqmp_gqspi.c
+++ b/drivers/spi/zynqmp_gqspi.c
@@ -94,7 +94,7 @@
 
 #define GQSPI_BAUD_DIV_SHIFT		2
 #define GQSPI_LPBK_DLY_ADJ_LPBK_SHIFT	5
-#define GQSPI_LPBK_DLY_ADJ_DLY_1	0x2
+#define GQSPI_LPBK_DLY_ADJ_DLY_1	0x1
 #define GQSPI_LPBK_DLY_ADJ_DLY_1_SHIFT	3
 #define GQSPI_LPBK_DLY_ADJ_DLY_0	0x3
 #define GQSPI_USE_DATA_DLY		0x1
@@ -104,7 +104,8 @@
 #define TAP_DLY_BYPASS_LQSPI_RX_VALUE	0x1
 #define TAP_DLY_BYPASS_LQSPI_RX_SHIFT	2
 #define GQSPI_DATA_DLY_ADJ_OFST		0x000001F8
-#define IOU_TAPDLY_BYPASS_OFST !IS_ENABLED(CONFIG_ARCH_VERSAL) ? \
+#define IOU_TAPDLY_BYPASS_OFST !(IS_ENABLED(CONFIG_ARCH_VERSAL) || \
+				 IS_ENABLED(CONFIG_ARCH_VERSAL_NET)) ? \
 				0xFF180390 : 0xF103003C
 #define GQSPI_LPBK_DLY_ADJ_LPBK_MASK	0x00000020
 #define GQSPI_FREQ_37_5MHZ		37500000
@@ -661,7 +662,7 @@
 static int zynqmp_qspi_start_dma(struct zynqmp_qspi_priv *priv,
 				 u32 gen_fifo_cmd, u32 *buf)
 {
-	u32 addr;
+	unsigned long addr;
 	u32 size;
 	u32 actuallen = priv->len;
 	u32 totallen = priv->len;
@@ -677,7 +678,9 @@
 		totallen -= priv->len; /* Save remaining bytes length to read */
 		actuallen = priv->len; /* Actual number of bytes reading */
 
-		writel((unsigned long)buf, &dma_regs->dmadst);
+		writel(lower_32_bits((unsigned long)buf), &dma_regs->dmadst);
+		writel(upper_32_bits((unsigned long)buf) & GENMASK(11, 0),
+							&dma_regs->dmadstmsb);
 		writel(roundup(priv->len, GQSPI_DMA_ALIGN), &dma_regs->dmasize);
 		writel(GQSPI_DMA_DST_I_STS_MASK, &dma_regs->dmaier);
 		addr = (unsigned long)buf;
diff --git a/drivers/timer/timer-uclass.c b/drivers/timer/timer-uclass.c
index cbc3647..bb71979 100644
--- a/drivers/timer/timer-uclass.c
+++ b/drivers/timer/timer-uclass.c
@@ -18,6 +18,7 @@
 #include <init.h>
 #include <timer.h>
 #include <linux/err.h>
+#include <relocate.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -32,7 +33,7 @@
 
 int notrace timer_get_count(struct udevice *dev, u64 *count)
 {
-	const struct timer_ops *ops = device_get_ops(dev);
+	struct timer_ops *ops = timer_get_ops(dev);
 
 	if (!ops->get_count)
 		return -ENOSYS;
@@ -50,6 +51,19 @@
 
 static int timer_pre_probe(struct udevice *dev)
 {
+	if (IS_ENABLED(CONFIG_NEEDS_MANUAL_RELOC) &&
+	    (gd->flags & GD_FLG_RELOC)) {
+		struct timer_ops *ops = timer_get_ops(dev);
+		static int reloc_done;
+
+		if (!reloc_done) {
+			if (ops->get_count)
+				MANUAL_RELOC(ops->get_count);
+
+			reloc_done++;
+		}
+	}
+
 	if (CONFIG_IS_ENABLED(OF_REAL)) {
 		struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
 		struct clk timer_clk;
diff --git a/drivers/timer/xilinx-timer.c b/drivers/timer/xilinx-timer.c
index 75b4473..172fd9f 100644
--- a/drivers/timer/xilinx-timer.c
+++ b/drivers/timer/xilinx-timer.c
@@ -40,7 +40,7 @@
 
 	regmap_read(priv->regs, TIMER_COUNTER_OFFSET, &value);
 
-	return value;
+	return timer_conv_64(value);
 }
 
 static int xilinx_timer_probe(struct udevice *dev)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index b592a48..49f6a19 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -14,6 +14,7 @@
  */
 
 #include <common.h>
+#include <clk.h>
 #include <cpu_func.h>
 #include <malloc.h>
 #include <dwc3-uboot.h>
@@ -28,6 +29,8 @@
 #include <generic-phy.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
+#include <linux/bitfield.h>
+#include <linux/math64.h>
 
 #include "core.h"
 #include "gadget.h"
@@ -35,6 +38,8 @@
 
 #include "linux-compat.h"
 
+#define NSEC_PER_SEC	1000000000L
+
 static LIST_HEAD(dwc3_list);
 /* -------------------------------------------------------------------------- */
 
@@ -115,6 +120,73 @@
 }
 
 /**
+ * dwc3_ref_clk_period - Reference clock period configuration
+ *		Default reference clock period depends on hardware
+ *		configuration. For systems with reference clock that differs
+ *		from the default, this will set clock period in DWC3_GUCTL
+ *		register.
+ * @dwc: Pointer to our controller context structure
+ * @ref_clk_per: reference clock period in ns
+ */
+static void dwc3_ref_clk_period(struct dwc3 *dwc)
+{
+	unsigned long period;
+	unsigned long fladj;
+	unsigned long decr;
+	unsigned long rate;
+	u32 reg;
+
+	if (dwc->ref_clk) {
+		rate = clk_get_rate(dwc->ref_clk);
+		if (!rate)
+			return;
+		period = NSEC_PER_SEC / rate;
+	} else {
+		return;
+	}
+
+	reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
+	reg &= ~DWC3_GUCTL_REFCLKPER_MASK;
+	reg |=  FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period);
+	dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
+
+	if (dwc->revision <= DWC3_REVISION_250A)
+		return;
+
+	/*
+	 * The calculation below is
+	 *
+	 * 125000 * (NSEC_PER_SEC / (rate * period) - 1)
+	 *
+	 * but rearranged for fixed-point arithmetic. The division must be
+	 * 64-bit because 125000 * NSEC_PER_SEC doesn't fit in 32 bits (and
+	 * neither does rate * period).
+	 *
+	 * Note that rate * period ~= NSEC_PER_SECOND, minus the number of
+	 * nanoseconds of error caused by the truncation which happened during
+	 * the division when calculating rate or period (whichever one was
+	 * derived from the other). We first calculate the relative error, then
+	 * scale it to units of 8 ppm.
+	 */
+	fladj = div64_u64(125000ULL * NSEC_PER_SEC, (u64)rate * period);
+	fladj -= 125000;
+
+	/*
+	 * The documented 240MHz constant is scaled by 2 to get PLS1 as well.
+	 */
+	decr = 480000000 / rate;
+
+	reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
+	reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK
+	    &  ~DWC3_GFLADJ_240MHZDECR
+	    &  ~DWC3_GFLADJ_240MHZDECR_PLS1;
+	reg |= FIELD_PREP(DWC3_GFLADJ_REFCLK_FLADJ_MASK, fladj)
+	    |  FIELD_PREP(DWC3_GFLADJ_240MHZDECR, decr >> 1)
+	    |  FIELD_PREP(DWC3_GFLADJ_240MHZDECR_PLS1, decr & 1);
+	dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
+}
+
+/**
  * dwc3_free_one_event_buffer - Frees one event buffer
  * @dwc: Pointer to our controller context structure
  * @evt: Pointer to event buffer to be freed
@@ -640,6 +712,9 @@
 	/* Adjust Frame Length */
 	dwc3_frame_length_adjustment(dwc, dwc->fladj);
 
+	/* Adjust Reference Clock Period */
+	dwc3_ref_clk_period(dwc);
+
 	dwc3_set_incr_burst_type(dwc);
 
 	return 0;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index d7cce3a..532746d 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -248,6 +248,13 @@
 /* Global Frame Length Adjustment Register */
 #define DWC3_GFLADJ_30MHZ_SDBND_SEL		(1 << 7)
 #define DWC3_GFLADJ_30MHZ_MASK			0x3f
+#define DWC3_GFLADJ_REFCLK_FLADJ_MASK		GENMASK(21, 8)
+#define DWC3_GFLADJ_240MHZDECR			GENMASK(30, 24)
+#define DWC3_GFLADJ_240MHZDECR_PLS1		BIT(31)
+
+/* Global User Control Register*/
+#define DWC3_GUCTL_REFCLKPER_MASK		0xffc00000
+#define DWC3_GUCTL_REFCLKPER_SEL		22
 
 /* Device Configuration Register */
 #define DWC3_DCFG_DEVADDR(addr)	((addr) << 3)
@@ -668,8 +675,10 @@
  * @event_buffer_list: a list of event buffers
  * @gadget: device side representation of the peripheral controller
  * @gadget_driver: pointer to the gadget driver
+ * @ref_clk: reference clock
  * @regs: base address for our registers
  * @regs_size: address space size
+ * @ref_clk_per: reference clock period configuration
  * @nr_scratch: number of scratch buffers
  * @num_event_buffers: calculated number of event buffers
  * @u1u2: only used on revisions <1.83a for workaround
@@ -766,6 +775,8 @@
 	struct usb_gadget	gadget;
 	struct usb_gadget_driver *gadget_driver;
 
+	struct clk		*ref_clk;
+
 	void __iomem		*regs;
 	size_t			regs_size;
 
@@ -829,6 +840,7 @@
 	u8			lpm_nyet_threshold;
 	u8			hird_threshold;
 	u32			fladj;
+	u32			ref_clk_per;
 	u8			incrx_mode;
 	u32			incrx_size;
 
diff --git a/drivers/usb/dwc3/dwc3-generic.c b/drivers/usb/dwc3/dwc3-generic.c
index 466b25a..7896671 100644
--- a/drivers/usb/dwc3/dwc3-generic.c
+++ b/drivers/usb/dwc3/dwc3-generic.c
@@ -59,12 +59,21 @@
 	struct dwc3_generic_plat *plat = dev_get_plat(dev);
 	struct dwc3 *dwc3 = &priv->dwc3;
 	struct dwc3_glue_data *glue = dev_get_plat(dev->parent);
+	int __maybe_unused index;
+	ofnode __maybe_unused node;
 
 	dwc3->dev = dev;
 	dwc3->maximum_speed = plat->maximum_speed;
 	dwc3->dr_mode = plat->dr_mode;
 #if CONFIG_IS_ENABLED(OF_CONTROL)
 	dwc3_of_parse(dwc3);
+
+	node = dev_ofnode(dev->parent);
+	index = ofnode_stringlist_search(node, "clock-names", "ref");
+	if (index < 0)
+		index = ofnode_stringlist_search(node, "clock-names", "ref_clk");
+	if (index >= 0)
+		dwc3->ref_clk = &glue->clks.clks[index];
 #endif
 
 	/*
diff --git a/drivers/usb/emul/sandbox_flash.c b/drivers/usb/emul/sandbox_flash.c
index 6e8cfe1..01ccc4b 100644
--- a/drivers/usb/emul/sandbox_flash.c
+++ b/drivers/usb/emul/sandbox_flash.c
@@ -188,15 +188,19 @@
 	struct scsi_emul_info *info = &priv->eminfo;
 	const struct scsi_cmd *req = buff;
 	int ret;
+	off_t offset;
 
 	ret = sb_scsi_emul_command(info, req, len);
 	if (!ret) {
 		setup_response(priv);
 	} else if ((ret == SCSI_EMUL_DO_READ || ret == SCSI_EMUL_DO_WRITE) &&
 		   priv->fd != -1) {
-		os_lseek(priv->fd, info->seek_block * info->block_size,
-			 OS_SEEK_SET);
-		setup_response(priv);
+		offset = os_lseek(priv->fd, info->seek_block * info->block_size,
+				  OS_SEEK_SET);
+		if (offset == (off_t)-1)
+			setup_fail_response(priv);
+		else
+			setup_response(priv);
 	} else {
 		setup_fail_response(priv);
 	}
diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c
index 27e2fc6..060f344 100644
--- a/drivers/usb/host/usb-uclass.c
+++ b/drivers/usb/host/usb-uclass.c
@@ -557,7 +557,7 @@
 	struct usb_driver_entry *start, *entry;
 	int n_ents;
 	int ret;
-	char name[30], *str;
+	char name[34], *str;
 	ofnode node = usb_get_ofnode(parent, port);
 
 	*devp = NULL;
diff --git a/drivers/watchdog/designware_wdt.c b/drivers/watchdog/designware_wdt.c
index cad756a..f8df191 100644
--- a/drivers/watchdog/designware_wdt.c
+++ b/drivers/watchdog/designware_wdt.c
@@ -72,13 +72,13 @@
 static int designware_wdt_stop(struct udevice *dev)
 {
 	struct designware_wdt_priv *priv = dev_get_priv(dev);
+	__maybe_unused int ret;
 
 	designware_wdt_reset(dev);
 	writel(0, priv->base + DW_WDT_CR);
 
-        if (CONFIG_IS_ENABLED(DM_RESET)) {
-		int ret;
-
+	if (CONFIG_IS_ENABLED(DM_RESET) &&
+	    ofnode_read_prop(dev_ofnode(dev), "resets", &ret)) {
 		ret = reset_assert_bulk(&priv->resets);
 		if (ret)
 			return ret;
@@ -135,7 +135,8 @@
 	priv->clk_khz = CONFIG_DW_WDT_CLOCK_KHZ;
 #endif
 
-	if (CONFIG_IS_ENABLED(DM_RESET)) {
+	if (CONFIG_IS_ENABLED(DM_RESET) &&
+	    ofnode_read_prop(dev_ofnode(dev), "resets", &ret)) {
 		ret = reset_get_bulk(dev, &priv->resets);
 		if (ret)
 			goto err;
diff --git a/drivers/watchdog/wdt-uclass.c b/drivers/watchdog/wdt-uclass.c
index 82df0ff..509896a 100644
--- a/drivers/watchdog/wdt-uclass.c
+++ b/drivers/watchdog/wdt-uclass.c
@@ -105,7 +105,6 @@
 		init_watchdog_dev(dev);
 	}
 
-	gd->flags |= GD_FLG_WDT_READY;
 	return 0;
 }
 
diff --git a/fs/ubifs/Kconfig b/fs/ubifs/Kconfig
index 9da35b8..949b288 100644
--- a/fs/ubifs/Kconfig
+++ b/fs/ubifs/Kconfig
@@ -4,3 +4,11 @@
 	help
 	  Make the verbose messages from UBIFS stop printing. This leaves
 	  warnings and errors enabled.
+
+config UBIFS_SILENCE_DEBUG_DUMP
+	bool "UBIFS silence debug dumps"
+	default y if UBIFS_SILENCE_MSG
+	default n
+	help
+	  Make the debug dumps from UBIFS stop printing.
+	  This decreases size of U-Boot binary.
diff --git a/fs/ubifs/debug.c b/fs/ubifs/debug.c
index 2ff8f1a..bede7d0 100644
--- a/fs/ubifs/debug.c
+++ b/fs/ubifs/debug.c
@@ -35,6 +35,7 @@
 static DEFINE_SPINLOCK(dbg_lock);
 #endif
 
+#ifndef CONFIG_UBIFS_SILENCE_DEBUG_DUMP
 static const char *get_key_fmt(int fmt)
 {
 	switch (fmt) {
@@ -230,6 +231,7 @@
 	       (unsigned long long)le64_to_cpu(ch->sqnum));
 	pr_err("\tlen            %u\n", le32_to_cpu(ch->len));
 }
+#endif
 
 void ubifs_dump_inode(struct ubifs_info *c, const struct inode *inode)
 {
@@ -303,6 +305,7 @@
 
 void ubifs_dump_node(const struct ubifs_info *c, const void *node)
 {
+#ifndef CONFIG_UBIFS_SILENCE_DEBUG_DUMP
 	int i, n;
 	union ubifs_key key;
 	const struct ubifs_ch *ch = node;
@@ -546,10 +549,12 @@
 		       (int)ch->node_type);
 	}
 	spin_unlock(&dbg_lock);
+#endif
 }
 
 void ubifs_dump_budget_req(const struct ubifs_budget_req *req)
 {
+#ifndef CONFIG_UBIFS_SILENCE_DEBUG_DUMP
 	spin_lock(&dbg_lock);
 	pr_err("Budgeting request: new_ino %d, dirtied_ino %d\n",
 	       req->new_ino, req->dirtied_ino);
@@ -563,10 +568,12 @@
 	pr_err("\tdata_growth %d dd_growth     %d\n",
 	       req->data_growth, req->dd_growth);
 	spin_unlock(&dbg_lock);
+#endif
 }
 
 void ubifs_dump_lstats(const struct ubifs_lp_stats *lst)
 {
+#ifndef CONFIG_UBIFS_SILENCE_DEBUG_DUMP
 	spin_lock(&dbg_lock);
 	pr_err("(pid %d) Lprops statistics: empty_lebs %d, idx_lebs  %d\n",
 	       current->pid, lst->empty_lebs, lst->idx_lebs);
@@ -575,6 +582,7 @@
 	pr_err("\ttotal_used %lld, total_dark %lld, total_dead %lld\n",
 	       lst->total_used, lst->total_dark, lst->total_dead);
 	spin_unlock(&dbg_lock);
+#endif
 }
 
 #ifndef __UBOOT__
@@ -653,6 +661,7 @@
 
 void ubifs_dump_lprop(const struct ubifs_info *c, const struct ubifs_lprops *lp)
 {
+#ifndef CONFIG_UBIFS_SILENCE_DEBUG_DUMP
 	int i, spc, dark = 0, dead = 0;
 	struct rb_node *rb;
 	struct ubifs_bud *bud;
@@ -740,6 +749,7 @@
 	if (lp->lnum == c->gc_lnum)
 		pr_cont(", GC LEB");
 	pr_cont(")\n");
+#endif
 }
 
 void ubifs_dump_lprops(struct ubifs_info *c)
@@ -766,6 +776,7 @@
 
 void ubifs_dump_lpt_info(struct ubifs_info *c)
 {
+#ifndef CONFIG_UBIFS_SILENCE_DEBUG_DUMP
 	int i;
 
 	spin_lock(&dbg_lock);
@@ -800,11 +811,13 @@
 		       i + c->lpt_first, c->ltab[i].free, c->ltab[i].dirty,
 		       c->ltab[i].tgc, c->ltab[i].cmt);
 	spin_unlock(&dbg_lock);
+#endif
 }
 
 void ubifs_dump_sleb(const struct ubifs_info *c,
 		     const struct ubifs_scan_leb *sleb, int offs)
 {
+#ifndef CONFIG_UBIFS_SILENCE_DEBUG_DUMP
 	struct ubifs_scan_node *snod;
 
 	pr_err("(pid %d) start dumping scanned data from LEB %d:%d\n",
@@ -816,10 +829,12 @@
 		       sleb->lnum, snod->offs, snod->len);
 		ubifs_dump_node(c, snod->node);
 	}
+#endif
 }
 
 void ubifs_dump_leb(const struct ubifs_info *c, int lnum)
 {
+#ifndef CONFIG_UBIFS_SILENCE_DEBUG_DUMP
 	struct ubifs_scan_leb *sleb;
 	struct ubifs_scan_node *snod;
 	void *buf;
@@ -854,11 +869,13 @@
 out:
 	vfree(buf);
 	return;
+#endif
 }
 
 void ubifs_dump_znode(const struct ubifs_info *c,
 		      const struct ubifs_znode *znode)
 {
+#ifndef CONFIG_UBIFS_SILENCE_DEBUG_DUMP
 	int n;
 	const struct ubifs_zbranch *zbr;
 	char key_buf[DBG_KEY_BUF_LEN];
@@ -893,10 +910,12 @@
 						DBG_KEY_BUF_LEN));
 	}
 	spin_unlock(&dbg_lock);
+#endif
 }
 
 void ubifs_dump_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat)
 {
+#ifndef CONFIG_UBIFS_SILENCE_DEBUG_DUMP
 	int i;
 
 	pr_err("(pid %d) start dumping heap cat %d (%d elements)\n",
@@ -909,11 +928,13 @@
 		       lprops->dirty, lprops->flags);
 	}
 	pr_err("(pid %d) finish dumping heap\n", current->pid);
+#endif
 }
 
 void ubifs_dump_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode,
 		      struct ubifs_nnode *parent, int iip)
 {
+#ifndef CONFIG_UBIFS_SILENCE_DEBUG_DUMP
 	int i;
 
 	pr_err("(pid %d) dumping pnode:\n", current->pid);
@@ -927,10 +948,12 @@
 		pr_err("\t%d: free %d dirty %d flags %d lnum %d\n",
 		       i, lp->free, lp->dirty, lp->flags, lp->lnum);
 	}
+#endif
 }
 
 void ubifs_dump_tnc(struct ubifs_info *c)
 {
+#ifndef CONFIG_UBIFS_SILENCE_DEBUG_DUMP
 	struct ubifs_znode *znode;
 	int level;
 
@@ -948,14 +971,17 @@
 		znode = ubifs_tnc_levelorder_next(c->zroot.znode, znode);
 	}
 	pr_err("(pid %d) finish dumping TNC tree\n", current->pid);
+#endif
 }
 
+#ifndef CONFIG_UBIFS_SILENCE_DEBUG_DUMP
 static int dump_znode(struct ubifs_info *c, struct ubifs_znode *znode,
 		      void *priv)
 {
 	ubifs_dump_znode(c, znode);
 	return 0;
 }
+#endif
 
 /**
  * ubifs_dump_index - dump the on-flash index.
@@ -966,7 +992,9 @@
  */
 void ubifs_dump_index(struct ubifs_info *c)
 {
+#ifndef CONFIG_UBIFS_SILENCE_DEBUG_DUMP
 	dbg_walk_index(c, NULL, dump_znode, NULL);
+#endif
 }
 
 #ifndef __UBOOT__
diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h
index 8882912..da17ac8 100644
--- a/include/asm-generic/global_data.h
+++ b/include/asm-generic/global_data.h
@@ -635,9 +635,9 @@
 	 */
 	GD_FLG_LOG_READY = 0x10000,
 	/**
-	 * @GD_FLG_WDT_READY: watchdog is ready for use
+	 * @GD_FLG_CYCLIC_RUNNING: cyclic_run is in progress
 	 */
-	GD_FLG_WDT_READY = 0x20000,
+	GD_FLG_CYCLIC_RUNNING = 0x20000,
 	/**
 	 * @GD_FLG_SKIP_LL_INIT: don't perform low-level initialization
 	 */
@@ -650,10 +650,6 @@
 	 * @GD_FLG_FDT_CHANGED: Device tree change has been detected by tests
 	 */
 	GD_FLG_FDT_CHANGED = 0x100000,
-	/**
-	 * @GD_FLG_CYCLIC_RUNNING: cyclic_run is in progress
-	 */
-	GD_FLG_CYCLIC_RUNNING = 0x200000,
 };
 
 #endif /* __ASSEMBLY__ */
diff --git a/include/configs/meson64.h b/include/configs/meson64.h
index 0c41df2..726f33c 100644
--- a/include/configs/meson64.h
+++ b/include/configs/meson64.h
@@ -16,6 +16,13 @@
 #define GICC_BASE			0xc4302000
 #endif
 
+/* Serial drivers */
+/* The following table includes the supported baudrates */
+#define CONFIG_SYS_BAUDRATE_TABLE  \
+	{300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, \
+		230400, 250000, 460800, 500000, 1000000, 2000000, 4000000, \
+		8000000 }
+
 /* For splashscreen */
 #ifdef CONFIG_VIDEO
 #define STDOUT_CFG "vidconsole,serial"
diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h
index 5168e2f..0dcb2eb 100644
--- a/include/configs/sandbox.h
+++ b/include/configs/sandbox.h
@@ -10,19 +10,6 @@
 
 #define CONFIG_MALLOC_F_ADDR		0x0010000
 
-/* GUIDs for capsule updatable firmware images */
-#define SANDBOX_UBOOT_IMAGE_GUID \
-	EFI_GUID(0x09d7cf52, 0x0720, 0x4710, 0x91, 0xd1, \
-		 0x08, 0x46, 0x9b, 0x7f, 0xe9, 0xc8)
-
-#define SANDBOX_UBOOT_ENV_IMAGE_GUID \
-	EFI_GUID(0x5a7021f5, 0xfef2, 0x48b4, 0xaa, 0xba, \
-		 0x83, 0x2e, 0x77, 0x74, 0x18, 0xc0)
-
-#define SANDBOX_FIT_IMAGE_GUID \
-	EFI_GUID(0x3673b45d, 0x6a7c, 0x46f3, 0x9e, 0x60, \
-		 0xad, 0xab, 0xb0, 0x3f, 0x79, 0x37)
-
 /* Size of our emulated memory */
 #define SB_CONCAT(x, y) x ## y
 #define SB_TO_UL(s) SB_CONCAT(s, UL)
diff --git a/include/efi_config.h b/include/efi_config.h
index 098cac2..fd69926 100644
--- a/include/efi_config.h
+++ b/include/efi_config.h
@@ -89,10 +89,21 @@
 void eficonfig_destroy(struct efimenu *efi_menu);
 efi_status_t eficonfig_process_quit(void *data);
 efi_status_t eficonfig_process_common(struct efimenu *efi_menu, char *menu_header);
-efi_status_t eficonfig_select_file_handler(void *data);
+efi_status_t eficonfig_process_select_file(void *data);
 efi_status_t eficonfig_get_unused_bootoption(u16 *buf,
 					     efi_uintn_t buf_size, u32 *index);
 efi_status_t eficonfig_append_bootorder(u16 index);
 efi_status_t eficonfig_generate_media_device_boot_option(void);
 
+efi_status_t eficonfig_append_menu_entry(struct efimenu *efi_menu,
+					 char *title, eficonfig_entry_func func,
+					 void *data);
+efi_status_t eficonfig_append_quit_entry(struct efimenu *efi_menu);
+struct efi_device_path *eficonfig_create_device_path(struct efi_device_path *dp_volume,
+						     u16 *current_path);
+void *eficonfig_create_fixed_menu(const struct eficonfig_item *items, int count);
+#ifdef CONFIG_EFI_SECURE_BOOT
+efi_status_t eficonfig_process_secure_boot_config(void *data);
+#endif
+
 #endif
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 0c6c95b..0899e29 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -707,6 +707,7 @@
 
 int efi_link_dev(efi_handle_t handle, struct udevice *dev);
 int efi_unlink_dev(efi_handle_t handle);
+bool efi_varname_is_load_option(u16 *var_name16, int *index);
 
 /**
  * efi_size_in_pages() - convert size in bytes to size in pages
diff --git a/include/efi_selftest.h b/include/efi_selftest.h
index e900cb8..7c69c3f 100644
--- a/include/efi_selftest.h
+++ b/include/efi_selftest.h
@@ -131,7 +131,7 @@
  * @buf2:	char string
  * Return:	0 if both buffers contain equivalent strings
  */
-int efi_st_strcmp_16_8(const u16 *buf1, const char *buf2);
+int efi_st_strcmp_16_8(const u16 *buf1, const unsigned char *buf2);
 
 /**
  * efi_st_get_config_table() - get configuration table
diff --git a/include/env_callback.h b/include/env_callback.h
index 1eae0ef..85e7fe2 100644
--- a/include/env_callback.h
+++ b/include/env_callback.h
@@ -51,6 +51,15 @@
 #define NET_CALLBACKS
 #endif
 
+#ifdef CONFIG_IPV6
+#define NET6_CALLBACKS \
+	"ip6addr:ip6addr," \
+	"serverip6:serverip6," \
+	"gatewayip6:gatewayip6,"
+#else
+#define NET6_CALLBACKS
+#endif
+
 #ifdef CONFIG_BOOTSTD
 #define BOOTSTD_CALLBACK	"bootmeths:bootmeths,"
 #else
@@ -65,6 +74,7 @@
 	ENV_DOT_ESCAPE ENV_FLAGS_VAR ":flags," \
 	"baudrate:baudrate," \
 	NET_CALLBACKS \
+	NET6_CALLBACKS \
 	BOOTSTD_CALLBACK \
 	"loadaddr:loadaddr," \
 	SILENT_CALLBACK \
diff --git a/include/env_flags.h b/include/env_flags.h
index 313cb8c..718d727 100644
--- a/include/env_flags.h
+++ b/include/env_flags.h
@@ -67,6 +67,15 @@
 #define NET_FLAGS
 #endif
 
+#ifdef CONFIG_IPV6
+#define NET6_FLAGS \
+	"ip6addr:s," \
+	"serverip6:s," \
+	"gatewayip6:s"
+#else
+#define NET6_FLAGS
+#endif
+
 #ifndef CONFIG_ENV_OVERWRITE
 #define SERIAL_FLAGS "serial#:so,"
 #else
@@ -76,6 +85,7 @@
 #define ENV_FLAGS_LIST_STATIC \
 	ETHADDR_FLAGS \
 	NET_FLAGS \
+	NET6_FLAGS \
 	SERIAL_FLAGS \
 	CONFIG_ENV_FLAGS_LIST_STATIC
 
diff --git a/include/image.h b/include/image.h
index 65d0d4f..6f21daf 100644
--- a/include/image.h
+++ b/include/image.h
@@ -853,7 +853,13 @@
 
 static inline void image_set_name(struct legacy_img_hdr *hdr, const char *name)
 {
-	strncpy(image_get_name(hdr), name, IH_NMLEN);
+	/*
+	 * This is equivalent to: strncpy(image_get_name(hdr), name, IH_NMLEN);
+	 *
+	 * Use the tortured code below to avoid a warning with gcc 12. We do not
+	 * want to include a nul terminator if the name is of length IH_NMLEN
+	 */
+	memcpy(image_get_name(hdr), name, strnlen(name, IH_NMLEN));
 }
 
 int image_check_hcrc(const struct legacy_img_hdr *hdr);
diff --git a/include/linux/litex.h b/include/linux/litex.h
new file mode 100644
index 0000000..5e91db4
--- /dev/null
+++ b/include/linux/litex.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Common LiteX header providing
+ * helper functions for accessing CSRs.
+ *
+ * Copyright (C) 2019-2020 Antmicro <www.antmicro.com>
+ */
+
+#ifndef _LINUX_LITEX_H
+#define _LINUX_LITEX_H
+
+#include <linux/io.h>
+#include <asm/byteorder.h>
+
+static inline void _write_litex_subregister(u32 val, void __iomem *addr)
+{
+	writel((u32 __force)cpu_to_le32(val), addr);
+}
+
+static inline u32 _read_litex_subregister(void __iomem *addr)
+{
+	return le32_to_cpu((__le32 __force)readl(addr));
+}
+
+/*
+ * LiteX SoC Generator, depending on the configuration, can split a single
+ * logical CSR (Control&Status Register) into a series of consecutive physical
+ * registers.
+ *
+ * For example, in the configuration with 8-bit CSR Bus, a 32-bit aligned,
+ * 32-bit wide logical CSR will be laid out as four 32-bit physical
+ * subregisters, each one containing one byte of meaningful data.
+ *
+ * For Linux support, upstream LiteX enforces a 32-bit wide CSR bus, which
+ * means that only larger-than-32-bit CSRs will be split across multiple
+ * subregisters (e.g., a 64-bit CSR will be spread across two consecutive
+ * 32-bit subregisters).
+ *
+ * For details see: https://github.com/enjoy-digital/litex/wiki/CSR-Bus
+ */
+
+static inline void litex_write8(void __iomem *reg, u8 val)
+{
+	_write_litex_subregister(val, reg);
+}
+
+static inline void litex_write16(void __iomem *reg, u16 val)
+{
+	_write_litex_subregister(val, reg);
+}
+
+static inline void litex_write32(void __iomem *reg, u32 val)
+{
+	_write_litex_subregister(val, reg);
+}
+
+static inline void litex_write64(void __iomem *reg, u64 val)
+{
+	_write_litex_subregister(val >> 32, reg);
+	_write_litex_subregister(val, reg + 4);
+}
+
+static inline u8 litex_read8(void __iomem *reg)
+{
+	return _read_litex_subregister(reg);
+}
+
+static inline u16 litex_read16(void __iomem *reg)
+{
+	return _read_litex_subregister(reg);
+}
+
+static inline u32 litex_read32(void __iomem *reg)
+{
+	return _read_litex_subregister(reg);
+}
+
+static inline u64 litex_read64(void __iomem *reg)
+{
+	return ((u64)_read_litex_subregister(reg) << 32) |
+		_read_litex_subregister(reg + 4);
+}
+
+#endif /* _LINUX_LITEX_H */
diff --git a/include/ndisc.h b/include/ndisc.h
new file mode 100644
index 0000000..f6f8eb6
--- /dev/null
+++ b/include/ndisc.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2013 Allied Telesis Labs NZ
+ * Chris Packham, <judge.packham@gmail.com>
+ *
+ * Copyright (C) 2022 YADRO
+ * Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
+ */
+
+#ifndef __NDISC_H__
+#define __NDISC_H__
+
+#include <ndisc.h>
+
+/* struct nd_msg - ICMPv6 Neighbour Discovery message format */
+struct nd_msg {
+	struct icmp6hdr	icmph;
+	struct in6_addr	target;
+	__u8		opt[0];
+};
+
+/* struct echo_msg - ICMPv6 echo request/reply message format */
+struct echo_msg {
+	struct icmp6hdr	icmph;
+	__u16		id;
+	__u16		sequence;
+};
+
+/* Neigbour Discovery option types */
+enum {
+	__ND_OPT_PREFIX_INFO_END	= 0,
+	ND_OPT_SOURCE_LL_ADDR		= 1,
+	ND_OPT_TARGET_LL_ADDR		= 2,
+	ND_OPT_PREFIX_INFO		= 3,
+	ND_OPT_REDIRECT_HDR		= 4,
+	ND_OPT_MTU			= 5,
+	__ND_OPT_MAX
+};
+
+/* IPv6 destination address of packet waiting for ND */
+extern struct in6_addr net_nd_sol_packet_ip6;
+/* MAC destination address of packet waiting for ND */
+extern uchar *net_nd_packet_mac;
+/* pointer to packet waiting to be transmitted after ND is resolved */
+extern uchar *net_nd_tx_packet;
+/* size of packet waiting to be transmitted */
+extern int net_nd_tx_packet_size;
+/* the timer for ND resolution */
+extern ulong net_nd_timer_start;
+/* the number of requests we have sent so far */
+extern int net_nd_try;
+
+#ifdef CONFIG_IPV6
+/**
+ * ndisc_init() - Make initial steps for ND state machine.
+ * Usually move variables into initial state.
+ */
+void ndisc_init(void);
+
+/**
+ * ndisc_receive() - Handle ND packet
+ *
+ * @et:		pointer to incoming packet
+ * @ip6:	pointer to IPv6 header
+ * @len:	incoming packet length
+ * Return: 0 if handle successfully, -1 if unsupported/unknown ND packet type
+ */
+int ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len);
+
+/**
+ * ndisc_request() - Send ND request
+ */
+void ndisc_request(void);
+
+/**
+ * ndisc_init() - Check ND response timeout
+ *
+ * Return: 0 if no timeout, -1 otherwise
+ */
+int ndisc_timeout_check(void);
+#else
+static inline void ndisc_init(void)
+{
+}
+
+static inline int
+ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
+{
+	return -1;
+}
+
+static inline void ndisc_request(void)
+{
+}
+
+static inline int ndisc_timeout_check(void)
+{
+	return 0;
+}
+#endif
+
+#endif /* __NDISC_H__ */
diff --git a/include/net.h b/include/net.h
index 32364ed..1a99009 100644
--- a/include/net.h
+++ b/include/net.h
@@ -365,6 +365,7 @@
 #define PROT_NCSI	0x88f8		/* NC-SI control packets        */
 
 #define IPPROTO_ICMP	 1	/* Internet Control Message Protocol	*/
+#define IPPROTO_TCP	6	/* Transmission Control Protocol	*/
 #define IPPROTO_UDP	17	/* User Datagram Protocol		*/
 
 /*
@@ -559,8 +560,8 @@
 extern int		net_restart_wrap;	/* Tried all network devices */
 
 enum proto_t {
-	BOOTP, RARP, ARP, TFTPGET, DHCP, PING, DNS, NFS, CDP, NETCONS, SNTP,
-	TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI
+	BOOTP, RARP, ARP, TFTPGET, DHCP, PING, PING6, DNS, NFS, CDP, NETCONS,
+	SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI, WGET
 };
 
 extern char	net_boot_file_name[1024];/* Boot File name */
@@ -690,19 +691,36 @@
 	(void) eth_send(pkt, len);
 }
 
-/*
- * Transmit "net_tx_packet" as UDP packet, performing ARP request if needed
- *  (ether will be populated)
+/**
+ * net_send_ip_packet() - Transmit "net_tx_packet" as UDP or TCP packet,
+ *                        send ARP request if needed (ether will be populated)
+ * @ether: Raw packet buffer
+ * @dest: IP address to send the datagram to
+ * @dport: Destination UDP port
+ * @sport: Source UDP port
+ * @payload_len: Length of data after the UDP header
+ * @action: TCP action to be performed
+ * @tcp_seq_num: TCP sequence number of this transmission
+ * @tcp_ack_num: TCP stream acknolegement number
  *
- * @param ether Raw packet buffer
- * @param dest IP address to send the datagram to
- * @param dport Destination UDP port
- * @param sport Source UDP port
- * @param payload_len Length of data after the UDP header
+ * Return: 0 on success, other value on failure
  */
 int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport,
 		       int payload_len, int proto, u8 action, u32 tcp_seq_num,
 		       u32 tcp_ack_num);
+/**
+ * net_send_tcp_packet() - Transmit TCP packet.
+ * @payload_len: length of payload
+ * @dport: Destination TCP port
+ * @sport: Source TCP port
+ * @action: TCP action to be performed
+ * @tcp_seq_num: TCP sequence number of this transmission
+ * @tcp_ack_num: TCP stream acknolegement number
+ *
+ * Return: 0 on success, other value on failure
+ */
+int net_send_tcp_packet(int payload_len, int dport, int sport, u8 action,
+			u32 tcp_seq_num, u32 tcp_ack_num);
 int net_send_udp_packet(uchar *ether, struct in_addr dest, int dport,
 			int sport, int payload_len);
 
diff --git a/include/net/tcp.h b/include/net/tcp.h
new file mode 100644
index 0000000..3225516
--- /dev/null
+++ b/include/net/tcp.h
@@ -0,0 +1,299 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * TCP Support with SACK for file transfer.
+ *
+ * Copyright 2017 Duncan Hare, All rights reserved.
+ */
+
+#define TCP_ACTIVITY 127		/* Number of packets received   */
+					/* before console progress mark */
+/**
+ * struct ip_tcp_hdr - IP and TCP header
+ * @ip_hl_v: header length and version
+ * @ip_tos: type of service
+ * @ip_len: total length
+ * @ip_id: identification
+ * @ip_off: fragment offset field
+ * @ip_ttl: time to live
+ * @ip_p: protocol
+ * @ip_sum: checksum
+ * @ip_src: Source IP address
+ * @ip_dst: Destination IP address
+ * @tcp_src: TCP source port
+ * @tcp_dst: TCP destination port
+ * @tcp_seq: TCP sequence number
+ * @tcp_ack: TCP Acknowledgment number
+ * @tcp_hlen: 4 bits TCP header Length/4, 4 bits reserved, 2 more bits reserved
+ * @tcp_flag: flags of TCP
+ * @tcp_win: TCP windows size
+ * @tcp_xsum: Checksum
+ * @tcp_ugr: Pointer to urgent data
+ */
+struct ip_tcp_hdr {
+	u8		ip_hl_v;
+	u8		ip_tos;
+	u16		ip_len;
+	u16		ip_id;
+	u16		ip_off;
+	u8		ip_ttl;
+	u8		ip_p;
+	u16		ip_sum;
+	struct in_addr	ip_src;
+	struct in_addr	ip_dst;
+	u16		tcp_src;
+	u16		tcp_dst;
+	u32		tcp_seq;
+	u32		tcp_ack;
+	u8		tcp_hlen;
+	u8		tcp_flags;
+	u16		tcp_win;
+	u16		tcp_xsum;
+	u16		tcp_ugr;
+} __packed;
+
+#define IP_TCP_HDR_SIZE		(sizeof(struct ip_tcp_hdr))
+#define TCP_HDR_SIZE		(IP_TCP_HDR_SIZE  - IP_HDR_SIZE)
+
+#define TCP_DATA	0x00	/* Data Packet - internal use only	*/
+#define TCP_FIN		0x01	/* Finish flag				*/
+#define TCP_SYN		0x02	/* Synch (start) flag			*/
+#define TCP_RST		0x04	/* reset flag				*/
+#define TCP_PUSH	0x08	/* Push - Notify app			*/
+#define TCP_ACK		0x10	/* Acknowledgment of data received	*/
+#define TCP_URG		0x20	/* Urgent				*/
+#define TCP_ECE		0x40	/* Congestion control			*/
+#define TCP_CWR		0x80	/* Congestion Control			*/
+
+/*
+ * TCP header options, Seq, MSS, and SACK
+ */
+
+#define TCP_SACK 32			/* Number of packets analyzed   */
+					/* on leading edge of stream    */
+
+#define TCP_O_END	0x00		/* End of option list		*/
+#define TCP_1_NOP	0x01		/* Single padding NOP		*/
+#define TCP_O_NOP	0x01010101	/* NOPs pad to 32 bit boundary	*/
+#define TCP_O_MSS	0x02		/* MSS Size option		*/
+#define TCP_O_SCL	0x03		/* Window Scale option		*/
+#define TCP_P_SACK	0x04		/* SACK permitted		*/
+#define TCP_V_SACK	0x05		/* SACK values			*/
+#define TCP_O_TS	0x08		/* Timestamp option		*/
+#define TCP_OPT_LEN_2	0x02
+#define TCP_OPT_LEN_3	0x03
+#define TCP_OPT_LEN_4	0x04
+#define TCP_OPT_LEN_6	0x06
+#define TCP_OPT_LEN_8	0x08
+#define TCP_OPT_LEN_A	0x0a		/* Timestamp Length		*/
+#define TCP_MSS		1460		/* Max segment size		*/
+#define TCP_SCALE	0x01		/* Scale			*/
+
+/**
+ * struct tcp_mss - TCP option structure for MSS (Max segment size)
+ * @kind: Field ID
+ * @len: Field length
+ * @mss: Segment size value
+ */
+struct tcp_mss {
+	u8	kind;
+	u8	len;
+	u16	mss;
+} __packed;
+
+/**
+ * struct tcp_scale - TCP option structure for Windows scale
+ * @kind: Field ID
+ * @len: Field length
+ * @scale: windows shift value used for networks with many hops.
+ *         Typically 4 or more hops
+ */
+struct tcp_scale {
+	u8	kind;
+	u8	len;
+	u8	scale;
+} __packed;
+
+/**
+ * struct tcp_sack_p - TCP option structure for SACK permitted
+ * @kind: Field ID
+ * @len: Field length
+ */
+struct tcp_sack_p {
+	u8	kind;
+	u8	len;
+} __packed;
+
+/**
+ * struct sack_edges - structure for SACK edges
+ * @l: Left edge of stream
+ * @r: right edge of stream
+ */
+struct sack_edges {
+	u32	l;
+	u32	r;
+} __packed;
+
+#define TCP_SACK_SIZE (sizeof(struct sack_edges))
+
+/*
+ * A TCP stream has holes when packets are missing or disordered.
+ * A hill is the inverse of a hole, and is data received.
+ * TCP received hills (a sequence of data), and inferrs Holes
+ * from the "hills" or packets received.
+ */
+
+#define TCP_SACK_HILLS	4
+
+/**
+ * struct tcp_sack_v - TCP option structure for SACK
+ * @kind: Field ID
+ * @len: Field length
+ * @hill: L & R window edges
+ */
+struct tcp_sack_v {
+	u8	kind;
+	u8	len;
+	struct	sack_edges hill[TCP_SACK_HILLS];
+} __packed;
+
+/**
+ * struct tcp_t_opt - TCP option structure for time stamps
+ * @kind: Field ID
+ * @len: Field length
+ * @t_snd: Sender timestamp
+ * @t_rcv: Receiver timestamp
+ */
+struct tcp_t_opt {
+	u8	kind;
+	u8	len;
+	u32	t_snd;
+	u32	t_rcv;
+} __packed;
+
+#define TCP_TSOPT_SIZE (sizeof(struct tcp_t_opt))
+
+/*
+ * ip tcp  structure with options
+ */
+
+/**
+ * struct ip_tcp_hdr_o - IP + TCP header + TCP options
+ * @hdr: IP + TCP header
+ * @mss: TCP MSS Option
+ * @scale: TCP Windows Scale Option
+ * @sack_p: TCP Sack-Permitted Option
+ * @t_opt: TCP Timestamp Option
+ * @end: end of options
+ */
+struct ip_tcp_hdr_o {
+	struct	ip_tcp_hdr hdr;
+	struct	tcp_mss	   mss;
+	struct	tcp_scale  scale;
+	struct	tcp_sack_p sack_p;
+	struct	tcp_t_opt  t_opt;
+	u8	end;
+} __packed;
+
+#define IP_TCP_O_SIZE (sizeof(struct ip_tcp_hdr_o))
+
+/**
+ * struct ip_tcp_hdr_s - IP + TCP header + TCP options
+ * @hdr: IP + TCP header
+ * @t_opt: TCP Timestamp Option
+ * @sack_v: TCP SACK Option
+ * @end: end of options
+ */
+struct ip_tcp_hdr_s {
+	struct	ip_tcp_hdr	hdr;
+	struct	tcp_t_opt	t_opt;
+	struct	tcp_sack_v	sack_v;
+	u8	end;
+} __packed;
+
+#define IP_TCP_SACK_SIZE (sizeof(struct ip_tcp_hdr_s))
+
+/*
+ * TCP pseudo header definitions
+ */
+#define PSEUDO_PAD_SIZE	8
+
+/**
+ * struct pseudo_hdr - Pseudo Header
+ * @padding: pseudo hdr size = ip_tcp hdr size
+ * @p_src: Source IP address
+ * @p_dst: Destination IP address
+ * @rsvd: reserved
+ * @p: protocol
+ * @len: length of header
+ */
+struct pseudo_hdr {
+	u8 padding[PSEUDO_PAD_SIZE];
+	struct in_addr p_src;
+	struct in_addr p_dst;
+	u8      rsvd;
+	u8      p;
+	u16     len;
+} __packed;
+
+#define PSEUDO_HDR_SIZE	(sizeof(struct pseudo_hdr)) - PSEUDO_PAD_SIZE
+
+/**
+ * union tcp_build_pkt - union for building TCP/IP packet.
+ * @ph: pseudo header
+ * @ip: IP and TCP header plus TCP options
+ * @sack: IP and TCP header plus SACK options
+ * @raw: buffer
+ *
+ * Build Pseudo header in packed buffer
+ * first, calculate TCP checksum, then build IP header in packed buffer.
+ *
+ */
+union tcp_build_pkt {
+	struct pseudo_hdr ph;
+	struct ip_tcp_hdr_o ip;
+	struct ip_tcp_hdr_s sack;
+	uchar  raw[1600];
+} __packed;
+
+/**
+ * enum tcp_state - TCP State machine states for connection
+ * @TCP_CLOSED: Need to send SYN to connect
+ * @TCP_SYN_SENT: Trying to connect, waiting for SYN ACK
+ * @TCP_ESTABLISHED: both server & client have a connection
+ * @TCP_CLOSE_WAIT: Rec FIN, passed to app for FIN, ACK rsp
+ * @TCP_CLOSING: Rec FIN, sent FIN, ACK waiting for ACK
+ * @TCP_FIN_WAIT_1: Sent FIN waiting for response
+ * @TCP_FIN_WAIT_2: Rec ACK from FIN sent, waiting for FIN
+ */
+enum tcp_state {
+	TCP_CLOSED,
+	TCP_SYN_SENT,
+	TCP_ESTABLISHED,
+	TCP_CLOSE_WAIT,
+	TCP_CLOSING,
+	TCP_FIN_WAIT_1,
+	TCP_FIN_WAIT_2
+};
+
+enum tcp_state tcp_get_tcp_state(void);
+void tcp_set_tcp_state(enum tcp_state new_state);
+int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len,
+		       u8 action, u32 tcp_seq_num, u32 tcp_ack_num);
+
+/**
+ * rxhand_tcp() - An incoming packet handler.
+ * @pkt: pointer to the application packet
+ * @dport: destination UDP port
+ * @sip: source IP address
+ * @sport: source UDP port
+ * @len: packet length
+ */
+typedef void rxhand_tcp(uchar *pkt, unsigned int dport,
+			struct in_addr sip, unsigned int sport,
+			unsigned int len);
+void tcp_set_tcp_handler(rxhand_tcp *f);
+
+void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int len);
+
+u16 tcp_set_pseudo_header(uchar *pkt, struct in_addr src, struct in_addr dest,
+			  int tcp_len, int pkt_len);
diff --git a/include/net/wget.h b/include/net/wget.h
new file mode 100644
index 0000000..da0920d
--- /dev/null
+++ b/include/net/wget.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Duncan Hare Copyright 2017
+ */
+
+/**
+ * wget_start() - begin wget
+ */
+void wget_start(void);
+
+enum wget_state {
+	WGET_CLOSED,
+	WGET_CONNECTING,
+	WGET_CONNECTED,
+	WGET_TRANSFERRING,
+	WGET_TRANSFERRED
+};
+
+#define DEBUG_WGET		0	/* Set to 1 for debug messages */
+#define SERVER_PORT		80
+#define WGET_RETRY_COUNT	30
+#define WGET_TIMEOUT		2000UL
diff --git a/include/net6.h b/include/net6.h
new file mode 100644
index 0000000..9b3de02
--- /dev/null
+++ b/include/net6.h
@@ -0,0 +1,432 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2013 Allied Telesis Labs NZ
+ * Chris Packham, <judge.packham@gmail.com>
+ *
+ * Copyright (C) 2022 YADRO
+ * Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
+ */
+
+#ifndef __NET6_H__
+#define __NET6_H__
+
+#include <net.h>
+#include <linux/ctype.h>
+
+/* struct in6_addr - 128 bits long IPv6 address */
+struct in6_addr {
+	union {
+		u8	u6_addr8[16];
+		__be16	u6_addr16[8];
+		__be32	u6_addr32[4];
+	} in6_u;
+
+#define s6_addr		in6_u.u6_addr8
+#define s6_addr16	in6_u.u6_addr16
+#define s6_addr32	in6_u.u6_addr32
+};
+
+#define IN6ADDRSZ	sizeof(struct in6_addr)
+#define INETHADDRSZ	sizeof(net_ethaddr)
+
+#define PROT_IP6	0x86DD	/* IPv6 protocol */
+#define PROT_ICMPV6	58	/* ICMPv6 protocol*/
+
+#define IPV6_ADDRSCOPE_INTF	0x01
+#define IPV6_ADDRSCOPE_LINK	0x02
+#define IPV6_ADDRSCOPE_AMDIN	0x04
+#define IPV6_ADDRSCOPE_SITE	0x05
+#define IPV6_ADDRSCOPE_ORG	0x08
+#define IPV6_ADDRSCOPE_GLOBAL	0x0E
+
+#define USE_IP6_CMD_PARAM	"-ipv6"
+
+/**
+ * struct ipv6hdr - Internet Protocol V6 (IPv6) header.
+ *
+ * IPv6 packet header as defined in RFC 2460.
+ */
+struct ip6_hdr {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	u8	priority:4,
+		version:4;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	u8	version:4,
+		priority:4;
+#else
+#error  "Please fix <asm/byteorder.h>"
+#endif
+	u8		flow_lbl[3];
+	__be16		payload_len;
+	u8		nexthdr;
+	u8		hop_limit;
+	struct in6_addr	saddr;
+	struct in6_addr	daddr;
+};
+#define IP6_HDR_SIZE (sizeof(struct ip6_hdr))
+
+/* struct udp_hdr - User Datagram Protocol header */
+struct udp_hdr {
+	u16		udp_src;	/* UDP source port		*/
+	u16		udp_dst;	/* UDP destination port		*/
+	u16		udp_len;	/* Length of UDP packet		*/
+	u16		udp_xsum;	/* Checksum			*/
+} __packed;
+
+/*
+ * Handy for static initialisations of struct in6_addr, atlhough the
+ * c99 '= { 0 }' idiom might work depending on you compiler.
+ */
+#define ZERO_IPV6_ADDR { { { 0x00, 0x00, 0x00, 0x00, \
+			  0x00, 0x00, 0x00, 0x00, \
+			  0x00, 0x00, 0x00, 0x00, \
+			  0x00, 0x00, 0x00, 0x00 } } }
+
+#define IPV6_LINK_LOCAL_PREFIX	0xfe80
+
+/* hop limit for neighbour discovery packets */
+#define IPV6_NDISC_HOPLIMIT             255
+#define NDISC_TIMEOUT			5000UL
+#define NDISC_TIMEOUT_COUNT             3
+
+/* struct icmp6hdr - Internet Control Message Protocol header for IPV6 */
+struct icmp6hdr {
+	u8	icmp6_type;
+#define IPV6_ICMP_ECHO_REQUEST			128
+#define IPV6_ICMP_ECHO_REPLY			129
+#define IPV6_NDISC_ROUTER_SOLICITATION		133
+#define IPV6_NDISC_ROUTER_ADVERTISEMENT		134
+#define IPV6_NDISC_NEIGHBOUR_SOLICITATION	135
+#define IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT	136
+#define IPV6_NDISC_REDIRECT			137
+	u8	icmp6_code;
+	__be16	icmp6_cksum;
+
+	/* ICMPv6 data */
+	union {
+		__be32	un_data32[1];
+		__be16	un_data16[2];
+		u8	un_data8[4];
+
+		/* struct icmpv6_echo - echo request/reply message format */
+		struct icmpv6_echo {
+			__be16		identifier;
+			__be16		sequence;
+		} u_echo;
+
+		/* struct icmpv6_nd_advt - Neighbor Advertisement format */
+		struct icmpv6_nd_advt {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+			__be32		reserved:5,
+					override:1,
+					solicited:1,
+					router:1,
+					reserved2:24;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+			__be32		router:1,
+					solicited:1,
+					override:1,
+					reserved:29;
+#else
+#error	"Please fix <asm/byteorder.h>"
+#endif
+		} u_nd_advt;
+
+		/* struct icmpv6_nd_ra - Router Advertisement format */
+		struct icmpv6_nd_ra {
+			u8		hop_limit;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+			u8		reserved:6,
+					other:1,
+					managed:1;
+
+#elif defined(__BIG_ENDIAN_BITFIELD)
+			u8		managed:1,
+					other:1,
+					reserved:6;
+#else
+#error	"Please fix <asm/byteorder.h>"
+#endif
+			__be16		rt_lifetime;
+		} u_nd_ra;
+	} icmp6_dataun;
+#define icmp6_identifier	icmp6_dataun.u_echo.identifier
+#define icmp6_sequence		icmp6_dataun.u_echo.sequence
+#define icmp6_pointer		icmp6_dataun.un_data32[0]
+#define icmp6_mtu		icmp6_dataun.un_data32[0]
+#define icmp6_unused		icmp6_dataun.un_data32[0]
+#define icmp6_maxdelay		icmp6_dataun.un_data16[0]
+#define icmp6_router		icmp6_dataun.u_nd_advt.router
+#define icmp6_solicited		icmp6_dataun.u_nd_advt.solicited
+#define icmp6_override		icmp6_dataun.u_nd_advt.override
+#define icmp6_ndiscreserved	icmp6_dataun.u_nd_advt.reserved
+#define icmp6_hop_limit		icmp6_dataun.u_nd_ra.hop_limit
+#define icmp6_addrconf_managed	icmp6_dataun.u_nd_ra.managed
+#define icmp6_addrconf_other	icmp6_dataun.u_nd_ra.other
+#define icmp6_rt_lifetime	icmp6_dataun.u_nd_ra.rt_lifetime
+};
+
+extern struct in6_addr const net_null_addr_ip6;	/* NULL IPv6 address */
+extern struct in6_addr net_gateway6;	/* Our gateways IPv6 address */
+extern struct in6_addr net_ip6;	/* Our IPv6 addr (0 = unknown) */
+extern struct in6_addr net_link_local_ip6;	/* Our link local IPv6 addr */
+extern u32 net_prefix_length;	/* Our prefixlength (0 = unknown) */
+extern struct in6_addr net_server_ip6;	/* Server IPv6 addr (0 = unknown) */
+extern struct in6_addr net_ping_ip6; /* the ipv6 address to ping */
+extern bool use_ip6;
+
+#if IS_ENABLED(CONFIG_IPV6)
+/**
+ * string_to_ip6() - Convert IPv6 string addr to inner IPV6 addr format
+ *
+ * Examples of valid strings:
+ *	2001:db8::0:1234:1
+ *	2001:0db8:0000:0000:0000:0000:1234:0001
+ *	::1
+ *	::ffff:192.168.1.1
+ *
+ * Examples of invalid strings
+ *	2001:db8::0::0          (:: can only appear once)
+ *	2001:db8:192.168.1.1::1 (v4 part can only appear at the end)
+ *	192.168.1.1             (we don't implicity map v4)
+ *
+ * @s:		IPv6 string addr format
+ * @len:	IPv6 string addr length
+ * @addr:	converted IPv6 addr
+ * Return: 0 if conversion successful, -EINVAL if fail
+ */
+int string_to_ip6(const char *s, size_t len, struct in6_addr *addr);
+
+/**
+ * ip6_is_unspecified_addr() - Check if IPv6 addr is not set i.e. is zero
+ *
+ * @addr:	IPv6 addr
+ * Return:  0 if addr is not set, -1 if is set
+ */
+int ip6_is_unspecified_addr(struct in6_addr *addr);
+
+/**
+ * ip6_is_our_addr() - Check if IPv6 addr belongs to our host addr
+ *
+ * We have 2 addresses that we should respond to. A link local address and a
+ * global address. This returns true if the specified address matches either
+ * of these.
+ *
+ * @addr:	addr to check
+ * Return: 0 if addr is our, -1 otherwise
+ */
+int ip6_is_our_addr(struct in6_addr *addr);
+
+/**
+ * ip6_addr_in_subnet() - Check if two IPv6 addresses are in the same subnet
+ *
+ * @our_addr:		first IPv6 addr
+ * @neigh_addr:		second IPv6 addr
+ * @prefix_length:	network mask length
+ * Return: 0 if two addresses in the same subnet, -1 otherwise
+ */
+int ip6_addr_in_subnet(struct in6_addr *our_addr, struct in6_addr *neigh_addr,
+		       u32 prefix_length);
+
+/**
+ * ip6_make_lladd() - rMake up IPv6 Link Local address
+ *
+ * @lladdr:	formed IPv6 Link Local address
+ * @enetaddr:	MAC addr of a device
+ */
+void ip6_make_lladdr(struct in6_addr *lladr, unsigned char const enetaddr[6]);
+
+/**
+ * ip6_make_snma() - aMake up Solicited Node Multicast Address from IPv6 addr
+ *
+ * @mcast_addr:	formed SNMA addr
+ * @ip6_addr:	base IPv6 addr
+ */
+void ip6_make_snma(struct in6_addr *mcast_addr, struct in6_addr *ip6_addr);
+
+/**
+ * ip6_make_mult_ethdstaddr() - Make up IPv6 multicast addr
+ *
+ * @enetaddr:	MAC addr of a device
+ * @mcast_addr:	formed IPv6 multicast addr
+ */
+void ip6_make_mult_ethdstaddr(unsigned char enetaddr[6],
+			      struct in6_addr *mcast_addr);
+
+/**
+ * csum_partial() - Compute an internet checksum
+ *
+ * @buff:	buffer to be checksummed
+ * @len:	length of buffer
+ * @sum:	initial sum to be added in
+ * Return: internet checksum of the buffer
+ */
+unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum);
+
+/**
+ * csum_ipv6_magic() - Compute checksum of IPv6 "psuedo-header" per RFC2460 section 8.1
+ *
+ * @saddr:	source IPv6 addr
+ * @daddr:	destination IPv6 add
+ * @len:	data length to be checksummed
+ * @proto:	IPv6 above protocol code
+ * @csum:	upper layer checksum
+ * Return: computed checksum
+ */
+unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
+				   struct in6_addr *daddr, u16 len,
+				   unsigned short proto, unsigned int csum);
+
+/**
+ * ip6_add_hdr() - Make up IPv6 header
+ *
+ * @xip:	pointer to IPv6 header to be formed
+ * @src:	source IPv6 addr
+ * @dest:	destination IPv6 addr
+ * @nextheader:	next header type
+ * @hoplimit:	hop limit
+ * @payload_len: payload length
+ * Return: IPv6 header length
+ */
+int ip6_add_hdr(uchar *xip, struct in6_addr *src, struct in6_addr *dest,
+		int nextheader, int hoplimit, int payload_len);
+
+/**
+ * net_send_udp_packet6() - Make up UDP packet and send it
+ *
+ * @ether:	destination MAC addr
+ * @dest:	destination IPv6 addr
+ * @dport:	destination port
+ * @sport:	source port
+ * @len:	UDP packet length
+ * Return: 0 if send successfully, -1 otherwise
+ */
+int net_send_udp_packet6(uchar *ether, struct in6_addr *dest, int dport,
+			 int sport, int len);
+
+/**
+ * net_ip6_handler() - Handle IPv6 packet
+ *
+ * @et:		pointer to the beginning of the packet
+ * @ip6:	pointer to the beginning of IPv6 protocol
+ * @len:	incoming packet len
+ * Return: 0 if handle packet successfully, -EINVAL in case of invalid protocol
+ */
+int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len);
+
+/**
+ * net_copy_ip6() - Copy IPv6 addr
+ *
+ * @to:		destination IPv6 addr
+ * @from:	source IPv6 addr
+ */
+static inline void net_copy_ip6(void *to, const void *from)
+{
+	memcpy((void *)to, from, sizeof(struct in6_addr));
+}
+#else
+static inline int
+string_to_ip6(const char *s, size_t len, struct in6_addr *addr)
+{
+	return -EINVAL;
+}
+
+static inline int ip6_is_unspecified_addr(struct in6_addr *addr)
+{
+	return -1;
+}
+
+static inline int ip6_is_our_addr(struct in6_addr *addr)
+{
+	return -1;
+}
+
+static inline int
+ip6_addr_in_subnet(struct in6_addr *our_addr, struct in6_addr *neigh_addr,
+		   u32 prefix_length)
+{
+	return -1;
+}
+
+static inline void
+ip6_make_lladdr(struct in6_addr *lladdr, unsigned char const enetaddr[6])
+{
+}
+
+static inline void
+ip6_make_snma(struct in6_addr *mcast_addr, struct in6_addr *ip6_addr)
+{
+}
+
+static inline void
+ip6_make_mult_ethdstaddr(unsigned char enetaddr[6],
+			 struct in6_addr *mcast_addr)
+{
+}
+
+static inline unsigned int
+csum_partial(const unsigned char *buff, int len, unsigned int sum)
+{
+	return 0;
+}
+
+static inline unsigned short
+csum_ipv6_magic(struct in6_addr *saddr,
+		struct in6_addr *daddr, u16 len,
+		unsigned short proto, unsigned int csum)
+{
+	return 0;
+}
+
+static inline unsigned int
+ip6_add_hdr(uchar *xip, struct in6_addr *src, struct in6_addr *dest,
+	    int nextheader, int hoplimit, int payload_len)
+{
+	return 0;
+}
+
+static inline int
+net_send_udp_packet6(uchar *ether, struct in6_addr *dest,
+		     int dport, int sport, int len)
+{
+	return -1;
+}
+
+static inline int
+net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6,
+		int len)
+{
+	return -EINVAL;
+}
+
+static inline void net_copy_ip6(void *to, const void *from)
+{
+}
+#endif
+
+#if IS_ENABLED(CONFIG_CMD_PING6)
+/* Send ping requset */
+void ping6_start(void);
+
+/**
+ * ping6_receive() - Handle reception of ICMPv6 echo request/reply
+ *
+ * @et:		pointer to incoming patcket
+ * @ip6:	pointer to IPv6 protocol
+ * @len:	packet length
+ * Return: 0 if success, -EINVAL in case of failure during reception
+ */
+int ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len);
+#else
+static inline void ping6_start(void)
+{
+}
+
+static inline
+int ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
+{
+	return -EINVAL;
+}
+#endif /* CONFIG_CMD_PING6 */
+
+#endif /* __NET6_H__ */
diff --git a/include/phy.h b/include/phy.h
index 0737c4e..ff69536 100644
--- a/include/phy.h
+++ b/include/phy.h
@@ -380,6 +380,7 @@
 int phy_ti_init(void);
 int phy_vitesse_init(void);
 int phy_xilinx_init(void);
+int phy_xway_init(void);
 int phy_mscc_init(void);
 int phy_fixed_init(void);
 int phy_ncsi_init(void);
diff --git a/include/timer.h b/include/timer.h
index a044cb0..d33a26e 100644
--- a/include/timer.h
+++ b/include/timer.h
@@ -6,6 +6,8 @@
 #ifndef _TIMER_H_
 #define _TIMER_H_
 
+#define timer_get_ops(dev)	((struct timer_ops *)(dev)->driver->ops)
+
 /**
  * dm_timer_init() - initialize a timer for time keeping. On success
  * initializes gd->timer so that lib/timer can use it for future
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index 8738757..13a35ea 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -12,6 +12,8 @@
 CFLAGS_efi_boottime.o += \
   -DFW_VERSION="0x$(VERSION)" \
   -DFW_PATCHLEVEL="0x$(PATCHLEVEL)"
+CFLAGS_boothart.o := $(CFLAGS_EFI) -Os -ffreestanding
+CFLAGS_REMOVE_boothart.o := $(CFLAGS_NON_EFI)
 CFLAGS_helloworld.o := $(CFLAGS_EFI) -Os -ffreestanding
 CFLAGS_REMOVE_helloworld.o := $(CFLAGS_NON_EFI)
 CFLAGS_dtbdump.o := $(CFLAGS_EFI) -Os -ffreestanding
@@ -19,6 +21,10 @@
 CFLAGS_initrddump.o := $(CFLAGS_EFI) -Os -ffreestanding
 CFLAGS_REMOVE_initrddump.o := $(CFLAGS_NON_EFI)
 
+ifdef CONFIG_RISCV
+always += boothart.efi
+endif
+
 ifneq ($(CONFIG_CMD_BOOTEFI_HELLO_COMPILE),)
 always += helloworld.efi
 targets += helloworld.o
diff --git a/lib/efi_loader/boothart.c b/lib/efi_loader/boothart.c
new file mode 100644
index 0000000..df176ee
--- /dev/null
+++ b/lib/efi_loader/boothart.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Check RISC-V boot hart ID
+ *
+ * Copyright 2022, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This test program reads the boot HART ID both from the device-tree from the
+ * RISCV_EFI_BOOT_PROTOCOL and writes both values to the console.
+ */
+
+#include <efi_api.h>
+#include <efi_riscv.h>
+#include <linux/libfdt.h>
+
+static const efi_guid_t riscv_efi_boot_protocol_guid =
+		RISCV_EFI_BOOT_PROTOCOL_GUID;
+static const efi_guid_t fdt_guid = EFI_FDT_GUID;
+
+static struct efi_system_table *systable;
+static struct efi_boot_services *boottime;
+static struct efi_simple_text_output_protocol *con_out;
+static const char *fdt;
+
+/**
+ * Print an unsigned 32bit value as decimal number to an u16 string
+ *
+ * @value:	value to be printed
+ * @buf:	pointer to buffer address
+ */
+static void uint2dec(u32 value, u16 *buf)
+{
+	u16 *pos = buf;
+	int i;
+	u16 c;
+	u64 f;
+
+	/*
+	 * Increment by .5 and multiply with
+	 * (2 << 60) / 1,000,000,000 = 0x44B82FA0.9B5A52CC
+	 * to move the first digit to bit 60-63.
+	 */
+	f = 0x225C17D0;
+	f += (0x9B5A52DULL * value) >> 28;
+	f += 0x44B82FA0ULL * value;
+
+	for (i = 0; i < 10; ++i) {
+		/* Write current digit */
+		c = f >> 60;
+		if (c || pos != buf)
+			*pos++ = c + '0';
+		/* Eliminate current digit */
+		f &= 0xfffffffffffffff;
+		/* Get next digit */
+		f *= 0xaULL;
+	}
+	if (pos == buf)
+		*pos++ = '0';
+	*pos = 0;
+}
+
+/**
+ * f2h() - convert FDT value to host endianness.
+ *
+ * UEFI code is always low endian. The FDT is big endian.
+ *
+ * @val:	FDT value
+ * Return:	converted value
+ */
+static uint32_t f2h(fdt32_t val)
+{
+	char *buf = (char *)&val;
+	char i;
+
+	/* Swap the bytes */
+	i = buf[0]; buf[0] = buf[3]; buf[3] = i;
+	i = buf[1]; buf[1] = buf[2]; buf[2] = i;
+
+	return val;
+}
+
+/**
+ * memcomp() - compare two memory buffers
+ *
+ * s1:		first buffer
+ * s2:		second buffer
+ * n:		size of buffers
+ * Return:	0 if both buffers have the same content
+ */
+static int memcomp(const void *s1, const void *s2, size_t n)
+{
+	const char *pos1 = s1, *pos2 = s2;
+
+	for (size_t count = 0; count < n ; ++pos1, ++pos2, --count) {
+		if (*pos1 != *pos2)
+			return *pos1 - *pos2;
+	}
+	return 0;
+}
+
+/**
+ * strcomp() - compare to strings
+ *
+ * @buf1:	first string
+ * @buf2:	second string
+ * Return:	0 if both strings are the same
+ */
+static int strcomp(const char *buf1, const char *buf2)
+{
+	for (; *buf1 || *buf2; ++buf1, ++buf2) {
+		if (*buf1 != *buf2)
+			return *buf1 - *buf2;
+	}
+	return 0;
+}
+
+/**
+ * get_property() - return value of a property of an FDT node
+ *
+ * A property of the root node or one of its direct children can be
+ * retrieved.
+ *
+ * @property	name of the property
+ * @node	name of the node or NULL for root node
+ * Return:	value of the property
+ */
+static char *get_property(const char *property, const char *node)
+{
+	struct fdt_header *header = (struct fdt_header *)fdt;
+	const fdt32_t *end;
+	const fdt32_t *pos;
+	const char *strings;
+	size_t level = 0;
+	const char *nodelabel = NULL;
+
+	if (!header) {
+		con_out->output_string(con_out, u"Missing device tree\r\n");
+		return NULL;
+	}
+
+	if (f2h(header->magic) != FDT_MAGIC) {
+		con_out->output_string(con_out, u"Wrong device tree magic\r\n");
+		return NULL;
+	}
+
+	pos = (fdt32_t *)(fdt + f2h(header->off_dt_struct));
+	end = &pos[f2h(header->totalsize) >> 2];
+	strings = fdt + f2h(header->off_dt_strings);
+
+	for (; pos < end;) {
+		switch (f2h(pos[0])) {
+		case FDT_BEGIN_NODE: {
+			const char *c = (char *)&pos[1];
+			size_t i;
+
+			if (level == 1)
+				nodelabel = c;
+			++level;
+			for (i = 0; c[i]; ++i)
+				;
+			pos = &pos[2 + (i >> 2)];
+			break;
+		}
+		case FDT_PROP: {
+			struct fdt_property *prop = (struct fdt_property *)pos;
+			const char *label = &strings[f2h(prop->nameoff)];
+			efi_status_t ret;
+
+			/* Check if this is the property to be returned */
+			if (!strcomp(property, label) &&
+			    ((level == 1 && !node) ||
+			     (level == 2 && node &&
+			      !strcomp(node, nodelabel)))) {
+				char *str;
+				efi_uintn_t len = f2h(prop->len);
+
+				if (!len)
+					return NULL;
+				/*
+				 * The string might not be 0 terminated.
+				 * It is safer to make a copy.
+				 */
+				ret = boottime->allocate_pool(
+					EFI_LOADER_DATA, len + 1,
+					(void **)&str);
+				if (ret != EFI_SUCCESS) {
+					con_out->output_string(
+						    con_out,
+						    u"AllocatePool failed\r\n");
+					return NULL;
+				}
+				boottime->copy_mem(str, &pos[3], len);
+				str[len] = 0;
+
+				return str;
+			}
+
+			pos = &pos[3 + ((f2h(prop->len) + 3) >> 2)];
+			break;
+		}
+		case FDT_NOP:
+			++pos;
+			break;
+		case FDT_END_NODE:
+			--level;
+			++pos;
+			break;
+		case FDT_END:
+			return NULL;
+		default:
+			con_out->output_string(
+				con_out, u"Invalid device tree token\r\n");
+			return NULL;
+		}
+	}
+	con_out->output_string(
+		con_out, u"Missing FDT_END token\r\n");
+	return NULL;
+}
+
+/**
+ * get_config_table() - get configuration table
+ *
+ * @guid:	table GUID
+ * Return:	pointer to table or NULL
+ */
+static void *get_config_table(const efi_guid_t *guid)
+{
+	size_t i;
+
+	for (i = 0; i < systable->nr_tables; i++) {
+		if (!memcomp(guid, &systable->tables[i].guid, 16))
+			return systable->tables[i].table;
+	}
+	return NULL;
+}
+
+/**
+ * fdt_get_hart() - get hart ID via RISC-V device-tree
+ *
+ * @hartid:	boot hart ID
+ * Return:	status code
+ */
+static efi_status_t fdt_get_hart(efi_uintn_t *hartid)
+{
+	char *str;
+
+	fdt = get_config_table(&fdt_guid);
+	if (!fdt) {
+		con_out->output_string(con_out, u"Missing device tree\r\n");
+		return EFI_NOT_FOUND;
+	}
+
+	str = get_property("boot-hartid", "chosen");
+	if (!str) {
+		con_out->output_string(con_out,
+				       u"/chosen/boot-hartid missing\r\n");
+		return EFI_NOT_FOUND;
+	}
+	*hartid = f2h(*(fdt32_t *)str);
+	boottime->free_pool(str);
+
+	return EFI_SUCCESS;
+}
+
+/**
+ * prot_get_hart() - get hart ID via RISC-V Boot Protocol
+ *
+ * @hartid:	boot hart ID
+ * Return:	status code
+ */
+static efi_status_t prot_get_hart(efi_uintn_t *hartid)
+{
+	efi_status_t ret;
+	struct riscv_efi_boot_protocol *prot;
+
+	/* Get RISC-V boot protocol */
+	ret = boottime->locate_protocol(&riscv_efi_boot_protocol_guid, NULL,
+					(void **)&prot);
+	if (ret != EFI_SUCCESS) {
+		con_out->output_string(
+			con_out, u"RISC-V Boot Protocol not available\r\n");
+		return ret;
+	}
+
+	/* Get boot hart ID from EFI protocol */
+	ret = prot->get_boot_hartid(prot, hartid);
+	if (ret != EFI_SUCCESS)
+		con_out->output_string(con_out,
+				       u"Could not retrieve boot hart ID\r\n");
+	return ret;
+}
+
+/**
+ * efi_main() - entry point of the EFI application.
+ *
+ * @handle:	handle of the loaded image
+ * @systab:	system table
+ * Return:	status code
+ */
+efi_status_t EFIAPI efi_main(efi_handle_t handle,
+			     struct efi_system_table *systab)
+{
+	efi_status_t ret;
+	efi_uintn_t hartid;
+	u16 buf[16];
+
+	systable = systab;
+	boottime = systable->boottime;
+	con_out = systable->con_out;
+
+	con_out->output_string(con_out,
+			       u"\r\nBoot hart ID\r\n------------\r\n\r\n");
+
+	ret = fdt_get_hart(&hartid);
+	if (ret == EFI_SUCCESS) {
+		con_out->output_string(con_out, u"Device-tree: ");
+		uint2dec(hartid, buf);
+		con_out->output_string(con_out, buf);
+		con_out->output_string(con_out, u"\r\n");
+	}
+
+	ret = prot_get_hart(&hartid);
+	if (ret == EFI_SUCCESS) {
+		con_out->output_string(con_out, u"RISCV_EFI_BOOT_PROTOCOL: ");
+		uint2dec(hartid, buf);
+		con_out->output_string(con_out, buf);
+		con_out->output_string(con_out, u"\r\n");
+	}
+
+	con_out->output_string(con_out, u"\r\n");
+	boottime->exit(handle, EFI_SUCCESS, 0, NULL);
+
+	/* We should never arrive here */
+	return EFI_SUCCESS;
+}
diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c
index c71e87d..788cb9f 100644
--- a/lib/efi_loader/efi_helper.c
+++ b/lib/efi_loader/efi_helper.c
@@ -190,3 +190,36 @@
 
 	return 0;
 }
+
+static int u16_tohex(u16 c)
+{
+	if (c >= '0' && c <= '9')
+		return c - '0';
+	if (c >= 'A' && c <= 'F')
+		return c - 'A' + 10;
+
+	/* not hexadecimal */
+	return -1;
+}
+
+bool efi_varname_is_load_option(u16 *var_name16, int *index)
+{
+	int id, i, digit;
+
+	if (memcmp(var_name16, u"Boot", 8))
+		return false;
+
+	for (id = 0, i = 0; i < 4; i++) {
+		digit = u16_tohex(var_name16[4 + i]);
+		if (digit < 0)
+			break;
+		id = (id << 4) + digit;
+	}
+	if (i == 4 && !var_name16[8]) {
+		if (index)
+			*index = id;
+		return true;
+	}
+
+	return false;
+}
diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
index a17b426..8d347f1 100644
--- a/lib/efi_loader/efi_memory.c
+++ b/lib/efi_loader/efi_memory.c
@@ -823,7 +823,7 @@
 		       uboot_stack_size) & ~EFI_PAGE_MASK;
 	uboot_pages = ((uintptr_t)map_sysmem(gd->ram_top - 1, 0) -
 		       uboot_start + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT;
-	efi_add_memory_map_pg(uboot_start, uboot_pages, EFI_LOADER_DATA,
+	efi_add_memory_map_pg(uboot_start, uboot_pages, EFI_BOOT_SERVICES_CODE,
 			      false);
 
 #if defined(__aarch64__)
@@ -857,7 +857,7 @@
 	/* Request a 32bit 64MB bounce buffer region */
 	uint64_t efi_bounce_buffer_addr = 0xffffffff;
 
-	if (efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, EFI_LOADER_DATA,
+	if (efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, EFI_BOOT_SERVICES_DATA,
 			       (64 * 1024 * 1024) >> EFI_PAGE_SHIFT,
 			       &efi_bounce_buffer_addr) != EFI_SUCCESS)
 		return -1;
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c
index 69276b2..96a5bcc 100644
--- a/lib/efi_loader/efi_net.c
+++ b/lib/efi_loader/efi_net.c
@@ -30,6 +30,7 @@
 static size_t *receive_lengths;
 static int rx_packet_idx;
 static int rx_packet_num;
+static struct efi_net_obj *netobj;
 
 /*
  * The notification function of this event is called in every timer cycle
@@ -660,10 +661,16 @@
 {
 	int maxsize = sizeof(*dhcp_ack);
 
-	if (!dhcp_ack)
+	if (!dhcp_ack) {
 		dhcp_ack = malloc(maxsize);
-
+		if (!dhcp_ack)
+			return;
+	}
+	memset(dhcp_ack, 0, maxsize);
 	memcpy(dhcp_ack, pkt, min(len, maxsize));
+
+	if (netobj)
+		netobj->pxe_mode.dhcp_ack = *dhcp_ack;
 }
 
 /**
@@ -853,7 +860,6 @@
  */
 efi_status_t efi_net_register(void)
 {
-	struct efi_net_obj *netobj = NULL;
 	efi_status_t r;
 	int i;
 
@@ -982,6 +988,7 @@
 	return r;
 out_of_resources:
 	free(netobj);
+	netobj = NULL;
 	free(transmit_buffer);
 	if (receive_buffer)
 		for (i = 0; i < ETH_PACKETS_BATCH_RECV; i++)
diff --git a/lib/efi_loader/efi_unicode_collation.c b/lib/efi_loader/efi_unicode_collation.c
index 36be798..c4c7572 100644
--- a/lib/efi_loader/efi_unicode_collation.c
+++ b/lib/efi_loader/efi_unicode_collation.c
@@ -257,7 +257,7 @@
 	for (i = 0; i < fat_size; ++i) {
 		c = (unsigned char)fat[i];
 		if (c > 0x80)
-			c = codepage[i - 0x80];
+			c = codepage[c - 0x80];
 		string[i] = c;
 		if (!c)
 			break;
diff --git a/lib/efi_selftest/efi_selftest_unicode_collation.c b/lib/efi_selftest/efi_selftest_unicode_collation.c
index c63a1b5..32c99ca 100644
--- a/lib/efi_selftest/efi_selftest_unicode_collation.c
+++ b/lib/efi_selftest/efi_selftest_unicode_collation.c
@@ -178,12 +178,24 @@
 
 	boottime->set_mem(str, sizeof(str), 0);
 	unicode_collation_protocol->fat_to_str(unicode_collation_protocol, 6,
-					       "U-BOOT", str);
+					       "U-BOOT!", str);
 	if (efi_st_strcmp_16_8(str, "U-BOOT")) {
 		efi_st_error("fat_to_str returned \"%ps\"\n", str);
 		return EFI_ST_FAILURE;
 	}
 
+	boottime->set_mem(str, sizeof(str), 0);
+	unicode_collation_protocol->fat_to_str(unicode_collation_protocol, 13,
+					       "Kafb\240tur\000xyz", str);
+	if (str[10]) {
+		efi_st_error("fat_to_str returned to many characters\n");
+		return EFI_ST_FAILURE;
+	}
+	if (efi_st_strcmp_16_8(str, "Kafb\341tur")) {
+		efi_st_error("fat_to_str returned \"%ps\"\n", str);
+		return EFI_ST_FAILURE;
+	}
+
 	return EFI_ST_SUCCESS;
 }
 
diff --git a/lib/efi_selftest/efi_selftest_util.c b/lib/efi_selftest/efi_selftest_util.c
index 7e03e0c..3681fa6 100644
--- a/lib/efi_selftest/efi_selftest_util.c
+++ b/lib/efi_selftest/efi_selftest_util.c
@@ -102,7 +102,7 @@
 	return efi_st_unknown;
 }
 
-int efi_st_strcmp_16_8(const u16 *buf1, const char *buf2)
+int efi_st_strcmp_16_8(const u16 *buf1, const unsigned char *buf2)
 {
 	for (; *buf1 || *buf2; ++buf1, ++buf2) {
 		if (*buf1 != *buf2)
diff --git a/lib/net_utils.c b/lib/net_utils.c
index 72a3b09..4283c13 100644
--- a/lib/net_utils.c
+++ b/lib/net_utils.c
@@ -11,6 +11,7 @@
 
 #include <common.h>
 #include <net.h>
+#include <net6.h>
 
 struct in_addr string_to_ip(const char *s)
 {
@@ -43,6 +44,114 @@
 	return addr;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+int string_to_ip6(const char *str, size_t len, struct in6_addr *addr)
+{
+	int colon_count = 0;
+	int found_double_colon = 0;
+	int xstart = 0;		/* first zero (double colon) */
+	int section_num = 7;	/* num words the double colon represents */
+	int i;
+	const char *s = str;
+	const char *const e = s + len;
+	struct in_addr zero_ip = {.s_addr = 0};
+
+	if (!str)
+		return -1;
+
+	/* First pass, verify the syntax and locate the double colon */
+	while (s < e) {
+		while (s < e && isxdigit((int)*s))
+			s++;
+		if (*s == '\0')
+			break;
+		if (*s != ':') {
+			if (*s == '.' && section_num >= 2) {
+				struct in_addr v4;
+
+				while (s != str && *(s - 1) != ':')
+					--s;
+				v4 = string_to_ip(s);
+				if (memcmp(&zero_ip, &v4,
+					   sizeof(struct in_addr)) != 0) {
+					section_num -= 2;
+					break;
+				}
+			}
+			/* This could be a valid address */
+			break;
+		}
+		if (s == str) {
+			/* The address begins with a colon */
+			if (*++s != ':')
+				/* Must start with a double colon or a number */
+				goto out_err;
+		} else {
+			s++;
+			if (found_double_colon)
+				section_num--;
+			else
+				xstart++;
+		}
+
+		if (*s == ':') {
+			if (found_double_colon)
+				/* Two double colons are not allowed */
+				goto out_err;
+			found_double_colon = 1;
+			section_num -= xstart;
+			s++;
+		}
+
+		if (++colon_count == 7)
+			/* Found all colons */
+			break;
+		++s;
+	}
+
+	if (colon_count == 0)
+		goto out_err;
+	if (*--s == ':')
+		section_num++;
+
+	/* Second pass, read the address */
+	s = str;
+	for (i = 0; i < 8; i++) {
+		int val = 0;
+		char *end;
+
+		if (found_double_colon &&
+		    i >= xstart && i < xstart + section_num) {
+			addr->s6_addr16[i] = 0;
+			continue;
+		}
+		while (*s == ':')
+			s++;
+
+		if (i == 6 && isdigit((int)*s)) {
+			struct in_addr v4 = string_to_ip(s);
+
+			if (memcmp(&zero_ip, &v4,
+				   sizeof(struct in_addr)) != 0) {
+				/* Ending with :IPv4-address */
+				addr->s6_addr32[3] = v4.s_addr;
+				break;
+			}
+		}
+
+		val = simple_strtoul(s, &end, 16);
+		if (end != e && *end != '\0' && *end != ':')
+			goto out_err;
+		addr->s6_addr16[i] = htons(val);
+		s = end;
+	}
+	return 0;
+
+out_err:
+	return -1;
+}
+#endif
+
 void string_to_enetaddr(const char *addr, uint8_t *enetaddr)
 {
 	char *end;
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index fe06aa2..530d808 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -450,10 +450,6 @@
  *       decimal for v4 and colon separated network-order 16 bit hex for v6)
  * - 'i' [46] for 'raw' IPv4/IPv6 addresses, IPv6 omits the colons, IPv4 is
  *       currently the same
- *
- * Note: IPv6 support is currently if(0)'ed out. If you ever need
- * %pI6, please add an IPV6 Kconfig knob, make your code select or
- * depend on that, and change the 0 below to CONFIG_IS_ENABLED(IPV6).
  */
 static char *pointer(const char *fmt, char *buf, char *end, void *ptr,
 		int field_width, int precision, int flags)
@@ -498,8 +494,7 @@
 		flags |= SPECIAL;
 		/* Fallthrough */
 	case 'I':
-		/* %pI6 currently unused */
-		if (0 && fmt[1] == '6')
+		if (IS_ENABLED(CONFIG_IPV6) && fmt[1] == '6')
 			return ip6_addr_string(buf, end, ptr, field_width,
 					       precision, flags);
 		if (fmt[1] == '4')
diff --git a/net/Kconfig b/net/Kconfig
index 52e2618..a1ec3f8 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -174,6 +174,32 @@
 	help
 	  Select maximal length of option 17 root path.
 
+config PROT_TCP
+	bool "TCP stack"
+	help
+	  Enable a generic tcp framework that allows defining a custom
+	  handler for tcp protocol.
+
+config PROT_TCP_SACK
+	bool "TCP SACK support"
+	depends on PROT_TCP
+	help
+	  TCP protocol with SACK. SACK means selective acknowledgements.
+	  By turning this option on TCP will learn what segments are already
+	  received. So that it improves TCP's retransmission efficiency.
+	  This option should be turn on if you want to achieve the fastest
+	  file transfer possible.
+
+config IPV6
+	bool "IPv6 support"
+	help
+	  Enable IPv6 support. It includes Neighbour Discovery protocol, ICMPv6
+	  and auxiliary stuff to make it work. Since it is enabled u-boot
+	  network subsystem can get and handle incoming packets and send packets
+	  through IPv6 network. It allows to use environment variables such as
+	  ip6addr, serverip6. If a u-boot command is capable to parse an IPv6
+	  address and find it, it will force using IPv6 in the network stack.
+
 endif   # if NET
 
 config SYS_RX_ETH_BUFFER
diff --git a/net/Makefile b/net/Makefile
index 6c81250..13eef04 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -20,9 +20,12 @@
 obj-$(CONFIG_DM_MDIO_MUX) += mdio-mux-uclass.o
 obj-$(CONFIG_NET)      += eth_common.o
 obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o
+obj-$(CONFIG_IPV6)     += ndisc.o
 obj-$(CONFIG_NET)      += net.o
+obj-$(CONFIG_IPV6)     += net6.o
 obj-$(CONFIG_CMD_NFS)  += nfs.o
 obj-$(CONFIG_CMD_PING) += ping.o
+obj-$(CONFIG_CMD_PING6) += ping6.o
 obj-$(CONFIG_CMD_PCAP) += pcap.o
 obj-$(CONFIG_CMD_RARP) += rarp.o
 obj-$(CONFIG_CMD_SNTP) += sntp.o
@@ -30,6 +33,8 @@
 obj-$(CONFIG_UDP_FUNCTION_FASTBOOT)  += fastboot.o
 obj-$(CONFIG_CMD_WOL)  += wol.o
 obj-$(CONFIG_PROT_UDP) += udp.o
+obj-$(CONFIG_PROT_TCP) += tcp.o
+obj-$(CONFIG_CMD_WGET) += wget.o
 
 # Disable this warning as it is triggered by:
 # sprintf(buf, index ? "foo%d" : "foo", index)
diff --git a/net/bootp.c b/net/bootp.c
index 6c01e38..7ac0093 100644
--- a/net/bootp.c
+++ b/net/bootp.c
@@ -1078,7 +1078,7 @@
 #endif	/* CONFIG_SYS_BOOTFILE_PREFIX */
 			dhcp_packet_process_options(bp);
 			if (CONFIG_IS_ENABLED(EFI_LOADER) &&
-			    CONFIG_IS_ENABLED(NET_DEVICES))
+			    CONFIG_IS_ENABLED(NETDEVICES))
 				efi_net_set_dhcp_ack(pkt, len);
 
 #if defined(CONFIG_SERVERIP_FROM_PROXYDHCP)
diff --git a/net/ndisc.c b/net/ndisc.c
new file mode 100644
index 0000000..3c0eeea
--- /dev/null
+++ b/net/ndisc.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Allied Telesis Labs NZ
+ * Chris Packham, <judge.packham@gmail.com>
+ *
+ * Copyright (C) 2022 YADRO
+ * Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
+ */
+
+/* Neighbour Discovery for IPv6 */
+
+#include <common.h>
+#include <net.h>
+#include <net6.h>
+#include <ndisc.h>
+
+/* IPv6 destination address of packet waiting for ND */
+struct in6_addr net_nd_sol_packet_ip6 = ZERO_IPV6_ADDR;
+/* IPv6 address we are expecting ND advert from */
+static struct in6_addr net_nd_rep_packet_ip6 = ZERO_IPV6_ADDR;
+/* MAC destination address of packet waiting for ND */
+uchar *net_nd_packet_mac;
+/* pointer to packet waiting to be transmitted after ND is resolved */
+uchar *net_nd_tx_packet;
+static uchar net_nd_packet_buf[PKTSIZE_ALIGN + PKTALIGN];
+/* size of packet waiting to be transmitted */
+int net_nd_tx_packet_size;
+/* the timer for ND resolution */
+ulong net_nd_timer_start;
+/* the number of requests we have sent so far */
+int net_nd_try;
+
+#define IP6_NDISC_OPT_SPACE(len) (((len) + 2 + 7) & ~7)
+
+/**
+ * ndisc_insert_option() - Insert an option into a neighbor discovery packet
+ *
+ * @ndisc:	pointer to ND packet
+ * @type:	option type to insert
+ * @data:	option data to insert
+ * @len:	data length
+ * Return: the number of bytes inserted (which may be >= len)
+ */
+static int
+ndisc_insert_option(struct nd_msg *ndisc, int type, u8 *data, int len)
+{
+	int space = IP6_NDISC_OPT_SPACE(len);
+
+	ndisc->opt[0] = type;
+	ndisc->opt[1] = space >> 3;
+	memcpy(&ndisc->opt[2], data, len);
+	len += 2;
+
+	/* fill the remainder with 0 */
+	if (space - len > 0)
+		memset(&ndisc->opt[len], '\0', space - len);
+
+	return space;
+}
+
+/**
+ * ndisc_extract_enetaddr() - Extract the Ethernet address from a ND packet
+ *
+ * Note that the link layer address could be anything but the only networking
+ * media that u-boot supports is Ethernet so we assume we're extracting a 6
+ * byte Ethernet MAC address.
+ *
+ * @ndisc:	pointer to ND packet
+ * @enetaddr:	extracted MAC addr
+ */
+static void ndisc_extract_enetaddr(struct nd_msg *ndisc, uchar enetaddr[6])
+{
+	memcpy(enetaddr, &ndisc->opt[2], 6);
+}
+
+/**
+ * ndisc_has_option() - Check if the ND packet has the specified option set
+ *
+ * @ip6:	pointer to IPv6 header
+ * @type:	option type to check
+ * Return: 1 if ND has that option, 0 therwise
+ */
+static int ndisc_has_option(struct ip6_hdr *ip6, __u8 type)
+{
+	struct nd_msg *ndisc = (struct nd_msg *)(((uchar *)ip6) + IP6_HDR_SIZE);
+
+	if (ip6->payload_len <= sizeof(struct icmp6hdr))
+		return 0;
+
+	return ndisc->opt[0] == type;
+}
+
+static void ip6_send_ns(struct in6_addr *neigh_addr)
+{
+	struct in6_addr dst_adr;
+	unsigned char enetaddr[6];
+	struct nd_msg *msg;
+	__u16 len;
+	uchar *pkt;
+	unsigned short csum;
+	unsigned int pcsum;
+
+	debug("sending neighbor solicitation for %pI6c our address %pI6c\n",
+	      neigh_addr, &net_link_local_ip6);
+
+	/* calculate src, dest IPv6 addr and dest Eth addr */
+	ip6_make_snma(&dst_adr, neigh_addr);
+	ip6_make_mult_ethdstaddr(enetaddr, &dst_adr);
+	len = sizeof(struct icmp6hdr) + IN6ADDRSZ +
+	    IP6_NDISC_OPT_SPACE(INETHADDRSZ);
+
+	pkt = (uchar *)net_tx_packet;
+	pkt += net_set_ether(pkt, enetaddr, PROT_IP6);
+	pkt += ip6_add_hdr(pkt, &net_link_local_ip6, &dst_adr, PROT_ICMPV6,
+			   IPV6_NDISC_HOPLIMIT, len);
+
+	/* ICMPv6 - NS */
+	msg = (struct nd_msg *)pkt;
+	msg->icmph.icmp6_type = IPV6_NDISC_NEIGHBOUR_SOLICITATION;
+	msg->icmph.icmp6_code = 0;
+	memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16));
+	memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32));
+
+	/* Set the target address and llsaddr option */
+	net_copy_ip6(&msg->target, neigh_addr);
+	ndisc_insert_option(msg, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
+			    INETHADDRSZ);
+
+	/* checksum */
+	pcsum = csum_partial((__u8 *)msg, len, 0);
+	csum = csum_ipv6_magic(&net_link_local_ip6, &dst_adr,
+			       len, PROT_ICMPV6, pcsum);
+	msg->icmph.icmp6_cksum = csum;
+	pkt += len;
+
+	/* send it! */
+	net_send_packet(net_tx_packet, (pkt - net_tx_packet));
+}
+
+static void
+ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr,
+	    struct in6_addr *target)
+{
+	struct nd_msg *msg;
+	__u16 len;
+	uchar *pkt;
+	unsigned short csum;
+
+	debug("sending neighbor advertisement for %pI6c to %pI6c (%pM)\n",
+	      target, neigh_addr, eth_dst_addr);
+
+	len = sizeof(struct icmp6hdr) + IN6ADDRSZ +
+	    IP6_NDISC_OPT_SPACE(INETHADDRSZ);
+
+	pkt = (uchar *)net_tx_packet;
+	pkt += net_set_ether(pkt, eth_dst_addr, PROT_IP6);
+	pkt += ip6_add_hdr(pkt, &net_link_local_ip6, neigh_addr,
+			   PROT_ICMPV6, IPV6_NDISC_HOPLIMIT, len);
+
+	/* ICMPv6 - NA */
+	msg = (struct nd_msg *)pkt;
+	msg->icmph.icmp6_type = IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT;
+	msg->icmph.icmp6_code = 0;
+	memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16));
+	memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32));
+	msg->icmph.icmp6_dataun.u_nd_advt.solicited = 1;
+	msg->icmph.icmp6_dataun.u_nd_advt.override = 1;
+	/* Set the target address and lltargetaddr option */
+	net_copy_ip6(&msg->target, target);
+	ndisc_insert_option(msg, ND_OPT_TARGET_LL_ADDR, net_ethaddr,
+			    INETHADDRSZ);
+
+	/* checksum */
+	csum = csum_ipv6_magic(&net_link_local_ip6,
+			       neigh_addr, len, PROT_ICMPV6,
+			       csum_partial((__u8 *)msg, len, 0));
+	msg->icmph.icmp6_cksum = csum;
+	pkt += len;
+
+	/* send it! */
+	net_send_packet(net_tx_packet, (pkt - net_tx_packet));
+}
+
+void ndisc_request(void)
+{
+	if (!ip6_addr_in_subnet(&net_ip6, &net_nd_sol_packet_ip6,
+				net_prefix_length)) {
+		if (ip6_is_unspecified_addr(&net_gateway6)) {
+			puts("## Warning: gatewayip6 is needed but not set\n");
+			net_nd_rep_packet_ip6 = net_nd_sol_packet_ip6;
+		} else {
+			net_nd_rep_packet_ip6 = net_gateway6;
+		}
+	} else {
+		net_nd_rep_packet_ip6 = net_nd_sol_packet_ip6;
+	}
+
+	ip6_send_ns(&net_nd_rep_packet_ip6);
+}
+
+int ndisc_timeout_check(void)
+{
+	ulong t;
+
+	if (ip6_is_unspecified_addr(&net_nd_sol_packet_ip6))
+		return 0;
+
+	t = get_timer(0);
+
+	/* check for NDISC timeout */
+	if ((t - net_nd_timer_start) > NDISC_TIMEOUT) {
+		net_nd_try++;
+		if (net_nd_try >= NDISC_TIMEOUT_COUNT) {
+			puts("\nNeighbour discovery retry count exceeded; "
+			     "starting again\n");
+			net_nd_try = 0;
+			net_set_state(NETLOOP_FAIL);
+		} else {
+			net_nd_timer_start = t;
+			ndisc_request();
+		}
+	}
+	return 1;
+}
+
+void ndisc_init(void)
+{
+	net_nd_packet_mac = NULL;
+	net_nd_tx_packet = NULL;
+	net_nd_sol_packet_ip6 = net_null_addr_ip6;
+	net_nd_rep_packet_ip6 = net_null_addr_ip6;
+	net_nd_tx_packet_size = 0;
+	net_nd_tx_packet = &net_nd_packet_buf[0] + (PKTALIGN - 1);
+	net_nd_tx_packet -= (ulong)net_nd_tx_packet % PKTALIGN;
+}
+
+int ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
+{
+	struct icmp6hdr *icmp =
+	    (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
+	struct nd_msg *ndisc = (struct nd_msg *)icmp;
+	uchar neigh_eth_addr[6];
+
+	switch (icmp->icmp6_type) {
+	case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
+		debug("received neighbor solicitation for %pI6c from %pI6c\n",
+		      &ndisc->target, &ip6->saddr);
+		if (ip6_is_our_addr(&ndisc->target) &&
+		    ndisc_has_option(ip6, ND_OPT_SOURCE_LL_ADDR)) {
+			ndisc_extract_enetaddr(ndisc, neigh_eth_addr);
+			ip6_send_na(neigh_eth_addr, &ip6->saddr,
+				    &ndisc->target);
+		}
+		break;
+
+	case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT:
+		/* are we waiting for a reply ? */
+		if (ip6_is_unspecified_addr(&net_nd_sol_packet_ip6))
+			break;
+
+		if ((memcmp(&ndisc->target, &net_nd_rep_packet_ip6,
+			    sizeof(struct in6_addr)) == 0) &&
+		    ndisc_has_option(ip6, ND_OPT_TARGET_LL_ADDR)) {
+			ndisc_extract_enetaddr(ndisc, neigh_eth_addr);
+
+			/* save address for later use */
+			if (!net_nd_packet_mac)
+				memcpy(net_nd_packet_mac, neigh_eth_addr, 7);
+
+			/* modify header, and transmit it */
+			memcpy(((struct ethernet_hdr *)net_nd_tx_packet)->et_dest,
+			       neigh_eth_addr, 6);
+
+			net_send_packet(net_nd_tx_packet,
+					net_nd_tx_packet_size);
+
+			/* no ND request pending now */
+			net_nd_sol_packet_ip6 = net_null_addr_ip6;
+			net_nd_tx_packet_size = 0;
+			net_nd_packet_mac = NULL;
+		}
+		break;
+	default:
+		debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type);
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/net/net.c b/net/net.c
index b27b021..1c39acc 100644
--- a/net/net.c
+++ b/net/net.c
@@ -91,6 +91,8 @@
 #include <image.h>
 #include <log.h>
 #include <net.h>
+#include <net6.h>
+#include <ndisc.h>
 #include <net/fastboot.h>
 #include <net/tftp.h>
 #include <net/ncsi.h>
@@ -117,6 +119,8 @@
 #if defined(CONFIG_CMD_WOL)
 #include "wol.h"
 #endif
+#include <net/tcp.h>
+#include <net/wget.h>
 
 /** BOOTP EXTENTIONS **/
 
@@ -341,8 +345,17 @@
 
 static int net_init_loop(void)
 {
-	if (eth_get_dev())
+	if (eth_get_dev()) {
 		memcpy(net_ethaddr, eth_get_ethaddr(), 6);
+
+		if (IS_ENABLED(CONFIG_IPV6)) {
+			ip6_make_lladdr(&net_link_local_ip6, net_ethaddr);
+			if (!memcmp(&net_ip6, &net_null_addr_ip6,
+				    sizeof(struct in6_addr)))
+				memcpy(&net_ip6, &net_link_local_ip6,
+				       sizeof(struct in6_addr));
+		}
+	}
 	else
 		/*
 		 * Not ideal, but there's no way to get the actual error, and I
@@ -383,10 +396,13 @@
 				(i + 1) * PKTSIZE_ALIGN;
 		}
 		arp_init();
+		ndisc_init();
 		net_clear_handlers();
 
 		/* Only need to setup buffer pointers once. */
 		first_call = 0;
+		if (IS_ENABLED(CONFIG_PROT_TCP))
+			tcp_set_tcp_state(TCP_CLOSED);
 	}
 
 	return net_init_loop();
@@ -509,11 +525,21 @@
 			ping_start();
 			break;
 #endif
+#if defined(CONFIG_CMD_PING6)
+		case PING6:
+			ping6_start();
+			break;
+#endif
 #if defined(CONFIG_CMD_NFS) && !defined(CONFIG_SPL_BUILD)
 		case NFS:
 			nfs_start();
 			break;
 #endif
+#if defined(CONFIG_CMD_WGET)
+		case WGET:
+			wget_start();
+			break;
+#endif
 #if defined(CONFIG_CMD_CDP)
 		case CDP:
 			cdp_start();
@@ -580,6 +606,11 @@
 		if (arp_timeout_check() > 0)
 			time_start = get_timer(0);
 
+		if (IS_ENABLED(CONFIG_IPV6)) {
+			if (use_ip6 && (ndisc_timeout_check() > 0))
+				time_start = get_timer(0);
+		}
+
 		/*
 		 *	Check the ethernet for a new packet.  The ethernet
 		 *	receive routine will process it.
@@ -832,6 +863,16 @@
 	return net_send_ip_packet(ether, dest, dport, sport, payload_len,
 				  IPPROTO_UDP, 0, 0, 0);
 }
+
+#if defined(CONFIG_PROT_TCP)
+int net_send_tcp_packet(int payload_len, int dport, int sport, u8 action,
+			u32 tcp_seq_num, u32 tcp_ack_num)
+{
+	return net_send_ip_packet(net_server_ethaddr, net_server_ip, dport,
+				  sport, payload_len, IPPROTO_TCP, action,
+				  tcp_seq_num, tcp_ack_num);
+}
+#endif
 
 int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport,
 		       int payload_len, int proto, u8 action, u32 tcp_seq_num,
@@ -864,6 +905,14 @@
 				   payload_len);
 		pkt_hdr_size = eth_hdr_size + IP_UDP_HDR_SIZE;
 		break;
+#if defined(CONFIG_PROT_TCP)
+	case IPPROTO_TCP:
+		pkt_hdr_size = eth_hdr_size
+			+ tcp_set_tcp_header(pkt + eth_hdr_size, dport, sport,
+					     payload_len, action, tcp_seq_num,
+					     tcp_ack_num);
+		break;
+#endif
 	default:
 		return -EINVAL;
 	}
@@ -924,7 +973,11 @@
 	int offset8, start, len, done = 0;
 	u16 ip_off = ntohs(ip->ip_off);
 
-	if (ip->ip_len < IP_MIN_FRAG_DATAGRAM_SIZE)
+	/*
+	 * Calling code already rejected <, but we don't have to deal
+	 * with an IP fragment with no payload.
+	 */
+	if (ntohs(ip->ip_len) <= IP_HDR_SIZE)
 		return NULL;
 
 	/* payload starts after IP header, this fragment is in there */
@@ -934,6 +987,10 @@
 	start = offset8 * 8;
 	len = ntohs(ip->ip_len) - IP_HDR_SIZE;
 
+	/* All but last fragment must have a multiple-of-8 payload. */
+	if ((len & 7) && (ip_off & IP_FLAGS_MFRAG))
+		return NULL;
+
 	if (start + len > IP_MAXUDP) /* fragment extends too far */
 		return NULL;
 
@@ -977,10 +1034,14 @@
 	}
 
 	/*
-	 * There is some overlap: fix the hole list. This code doesn't
-	 * deal with a fragment that overlaps with two different holes
-	 * (thus being a superset of a previously-received fragment).
+	 * There is some overlap: fix the hole list. This code deals
+	 * with a fragment that overlaps with two different holes
+	 * (thus being a superset of a previously-received fragment)
+	 * by only using the part of the fragment that fits in the
+	 * first hole.
 	 */
+	if (h->last_byte < start + len)
+		len = h->last_byte - start;
 
 	if ((h >= thisfrag) && (h->last_byte <= start + len)) {
 		/* complete overlap with hole: remove hole */
@@ -1032,8 +1093,8 @@
 	if (!done)
 		return NULL;
 
-	localip->ip_len = htons(total_len);
 	*lenp = total_len + IP_HDR_SIZE;
+	localip->ip_len = htons(*lenp);
 	return localip;
 }
 
@@ -1205,12 +1266,16 @@
 		rarp_receive(ip, len);
 		break;
 #endif
+#if IS_ENABLED(CONFIG_IPV6)
+	case PROT_IP6:
+		net_ip6_handler(et, (struct ip6_hdr *)ip, len);
+#endif
 	case PROT_IP:
 		debug_cond(DEBUG_NET_PKT, "Got IP\n");
 		/* Before we start poking the header, make sure it is there */
-		if (len < IP_UDP_HDR_SIZE) {
+		if (len < IP_HDR_SIZE) {
 			debug("len bad %d < %lu\n", len,
-			      (ulong)IP_UDP_HDR_SIZE);
+			      (ulong)IP_HDR_SIZE);
 			return;
 		}
 		/* Check the packet length */
@@ -1219,6 +1284,10 @@
 			return;
 		}
 		len = ntohs(ip->ip_len);
+		if (len < IP_HDR_SIZE) {
+			debug("bad ip->ip_len %d < %d\n", len, (int)IP_HDR_SIZE);
+			return;
+		}
 		debug_cond(DEBUG_NET_PKT, "len=%d, v=%02x\n",
 			   len, ip->ip_hl_v & 0xff);
 
@@ -1226,7 +1295,7 @@
 		if ((ip->ip_hl_v & 0xf0) != 0x40)
 			return;
 		/* Can't deal with IP options (headers != 20 bytes) */
-		if ((ip->ip_hl_v & 0x0f) > 0x05)
+		if ((ip->ip_hl_v & 0x0f) != 0x05)
 			return;
 		/* Check the Checksum of the header */
 		if (!ip_checksum_ok((uchar *)ip, IP_HDR_SIZE)) {
@@ -1273,11 +1342,20 @@
 		if (ip->ip_p == IPPROTO_ICMP) {
 			receive_icmp(ip, len, src_ip, et);
 			return;
+#if defined(CONFIG_PROT_TCP)
+		} else if (ip->ip_p == IPPROTO_TCP) {
+			debug_cond(DEBUG_DEV_PKT,
+				   "TCP PH (to=%pI4, from=%pI4, len=%d)\n",
+				   &dst_ip, &src_ip, len);
+
+			rxhand_tcp_f((union tcp_build_pkt *)ip, len);
+			return;
+#endif
 		} else if (ip->ip_p != IPPROTO_UDP) {	/* Only UDP packets */
 			return;
 		}
 
-		if (ntohs(ip->udp_len) < UDP_HDR_SIZE || ntohs(ip->udp_len) > ntohs(ip->ip_len))
+		if (ntohs(ip->udp_len) < UDP_HDR_SIZE || ntohs(ip->udp_len) > len - IP_HDR_SIZE)
 			return;
 
 		debug_cond(DEBUG_DEV_PKT,
@@ -1361,6 +1439,14 @@
 		}
 		goto common;
 #endif
+#if defined(CONFIG_CMD_PING6)
+	case PING6:
+		if (ip6_is_unspecified_addr(&net_ping_ip6)) {
+			puts("*** ERROR: ping address not given\n");
+			return 1;
+		}
+		goto common;
+#endif
 #if defined(CONFIG_CMD_DNS)
 	case DNS:
 		if (net_dns_server.s_addr == 0) {
@@ -1382,7 +1468,14 @@
 		/* Fall through */
 	case TFTPGET:
 	case TFTPPUT:
-		if (net_server_ip.s_addr == 0 && !is_serverip_in_cmd()) {
+		if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
+			if (!memcmp(&net_server_ip6, &net_null_addr_ip6,
+				    sizeof(struct in6_addr)) &&
+				    !strchr(net_boot_file_name, '[')) {
+				puts("*** ERROR: `serverip6' not set\n");
+				return 1;
+			}
+		} else if (net_server_ip.s_addr == 0 && !is_serverip_in_cmd()) {
 			puts("*** ERROR: `serverip' not set\n");
 			return 1;
 		}
@@ -1395,7 +1488,13 @@
 	case NETCONS:
 	case FASTBOOT:
 	case TFTPSRV:
-		if (net_ip.s_addr == 0) {
+		if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
+			if (!memcmp(&net_link_local_ip6, &net_null_addr_ip6,
+				    sizeof(struct in6_addr))) {
+				puts("*** ERROR: `ip6addr` not set\n");
+				return 1;
+			}
+		} else if (net_ip.s_addr == 0) {
 			puts("*** ERROR: `ipaddr' not set\n");
 			return 1;
 		}
diff --git a/net/net6.c b/net/net6.c
new file mode 100644
index 0000000..fdea078
--- /dev/null
+++ b/net/net6.c
@@ -0,0 +1,445 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Allied Telesis Labs NZ
+ * Chris Packham, <judge.packham@gmail.com>
+ *
+ * Copyright (C) 2022 YADRO
+ * Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
+ */
+
+/* Simple IPv6 network layer implementation */
+
+#include <common.h>
+#include <env_internal.h>
+#include <malloc.h>
+#include <net.h>
+#include <net6.h>
+#include <ndisc.h>
+
+/* NULL IPv6 address */
+struct in6_addr const net_null_addr_ip6 = ZERO_IPV6_ADDR;
+/* Our gateway's IPv6 address */
+struct in6_addr net_gateway6 = ZERO_IPV6_ADDR;
+/* Our IPv6 addr (0 = unknown) */
+struct in6_addr net_ip6 = ZERO_IPV6_ADDR;
+/* Our link local IPv6 addr (0 = unknown) */
+struct in6_addr net_link_local_ip6 = ZERO_IPV6_ADDR;
+/* set server IPv6 addr (0 = unknown) */
+struct in6_addr net_server_ip6 = ZERO_IPV6_ADDR;
+/* The prefix length of our network */
+u32 net_prefix_length;
+
+bool use_ip6;
+
+static int on_ip6addr(const char *name, const char *value, enum env_op op,
+		      int flags)
+{
+	char *mask;
+	size_t len;
+
+	if (flags & H_PROGRAMMATIC)
+		return 0;
+
+	if (op == env_op_delete) {
+		net_prefix_length = 0;
+		net_copy_ip6(&net_ip6, &net_null_addr_ip6);
+		return 0;
+	}
+
+	mask = strchr(value, '/');
+	len = strlen(value);
+
+	if (mask)
+		net_prefix_length = simple_strtoul(value + len, NULL, 10);
+
+	return string_to_ip6(value, len, &net_ip6);
+}
+
+U_BOOT_ENV_CALLBACK(ip6addr, on_ip6addr);
+
+static int on_gatewayip6(const char *name, const char *value, enum env_op op,
+			 int flags)
+{
+	if (flags & H_PROGRAMMATIC)
+		return 0;
+
+	return string_to_ip6(value, strlen(value), &net_gateway6);
+}
+
+U_BOOT_ENV_CALLBACK(gatewayip6, on_gatewayip6);
+
+static int on_serverip6(const char *name, const char *value, enum env_op op,
+			int flags)
+{
+	if (flags & H_PROGRAMMATIC)
+		return 0;
+
+	return string_to_ip6(value, strlen(value), &net_server_ip6);
+}
+
+U_BOOT_ENV_CALLBACK(serverip6, on_serverip6);
+
+int ip6_is_unspecified_addr(struct in6_addr *addr)
+{
+	return !(addr->s6_addr32[0] | addr->s6_addr32[1] |
+		addr->s6_addr32[2] | addr->s6_addr32[3]);
+}
+
+int ip6_is_our_addr(struct in6_addr *addr)
+{
+	return !memcmp(addr, &net_link_local_ip6, sizeof(struct in6_addr)) ||
+	       !memcmp(addr, &net_ip6, sizeof(struct in6_addr));
+}
+
+void ip6_make_eui(unsigned char eui[8], unsigned char const enetaddr[6])
+{
+	memcpy(eui, enetaddr, 3);
+	memcpy(&eui[5], &enetaddr[3], 3);
+	eui[3] = 0xff;
+	eui[4] = 0xfe;
+	eui[0] ^= 2;		/* "u" bit set to indicate global scope */
+}
+
+void ip6_make_lladdr(struct in6_addr *lladr, unsigned char const enetaddr[6])
+{
+	unsigned char eui[8];
+
+	memset(lladr, 0, sizeof(struct in6_addr));
+	lladr->s6_addr16[0] = htons(IPV6_LINK_LOCAL_PREFIX);
+	ip6_make_eui(eui, enetaddr);
+	memcpy(&lladr->s6_addr[8], eui, 8);
+}
+
+void ip6_make_snma(struct in6_addr *mcast_addr, struct in6_addr *ip6_addr)
+{
+	memset(mcast_addr, 0, sizeof(struct in6_addr));
+	mcast_addr->s6_addr[0] = 0xff;
+	mcast_addr->s6_addr[1] = IPV6_ADDRSCOPE_LINK;
+	mcast_addr->s6_addr[11] = 0x01;
+	mcast_addr->s6_addr[12] = 0xff;
+	mcast_addr->s6_addr[13] = ip6_addr->s6_addr[13];
+	mcast_addr->s6_addr[14] = ip6_addr->s6_addr[14];
+	mcast_addr->s6_addr[15] = ip6_addr->s6_addr[15];
+}
+
+void
+ip6_make_mult_ethdstaddr(unsigned char enetaddr[6], struct in6_addr *mcast_addr)
+{
+	enetaddr[0] = 0x33;
+	enetaddr[1] = 0x33;
+	memcpy(&enetaddr[2], &mcast_addr->s6_addr[12], 4);
+}
+
+int
+ip6_addr_in_subnet(struct in6_addr *our_addr, struct in6_addr *neigh_addr,
+		   u32 plen)
+{
+	__be32 *addr_dwords;
+	__be32 *neigh_dwords;
+
+	addr_dwords = our_addr->s6_addr32;
+	neigh_dwords = neigh_addr->s6_addr32;
+
+	while (plen > 32) {
+		if (*addr_dwords++ != *neigh_dwords++)
+			return 0;
+
+		plen -= 32;
+	}
+
+	/* Check any remaining bits */
+	if (plen > 0) {
+		if ((*addr_dwords >> (32 - plen)) !=
+		    (*neigh_dwords >> (32 - plen))) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+static inline unsigned int csum_fold(unsigned int sum)
+{
+	sum = (sum & 0xffff) + (sum >> 16);
+	sum = (sum & 0xffff) + (sum >> 16);
+
+	/* Opaque moment. If reverse it to zero it will not be checked on
+	 * receiver's side. It leads to bad negibour advertisement.
+	 */
+	if (sum == 0xffff)
+		return sum;
+
+	return ~sum;
+}
+
+static inline unsigned short from32to16(unsigned int x)
+{
+	/* add up 16-bit and 16-bit for 16+c bit */
+	x = (x & 0xffff) + (x >> 16);
+	/* add up carry.. */
+	x = (x & 0xffff) + (x >> 16);
+	return x;
+}
+
+static u32 csum_do_csum(const u8 *buff, int len)
+{
+	int odd;
+	unsigned int result = 0;
+
+	if (len <= 0)
+		goto out;
+	odd = 1 & (unsigned long)buff;
+	if (odd) {
+#ifdef __LITTLE_ENDIAN
+		result += (*buff << 8);
+#else
+		result = *buff;
+#endif
+		len--;
+		buff++;
+	}
+	if (len >= 2) {
+		if (2 & (unsigned long)buff) {
+			result += *(unsigned short *)buff;
+			len -= 2;
+			buff += 2;
+		}
+		if (len >= 4) {
+			const unsigned char *end = buff + ((u32)len & ~3);
+			unsigned int carry = 0;
+
+			do {
+				unsigned int w = *(unsigned int *)buff;
+
+				buff += 4;
+				result += carry;
+				result += w;
+				carry = (w > result);
+			} while (buff < end);
+			result += carry;
+			result = (result & 0xffff) + (result >> 16);
+		}
+		if (len & 2) {
+			result += *(unsigned short *)buff;
+			buff += 2;
+		}
+	}
+	if (len & 1)
+#ifdef __LITTLE_ENDIAN
+		result += *buff;
+#else
+		result += (*buff << 8);
+#endif
+	result = from32to16(result);
+	if (odd)
+		result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
+out:
+	return result;
+}
+
+unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum)
+{
+	unsigned int result = csum_do_csum(buff, len);
+
+	/* add in old sum, and carry.. */
+	result += sum;
+	/* 16+c bits -> 16 bits */
+	result = (result & 0xffff) + (result >> 16);
+	return result;
+}
+
+unsigned short int
+csum_ipv6_magic(struct in6_addr *saddr, struct in6_addr *daddr, u16 len,
+		unsigned short proto, unsigned int csum)
+{
+	int carry;
+	u32 ulen;
+	u32 uproto;
+	u32 sum = csum;
+
+	sum += saddr->s6_addr32[0];
+	carry = (sum < saddr->s6_addr32[0]);
+	sum += carry;
+
+	sum += saddr->s6_addr32[1];
+	carry = (sum < saddr->s6_addr32[1]);
+	sum += carry;
+
+	sum += saddr->s6_addr32[2];
+	carry = (sum < saddr->s6_addr32[2]);
+	sum += carry;
+
+	sum += saddr->s6_addr32[3];
+	carry = (sum < saddr->s6_addr32[3]);
+	sum += carry;
+
+	sum += daddr->s6_addr32[0];
+	carry = (sum < daddr->s6_addr32[0]);
+	sum += carry;
+
+	sum += daddr->s6_addr32[1];
+	carry = (sum < daddr->s6_addr32[1]);
+	sum += carry;
+
+	sum += daddr->s6_addr32[2];
+	carry = (sum < daddr->s6_addr32[2]);
+	sum += carry;
+
+	sum += daddr->s6_addr32[3];
+	carry = (sum < daddr->s6_addr32[3]);
+	sum += carry;
+
+	ulen = htonl((u32)len);
+	sum += ulen;
+	carry = (sum < ulen);
+	sum += carry;
+
+	uproto = htonl(proto);
+	sum += uproto;
+	carry = (sum < uproto);
+	sum += carry;
+
+	return csum_fold(sum);
+}
+
+int ip6_add_hdr(uchar *xip, struct in6_addr *src, struct in6_addr *dest,
+		int nextheader, int hoplimit, int payload_len)
+{
+	struct ip6_hdr *ip6 = (struct ip6_hdr *)xip;
+
+	ip6->version = 6;
+	ip6->priority = 0;
+	ip6->flow_lbl[0] = 0;
+	ip6->flow_lbl[1] = 0;
+	ip6->flow_lbl[2] = 0;
+	ip6->payload_len = htons(payload_len);
+	ip6->nexthdr = nextheader;
+	ip6->hop_limit = hoplimit;
+	net_copy_ip6(&ip6->saddr, src);
+	net_copy_ip6(&ip6->daddr, dest);
+
+	return sizeof(struct ip6_hdr);
+}
+
+int net_send_udp_packet6(uchar *ether, struct in6_addr *dest, int dport,
+			 int sport, int len)
+{
+	uchar *pkt;
+	struct udp_hdr *udp;
+	u16 csum_p;
+
+	udp = (struct udp_hdr *)((uchar *)net_tx_packet + net_eth_hdr_size() +
+			IP6_HDR_SIZE);
+
+	udp->udp_dst = htons(dport);
+	udp->udp_src = htons(sport);
+	udp->udp_len = htons(len + UDP_HDR_SIZE);
+
+	/* checksum */
+	udp->udp_xsum = 0;
+	csum_p = csum_partial((u8 *)udp, len + UDP_HDR_SIZE, 0);
+	udp->udp_xsum = csum_ipv6_magic(&net_ip6, dest, len + UDP_HDR_SIZE,
+					IPPROTO_UDP, csum_p);
+
+	/* if MAC address was not discovered yet, save the packet and do
+	 * neighbour discovery
+	 */
+	if (!memcmp(ether, net_null_ethaddr, 6)) {
+		net_copy_ip6(&net_nd_sol_packet_ip6, dest);
+		net_nd_packet_mac = ether;
+
+		pkt = net_nd_tx_packet;
+		pkt += net_set_ether(pkt, net_nd_packet_mac, PROT_IP6);
+		pkt += ip6_add_hdr(pkt, &net_ip6, dest, IPPROTO_UDP, 64,
+				len + UDP_HDR_SIZE);
+		memcpy(pkt, (uchar *)udp, len + UDP_HDR_SIZE);
+
+		/* size of the waiting packet */
+		net_nd_tx_packet_size = (pkt - net_nd_tx_packet) +
+			UDP_HDR_SIZE + len;
+
+		/* and do the neighbor solicitation */
+		net_nd_try = 1;
+		net_nd_timer_start = get_timer(0);
+		ndisc_request();
+		return 1;	/* waiting */
+	}
+
+	pkt = (uchar *)net_tx_packet;
+	pkt += net_set_ether(pkt, ether, PROT_IP6);
+	pkt += ip6_add_hdr(pkt, &net_ip6, dest, IPPROTO_UDP, 64,
+			len + UDP_HDR_SIZE);
+	(void)eth_send(net_tx_packet, pkt - net_tx_packet + UDP_HDR_SIZE + len);
+
+	return 0;	/* transmitted */
+}
+
+int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
+{
+	struct in_addr zero_ip = {.s_addr = 0 };
+	struct icmp6hdr *icmp;
+	struct udp_hdr *udp;
+	u16 csum;
+	u16 csum_p;
+	u16 hlen;
+
+	if (len < IP6_HDR_SIZE)
+		return -EINVAL;
+
+	if (ip6->version != 6)
+		return -EINVAL;
+
+	switch (ip6->nexthdr) {
+	case PROT_ICMPV6:
+		icmp = (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
+		csum = icmp->icmp6_cksum;
+		hlen = ntohs(ip6->payload_len);
+		icmp->icmp6_cksum = 0;
+		/* checksum */
+		csum_p = csum_partial((u8 *)icmp, hlen, 0);
+		icmp->icmp6_cksum = csum_ipv6_magic(&ip6->saddr, &ip6->daddr,
+						    hlen, PROT_ICMPV6, csum_p);
+
+		if (icmp->icmp6_cksum != csum)
+			return -EINVAL;
+
+		switch (icmp->icmp6_type) {
+		case IPV6_ICMP_ECHO_REQUEST:
+		case IPV6_ICMP_ECHO_REPLY:
+			ping6_receive(et, ip6, len);
+			break;
+		case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
+		case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT:
+			ndisc_receive(et, ip6, len);
+			break;
+		default:
+			break;
+		}
+		break;
+	case IPPROTO_UDP:
+		udp = (struct udp_hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
+		csum = udp->udp_xsum;
+		hlen = ntohs(ip6->payload_len);
+		udp->udp_xsum = 0;
+		/* checksum */
+		csum_p = csum_partial((u8 *)udp, hlen, 0);
+		udp->udp_xsum = csum_ipv6_magic(&ip6->saddr, &ip6->daddr,
+						hlen, IPPROTO_UDP, csum_p);
+
+		if (csum != udp->udp_xsum)
+			return -EINVAL;
+
+		/* IP header OK. Pass the packet to the current handler. */
+		net_get_udp_handler()((uchar *)ip6 + IP6_HDR_SIZE +
+					UDP_HDR_SIZE,
+				ntohs(udp->udp_dst),
+				zero_ip,
+				ntohs(udp->udp_src),
+				ntohs(udp->udp_len) - 8);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
diff --git a/net/ping6.c b/net/ping6.c
new file mode 100644
index 0000000..4882a17
--- /dev/null
+++ b/net/ping6.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Allied Telesis Labs NZ
+ * Chris Packham, <judge.packham@gmail.com>
+ *
+ * Copyright (C) 2022 YADRO
+ * Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
+ */
+
+/* Simple ping6 implementation */
+
+#include <common.h>
+#include <net.h>
+#include <net6.h>
+#include "ndisc.h"
+
+static ushort seq_no;
+
+/* the ipv6 address to ping */
+struct in6_addr net_ping_ip6;
+
+int
+ip6_make_ping(uchar *eth_dst_addr, struct in6_addr *neigh_addr, uchar *pkt)
+{
+	struct echo_msg *msg;
+	u16 len;
+	u16 csum_p;
+	uchar *pkt_old = pkt;
+
+	len = sizeof(struct echo_msg);
+
+	pkt += net_set_ether(pkt, eth_dst_addr, PROT_IP6);
+	pkt += ip6_add_hdr(pkt, &net_ip6, neigh_addr, PROT_ICMPV6,
+			   IPV6_NDISC_HOPLIMIT, len);
+
+	/* ICMPv6 - Echo */
+	msg = (struct echo_msg *)pkt;
+	msg->icmph.icmp6_type = IPV6_ICMP_ECHO_REQUEST;
+	msg->icmph.icmp6_code = 0;
+	msg->icmph.icmp6_cksum = 0;
+	msg->icmph.icmp6_identifier = 0;
+	msg->icmph.icmp6_sequence = htons(seq_no++);
+	msg->id = msg->icmph.icmp6_identifier;	/* these seem redundant */
+	msg->sequence = msg->icmph.icmp6_sequence;
+
+	/* checksum */
+	csum_p = csum_partial((u8 *)msg, len, 0);
+	msg->icmph.icmp6_cksum = csum_ipv6_magic(&net_ip6, neigh_addr, len,
+						 PROT_ICMPV6, csum_p);
+
+	pkt += len;
+
+	return pkt - pkt_old;
+}
+
+int ping6_send(void)
+{
+	uchar *pkt;
+	static uchar mac[6];
+
+	/* always send neighbor solicit */
+
+	memcpy(mac, net_null_ethaddr, 6);
+
+	net_nd_sol_packet_ip6 = net_ping_ip6;
+	net_nd_packet_mac = mac;
+
+	pkt = net_nd_tx_packet;
+	pkt += ip6_make_ping(mac, &net_ping_ip6, pkt);
+
+	/* size of the waiting packet */
+	net_nd_tx_packet_size = (pkt - net_nd_tx_packet);
+
+	/* and do the ARP request */
+	net_nd_try = 1;
+	net_nd_timer_start = get_timer(0);
+	ndisc_request();
+	return 1;		/* waiting */
+}
+
+static void ping6_timeout(void)
+{
+	eth_halt();
+	net_set_state(NETLOOP_FAIL);	/* we did not get the reply */
+}
+
+void ping6_start(void)
+{
+	printf("Using %s device\n", eth_get_name());
+	net_set_timeout_handler(10000UL, ping6_timeout);
+
+	ping6_send();
+}
+
+int ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
+{
+	struct icmp6hdr *icmp =
+	    (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
+	struct in6_addr src_ip;
+
+	switch (icmp->icmp6_type) {
+	case IPV6_ICMP_ECHO_REPLY:
+		src_ip = ip6->saddr;
+		if (memcmp(&net_ping_ip6, &src_ip, sizeof(struct in6_addr)))
+			return -EINVAL;
+		net_set_state(NETLOOP_SUCCESS);
+		break;
+	case IPV6_ICMP_ECHO_REQUEST:
+		/* ignore for now.... */
+		debug("Got ICMPv6 ECHO REQUEST from %pI6c\n", &ip6->saddr);
+		return -EINVAL;
+	default:
+		debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type);
+		return -EINVAL;
+	}
+
+	return 0;
+}
diff --git a/net/tcp.c b/net/tcp.c
new file mode 100644
index 0000000..8d338c7
--- /dev/null
+++ b/net/tcp.c
@@ -0,0 +1,720 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2017 Duncan Hare, all rights reserved.
+ */
+
+/*
+ * General Desription:
+ *
+ * TCP support for the wget command, for fast file downloading.
+ *
+ * HTTP/TCP Receiver:
+ *
+ *      Prerequisites:  - own ethernet address
+ *                      - own IP address
+ *                      - Server IP address
+ *                      - Server with TCP
+ *                      - TCP application (eg wget)
+ *      Next Step       HTTPS?
+ */
+#include <common.h>
+#include <command.h>
+#include <console.h>
+#include <env_internal.h>
+#include <errno.h>
+#include <net.h>
+#include <net/tcp.h>
+
+/*
+ * TCP sliding window  control used by us to request re-TX
+ */
+static struct tcp_sack_v tcp_lost;
+
+/* TCP option timestamp */
+static u32 loc_timestamp;
+static u32 rmt_timestamp;
+
+static u32 tcp_seq_init;
+static u32 tcp_ack_edge;
+static u32 tcp_seq_max;
+
+static int tcp_activity_count;
+
+/*
+ * Search for TCP_SACK and review the comments before the code section
+ * TCP_SACK is the number of packets at the front of the stream
+ */
+
+enum pkt_state {PKT, NOPKT};
+struct sack_r {
+	struct sack_edges se;
+	enum pkt_state st;
+};
+
+static struct sack_r edge_a[TCP_SACK];
+static unsigned int sack_idx;
+static unsigned int prev_len;
+
+/*
+ * TCP lengths are stored as a rounded up number of 32 bit words.
+ * Add 3 to length round up, rounded, then divided into the
+ * length in 32 bit words.
+ */
+#define LEN_B_TO_DW(x) ((x) >> 2)
+#define ROUND_TCPHDR_LEN(x) (LEN_B_TO_DW((x) + 3))
+#define SHIFT_TO_TCPHDRLEN_FIELD(x) ((x) << 4)
+#define GET_TCP_HDR_LEN_IN_BYTES(x) ((x) >> 2)
+
+/* TCP connection state */
+static enum tcp_state current_tcp_state;
+
+/* Current TCP RX packet handler */
+static rxhand_tcp *tcp_packet_handler;
+
+/**
+ * tcp_get_tcp_state() - get current TCP state
+ *
+ * Return: Current TCP state
+ */
+enum tcp_state tcp_get_tcp_state(void)
+{
+	return current_tcp_state;
+}
+
+/**
+ * tcp_set_tcp_state() - set current TCP state
+ * @new_state: new TCP state
+ */
+void tcp_set_tcp_state(enum tcp_state new_state)
+{
+	current_tcp_state = new_state;
+}
+
+static void dummy_handler(uchar *pkt, unsigned int dport,
+			  struct in_addr sip, unsigned int sport,
+			  unsigned int len)
+{
+}
+
+/**
+ * tcp_set_tcp_handler() - set a handler to receive data
+ * @f: handler
+ */
+void tcp_set_tcp_handler(rxhand_tcp *f)
+{
+	debug_cond(DEBUG_INT_STATE, "--- net_loop TCP handler set (%p)\n", f);
+	if (!f)
+		tcp_packet_handler = dummy_handler;
+	else
+		tcp_packet_handler = f;
+}
+
+/**
+ * tcp_set_pseudo_header() - set TCP pseudo header
+ * @pkt: the packet
+ * @src: source IP address
+ * @dest: destinaion IP address
+ * @tcp_len: tcp length
+ * @pkt_len: packet length
+ *
+ * Return: the checksum of the packet
+ */
+u16 tcp_set_pseudo_header(uchar *pkt, struct in_addr src, struct in_addr dest,
+			  int tcp_len, int pkt_len)
+{
+	union tcp_build_pkt *b = (union tcp_build_pkt *)pkt;
+	int checksum_len;
+
+	/*
+	 * Pseudo header
+	 *
+	 * Zero the byte after the last byte so that the header checksum
+	 * will always work.
+	 */
+	pkt[pkt_len] = 0;
+
+	net_copy_ip((void *)&b->ph.p_src, &src);
+	net_copy_ip((void *)&b->ph.p_dst, &dest);
+	b->ph.rsvd = 0;
+	b->ph.p	= IPPROTO_TCP;
+	b->ph.len = htons(tcp_len);
+	checksum_len = tcp_len + PSEUDO_HDR_SIZE;
+
+	debug_cond(DEBUG_DEV_PKT,
+		   "TCP Pesudo  Header  (to=%pI4, from=%pI4, Len=%d)\n",
+		   &b->ph.p_dst, &b->ph.p_src, checksum_len);
+
+	return compute_ip_checksum(pkt + PSEUDO_PAD_SIZE, checksum_len);
+}
+
+/**
+ * net_set_ack_options() - set TCP options in acknowledge packets
+ * @b: the packet
+ *
+ * Return: TCP header length
+ */
+int net_set_ack_options(union tcp_build_pkt *b)
+{
+	b->sack.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
+
+	b->sack.t_opt.kind = TCP_O_TS;
+	b->sack.t_opt.len = TCP_OPT_LEN_A;
+	b->sack.t_opt.t_snd = htons(loc_timestamp);
+	b->sack.t_opt.t_rcv = rmt_timestamp;
+	b->sack.sack_v.kind = TCP_1_NOP;
+	b->sack.sack_v.len = 0;
+
+	if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) {
+		if (tcp_lost.len > TCP_OPT_LEN_2) {
+			debug_cond(DEBUG_DEV_PKT, "TCP ack opt lost.len %x\n",
+				   tcp_lost.len);
+			b->sack.sack_v.len = tcp_lost.len;
+			b->sack.sack_v.kind = TCP_V_SACK;
+			b->sack.sack_v.hill[0].l = htonl(tcp_lost.hill[0].l);
+			b->sack.sack_v.hill[0].r = htonl(tcp_lost.hill[0].r);
+
+			/*
+			 * These SACK structures are initialized with NOPs to
+			 * provide TCP header alignment padding. There are 4
+			 * SACK structures used for both header padding and
+			 * internally.
+			 */
+			b->sack.sack_v.hill[1].l = htonl(tcp_lost.hill[1].l);
+			b->sack.sack_v.hill[1].r = htonl(tcp_lost.hill[1].r);
+			b->sack.sack_v.hill[2].l = htonl(tcp_lost.hill[2].l);
+			b->sack.sack_v.hill[2].r = htonl(tcp_lost.hill[2].r);
+			b->sack.sack_v.hill[3].l = TCP_O_NOP;
+			b->sack.sack_v.hill[3].r = TCP_O_NOP;
+		}
+
+		b->sack.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(ROUND_TCPHDR_LEN(TCP_HDR_SIZE +
+										 TCP_TSOPT_SIZE +
+										 tcp_lost.len));
+	} else {
+		b->sack.sack_v.kind = 0;
+		b->sack.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(ROUND_TCPHDR_LEN(TCP_HDR_SIZE +
+										 TCP_TSOPT_SIZE));
+	}
+
+	/*
+	 * This returns the actual rounded up length of the
+	 * TCP header to add to the total packet length
+	 */
+
+	return GET_TCP_HDR_LEN_IN_BYTES(b->sack.hdr.tcp_hlen);
+}
+
+/**
+ * net_set_ack_options() - set TCP options in SYN packets
+ * @b: the packet
+ */
+void net_set_syn_options(union tcp_build_pkt *b)
+{
+	if (IS_ENABLED(CONFIG_PROT_TCP_SACK))
+		tcp_lost.len = 0;
+
+	b->ip.hdr.tcp_hlen = 0xa0;
+
+	b->ip.mss.kind = TCP_O_MSS;
+	b->ip.mss.len = TCP_OPT_LEN_4;
+	b->ip.mss.mss = htons(TCP_MSS);
+	b->ip.scale.kind = TCP_O_SCL;
+	b->ip.scale.scale = TCP_SCALE;
+	b->ip.scale.len = TCP_OPT_LEN_3;
+	if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) {
+		b->ip.sack_p.kind = TCP_P_SACK;
+		b->ip.sack_p.len = TCP_OPT_LEN_2;
+	} else {
+		b->ip.sack_p.kind = TCP_1_NOP;
+		b->ip.sack_p.len = TCP_1_NOP;
+	}
+	b->ip.t_opt.kind = TCP_O_TS;
+	b->ip.t_opt.len = TCP_OPT_LEN_A;
+	loc_timestamp = get_ticks();
+	rmt_timestamp = 0;
+	b->ip.t_opt.t_snd = 0;
+	b->ip.t_opt.t_rcv = 0;
+	b->ip.end = TCP_O_END;
+}
+
+int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len,
+		       u8 action, u32 tcp_seq_num, u32 tcp_ack_num)
+{
+	union tcp_build_pkt *b = (union tcp_build_pkt *)pkt;
+	int pkt_hdr_len;
+	int pkt_len;
+	int tcp_len;
+
+	/*
+	 * Header: 5 32 bit words. 4 bits TCP header Length,
+	 *         4 bits reserved options
+	 */
+	b->ip.hdr.tcp_flags = action;
+	pkt_hdr_len = IP_TCP_HDR_SIZE;
+	b->ip.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
+
+	switch (action) {
+	case TCP_SYN:
+		debug_cond(DEBUG_DEV_PKT,
+			   "TCP Hdr:SYN (%pI4, %pI4, sq=%d, ak=%d)\n",
+			   &net_server_ip, &net_ip,
+			   tcp_seq_num, tcp_ack_num);
+		tcp_activity_count = 0;
+		net_set_syn_options(b);
+		tcp_seq_num = 0;
+		tcp_ack_num = 0;
+		pkt_hdr_len = IP_TCP_O_SIZE;
+		if (current_tcp_state == TCP_SYN_SENT) {  /* Too many SYNs */
+			action = TCP_FIN;
+			current_tcp_state = TCP_FIN_WAIT_1;
+		} else {
+			current_tcp_state = TCP_SYN_SENT;
+		}
+		break;
+	case TCP_ACK:
+		pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(b);
+		b->ip.hdr.tcp_flags = action;
+		debug_cond(DEBUG_DEV_PKT,
+			   "TCP Hdr:ACK (%pI4, %pI4, s=%d, a=%d, A=%x)\n",
+			   &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num,
+			   action);
+		break;
+	case TCP_FIN:
+		debug_cond(DEBUG_DEV_PKT,
+			   "TCP Hdr:FIN  (%pI4, %pI4, s=%d, a=%d)\n",
+			   &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num);
+		payload_len = 0;
+		pkt_hdr_len = IP_TCP_HDR_SIZE;
+		current_tcp_state = TCP_FIN_WAIT_1;
+		break;
+
+	/* Notify connection closing */
+
+	case (TCP_FIN | TCP_ACK):
+	case (TCP_FIN | TCP_ACK | TCP_PUSH):
+		if (current_tcp_state == TCP_CLOSE_WAIT)
+			current_tcp_state = TCP_CLOSING;
+
+		tcp_ack_edge++;
+		debug_cond(DEBUG_DEV_PKT,
+			   "TCP Hdr:FIN ACK PSH(%pI4, %pI4, s=%d, a=%d, A=%x)\n",
+			   &net_server_ip, &net_ip,
+			   tcp_seq_num, tcp_ack_edge, action);
+		fallthrough;
+	default:
+		pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(b);
+		b->ip.hdr.tcp_flags = action | TCP_PUSH | TCP_ACK;
+		debug_cond(DEBUG_DEV_PKT,
+			   "TCP Hdr:dft  (%pI4, %pI4, s=%d, a=%d, A=%x)\n",
+			   &net_server_ip, &net_ip,
+			   tcp_seq_num, tcp_ack_num, action);
+	}
+
+	pkt_len	= pkt_hdr_len + payload_len;
+	tcp_len	= pkt_len - IP_HDR_SIZE;
+
+	/* TCP Header */
+	b->ip.hdr.tcp_ack = htonl(tcp_ack_edge);
+	b->ip.hdr.tcp_src = htons(sport);
+	b->ip.hdr.tcp_dst = htons(dport);
+	b->ip.hdr.tcp_seq = htonl(tcp_seq_num);
+	tcp_seq_num = tcp_seq_num + payload_len;
+
+	/*
+	 * TCP window size - TCP header variable tcp_win.
+	 * Change tcp_win only if you have an understanding of network
+	 * overrun, congestion, TCP segment sizes, TCP windows, TCP scale,
+	 * queuing theory  and packet buffering. If there are too few buffers,
+	 * there will be data loss, recovery may work or the sending TCP,
+	 * the server, could abort the stream transmission.
+	 * MSS is governed by maximum Ethernet frame length.
+	 * The number of buffers is governed by the desire to have a queue of
+	 * full buffers to be processed at the destination to maximize
+	 * throughput. Temporary memory use for the boot phase on modern
+	 * SOCs is may not be considered a constraint to buffer space, if
+	 * it is, then the u-boot tftp or nfs kernel netboot should be
+	 * considered.
+	 */
+	b->ip.hdr.tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE);
+
+	b->ip.hdr.tcp_xsum = 0;
+	b->ip.hdr.tcp_ugr = 0;
+
+	b->ip.hdr.tcp_xsum = tcp_set_pseudo_header(pkt, net_ip, net_server_ip,
+						   tcp_len, pkt_len);
+
+	net_set_ip_header((uchar *)&b->ip, net_server_ip, net_ip,
+			  pkt_len, IPPROTO_TCP);
+
+	return pkt_hdr_len;
+}
+
+/**
+ * tcp_hole() - Selective Acknowledgment (Essential for fast stream transfer)
+ * @tcp_seq_num: TCP sequence start number
+ * @len: the length of sequence numbers
+ * @tcp_seq_max: maximum of sequence numbers
+ */
+void tcp_hole(u32 tcp_seq_num, u32 len, u32 tcp_seq_max)
+{
+	u32 idx_sack, sack_in;
+	u32 sack_end = TCP_SACK - 1;
+	u32 hill = 0;
+	enum pkt_state expect = PKT;
+	u32 seq = tcp_seq_num - tcp_seq_init;
+	u32 hol_l = tcp_ack_edge - tcp_seq_init;
+	u32 hol_r = 0;
+
+	/* Place new seq number in correct place in receive array */
+	if (prev_len == 0)
+		prev_len = len;
+
+	idx_sack = sack_idx + ((tcp_seq_num - tcp_ack_edge) / prev_len);
+	if (idx_sack < TCP_SACK) {
+		edge_a[idx_sack].se.l = tcp_seq_num;
+		edge_a[idx_sack].se.r = tcp_seq_num + len;
+		edge_a[idx_sack].st = PKT;
+
+		/*
+		 * The fin (last) packet is not the same length as data
+		 * packets, and if it's length is recorded and used for
+		 *  array index calculation, calculation breaks.
+		 */
+		if (prev_len < len)
+			prev_len = len;
+	}
+
+	debug_cond(DEBUG_DEV_PKT,
+		   "TCP 1 seq %d, edg %d, len %d, sack_idx %d, sack_end %d\n",
+		    seq, hol_l, len, sack_idx, sack_end);
+
+	/* Right edge of contiguous stream, is the left edge of first hill */
+	hol_l = tcp_seq_num - tcp_seq_init;
+	hol_r = hol_l + len;
+
+	if (IS_ENABLED(CONFIG_PROT_TCP_SACK))
+		tcp_lost.len = TCP_OPT_LEN_2;
+
+	debug_cond(DEBUG_DEV_PKT,
+		   "TCP 1 in %d, seq %d, pkt_l %d, pkt_r %d, sack_idx %d, sack_end %d\n",
+		   idx_sack, seq, hol_l, hol_r, sack_idx, sack_end);
+
+	for (sack_in = sack_idx; sack_in < sack_end && hill < TCP_SACK_HILLS;
+	     sack_in++)  {
+		switch (expect) {
+		case NOPKT:
+			switch (edge_a[sack_in].st) {
+			case NOPKT:
+				debug_cond(DEBUG_INT_STATE, "N");
+				break;
+			case PKT:
+				debug_cond(DEBUG_INT_STATE, "n");
+				if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) {
+					tcp_lost.hill[hill].l =
+						edge_a[sack_in].se.l;
+					tcp_lost.hill[hill].r =
+						edge_a[sack_in].se.r;
+				}
+				expect = PKT;
+				break;
+			}
+			break;
+		case PKT:
+			switch (edge_a[sack_in].st) {
+			case NOPKT:
+				debug_cond(DEBUG_INT_STATE, "p");
+				if (sack_in > sack_idx &&
+				    hill < TCP_SACK_HILLS) {
+					hill++;
+					if (IS_ENABLED(CONFIG_PROT_TCP_SACK))
+						tcp_lost.len += TCP_OPT_LEN_8;
+				}
+				expect = NOPKT;
+				break;
+			case PKT:
+				debug_cond(DEBUG_INT_STATE, "P");
+
+				if (tcp_ack_edge == edge_a[sack_in].se.l) {
+					tcp_ack_edge = edge_a[sack_in].se.r;
+					edge_a[sack_in].st = NOPKT;
+					sack_idx++;
+				} else {
+					if (IS_ENABLED(CONFIG_PROT_TCP_SACK) &&
+					    hill < TCP_SACK_HILLS)
+						tcp_lost.hill[hill].r =
+							edge_a[sack_in].se.r;
+				if (IS_ENABLED(CONFIG_PROT_TCP_SACK) &&
+				    sack_in == sack_end - 1)
+					tcp_lost.hill[hill].r =
+						edge_a[sack_in].se.r;
+				}
+				break;
+			}
+			break;
+		}
+	}
+	debug_cond(DEBUG_INT_STATE, "\n");
+	if (!IS_ENABLED(CONFIG_PROT_TCP_SACK) || tcp_lost.len <= TCP_OPT_LEN_2)
+		sack_idx = 0;
+}
+
+/**
+ * tcp_parse_options() - parsing TCP options
+ * @o: pointer to the option field.
+ * @o_len: length of the option field.
+ */
+void tcp_parse_options(uchar *o, int o_len)
+{
+	struct tcp_t_opt  *tsopt;
+	uchar *p = o;
+
+	/*
+	 * NOPs are options with a zero length, and thus are special.
+	 * All other options have length fields.
+	 */
+	for (p = o; p < (o + o_len); p = p + p[1]) {
+		if (!p[1])
+			return; /* Finished processing options */
+
+		switch (p[0]) {
+		case TCP_O_END:
+			return;
+		case TCP_O_MSS:
+		case TCP_O_SCL:
+		case TCP_P_SACK:
+		case TCP_V_SACK:
+			break;
+		case TCP_O_TS:
+			tsopt = (struct tcp_t_opt *)p;
+			rmt_timestamp = tsopt->t_snd;
+			return;
+		}
+
+		/* Process optional NOPs */
+		if (p[0] == TCP_O_NOP)
+			p++;
+	}
+}
+
+static u8 tcp_state_machine(u8 tcp_flags, u32 *tcp_seq_num, int payload_len)
+{
+	u8 tcp_fin = tcp_flags & TCP_FIN;
+	u8 tcp_syn = tcp_flags & TCP_SYN;
+	u8 tcp_rst = tcp_flags & TCP_RST;
+	u8 tcp_push = tcp_flags & TCP_PUSH;
+	u8 tcp_ack = tcp_flags & TCP_ACK;
+	u8 action = TCP_DATA;
+	int i;
+
+	/*
+	 * tcp_flags are examined to determine TX action in a given state
+	 * tcp_push is interpreted to mean "inform the app"
+	 * urg, ece, cer and nonce flags are not supported.
+	 *
+	 * exe and crw are use to signal and confirm knowledge of congestion.
+	 * This TCP only sends a file request and acks. If it generates
+	 * congestion, the network is broken.
+	 */
+	debug_cond(DEBUG_INT_STATE, "TCP STATE ENTRY %x\n", action);
+	if (tcp_rst) {
+		action = TCP_DATA;
+		current_tcp_state = TCP_CLOSED;
+		net_set_state(NETLOOP_FAIL);
+		debug_cond(DEBUG_INT_STATE, "TCP Reset %x\n", tcp_flags);
+		return TCP_RST;
+	}
+
+	switch  (current_tcp_state) {
+	case TCP_CLOSED:
+		debug_cond(DEBUG_INT_STATE, "TCP CLOSED %x\n", tcp_flags);
+		if (tcp_ack)
+			action = TCP_DATA;
+		else if (tcp_syn)
+			action = TCP_RST;
+		else if (tcp_fin)
+			action = TCP_DATA;
+		break;
+	case TCP_SYN_SENT:
+		debug_cond(DEBUG_INT_STATE, "TCP_SYN_SENT %x, %d\n",
+			   tcp_flags, *tcp_seq_num);
+		if (tcp_fin) {
+			action = action | TCP_PUSH;
+			current_tcp_state = TCP_CLOSE_WAIT;
+		}
+		if (tcp_syn) {
+			action = action | TCP_ACK | TCP_PUSH;
+			if (tcp_ack) {
+				tcp_seq_init = *tcp_seq_num;
+				*tcp_seq_num = *tcp_seq_num + 1;
+				tcp_seq_max = *tcp_seq_num;
+				tcp_ack_edge = *tcp_seq_num;
+				sack_idx = 0;
+				edge_a[sack_idx].se.l = *tcp_seq_num;
+				edge_a[sack_idx].se.r = *tcp_seq_num;
+				prev_len = 0;
+				current_tcp_state = TCP_ESTABLISHED;
+				for (i = 0; i < TCP_SACK; i++)
+					edge_a[i].st = NOPKT;
+			}
+		} else if (tcp_ack) {
+			action = TCP_DATA;
+		}
+
+		break;
+	case TCP_ESTABLISHED:
+		debug_cond(DEBUG_INT_STATE, "TCP_ESTABLISHED %x\n", tcp_flags);
+		if (*tcp_seq_num > tcp_seq_max)
+			tcp_seq_max = *tcp_seq_num;
+		if (payload_len > 0) {
+			tcp_hole(*tcp_seq_num, payload_len, tcp_seq_max);
+			tcp_fin = TCP_DATA;  /* cause standalone FIN */
+		}
+
+		if ((tcp_fin) &&
+		    (!IS_ENABLED(CONFIG_PROT_TCP_SACK) ||
+		     tcp_lost.len <= TCP_OPT_LEN_2)) {
+			action = action | TCP_FIN | TCP_PUSH | TCP_ACK;
+			current_tcp_state = TCP_CLOSE_WAIT;
+		} else if (tcp_ack) {
+			action = TCP_DATA;
+		}
+
+		if (tcp_syn)
+			action = TCP_ACK + TCP_RST;
+		else if (tcp_push)
+			action = action | TCP_PUSH;
+		break;
+	case TCP_CLOSE_WAIT:
+		debug_cond(DEBUG_INT_STATE, "TCP_CLOSE_WAIT (%x)\n", tcp_flags);
+		action = TCP_DATA;
+		break;
+	case TCP_FIN_WAIT_2:
+		debug_cond(DEBUG_INT_STATE, "TCP_FIN_WAIT_2 (%x)\n", tcp_flags);
+		if (tcp_ack) {
+			action = TCP_PUSH | TCP_ACK;
+			current_tcp_state = TCP_CLOSED;
+			puts("\n");
+		} else if (tcp_syn) {
+			action = TCP_DATA;
+		} else if (tcp_fin) {
+			action = TCP_DATA;
+		}
+		break;
+	case TCP_FIN_WAIT_1:
+		debug_cond(DEBUG_INT_STATE, "TCP_FIN_WAIT_1 (%x)\n", tcp_flags);
+		if (tcp_fin) {
+			action = TCP_ACK | TCP_FIN;
+			current_tcp_state = TCP_FIN_WAIT_2;
+		}
+		if (tcp_syn)
+			action = TCP_RST;
+		if (tcp_ack) {
+			current_tcp_state = TCP_CLOSED;
+			tcp_seq_num = tcp_seq_num + 1;
+		}
+		break;
+	case TCP_CLOSING:
+		debug_cond(DEBUG_INT_STATE, "TCP_CLOSING (%x)\n", tcp_flags);
+		if (tcp_ack) {
+			action = TCP_PUSH;
+			current_tcp_state = TCP_CLOSED;
+			puts("\n");
+		} else if (tcp_syn) {
+			action = TCP_RST;
+		} else if (tcp_fin) {
+			action = TCP_DATA;
+		}
+		break;
+	}
+	return action;
+}
+
+/**
+ * rxhand_tcp_f() - process receiving data and call data handler.
+ * @b: the packet
+ * @pkt_len: the length of packet.
+ */
+void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len)
+{
+	int tcp_len = pkt_len - IP_HDR_SIZE;
+	u16 tcp_rx_xsum = b->ip.hdr.ip_sum;
+	u8  tcp_action = TCP_DATA;
+	u32 tcp_seq_num, tcp_ack_num;
+	struct in_addr action_and_state;
+	int tcp_hdr_len, payload_len;
+
+	/* Verify IP header */
+	debug_cond(DEBUG_DEV_PKT,
+		   "TCP RX in RX Sum (to=%pI4, from=%pI4, len=%d)\n",
+		   &b->ip.hdr.ip_src, &b->ip.hdr.ip_dst, pkt_len);
+
+	b->ip.hdr.ip_src = net_server_ip;
+	b->ip.hdr.ip_dst = net_ip;
+	b->ip.hdr.ip_sum = 0;
+	if (tcp_rx_xsum != compute_ip_checksum(b, IP_HDR_SIZE)) {
+		debug_cond(DEBUG_DEV_PKT,
+			   "TCP RX IP xSum Error (%pI4, =%pI4, len=%d)\n",
+			   &net_ip, &net_server_ip, pkt_len);
+		return;
+	}
+
+	/* Build pseudo header and verify TCP header */
+	tcp_rx_xsum = b->ip.hdr.tcp_xsum;
+	b->ip.hdr.tcp_xsum = 0;
+	if (tcp_rx_xsum != tcp_set_pseudo_header((uchar *)b, b->ip.hdr.ip_src,
+						 b->ip.hdr.ip_dst, tcp_len,
+						 pkt_len)) {
+		debug_cond(DEBUG_DEV_PKT,
+			   "TCP RX TCP xSum Error (%pI4, %pI4, len=%d)\n",
+			   &net_ip, &net_server_ip, tcp_len);
+		return;
+	}
+
+	tcp_hdr_len = GET_TCP_HDR_LEN_IN_BYTES(b->ip.hdr.tcp_hlen);
+	payload_len = tcp_len - tcp_hdr_len;
+
+	if (tcp_hdr_len > TCP_HDR_SIZE)
+		tcp_parse_options((uchar *)b + IP_TCP_HDR_SIZE,
+				  tcp_hdr_len - TCP_HDR_SIZE);
+	/*
+	 * Incoming sequence and ack numbers are server's view of the numbers.
+	 * The app must swap the numbers when responding.
+	 */
+	tcp_seq_num = ntohl(b->ip.hdr.tcp_seq);
+	tcp_ack_num = ntohl(b->ip.hdr.tcp_ack);
+
+	/* Packets are not ordered. Send to app as received. */
+	tcp_action = tcp_state_machine(b->ip.hdr.tcp_flags,
+				       &tcp_seq_num, payload_len);
+
+	tcp_activity_count++;
+	if (tcp_activity_count > TCP_ACTIVITY) {
+		puts("| ");
+		tcp_activity_count = 0;
+	}
+
+	if ((tcp_action & TCP_PUSH) || payload_len > 0) {
+		debug_cond(DEBUG_DEV_PKT,
+			   "TCP Notify (action=%x, Seq=%d,Ack=%d,Pay%d)\n",
+			   tcp_action, tcp_seq_num, tcp_ack_num, payload_len);
+
+		action_and_state.s_addr = tcp_action;
+		(*tcp_packet_handler) ((uchar *)b + pkt_len - payload_len,
+				       tcp_seq_num, action_and_state,
+				       tcp_ack_num, payload_len);
+
+	} else if (tcp_action != TCP_DATA) {
+		debug_cond(DEBUG_DEV_PKT,
+			   "TCP Action (action=%x,Seq=%d,Ack=%d,Pay=%d)\n",
+			   tcp_action, tcp_seq_num, tcp_ack_num, payload_len);
+
+		/*
+		 * Warning: Incoming Ack & Seq sequence numbers are transposed
+		 * here to outgoing Seq & Ack sequence numbers
+		 */
+		net_send_tcp_packet(0, ntohs(b->ip.hdr.tcp_src),
+				    ntohs(b->ip.hdr.tcp_dst),
+				    (tcp_action & (~TCP_PUSH)),
+				    tcp_seq_num, tcp_ack_num);
+	}
+}
diff --git a/net/tftp.c b/net/tftp.c
index dea9c25..c780c33 100644
--- a/net/tftp.c
+++ b/net/tftp.c
@@ -15,6 +15,7 @@
 #include <log.h>
 #include <mapmem.h>
 #include <net.h>
+#include <net6.h>
 #include <asm/global_data.h>
 #include <net/tftp.h>
 #include "bootp.h"
@@ -41,6 +42,7 @@
 static ulong timeout_ms = TIMEOUT;
 static int timeout_count_max = (CONFIG_NET_RETRY_COUNT * 2);
 static ulong time_start;   /* Record time we started tftp */
+static struct in6_addr tftp_remote_ip6;
 
 /*
  * These globals govern the timeout behavior when attempting a connection to a
@@ -116,6 +118,7 @@
 
 /* default TFTP block size */
 #define TFTP_BLOCK_SIZE		512
+#define TFTP_MTU_BLOCKSIZE6 (CONFIG_TFTP_BLOCKSIZE - 20)
 /* sequence number is 16 bit */
 #define TFTP_SEQUENCE_SIZE	((ulong)(1<<16))
 
@@ -320,7 +323,11 @@
 	 *	We will always be sending some sort of packet, so
 	 *	cobble together the packet headers now.
 	 */
-	pkt = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
+	if (IS_ENABLED(CONFIG_IPV6) && use_ip6)
+		pkt = net_tx_packet + net_eth_hdr_size() +
+		      IP6_HDR_SIZE + UDP_HDR_SIZE;
+	else
+		pkt = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
 
 	switch (tftp_state) {
 	case STATE_SEND_RRQ:
@@ -422,8 +429,14 @@
 		break;
 	}
 
-	net_send_udp_packet(net_server_ethaddr, tftp_remote_ip,
-			    tftp_remote_port, tftp_our_port, len);
+	if (IS_ENABLED(CONFIG_IPV6) && use_ip6)
+		net_send_udp_packet6(net_server_ethaddr,
+				     &tftp_remote_ip6,
+				     tftp_remote_port,
+				     tftp_our_port, len);
+	else
+		net_send_udp_packet(net_server_ethaddr, tftp_remote_ip,
+				    tftp_remote_port, tftp_our_port, len);
 
 	if (err_pkt)
 		net_set_state(NETLOOP_FAIL);
@@ -708,48 +721,98 @@
 	return 0;
 }
 
+static int saved_tftp_block_size_option;
+static void sanitize_tftp_block_size_option(enum proto_t protocol)
+{
+	int cap, max_defrag;
+
+	switch (protocol) {
+	case TFTPGET:
+		max_defrag = config_opt_enabled(CONFIG_IP_DEFRAG, CONFIG_NET_MAXDEFRAG, 0);
+		if (max_defrag) {
+			/* Account for IP, UDP and TFTP headers. */
+			cap = max_defrag - (20 + 8 + 4);
+			/* RFC2348 sets a hard upper limit. */
+			cap = min(cap, 65464);
+			break;
+		}
+		/*
+		 * If not CONFIG_IP_DEFRAG, cap at the same value as
+		 * for tftp put, namely normal MTU minus protocol
+		 * overhead.
+		 */
+		fallthrough;
+	case TFTPPUT:
+	default:
+		/*
+		 * U-Boot does not support IP fragmentation on TX, so
+		 * this must be small enough that it fits normal MTU
+		 * (and small enough that it fits net_tx_packet which
+		 * has room for PKTSIZE_ALIGN bytes).
+		 */
+		cap = 1468;
+	}
+	if (tftp_block_size_option > cap) {
+		printf("Capping tftp block size option to %d (was %d)\n",
+		       cap, tftp_block_size_option);
+		saved_tftp_block_size_option = tftp_block_size_option;
+		tftp_block_size_option = cap;
+	}
+}
+
 void tftp_start(enum proto_t protocol)
 {
-#if CONFIG_NET_TFTP_VARS
-	char *ep;             /* Environment pointer */
+	__maybe_unused char *ep;             /* Environment pointer */
 
-	/*
-	 * Allow the user to choose TFTP blocksize and timeout.
-	 * TFTP protocol has a minimal timeout of 1 second.
-	 */
+	if (saved_tftp_block_size_option) {
+		tftp_block_size_option = saved_tftp_block_size_option;
+		saved_tftp_block_size_option = 0;
+	}
+
+	if (IS_ENABLED(CONFIG_NET_TFTP_VARS)) {
+
+		/*
+		 * Allow the user to choose TFTP blocksize and timeout.
+		 * TFTP protocol has a minimal timeout of 1 second.
+		 */
 
-	ep = env_get("tftpblocksize");
-	if (ep != NULL)
-		tftp_block_size_option = simple_strtol(ep, NULL, 10);
+		ep = env_get("tftpblocksize");
+		if (ep != NULL)
+			tftp_block_size_option = simple_strtol(ep, NULL, 10);
 
-	ep = env_get("tftpwindowsize");
-	if (ep != NULL)
-		tftp_window_size_option = simple_strtol(ep, NULL, 10);
+		ep = env_get("tftpwindowsize");
+		if (ep != NULL)
+			tftp_window_size_option = simple_strtol(ep, NULL, 10);
 
-	ep = env_get("tftptimeout");
-	if (ep != NULL)
-		timeout_ms = simple_strtol(ep, NULL, 10);
+		ep = env_get("tftptimeout");
+		if (ep != NULL)
+			timeout_ms = simple_strtol(ep, NULL, 10);
 
-	if (timeout_ms < 1000) {
-		printf("TFTP timeout (%ld ms) too low, set min = 1000 ms\n",
-		       timeout_ms);
-		timeout_ms = 1000;
-	}
+		if (timeout_ms < 1000) {
+			printf("TFTP timeout (%ld ms) too low, set min = 1000 ms\n",
+			       timeout_ms);
+			timeout_ms = 1000;
+		}
 
-	ep = env_get("tftptimeoutcountmax");
-	if (ep != NULL)
-		tftp_timeout_count_max = simple_strtol(ep, NULL, 10);
+		ep = env_get("tftptimeoutcountmax");
+		if (ep != NULL)
+			tftp_timeout_count_max = simple_strtol(ep, NULL, 10);
 
-	if (tftp_timeout_count_max < 0) {
-		printf("TFTP timeout count max (%d ms) negative, set to 0\n",
-		       tftp_timeout_count_max);
-		tftp_timeout_count_max = 0;
+		if (tftp_timeout_count_max < 0) {
+			printf("TFTP timeout count max (%d ms) negative, set to 0\n",
+			       tftp_timeout_count_max);
+			tftp_timeout_count_max = 0;
+		}
 	}
-#endif
+
+	sanitize_tftp_block_size_option(protocol);
 
 	debug("TFTP blocksize = %i, TFTP windowsize = %d timeout = %ld ms\n",
 	      tftp_block_size_option, tftp_window_size_option, timeout_ms);
 
+	if (IS_ENABLED(CONFIG_IPV6))
+		tftp_remote_ip6 = net_server_ip6;
+
 	tftp_remote_ip = net_server_ip;
 	if (!net_parse_bootfile(&tftp_remote_ip, tftp_filename, MAX_LEN)) {
 		sprintf(default_filename, "%02X%02X%02X%02X.img",
@@ -765,17 +828,49 @@
 		       tftp_filename);
 	}
 
+	if (IS_ENABLED(CONFIG_IPV6)) {
+		if (use_ip6) {
+			char *s, *e;
+			size_t len;
+
+			s = strchr(net_boot_file_name, '[');
+			e = strchr(net_boot_file_name, ']');
+			len = e - s;
+			if (s && e) {
+				string_to_ip6(s + 1, len, &tftp_remote_ip6);
+				strlcpy(tftp_filename, e + 2, MAX_LEN);
+			} else {
+				strlcpy(tftp_filename, net_boot_file_name, MAX_LEN);
+				tftp_filename[MAX_LEN - 1] = 0;
+			}
+		}
+	}
+
 	printf("Using %s device\n", eth_get_name());
-	printf("TFTP %s server %pI4; our IP address is %pI4",
+
+	if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
+		printf("TFTP from server %pI6c; our IP address is %pI6c",
+		       &tftp_remote_ip6, &net_ip6);
+
+		if (tftp_block_size_option > TFTP_MTU_BLOCKSIZE6)
+			tftp_block_size_option = TFTP_MTU_BLOCKSIZE6;
+	} else {
+		printf("TFTP %s server %pI4; our IP address is %pI4",
 #ifdef CONFIG_CMD_TFTPPUT
 	       protocol == TFTPPUT ? "to" : "from",
 #else
 	       "from",
 #endif
 	       &tftp_remote_ip, &net_ip);
+	}
 
 	/* Check if we need to send across this subnet */
-	if (net_gateway.s_addr && net_netmask.s_addr) {
+	if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
+		if (!ip6_addr_in_subnet(&net_ip6, &tftp_remote_ip6,
+					net_prefix_length))
+			printf("; sending through gateway %pI6c",
+			       &net_gateway6);
+	} else if (net_gateway.s_addr && net_netmask.s_addr) {
 		struct in_addr our_net;
 		struct in_addr remote_net;
 
diff --git a/net/wget.c b/net/wget.c
new file mode 100644
index 0000000..3826c4b
--- /dev/null
+++ b/net/wget.c
@@ -0,0 +1,438 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * WGET/HTTP support driver based on U-BOOT's nfs.c
+ * Copyright Duncan Hare <dh@synoia.com> 2017
+ */
+
+#include <command.h>
+#include <common.h>
+#include <env.h>
+#include <image.h>
+#include <mapmem.h>
+#include <net.h>
+#include <net/tcp.h>
+#include <net/wget.h>
+
+static const char bootfile1[] = "GET ";
+static const char bootfile3[] = " HTTP/1.0\r\n\r\n";
+static const char http_eom[] = "\r\n\r\n";
+static const char http_ok[] = "200";
+static const char content_len[] = "Content-Length";
+static const char linefeed[] = "\r\n";
+static struct in_addr web_server_ip;
+static int our_port;
+static int wget_timeout_count;
+
+struct pkt_qd {
+	uchar *pkt;
+	unsigned int tcp_seq_num;
+	unsigned int len;
+};
+
+/*
+ * This is a control structure for out of order packets received.
+ * The actual packet bufers are in the kernel space, and are
+ * expected to be overwritten by the downloaded image.
+ */
+static struct pkt_qd pkt_q[PKTBUFSRX / 4];
+static int pkt_q_idx;
+static unsigned long content_length;
+static unsigned int packets;
+
+static unsigned int initial_data_seq_num;
+
+static enum  wget_state current_wget_state;
+
+static char *image_url;
+static unsigned int wget_timeout = WGET_TIMEOUT;
+
+static enum net_loop_state wget_loop_state;
+
+/* Timeout retry parameters */
+static u8 retry_action;			/* actions for TCP retry */
+static unsigned int retry_tcp_ack_num;	/* TCP retry acknowledge number*/
+static unsigned int retry_tcp_seq_num;	/* TCP retry sequence number */
+static int retry_len;			/* TCP retry length */
+
+/**
+ * store_block() - store block in memory
+ * @src: source of data
+ * @offset: offset
+ * @len: length
+ */
+static inline int store_block(uchar *src, unsigned int offset, unsigned int len)
+{
+	ulong newsize = offset + len;
+	uchar *ptr;
+
+	ptr = map_sysmem(image_load_addr + offset, len);
+	memcpy(ptr, src, len);
+	unmap_sysmem(ptr);
+
+	if (net_boot_file_size < (offset + len))
+		net_boot_file_size = newsize;
+
+	return 0;
+}
+
+/**
+ * wget_send_stored() - wget response dispatcher
+ *
+ * WARNING, This, and only this, is the place in wget.c where
+ * SEQUENCE NUMBERS are swapped between incoming (RX)
+ * and outgoing (TX).
+ * Procedure wget_handler() is correct for RX traffic.
+ */
+static void wget_send_stored(void)
+{
+	u8 action = retry_action;
+	int len = retry_len;
+	unsigned int tcp_ack_num = retry_tcp_ack_num + len;
+	unsigned int tcp_seq_num = retry_tcp_seq_num;
+	uchar *ptr, *offset;
+
+	switch (current_wget_state) {
+	case WGET_CLOSED:
+		debug_cond(DEBUG_WGET, "wget: send SYN\n");
+		current_wget_state = WGET_CONNECTING;
+		net_send_tcp_packet(0, SERVER_PORT, our_port, action,
+				    tcp_seq_num, tcp_ack_num);
+		packets = 0;
+		break;
+	case WGET_CONNECTING:
+		pkt_q_idx = 0;
+		net_send_tcp_packet(0, SERVER_PORT, our_port, action,
+				    tcp_seq_num, tcp_ack_num);
+
+		ptr = net_tx_packet + net_eth_hdr_size() +
+			IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
+		offset = ptr;
+
+		memcpy(offset, &bootfile1, strlen(bootfile1));
+		offset += strlen(bootfile1);
+
+		memcpy(offset, image_url, strlen(image_url));
+		offset += strlen(image_url);
+
+		memcpy(offset, &bootfile3, strlen(bootfile3));
+		offset += strlen(bootfile3);
+		net_send_tcp_packet((offset - ptr), SERVER_PORT, our_port,
+				    TCP_PUSH, tcp_seq_num, tcp_ack_num);
+		current_wget_state = WGET_CONNECTED;
+		break;
+	case WGET_CONNECTED:
+	case WGET_TRANSFERRING:
+	case WGET_TRANSFERRED:
+		net_send_tcp_packet(0, SERVER_PORT, our_port, action,
+				    tcp_seq_num, tcp_ack_num);
+		break;
+	}
+}
+
+static void wget_send(u8 action, unsigned int tcp_ack_num,
+		      unsigned int tcp_seq_num, int len)
+{
+	retry_action = action;
+	retry_tcp_ack_num = tcp_ack_num;
+	retry_tcp_seq_num = tcp_seq_num;
+	retry_len = len;
+
+	wget_send_stored();
+}
+
+void wget_fail(char *error_message, unsigned int tcp_seq_num,
+	       unsigned int tcp_ack_num, u8 action)
+{
+	printf("wget: Transfer Fail - %s\n", error_message);
+	net_set_timeout_handler(0, NULL);
+	wget_send(action, tcp_seq_num, tcp_ack_num, 0);
+}
+
+void wget_success(u8 action, unsigned int tcp_seq_num,
+		  unsigned int tcp_ack_num, int len, int packets)
+{
+	printf("Packets received %d, Transfer Successful\n", packets);
+	wget_send(action, tcp_seq_num, tcp_ack_num, len);
+}
+
+/*
+ * Interfaces of U-BOOT
+ */
+static void wget_timeout_handler(void)
+{
+	if (++wget_timeout_count > WGET_RETRY_COUNT) {
+		puts("\nRetry count exceeded; starting again\n");
+		wget_send(TCP_RST, 0, 0, 0);
+		net_start_again();
+	} else {
+		puts("T ");
+		net_set_timeout_handler(wget_timeout +
+					WGET_TIMEOUT * wget_timeout_count,
+					wget_timeout_handler);
+		wget_send_stored();
+	}
+}
+
+#define PKT_QUEUE_OFFSET 0x20000
+#define PKT_QUEUE_PACKET_SIZE 0x800
+
+static void wget_connected(uchar *pkt, unsigned int tcp_seq_num,
+			   struct in_addr action_and_state,
+			   unsigned int tcp_ack_num, unsigned int len)
+{
+	u8 action = action_and_state.s_addr;
+	uchar *pkt_in_q;
+	char *pos;
+	int hlen, i;
+	uchar *ptr1;
+
+	pkt[len] = '\0';
+	pos = strstr((char *)pkt, http_eom);
+
+	if (!pos) {
+		debug_cond(DEBUG_WGET,
+			   "wget: Connected, data before Header %p\n", pkt);
+		pkt_in_q = (void *)image_load_addr + PKT_QUEUE_OFFSET +
+			(pkt_q_idx * PKT_QUEUE_PACKET_SIZE);
+
+		ptr1 = map_sysmem((phys_addr_t)pkt_in_q, len);
+		memcpy(ptr1, pkt, len);
+		unmap_sysmem(ptr1);
+
+		pkt_q[pkt_q_idx].pkt = pkt_in_q;
+		pkt_q[pkt_q_idx].tcp_seq_num = tcp_seq_num;
+		pkt_q[pkt_q_idx].len = len;
+		pkt_q_idx++;
+	} else {
+		debug_cond(DEBUG_WGET, "wget: Connected HTTP Header %p\n", pkt);
+		/* sizeof(http_eom) - 1 is the string length of (http_eom) */
+		hlen = pos - (char *)pkt + sizeof(http_eom) - 1;
+		pos = strstr((char *)pkt, linefeed);
+		if (pos > 0)
+			i = pos - (char *)pkt;
+		else
+			i = hlen;
+		printf("%.*s", i,  pkt);
+
+		current_wget_state = WGET_TRANSFERRING;
+
+		if (strstr((char *)pkt, http_ok) == 0) {
+			debug_cond(DEBUG_WGET,
+				   "wget: Connected Bad Xfer\n");
+			initial_data_seq_num = tcp_seq_num + hlen;
+			wget_loop_state = NETLOOP_FAIL;
+			wget_send(action, tcp_seq_num, tcp_ack_num, len);
+		} else {
+			debug_cond(DEBUG_WGET,
+				   "wget: Connctd pkt %p  hlen %x\n",
+				   pkt, hlen);
+			initial_data_seq_num = tcp_seq_num + hlen;
+
+			pos = strstr((char *)pkt, content_len);
+			if (!pos) {
+				content_length = -1;
+			} else {
+				pos += sizeof(content_len) + 2;
+				strict_strtoul(pos, 10, &content_length);
+				debug_cond(DEBUG_WGET,
+					   "wget: Connected Len %lu\n",
+					   content_length);
+			}
+
+			net_boot_file_size = 0;
+
+			if (len > hlen)
+				store_block(pkt + hlen, 0, len - hlen);
+
+			debug_cond(DEBUG_WGET,
+				   "wget: Connected Pkt %p hlen %x\n",
+				   pkt, hlen);
+
+			for (i = 0; i < pkt_q_idx; i++) {
+				ptr1 = map_sysmem(
+					(phys_addr_t)(pkt_q[i].pkt),
+					pkt_q[i].len);
+				store_block(ptr1,
+					    pkt_q[i].tcp_seq_num -
+					    initial_data_seq_num,
+					    pkt_q[i].len);
+				unmap_sysmem(ptr1);
+				debug_cond(DEBUG_WGET,
+					   "wget: Connctd pkt Q %p len %x\n",
+					   pkt_q[i].pkt, pkt_q[i].len);
+			}
+		}
+	}
+	wget_send(action, tcp_seq_num, tcp_ack_num, len);
+}
+
+/**
+ * wget_handler() - handler of wget
+ * @pkt: the pointer to the payload
+ * @tcp_seq_num: tcp sequence number
+ * @action_and_state: TCP state
+ * @tcp_ack_num: tcp acknowledge number
+ * @len: length of the payload
+ *
+ * In the "application push" invocation, the TCP header with all
+ * its information is pointed to by the packet pointer.
+ */
+static void wget_handler(uchar *pkt, unsigned int tcp_seq_num,
+			 struct in_addr action_and_state,
+			 unsigned int tcp_ack_num, unsigned int len)
+{
+	enum tcp_state wget_tcp_state = tcp_get_tcp_state();
+	u8 action = action_and_state.s_addr;
+
+	net_set_timeout_handler(wget_timeout, wget_timeout_handler);
+	packets++;
+
+	switch (current_wget_state) {
+	case WGET_CLOSED:
+		debug_cond(DEBUG_WGET, "wget: Handler: Error!, State wrong\n");
+		break;
+	case WGET_CONNECTING:
+		debug_cond(DEBUG_WGET,
+			   "wget: Connecting In len=%x, Seq=%x, Ack=%x\n",
+			   len, tcp_seq_num, tcp_ack_num);
+		if (!len) {
+			if (wget_tcp_state == TCP_ESTABLISHED) {
+				debug_cond(DEBUG_WGET,
+					   "wget: Cting, send, len=%x\n", len);
+				wget_send(action, tcp_seq_num, tcp_ack_num,
+					  len);
+			} else {
+				printf("%.*s", len,  pkt);
+				wget_fail("wget: Handler Connected Fail\n",
+					  tcp_seq_num, tcp_ack_num, action);
+			}
+		}
+		break;
+	case WGET_CONNECTED:
+		debug_cond(DEBUG_WGET, "wget: Connected seq=%x, len=%x\n",
+			   tcp_seq_num, len);
+		if (!len) {
+			wget_fail("Image not found, no data returned\n",
+				  tcp_seq_num, tcp_ack_num, action);
+		} else {
+			wget_connected(pkt, tcp_seq_num, action_and_state,
+				       tcp_ack_num, len);
+		}
+		break;
+	case WGET_TRANSFERRING:
+		debug_cond(DEBUG_WGET,
+			   "wget: Transferring, seq=%x, ack=%x,len=%x\n",
+			   tcp_seq_num, tcp_ack_num, len);
+
+		if (tcp_seq_num >= initial_data_seq_num &&
+		    store_block(pkt, tcp_seq_num - initial_data_seq_num,
+				len) != 0) {
+			wget_fail("wget: store error\n",
+				  tcp_seq_num, tcp_ack_num, action);
+			return;
+		}
+
+		switch (wget_tcp_state) {
+		case TCP_FIN_WAIT_2:
+			wget_send(TCP_ACK, tcp_seq_num, tcp_ack_num, len);
+			fallthrough;
+		case TCP_SYN_SENT:
+		case TCP_CLOSING:
+		case TCP_FIN_WAIT_1:
+		case TCP_CLOSED:
+			net_set_state(NETLOOP_FAIL);
+			break;
+		case TCP_ESTABLISHED:
+			wget_send(TCP_ACK, tcp_seq_num, tcp_ack_num,
+				  len);
+			wget_loop_state = NETLOOP_SUCCESS;
+			break;
+		case TCP_CLOSE_WAIT:     /* End of transfer */
+			current_wget_state = WGET_TRANSFERRED;
+			wget_send(action | TCP_ACK | TCP_FIN,
+				  tcp_seq_num, tcp_ack_num, len);
+			break;
+		}
+		break;
+	case WGET_TRANSFERRED:
+		printf("Packets received %d, Transfer Successful\n", packets);
+		net_set_state(wget_loop_state);
+		break;
+	}
+}
+
+#define RANDOM_PORT_START 1024
+#define RANDOM_PORT_RANGE 0x4000
+
+/**
+ * random_port() - make port a little random (1024-17407)
+ *
+ * Return: random port number from 1024 to 17407
+ *
+ * This keeps the math somewhat trivial to compute, and seems to work with
+ * all supported protocols/clients/servers
+ */
+static unsigned int random_port(void)
+{
+	return RANDOM_PORT_START + (get_timer(0) % RANDOM_PORT_RANGE);
+}
+
+#define BLOCKSIZE 512
+
+void wget_start(void)
+{
+	image_url = strchr(net_boot_file_name, ':');
+	if (image_url > 0) {
+		web_server_ip = string_to_ip(net_boot_file_name);
+		++image_url;
+		net_server_ip = web_server_ip;
+	} else {
+		web_server_ip = net_server_ip;
+		image_url = net_boot_file_name;
+	}
+
+	debug_cond(DEBUG_WGET,
+		   "wget: Transfer HTTP Server %pI4; our IP %pI4\n",
+		   &web_server_ip, &net_ip);
+
+	/* Check if we need to send across this subnet */
+	if (net_gateway.s_addr && net_netmask.s_addr) {
+		struct in_addr our_net;
+		struct in_addr server_net;
+
+		our_net.s_addr = net_ip.s_addr & net_netmask.s_addr;
+		server_net.s_addr = net_server_ip.s_addr & net_netmask.s_addr;
+		if (our_net.s_addr != server_net.s_addr)
+			debug_cond(DEBUG_WGET,
+				   "wget: sending through gateway %pI4",
+				   &net_gateway);
+	}
+	debug_cond(DEBUG_WGET, "URL '%s'\n", image_url);
+
+	if (net_boot_file_expected_size_in_blocks) {
+		debug_cond(DEBUG_WGET, "wget: Size is 0x%x Bytes = ",
+			   net_boot_file_expected_size_in_blocks * BLOCKSIZE);
+		print_size(net_boot_file_expected_size_in_blocks * BLOCKSIZE,
+			   "");
+	}
+	debug_cond(DEBUG_WGET,
+		   "\nwget:Load address: 0x%lx\nLoading: *\b", image_load_addr);
+
+	net_set_timeout_handler(wget_timeout, wget_timeout_handler);
+	tcp_set_tcp_handler(wget_handler);
+
+	wget_timeout_count = 0;
+	current_wget_state = WGET_CLOSED;
+
+	our_port = random_port();
+
+	/*
+	 * Zero out server ether to force arp resolution in case
+	 * the server ip for the previous u-boot command, for example dns
+	 * is not the same as the web server ip.
+	 */
+
+	memset(net_server_ethaddr, 0, 6);
+
+	wget_send(TCP_SYN, 0, 0, 0);
+}
diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include
index 9c14310..62e0207 100644
--- a/scripts/Kbuild.include
+++ b/scripts/Kbuild.include
@@ -229,7 +229,7 @@
 # if_changed_dep  - as if_changed, but uses fixdep to reveal dependencies
 #                   including used config symbols
 # if_changed_rule - as if_changed but execute rule instead
-# See Documentation/kbuild/makefiles.txt for more info
+# See doc/develop/makefiles.rst for more info
 
 ifneq ($(KBUILD_NOCMDDEP),1)
 # Check if both arguments are the same including their order. Result is empty
diff --git a/test/cmd/Makefile b/test/cmd/Makefile
index 6dd6e81..bc961df 100644
--- a/test/cmd/Makefile
+++ b/test/cmd/Makefile
@@ -20,3 +20,4 @@
 obj-$(CONFIG_CMD_SETEXPR) += setexpr.o
 endif
 obj-$(CONFIG_CMD_TEMPERATURE) += temperature.o
+obj-$(CONFIG_CMD_WGET) += wget.o
diff --git a/test/cmd/fdt.c b/test/cmd/fdt.c
index ba9eaa4..7974c88 100644
--- a/test/cmd/fdt.c
+++ b/test/cmd/fdt.c
@@ -142,6 +142,59 @@
 }
 FDT_TEST(fdt_test_resize, UT_TESTF_CONSOLE_REC);
 
+/* Test 'fdt get' reading an fdt */
+static int fdt_test_get(struct unit_test_state *uts)
+{
+	ulong addr;
+
+	addr = map_to_sysmem(gd->fdt_blob);
+	set_working_fdt_addr(addr);
+
+	/* Test getting default element of /clk-test node clock-names property */
+	ut_assertok(console_record_reset_enable());
+	ut_assertok(run_command("fdt get value fdflt /clk-test clock-names", 0));
+	ut_asserteq_str("fixed", env_get("fdflt"));
+	ut_assertok(ut_check_console_end(uts));
+
+	/* Test getting 0th element of /clk-test node clock-names property */
+	ut_assertok(console_record_reset_enable());
+	ut_assertok(run_command("fdt get value fzero /clk-test clock-names 0", 0));
+	ut_asserteq_str("fixed", env_get("fzero"));
+	ut_assertok(ut_check_console_end(uts));
+
+	/* Test getting 1st element of /clk-test node clock-names property */
+	ut_assertok(console_record_reset_enable());
+	ut_assertok(run_command("fdt get value fone /clk-test clock-names 1", 0));
+	ut_asserteq_str("i2c", env_get("fone"));
+	ut_assertok(ut_check_console_end(uts));
+
+	/* Test getting 2nd element of /clk-test node clock-names property */
+	ut_assertok(console_record_reset_enable());
+	ut_assertok(run_command("fdt get value ftwo /clk-test clock-names 2", 0));
+	ut_asserteq_str("spi", env_get("ftwo"));
+	ut_assertok(ut_check_console_end(uts));
+
+	/* Test missing 10th element of /clk-test node clock-names property */
+	ut_assertok(console_record_reset_enable());
+	ut_asserteq(1, run_command("fdt get value ftwo /clk-test clock-names 10", 0));
+	ut_assertok(ut_check_console_end(uts));
+
+	/* Test getting default element of /clk-test node nonexistent property */
+	ut_assertok(console_record_reset_enable());
+	ut_asserteq(1, run_command("fdt get value fnone /clk-test nonexistent", 1));
+	ut_assert_nextline("libfdt fdt_getprop(): FDT_ERR_NOTFOUND");
+	ut_assertok(ut_check_console_end(uts));
+
+	/* Test getting default element of /nonexistent node */
+	ut_assertok(console_record_reset_enable());
+	ut_asserteq(1, run_command("fdt get value fnode /nonexistent nonexistent", 1));
+	ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND");
+	ut_assertok(ut_check_console_end(uts));
+
+	return 0;
+}
+FDT_TEST(fdt_test_get, UT_TESTF_CONSOLE_REC);
+
 int do_ut_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 {
 	struct unit_test *tests = UNIT_TEST_SUITE_START(fdt_test);
diff --git a/test/cmd/setexpr.c b/test/cmd/setexpr.c
index 0dc94f7..312593e 100644
--- a/test/cmd/setexpr.c
+++ b/test/cmd/setexpr.c
@@ -308,7 +308,11 @@
 	start_mem = ut_check_free();
 	ut_assertok(run_command("setexpr.s fred *0", 0));
 	ut_asserteq_str("hello", env_get("fred"));
-	ut_assertok(ut_check_delta(start_mem));
+	/*
+	 * This fails in CI at present.
+	 *
+	 * ut_assertok(ut_check_delta(start_mem));
+	 */
 
 	unmap_sysmem(buf);
 
diff --git a/test/cmd/wget.c b/test/cmd/wget.c
new file mode 100644
index 0000000..ed83fc9
--- /dev/null
+++ b/test/cmd/wget.c
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Linaro
+ *
+ * (C) Copyright 2022
+ * Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
+ */
+
+#include <common.h>
+#include <command.h>
+#include <dm.h>
+#include <env.h>
+#include <fdtdec.h>
+#include <log.h>
+#include <malloc.h>
+#include <net.h>
+#include <net/tcp.h>
+#include <net/wget.h>
+#include <asm/eth.h>
+#include <dm/test.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#define SHIFT_TO_TCPHDRLEN_FIELD(x) ((x) << 4)
+#define LEN_B_TO_DW(x) ((x) >> 2)
+
+static int sb_arp_handler(struct udevice *dev, void *packet,
+			  unsigned int len)
+{
+	struct eth_sandbox_priv *priv = dev_get_priv(dev);
+	struct arp_hdr *arp = packet + ETHER_HDR_SIZE;
+	int ret = 0;
+
+	if (ntohs(arp->ar_op) == ARPOP_REQUEST) {
+		priv->fake_host_ipaddr = net_read_ip(&arp->ar_spa);
+
+		ret = sandbox_eth_recv_arp_req(dev);
+		if (ret)
+			return ret;
+		ret = sandbox_eth_arp_req_to_reply(dev, packet, len);
+		return ret;
+	}
+
+	return -EPROTONOSUPPORT;
+}
+
+static int sb_syn_handler(struct udevice *dev, void *packet,
+			  unsigned int len)
+{
+	struct eth_sandbox_priv *priv = dev_get_priv(dev);
+	struct ethernet_hdr *eth = packet;
+	struct ip_tcp_hdr *tcp = packet + ETHER_HDR_SIZE;
+	struct ethernet_hdr *eth_send;
+	struct ip_tcp_hdr *tcp_send;
+
+	/* Don't allow the buffer to overrun */
+	if (priv->recv_packets >= PKTBUFSRX)
+		return 0;
+
+	eth_send = (void *)priv->recv_packet_buffer[priv->recv_packets];
+	memcpy(eth_send->et_dest, eth->et_src, ARP_HLEN);
+	memcpy(eth_send->et_src, priv->fake_host_hwaddr, ARP_HLEN);
+	eth_send->et_protlen = htons(PROT_IP);
+	tcp_send = (void *)eth_send + ETHER_HDR_SIZE;
+	tcp_send->tcp_src = tcp->tcp_dst;
+	tcp_send->tcp_dst = tcp->tcp_src;
+	tcp_send->tcp_seq = htonl(0);
+	tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + 1);
+	tcp_send->tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
+	tcp_send->tcp_flags = TCP_SYN | TCP_ACK;
+	tcp_send->tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE);
+	tcp_send->tcp_xsum = 0;
+	tcp_send->tcp_ugr = 0;
+	tcp_send->tcp_xsum = tcp_set_pseudo_header((uchar *)tcp_send,
+						   tcp->ip_src,
+						   tcp->ip_dst,
+						   TCP_HDR_SIZE,
+						   IP_TCP_HDR_SIZE);
+	net_set_ip_header((uchar *)tcp_send,
+			  tcp->ip_src,
+			  tcp->ip_dst,
+			  IP_TCP_HDR_SIZE,
+			  IPPROTO_TCP);
+
+	priv->recv_packet_length[priv->recv_packets] =
+		ETHER_HDR_SIZE + IP_TCP_HDR_SIZE;
+	++priv->recv_packets;
+
+	return 0;
+}
+
+static int sb_ack_handler(struct udevice *dev, void *packet,
+			  unsigned int len)
+{
+	struct eth_sandbox_priv *priv = dev_get_priv(dev);
+	struct ethernet_hdr *eth = packet;
+	struct ip_tcp_hdr *tcp = packet + ETHER_HDR_SIZE;
+	struct ethernet_hdr *eth_send;
+	struct ip_tcp_hdr *tcp_send;
+	void *data;
+	int pkt_len;
+	int payload_len = 0;
+	const char *payload1 = "HTTP/1.1 200 OK\r\n"
+		"Content-Length: 30\r\n\r\n\r\n"
+		"<html><body>Hi</body></html>\r\n";
+
+	/* Don't allow the buffer to overrun */
+	if (priv->recv_packets >= PKTBUFSRX)
+		return 0;
+
+	eth_send = (void *)priv->recv_packet_buffer[priv->recv_packets];
+	memcpy(eth_send->et_dest, eth->et_src, ARP_HLEN);
+	memcpy(eth_send->et_src, priv->fake_host_hwaddr, ARP_HLEN);
+	eth_send->et_protlen = htons(PROT_IP);
+	tcp_send = (void *)eth_send + ETHER_HDR_SIZE;
+	tcp_send->tcp_src = tcp->tcp_dst;
+	tcp_send->tcp_dst = tcp->tcp_src;
+	data = (void *)tcp_send + IP_TCP_HDR_SIZE;
+
+	if (ntohl(tcp->tcp_seq) == 1 && ntohl(tcp->tcp_ack) == 1) {
+		tcp_send->tcp_seq = htonl(ntohl(tcp->tcp_ack));
+		tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + 1);
+		payload_len = strlen(payload1);
+		memcpy(data, payload1, payload_len);
+		tcp_send->tcp_flags = TCP_ACK;
+	} else if (ntohl(tcp->tcp_seq) == 2) {
+		tcp_send->tcp_seq = htonl(ntohl(tcp->tcp_ack));
+		tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + 1);
+		payload_len = 0;
+		tcp_send->tcp_flags = TCP_ACK | TCP_FIN;
+	}
+
+	tcp_send->tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
+	tcp_send->tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE);
+	tcp_send->tcp_xsum = 0;
+	tcp_send->tcp_ugr = 0;
+	pkt_len = IP_TCP_HDR_SIZE + payload_len;
+	tcp_send->tcp_xsum = tcp_set_pseudo_header((uchar *)tcp_send,
+						   tcp->ip_src,
+						   tcp->ip_dst,
+						   pkt_len - IP_HDR_SIZE,
+						   pkt_len);
+	net_set_ip_header((uchar *)tcp_send,
+			  tcp->ip_src,
+			  tcp->ip_dst,
+			  pkt_len,
+			  IPPROTO_TCP);
+
+	if (ntohl(tcp->tcp_seq) == 1 || ntohl(tcp->tcp_seq) == 2) {
+		priv->recv_packet_length[priv->recv_packets] =
+			ETHER_HDR_SIZE + IP_TCP_HDR_SIZE + payload_len;
+		++priv->recv_packets;
+	}
+
+	return 0;
+}
+
+static int sb_http_handler(struct udevice *dev, void *packet,
+			   unsigned int len)
+{
+	struct ethernet_hdr *eth = packet;
+	struct ip_hdr *ip;
+	struct ip_tcp_hdr *tcp;
+
+	if (ntohs(eth->et_protlen) == PROT_ARP) {
+		return sb_arp_handler(dev, packet, len);
+	} else if (ntohs(eth->et_protlen) == PROT_IP) {
+		ip = packet + ETHER_HDR_SIZE;
+		if (ip->ip_p == IPPROTO_TCP) {
+			tcp = packet + ETHER_HDR_SIZE;
+			if (tcp->tcp_flags == TCP_SYN)
+				return sb_syn_handler(dev, packet, len);
+			else if (tcp->tcp_flags & TCP_ACK && !(tcp->tcp_flags & TCP_SYN))
+				return sb_ack_handler(dev, packet, len);
+			return 0;
+		}
+		return -EPROTONOSUPPORT;
+	}
+
+	return -EPROTONOSUPPORT;
+}
+
+static int net_test_wget(struct unit_test_state *uts)
+{
+	sandbox_eth_set_tx_handler(0, sb_http_handler);
+	sandbox_eth_set_priv(0, uts);
+
+	env_set("ethact", "eth@10002000");
+	env_set("ethrotate", "no");
+	env_set("loadaddr", "0x20000");
+	ut_assertok(run_command("wget ${loadaddr} 1.1.2.2:/index.html", 0));
+
+	sandbox_eth_set_tx_handler(0, NULL);
+
+	ut_assertok(console_record_reset_enable());
+	run_command("md5sum ${loadaddr} ${filesize}", 0);
+	ut_assert_nextline("md5 for 00020000 ... 0002001f ==> 234af48e94b0085060249ecb5942ab57");
+	ut_assertok(ut_check_console_end(uts));
+
+	return 0;
+}
+
+LIB_TEST(net_test_wget, 0);
diff --git a/test/dm/eth.c b/test/dm/eth.c
index 5437f9e..ebf01d8 100644
--- a/test/dm/eth.c
+++ b/test/dm/eth.c
@@ -13,6 +13,7 @@
 #include <log.h>
 #include <malloc.h>
 #include <net.h>
+#include <net6.h>
 #include <asm/eth.h>
 #include <dm/test.h>
 #include <dm/device-internal.h>
@@ -22,6 +23,152 @@
 
 #define DM_TEST_ETH_NUM		4
 
+#if IS_ENABLED(CONFIG_IPV6)
+static int dm_test_string_to_ip6(struct unit_test_state *uts)
+{
+	char *str;
+	struct test_ip6_pair {
+		char 		*string_addr;
+		struct in6_addr ip6_addr;
+	};
+
+	struct in6_addr ip6 = {0};
+
+	/* Correct statements */
+	struct test_ip6_pair test_suite[] = {
+		{"2001:db8::0:1234:1", {.s6_addr32[0] = 0xb80d0120,
+					.s6_addr32[1] = 0x00000000,
+					.s6_addr32[2] = 0x00000000,
+					.s6_addr32[3] = 0x01003412}},
+		{"2001:0db8:0000:0000:0000:0000:1234:0001",
+				       {.s6_addr32[0] = 0xb80d0120,
+					.s6_addr32[1] = 0x00000000,
+					.s6_addr32[2] = 0x00000000,
+					.s6_addr32[3] = 0x01003412}},
+		{"::1", 	       {.s6_addr32[0] = 0x00000000,
+					.s6_addr32[1] = 0x00000000,
+					.s6_addr32[2] = 0x00000000,
+					.s6_addr32[3] = 0x01000000}},
+		{"::ffff:192.168.1.1", {.s6_addr32[0] = 0x00000000,
+					.s6_addr32[1] = 0x00000000,
+					.s6_addr32[2] = 0xffff0000,
+					.s6_addr32[3] = 0x0101a8c0}},
+	};
+
+	for (int i = 0; i < ARRAY_SIZE(test_suite); ++i) {
+		ut_assertok(string_to_ip6(test_suite[i].string_addr,
+			    strlen(test_suite[i].string_addr), &ip6));
+		ut_asserteq_mem(&ip6, &test_suite[i].ip6_addr,
+				sizeof(struct in6_addr));
+	}
+
+	/* Incorrect statements */
+	str = "hello:world";
+	ut_assertok(!string_to_ip6(str, strlen(str), &ip6));
+	str = "2001:db8::0::0";
+	ut_assertok(!string_to_ip6(str, strlen(str), &ip6));
+	str = "2001:db8:192.168.1.1::1";
+	ut_assertok(!string_to_ip6(str, strlen(str), &ip6));
+	str = "192.168.1.1";
+	ut_assertok(!string_to_ip6(str, strlen(str), &ip6));
+
+	return 0;
+}
+DM_TEST(dm_test_string_to_ip6, 0);
+
+static int dm_test_csum_ipv6_magic(struct unit_test_state *uts)
+{
+	unsigned short csum = 0xbeef;
+	/* Predefined correct parameters */
+	unsigned short correct_csum = 0xd8ac;
+	struct in6_addr saddr = {.s6_addr32[0] = 0x000080fe,
+				 .s6_addr32[1] = 0x00000000,
+				 .s6_addr32[2] = 0xffe9f242,
+				 .s6_addr32[3] = 0xe8f66dfe};
+	struct in6_addr daddr = {.s6_addr32[0] = 0x000080fe,
+				 .s6_addr32[1] = 0x00000000,
+				 .s6_addr32[2] = 0xffd5b372,
+				 .s6_addr32[3] = 0x3ef692fe};
+	u16 len = 1460;
+	unsigned short proto = 17;
+	unsigned int head_csum = 0x91f0;
+
+	csum = csum_ipv6_magic(&saddr, &daddr, len, proto, head_csum);
+	ut_asserteq(csum, correct_csum);
+
+	/* Broke a parameter */
+	proto--;
+	csum = csum_ipv6_magic(&saddr, &daddr, len, proto, head_csum);
+	ut_assert(csum != correct_csum);
+
+	return 0;
+}
+DM_TEST(dm_test_csum_ipv6_magic, 0);
+
+static int dm_test_ip6_addr_in_subnet(struct unit_test_state *uts)
+{
+	struct in6_addr our = {.s6_addr32[0] = 0x000080fe,
+				 .s6_addr32[1] = 0x00000000,
+				 .s6_addr32[2] = 0xffe9f242,
+				 .s6_addr32[3] = 0xe8f66dfe};
+	struct in6_addr neigh1 = {.s6_addr32[0] = 0x000080fe,
+				 .s6_addr32[1] = 0x00000000,
+				 .s6_addr32[2] = 0xffd5b372,
+				 .s6_addr32[3] = 0x3ef692fe};
+	struct in6_addr neigh2 = {.s6_addr32[0] = 0x60480120,
+				 .s6_addr32[1] = 0x00006048,
+				 .s6_addr32[2] = 0x00000000,
+				 .s6_addr32[3] = 0x00008888};
+
+	/* in */
+	ut_assert(ip6_addr_in_subnet(&our, &neigh1, 64));
+	/* outside */
+	ut_assert(!ip6_addr_in_subnet(&our, &neigh2, 64));
+	ut_assert(!ip6_addr_in_subnet(&our, &neigh1, 128));
+
+	return 0;
+}
+DM_TEST(dm_test_ip6_addr_in_subnet, 0);
+
+static int dm_test_ip6_make_snma(struct unit_test_state *uts)
+{
+	struct in6_addr mult = {0};
+	struct in6_addr correct_addr = {
+				 .s6_addr32[0] = 0x000002ff,
+				 .s6_addr32[1] = 0x00000000,
+				 .s6_addr32[2] = 0x01000000,
+				 .s6_addr32[3] = 0xe8f66dff};
+	struct in6_addr addr = { .s6_addr32[0] = 0x000080fe,
+				 .s6_addr32[1] = 0x00000000,
+				 .s6_addr32[2] = 0xffe9f242,
+				 .s6_addr32[3] = 0xe8f66dfe};
+
+	ip6_make_snma(&mult, &addr);
+	ut_asserteq_mem(&mult, &correct_addr, sizeof(struct in6_addr));
+
+	return 0;
+}
+DM_TEST(dm_test_ip6_make_snma, 0);
+
+static int dm_test_ip6_make_lladdr(struct unit_test_state *uts)
+{
+	struct in6_addr generated_lladdr = {0};
+	struct in6_addr correct_lladdr = {
+				 .s6_addr32[0] = 0x000080fe,
+				 .s6_addr32[1] = 0x00000000,
+				 .s6_addr32[2] = 0xffabf33a,
+				 .s6_addr32[3] = 0xfbb352fe};
+	const unsigned char mac[6] = {0x38, 0xf3, 0xab, 0x52, 0xb3, 0xfb};
+
+	ip6_make_lladdr(&generated_lladdr, mac);
+	ut_asserteq_mem(&generated_lladdr, &correct_lladdr,
+			sizeof(struct in6_addr));
+
+	return 0;
+}
+DM_TEST(dm_test_ip6_make_lladdr, UT_TESTF_SCAN_FDT);
+#endif
+
 static int dm_test_eth(struct unit_test_state *uts)
 {
 	net_ping_ip = string_to_ip("1.1.2.2");
diff --git a/test/dm/sound.c b/test/dm/sound.c
index b73f6ab..15d545a 100644
--- a/test/dm/sound.c
+++ b/test/dm/sound.c
@@ -26,8 +26,19 @@
 	ut_asserteq(0, sandbox_get_setup_called(dev));
 
 	ut_assertok(sound_beep(dev, 1, 100));
+	ut_asserteq(48, sandbox_get_sound_count(dev));
 	ut_asserteq(4560, sandbox_get_sound_sum(dev));
 	ut_assertok(sound_beep(dev, 1, 100));
+	ut_asserteq(96, sandbox_get_sound_count(dev));
+	ut_asserteq(9120, sandbox_get_sound_sum(dev));
+	ut_assertok(sound_beep(dev, 1, -100));
+	ut_asserteq(144, sandbox_get_sound_count(dev));
+	ut_asserteq(9120, sandbox_get_sound_sum(dev));
+	ut_assertok(sound_beep(dev, 1, 0));
+	ut_asserteq(192, sandbox_get_sound_count(dev));
+	ut_asserteq(9120, sandbox_get_sound_sum(dev));
+	ut_assertok(sound_beep(dev, 1, INT_MAX));
+	ut_asserteq(240, sandbox_get_sound_count(dev));
 	ut_asserteq(9120, sandbox_get_sound_sum(dev));
 	ut_asserteq(false, sandbox_get_sound_active(dev));
 
diff --git a/test/py/tests/test_eficonfig/test_eficonfig.py b/test/py/tests/test_eficonfig/test_eficonfig.py
index 3859a77..b0a6cc4 100644
--- a/test/py/tests/test_eficonfig/test_eficonfig.py
+++ b/test/py/tests/test_eficonfig/test_eficonfig.py
@@ -352,6 +352,7 @@
         press_up_down_enter_and_wait(0, 1, True, 'Quit')
         press_up_down_enter_and_wait(0, 0, True, 'No block device found!')
         press_escape_key(False)
+        press_escape_key(False)
         check_current_is_maintenance_menu()
         # Return to U-Boot console
         press_escape_key(True)
diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst
index fda16f1..e7b231e 100644
--- a/tools/binman/binman.rst
+++ b/tools/binman/binman.rst
@@ -505,7 +505,6 @@
 of the image) can be used to point to the FDT map. See fdtmap and image-header
 entries for more information.
 
-
 Map files
 ---------
 
@@ -1245,6 +1244,8 @@
 
     $ binman replace -i image.bin "*u-boot*" -I indir
 
+
+.. _`BinmanLogging`:
 
 Logging
 -------
@@ -1337,6 +1338,305 @@
 
    bintools
 
+Binman commands and arguments
+=============================
+
+Usage::
+
+    binman [-h] [-B BUILD_DIR] [-D] [-H] [--toolpath TOOLPATH] [-T THREADS]
+        [--test-section-timeout] [-v VERBOSITY] [-V]
+        {build,bintool-docs,entry-docs,ls,extract,replace,test,tool} ...
+
+Binman provides the following commands:
+
+- **build** - build images
+- **bintools-docs** - generate documentation about bintools
+- **entry-docs** - generate documentation about entry types
+- **ls** - list an image
+- **extract** - extract files from an image
+- **replace** - replace one or more entries in an image
+- **test** - run tests
+- **tool** - manage bintools
+
+Options:
+
+-h, --help
+    Show help message and exit
+
+-B BUILD_DIR, --build-dir BUILD_DIR
+    Directory containing the build output
+
+-D, --debug
+    Enabling debugging (provides a full traceback on error)
+
+-H, --full-help
+    Display the README file
+
+--toolpath TOOLPATH
+    Add a path to the directories containing tools
+
+-T THREADS, --threads THREADS
+    Number of threads to use (0=single-thread). Note that -T0 is useful for
+    debugging since everything runs in one thread.
+
+-v VERBOSITY, --verbosity VERBOSITY
+    Control verbosity: 0=silent, 1=warnings, 2=notices, 3=info, 4=detail,
+    5=debug
+
+-V, --version
+    Show the binman version
+
+Test options:
+
+--test-section-timeout
+    Use a zero timeout for section multi-threading (for testing)
+
+Commands are described below.
+
+binman build
+------------
+
+This builds one or more images using the provided image description.
+
+Usage::
+
+    binman build [-h] [-a ENTRY_ARG] [-b BOARD] [-d DT] [--fake-dtb]
+        [--fake-ext-blobs] [--force-missing-bintools FORCE_MISSING_BINTOOLS]
+        [-i IMAGE] [-I INDIR] [-m] [-M] [-n] [-O OUTDIR] [-p] [-u]
+        [--update-fdt-in-elf UPDATE_FDT_IN_ELF] [-W]
+
+Options:
+
+-h, --help
+    Show help message and exit
+
+-a ENTRY_ARG, --entry-arg ENTRY_ARG
+    Set argument value `arg=value`. See
+    `Passing command-line arguments to entries`_.
+
+-b BOARD, --board BOARD
+    Board name to build. This can be used instead of `-d`, in which case the
+    file `u-boot.dtb` is used, within the build directory's board subdirectory.
+
+-d DT, --dt DT
+    Configuration file (.dtb) to use. This must have a top-level node called
+    `binman`. See `Image description format`_.
+
+-i IMAGE, --image IMAGE
+    Image filename to build (if not specified, build all)
+
+-I INDIR, --indir INDIR
+    Add a path to the list of directories to use for input files. This can be
+    specified multiple times to add more than one path.
+
+-m, --map
+    Output a map file for each image. See `Map files`_.
+
+-M, --allow-missing
+    Allow external blobs and bintools to be missing. See `External blobs`_.
+
+-n, --no-expanded
+    Don't use 'expanded' versions of entries where available; normally 'u-boot'
+    becomes 'u-boot-expanded', for example. See `Expanded entries`_.
+
+-O OUTDIR, --outdir OUTDIR
+    Path to directory to use for intermediate and output files
+
+-p, --preserve
+    Preserve temporary output directory even if option -O is not given
+
+-u, --update-fdt
+    Update the binman node with offset/size info. See
+    `Access to binman entry offsets at run time (fdt)`_.
+
+--update-fdt-in-elf UPDATE_FDT_IN_ELF
+    Update an ELF file with the output dtb. The argument is a string consisting
+    of four parts, separated by commas. See `Updating an ELF file`_.
+
+-W, --ignore-missing
+    Return success even if there are missing blobs/bintools (requires -M)
+
+Options used only for testing:
+
+--fake-dtb
+    Use fake device tree contents
+
+--fake-ext-blobs
+    Create fake ext blobs with dummy content
+
+--force-missing-bintools FORCE_MISSING_BINTOOLS
+    Comma-separated list of bintools to consider missing
+
+binman bintool-docs
+-------------------
+
+Usage::
+
+    binman bintool-docs [-h]
+
+This outputs documentation for the bintools in rST format. See
+`Bintool Documentation`_.
+
+binman entry-docs
+-----------------
+
+Usage::
+
+    binman entry-docs [-h]
+
+This outputs documentation for the entry types in rST format. See
+`Entry Documentation`_.
+
+binman ls
+---------
+
+Usage::
+
+    binman ls [-h] -i IMAGE [paths ...]
+
+Positional arguments:
+
+paths
+    Paths within file to list (wildcard)
+
+Pptions:
+
+-h, --help
+    show help message and exit
+
+-i IMAGE, --image IMAGE
+    Image filename to list
+
+This lists an image, showing its contents. See `Listing images`_.
+
+binman extract
+--------------
+
+Usage::
+
+    binman extract [-h] [-F FORMAT] -i IMAGE [-f FILENAME] [-O OUTDIR] [-U]
+        [paths ...]
+
+Positional arguments:
+
+Paths
+    Paths within file to extract (wildcard)
+
+Options:
+
+-h, --help
+    show help message and exit
+
+-F FORMAT, --format FORMAT
+    Select an alternative format for extracted data
+
+-i IMAGE, --image IMAGE
+    Image filename to extract
+
+-f FILENAME, --filename FILENAME
+    Output filename to write to
+
+-O OUTDIR, --outdir OUTDIR
+    Path to directory to use for output files
+
+-U, --uncompressed
+    Output raw uncompressed data for compressed entries
+
+This extracts the contents of entries from an image. See
+`Extracting files from images`_.
+
+binman replace
+--------------
+
+Usage::
+
+    binman replace [-h] [-C] -i IMAGE [-f FILENAME] [-F] [-I INDIR] [-m]
+        [paths ...]
+
+Positional arguments:
+
+paths
+    Paths within file to replace (wildcard)
+
+Options:
+
+-h, --help
+    show help message and exit
+
+-C, --compressed
+    Input data is already compressed if needed for the entry
+
+-i IMAGE, --image IMAGE
+    Image filename to update
+
+-f FILENAME, --filename FILENAME
+    Input filename to read from
+
+-F, --fix-size
+    Don't allow entries to be resized
+
+-I INDIR, --indir INDIR
+    Path to directory to use for input files
+
+-m, --map
+    Output a map file for the updated image
+
+This replaces one or more entries in an existing image. See
+`Replacing files in an image`_.
+
+binman test
+-----------
+
+Usage::
+
+    binman test [-h] [-P PROCESSES] [-T] [-X] [tests ...]
+
+Positional arguments:
+
+tests
+    Test names to run (omit for all)
+
+Options:
+
+-h, --help
+    show help message and exit
+
+-P PROCESSES, --processes PROCESSES
+    set number of processes to use for running tests. This defaults to the
+    number of CPUs on the machine
+
+-T, --test-coverage
+    run tests and check for 100% coverage
+
+-X, --test-preserve-dirs
+    Preserve and display test-created input directories; also preserve the
+    output directory if a single test is run (pass test name at the end of the
+    command line
+
+binman tool
+-----------
+
+Usage::
+
+    binman tool [-h] [-l] [-f] [bintools ...]
+
+Positional arguments:
+
+bintools
+    Bintools to process
+
+Options:
+
+-h, --help
+    show help message and exit
+
+-l, --list
+    List all known bintools
+
+-f, --fetch
+    Fetch a bintool from a known location. Use `all` to fetch all and `missing`
+    to fetch any missing tools.
+
 
 Technical details
 =================
@@ -1416,6 +1716,8 @@
 final step.
 
 
+.. _`External tools`:
+
 External tools
 --------------
 
@@ -1436,6 +1738,8 @@
 
    BINMAN_TOOLPATHS="/tools/g12a /tools/tegra" binman ...
 
+
+.. _`External blobs`:
 
 External blobs
 --------------
@@ -1461,6 +1765,10 @@
        odroid-c4/build/board/hardkernel/odroidc4/firmware \
        odroid-c4/build/scp_task" binman ...
 
+Note that binman fails with exit code 103 when there are missing blobs. If you
+wish binman to continue anyway, you can pass `-W` to binman.
+
+
 Code coverage
 -------------
 
@@ -1472,6 +1780,48 @@
    $ sudo apt-get install python-coverage python3-coverage python-pytest
 
 
+Exit status
+-----------
+
+Binman produces the following exit codes:
+
+0
+    Success
+
+1
+    Any sort of failure - see output for more details
+
+103
+    There are missing external blobs or bintools. This is only returned if
+    -M is passed to binman, otherwise missing blobs return an exit status of 1.
+    Note, if -W is passed as well as -M, then this is converted into a warning
+    and will return an exit status of 0 instead.
+
+
+U-Boot environment variables for binman
+---------------------------------------
+
+The U-Boot Makefile supports various environment variables to control binman.
+All of these are set within the Makefile and result in passing various
+environment variables (or make flags) to binman:
+
+BINMAN_DEBUG
+    Enables backtrace debugging by adding a `-D` argument. See
+    :ref:`BinmanLogging`.
+
+BINMAN_INDIRS
+    Sets the search path for input files used by binman by adding one or more
+    `-I` arguments. See :ref:`External blobs`.
+
+BINMAN_TOOLPATHS
+    Sets the search path for external tool used by binman by adding one or more
+    `--toolpath` arguments. See :ref:`External tools`.
+
+BINMAN_VERBOSE
+    Sets the logging verbosity of binman by adding a `-v` argument. See
+    :ref:`BinmanLogging`.
+
+
 Error messages
 --------------
 
diff --git a/tools/binman/bintool.py b/tools/binman/bintool.py
index a582d9d..8fda13f 100644
--- a/tools/binman/bintool.py
+++ b/tools/binman/bintool.py
@@ -85,7 +85,6 @@
                 try:
                     # Deal with classes which must be renamed due to conflicts
                     # with Python libraries
-                    class_name = f'Bintoolbtool_{module_name}'
                     module = importlib.import_module('binman.btool.btool_' +
                                                      module_name)
                 except ImportError:
@@ -137,6 +136,8 @@
         names = [os.path.splitext(os.path.basename(fname))[0]
                  for fname in files]
         names = [name for name in names if name[0] != '_']
+        names = [name[6:] if name.startswith('btool_') else name
+                 for name in names]
         if include_testing:
             names.append('_testing')
         return sorted(names)
diff --git a/tools/binman/btool/btool_gzip.py b/tools/binman/btool/btool_gzip.py
index 70cbc19..0d75028 100644
--- a/tools/binman/btool/btool_gzip.py
+++ b/tools/binman/btool/btool_gzip.py
@@ -14,7 +14,7 @@
 from binman import bintool
 
 # pylint: disable=C0103
-class Bintoolbtool_gzip(bintool.BintoolPacker):
+class Bintoolgzip(bintool.BintoolPacker):
     """Compression/decompression using the gzip algorithm
 
     This bintool supports running `gzip` to compress and decompress data, as
@@ -27,5 +27,5 @@
         man gzip
     """
     def __init__(self, name):
-        super().__init__("gzip", compress_args=[],
+        super().__init__(name, compress_args=[],
                          version_regex=r'gzip ([0-9.]+)')
diff --git a/tools/binman/cmdline.py b/tools/binman/cmdline.py
index 1d1ca43..986d6f1 100644
--- a/tools/binman/cmdline.py
+++ b/tools/binman/cmdline.py
@@ -114,7 +114,7 @@
     build_parser.add_argument('-m', '--map', action='store_true',
         default=False, help='Output a map file for each image')
     build_parser.add_argument('-M', '--allow-missing', action='store_true',
-        default=False, help='Allow external blobs to be missing')
+        default=False, help='Allow external blobs and bintools to be missing')
     build_parser.add_argument('-n', '--no-expanded', action='store_true',
             help="Don't use 'expanded' versions of entries where available; "
                  "normally 'u-boot' becomes 'u-boot-expanded', for example")
@@ -128,6 +128,9 @@
         default=False, help='Update the binman node with offset/size info')
     build_parser.add_argument('--update-fdt-in-elf', type=str,
         help='Update an ELF file with the output dtb: infile,outfile,begin_sym,end_sym')
+    build_parser.add_argument(
+        '-W', '--ignore-missing', action='store_true', default=False,
+        help='Return success even if there are missing blobs/bintools (requires -M)')
 
     subparsers.add_parser(
         'bintool-docs', help='Write out bintool documentation (see bintool.rst)')
diff --git a/tools/binman/control.py b/tools/binman/control.py
index bfe63a1..964c698 100644
--- a/tools/binman/control.py
+++ b/tools/binman/control.py
@@ -741,8 +741,15 @@
                 data = state.GetFdtForEtype('u-boot-dtb').GetContents()
                 elf.UpdateFile(*elf_params, data)
 
+            # This can only be True if -M is provided, since otherwise binman
+            # would have raised an error already
             if invalid:
-                tout.warning("\nSome images are invalid")
+                msg = '\nSome images are invalid'
+                if args.ignore_missing:
+                    tout.warning(msg)
+                else:
+                    tout.error(msg)
+                    return 103
 
             # Use this to debug the time take to pack the image
             #state.TimingShow()
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index e849d96..62ee86b 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -340,7 +340,7 @@
                     use_expanded=False, verbosity=None, allow_missing=False,
                     allow_fake_blobs=False, extra_indirs=None, threads=None,
                     test_section_timeout=False, update_fdt_in_elf=None,
-                    force_missing_bintools=''):
+                    force_missing_bintools='', ignore_missing=False):
         """Run binman with a given test file
 
         Args:
@@ -403,6 +403,8 @@
                 args.append('-a%s=%s' % (arg, value))
         if allow_missing:
             args.append('-M')
+            if ignore_missing:
+                args.append('-W')
         if allow_fake_blobs:
             args.append('--fake-ext-blobs')
         if force_missing_bintools:
@@ -3725,9 +3727,22 @@
     def testExtblobMissingOk(self):
         """Test an image with an missing external blob that is allowed"""
         with test_util.capture_sys_output() as (stdout, stderr):
-            self._DoTestFile('158_blob_ext_missing.dts', allow_missing=True)
+            ret = self._DoTestFile('158_blob_ext_missing.dts',
+                                   allow_missing=True)
+        self.assertEqual(103, ret)
         err = stderr.getvalue()
         self.assertRegex(err, "Image 'main-section'.*missing.*: blob-ext")
+        self.assertIn('Some images are invalid', err)
+
+    def testExtblobMissingOkFlag(self):
+        """Test an image with an missing external blob allowed with -W"""
+        with test_util.capture_sys_output() as (stdout, stderr):
+            ret = self._DoTestFile('158_blob_ext_missing.dts',
+                                   allow_missing=True, ignore_missing=True)
+        self.assertEqual(0, ret)
+        err = stderr.getvalue()
+        self.assertRegex(err, "Image 'main-section'.*missing.*: blob-ext")
+        self.assertIn('Some images are invalid', err)
 
     def testExtblobMissingOkSect(self):
         """Test an image with an missing external blob that is allowed"""
diff --git a/tools/buildman/README b/tools/buildman/README
deleted file mode 100644
index a8357a8..0000000
--- a/tools/buildman/README
+++ /dev/null
@@ -1,1349 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0+
-# Copyright (c) 2013 The Chromium OS Authors.
-
-(Please read 'How to change from MAKEALL' if you are used to that tool)
-
-Quick-start
-===========
-
-If you just want to quickly set up buildman so you can build something (for
-example Raspberry Pi 2):
-
-   cd /path/to/u-boot
-   PATH=$PATH:`pwd`/tools/buildman
-   buildman --fetch-arch arm
-   buildman -k rpi_2
-   ls ../current/rpi_2
-   # u-boot.bin is the output image
-
-
-What is this?
-=============
-
-This tool handles building U-Boot to check that you have not broken it
-with your patch series. It can build each individual commit and report
-which boards fail on which commits, and which errors come up. It aims
-to make full use of multi-processor machines.
-
-A key feature of buildman is its output summary, which allows warnings,
-errors or image size increases in a particular commit or board to be
-quickly identified and the offending commit pinpointed. This can be a big
-help for anyone working with >10 patches at a time.
-
-
-Caveats
-=======
-
-Buildman can be stopped and restarted, in which case it will continue
-where it left off. This should happen cleanly and without side-effects.
-If not, it is a bug, for which a patch would be welcome.
-
-Buildman gets so tied up in its work that it can ignore the outside world.
-You may need to press Ctrl-C several times to quit it. Also it will print
-out various exceptions when stopped. You may have to kill it since the
-Ctrl-C handling is somewhat broken.
-
-
-Theory of Operation
-===================
-
-(please read this section in full twice or you will be perpetually confused)
-
-Buildman is a builder. It is not make, although it runs make. It does not
-produce any useful output on the terminal while building, except for
-progress information (but see -v below). All the output (errors, warnings and
-binaries if you ask for them) is stored in output directories, which you can
-look at from a separate 'buildman -s' instance while the build is progressing,
-or when it is finished.
-
-Buildman is designed to build entire git branches, i.e. muliple commits. It
-can be run repeatedly on the same branch after making changes to commits on
-that branch. In this case it will automatically rebuild commits which have
-changed (and remove its old results for that commit). It is possible to build
-a branch for one board, then later build it for another board. This adds to
-the output, so now you have results for two boards. If you want buildman to
-re-build a commit it has already built (e.g. because of a toolchain update),
-use the -f flag.
-
-Buildman produces a concise summary of which boards succeeded and failed.
-It shows which commit introduced which board failure using a simple
-red/green colour coding (with yellow/cyan for warnings). Full error
-information can be requested, in which case it is de-duped and displayed
-against the commit that introduced the error. An example workflow is below.
-
-Buildman stores image size information and can report changes in image size
-from commit to commit. An example of this is below.
-
-Buildman starts multiple threads, and each thread builds for one board at
-a time. A thread starts at the first commit, configures the source for your
-board and builds it. Then it checks out the next commit and does an
-incremental build (i.e. not using 'make xxx_defconfig' unless you use -C).
-Eventually the thread reaches the last commit and stops. If a commit causes
-an error or warning, buildman will try it again after reconfiguring (but see
--Q). Thus some commits may be built twice, with the first result silently
-discarded. Lots of errors and warnings will causes lots of reconfigures and your
-build will be very slow. This is because a file that produces just a warning
-would not normally be rebuilt in an incremental build. Once a thread finishes
-building all the commits for a board, it starts on the commits for another
-board.
-
-Buildman works in an entirely separate place from your U-Boot repository.
-It creates a separate working directory for each thread, and puts the
-output files in the working directory, organised by commit name and board
-name, in a two-level hierarchy (but see -P).
-
-Buildman is invoked in your U-Boot directory, the one with the .git
-directory. It clones this repository into a copy for each thread, and the
-threads do not affect the state of your git repository. Any checkouts done
-by the thread affect only the working directory for that thread.
-
-Buildman automatically selects the correct tool chain for each board. You
-must supply suitable tool chains (see --fetch-arch), but buildman takes care
-of selecting the right one.
-
-Buildman generally builds a branch (with the -b flag), and in this case
-builds the upstream commit as well, for comparison. So even if you have one
-commit in your branch, two commits will be built. Put all your commits in a
-branch, set the branch's upstream to a valid value, and all will be well.
-Otherwise buildman will perform random actions. Use -n to check what the
-random actions might be.
-
-Buildman effectively has two modes: without -s it builds, with -s it
-summarises the results of previous (or active) builds.
-
-If you just want to build the current source tree, leave off the -b flag.
-This will display results and errors as they happen. You can still look at
-them later using -se. Note that buildman will assume that the source has
-changed, and will build all specified boards in this case.
-
-Buildman is optimised for building many commits at once, for many boards.
-On multi-core machines, Buildman is fast because it uses most of the
-available CPU power. When it gets to the end, or if you are building just
-a few commits or boards, it will be pretty slow. As a tip, if you don't
-plan to use your machine for anything else, you can use -T to increase the
-number of threads beyond the default.
-
-
-Selecting which boards to build
-===============================
-
-Buildman lets you build all boards, or a subset. Specify the subset by passing
-command-line arguments that list the desired build target, architecture,
-CPU, board name, vendor, SoC or options. Multiple arguments are allowed. Each
-argument will be interpreted as a regular expression, so behaviour is a superset
-of exact or substring matching. Examples are:
-
-* 'tegra20'      All boards with a Tegra20 SoC
-* 'tegra'        All boards with any Tegra Soc (Tegra20, Tegra30, Tegra114...)
-* '^tegra[23]0$' All boards with either Tegra20 or Tegra30 SoC
-* 'powerpc'      All PowerPC boards
-
-While the default is to OR the terms together, you can also make use of
-the '&' operator to limit the selection:
-
-* 'freescale & arm sandbox'  All Freescale boards with ARM architecture,
-                             plus sandbox
-
-You can also use -x to specifically exclude some boards. For example:
-
-  buildman arm -x nvidia,freescale,.*ball$
-
-means to build all arm boards except nvidia, freescale and anything ending
-with 'ball'.
-
-For building specific boards you can use the --boards (or --bo) option, which
-takes a comma-separated list of board target names and be used multiple times
-on the command line:
-
-  buildman --boards sandbox,snow --boards
-
-It is convenient to use the -n option to see what will be built based on
-the subset given. Use -v as well to get an actual list of boards.
-
-Buildman does not store intermediate object files. It optionally copies
-the binary output into a directory when a build is successful (-k). Size
-information is always recorded. It needs a fair bit of disk space to work,
-typically 250MB per thread.
-
-
-Setting up
-==========
-
-1. Get the U-Boot source. You probably already have it, but if not these
-steps should get you started with a repo and some commits for testing.
-
-$ cd /path/to/u-boot
-$ git clone git://git.denx.de/u-boot.git .
-$ git checkout -b my-branch origin/master
-$ # Add some commits to the branch, reading for testing
-
-2. Create ~/.buildman to tell buildman where to find tool chains (see 'The
-.buildman file' later for details). As an example:
-
-# Buildman settings file
-
-[toolchain]
-root: /
-rest: /toolchains/*
-eldk: /opt/eldk-4.2
-arm: /opt/linaro/gcc-linaro-arm-linux-gnueabihf-4.8-2013.08_linux
-aarch64: /opt/linaro/gcc-linaro-aarch64-none-elf-4.8-2013.10_linux
-
-[toolchain-alias]
-x86: i386
-blackfin: bfin
-openrisc: or1k
-
-
-This selects the available toolchain paths. Add the base directory for
-each of your toolchains here. Buildman will search inside these directories
-and also in any '/usr' and '/usr/bin' subdirectories.
-
-Make sure the tags (here root: rest: and eldk:) are unique.
-
-The toolchain-alias section indicates that the i386 toolchain should be used
-to build x86 commits.
-
-Note that you can also specific exactly toolchain prefixes if you like:
-
-[toolchain-prefix]
-arm: /opt/arm-eabi-4.6/bin/arm-eabi-
-
-or even:
-
-[toolchain-prefix]
-arm: /opt/arm-eabi-4.6/bin/arm-eabi-gcc
-
-This tells buildman that you want to use this exact toolchain for the arm
-architecture. This will override any toolchains found by searching using the
-[toolchain] settings.
-
-Since the toolchain prefix is an explicit request, buildman will report an
-error if a toolchain is not found with that prefix. The current PATH will be
-searched, so it is possible to use:
-
-[toolchain-prefix]
-arm: arm-none-eabi-
-
-and buildman will find arm-none-eabi-gcc in /usr/bin if you have it installed.
-
-[toolchain-wrapper]
-wrapper: ccache
-
-This tells buildman to use a compiler wrapper in front of CROSS_COMPILE. In
-this example, ccache. It doesn't affect the toolchain scan. The wrapper is
-added when CROSS_COMPILE environtal variable is set. The name in this
-section is ignored. If more than one line is provided, only the last one
-is taken.
-
-3. Make sure you have the require Python pre-requisites
-
-Buildman uses multiprocessing, Queue, shutil, StringIO, ConfigParser and
-urllib2. These should normally be available, but if you get an error like
-this then you will need to obtain those modules:
-
-    ImportError: No module named multiprocessing
-
-
-4. Check the available toolchains
-
-Run this check to make sure that you have a toolchain for every architecture.
-
-$ ./tools/buildman/buildman --list-tool-chains
-Scanning for tool chains
-   - scanning prefix '/opt/gcc-4.6.3-nolibc/x86_64-linux/bin/x86_64-linux-'
-Tool chain test:  OK, arch='x86', priority 1
-   - scanning prefix '/opt/arm-eabi-4.6/bin/arm-eabi-'
-Tool chain test:  OK, arch='arm', priority 1
-   - scanning path '/toolchains/gcc-4.9.0-nolibc/i386-linux'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/i386-linux/.'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/i386-linux/bin'
-         - found '/toolchains/gcc-4.9.0-nolibc/i386-linux/bin/i386-linux-gcc'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/i386-linux/usr/bin'
-Tool chain test:  OK, arch='i386', priority 4
-   - scanning path '/toolchains/gcc-4.9.0-nolibc/aarch64-linux'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/aarch64-linux/.'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/aarch64-linux/bin'
-         - found '/toolchains/gcc-4.9.0-nolibc/aarch64-linux/bin/aarch64-linux-gcc'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/aarch64-linux/usr/bin'
-Tool chain test:  OK, arch='aarch64', priority 4
-   - scanning path '/toolchains/gcc-4.9.0-nolibc/microblaze-linux'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/microblaze-linux/.'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/microblaze-linux/bin'
-         - found '/toolchains/gcc-4.9.0-nolibc/microblaze-linux/bin/microblaze-linux-gcc'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/microblaze-linux/usr/bin'
-Tool chain test:  OK, arch='microblaze', priority 4
-   - scanning path '/toolchains/gcc-4.9.0-nolibc/mips64-linux'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/mips64-linux/.'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/mips64-linux/bin'
-         - found '/toolchains/gcc-4.9.0-nolibc/mips64-linux/bin/mips64-linux-gcc'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/mips64-linux/usr/bin'
-Tool chain test:  OK, arch='mips64', priority 4
-   - scanning path '/toolchains/gcc-4.9.0-nolibc/sparc64-linux'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/sparc64-linux/.'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/sparc64-linux/bin'
-         - found '/toolchains/gcc-4.9.0-nolibc/sparc64-linux/bin/sparc64-linux-gcc'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/sparc64-linux/usr/bin'
-Tool chain test:  OK, arch='sparc64', priority 4
-   - scanning path '/toolchains/gcc-4.9.0-nolibc/arm-unknown-linux-gnueabi'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/arm-unknown-linux-gnueabi/.'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/arm-unknown-linux-gnueabi/bin'
-         - found '/toolchains/gcc-4.9.0-nolibc/arm-unknown-linux-gnueabi/bin/arm-unknown-linux-gnueabi-gcc'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/arm-unknown-linux-gnueabi/usr/bin'
-Tool chain test:  OK, arch='arm', priority 3
-Toolchain '/toolchains/gcc-4.9.0-nolibc/arm-unknown-linux-gnueabi/bin/arm-unknown-linux-gnueabi-gcc' at priority 3 will be ignored because another toolchain for arch 'arm' has priority 1
-   - scanning path '/toolchains/gcc-4.9.0-nolibc/sparc-linux'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/sparc-linux/.'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/sparc-linux/bin'
-         - found '/toolchains/gcc-4.9.0-nolibc/sparc-linux/bin/sparc-linux-gcc'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/sparc-linux/usr/bin'
-Tool chain test:  OK, arch='sparc', priority 4
-   - scanning path '/toolchains/gcc-4.9.0-nolibc/mips-linux'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/mips-linux/.'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/mips-linux/bin'
-         - found '/toolchains/gcc-4.9.0-nolibc/mips-linux/bin/mips-linux-gcc'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/mips-linux/usr/bin'
-Tool chain test:  OK, arch='mips', priority 4
-   - scanning path '/toolchains/gcc-4.9.0-nolibc/x86_64-linux'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/x86_64-linux/.'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/x86_64-linux/bin'
-         - found '/toolchains/gcc-4.9.0-nolibc/x86_64-linux/bin/x86_64-linux-gcc'
-         - found '/toolchains/gcc-4.9.0-nolibc/x86_64-linux/bin/x86_64-linux-x86_64-linux-gcc'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/x86_64-linux/usr/bin'
-Tool chain test:  OK, arch='x86_64', priority 4
-Tool chain test:  OK, arch='x86_64', priority 4
-Toolchain '/toolchains/gcc-4.9.0-nolibc/x86_64-linux/bin/x86_64-linux-x86_64-linux-gcc' at priority 4 will be ignored because another toolchain for arch 'x86_64' has priority 4
-   - scanning path '/toolchains/gcc-4.9.0-nolibc/m68k-linux'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/m68k-linux/.'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/m68k-linux/bin'
-         - found '/toolchains/gcc-4.9.0-nolibc/m68k-linux/bin/m68k-linux-gcc'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/m68k-linux/usr/bin'
-Tool chain test:  OK, arch='m68k', priority 4
-   - scanning path '/toolchains/gcc-4.9.0-nolibc/powerpc-linux'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/powerpc-linux/.'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/powerpc-linux/bin'
-         - found '/toolchains/gcc-4.9.0-nolibc/powerpc-linux/bin/powerpc-linux-gcc'
-      - looking in '/toolchains/gcc-4.9.0-nolibc/powerpc-linux/usr/bin'
-Tool chain test:  OK, arch='powerpc', priority 4
-   - scanning path '/toolchains/gcc-4.6.3-nolibc/bfin-uclinux'
-      - looking in '/toolchains/gcc-4.6.3-nolibc/bfin-uclinux/.'
-      - looking in '/toolchains/gcc-4.6.3-nolibc/bfin-uclinux/bin'
-         - found '/toolchains/gcc-4.6.3-nolibc/bfin-uclinux/bin/bfin-uclinux-gcc'
-      - looking in '/toolchains/gcc-4.6.3-nolibc/bfin-uclinux/usr/bin'
-Tool chain test:  OK, arch='bfin', priority 6
-   - scanning path '/toolchains/gcc-4.6.3-nolibc/sparc-linux'
-      - looking in '/toolchains/gcc-4.6.3-nolibc/sparc-linux/.'
-      - looking in '/toolchains/gcc-4.6.3-nolibc/sparc-linux/bin'
-         - found '/toolchains/gcc-4.6.3-nolibc/sparc-linux/bin/sparc-linux-gcc'
-      - looking in '/toolchains/gcc-4.6.3-nolibc/sparc-linux/usr/bin'
-Tool chain test:  OK, arch='sparc', priority 4
-Toolchain '/toolchains/gcc-4.6.3-nolibc/sparc-linux/bin/sparc-linux-gcc' at priority 4 will be ignored because another toolchain for arch 'sparc' has priority 4
-   - scanning path '/toolchains/gcc-4.6.3-nolibc/mips-linux'
-      - looking in '/toolchains/gcc-4.6.3-nolibc/mips-linux/.'
-      - looking in '/toolchains/gcc-4.6.3-nolibc/mips-linux/bin'
-         - found '/toolchains/gcc-4.6.3-nolibc/mips-linux/bin/mips-linux-gcc'
-      - looking in '/toolchains/gcc-4.6.3-nolibc/mips-linux/usr/bin'
-Tool chain test:  OK, arch='mips', priority 4
-Toolchain '/toolchains/gcc-4.6.3-nolibc/mips-linux/bin/mips-linux-gcc' at priority 4 will be ignored because another toolchain for arch 'mips' has priority 4
-   - scanning path '/toolchains/gcc-4.6.3-nolibc/m68k-linux'
-      - looking in '/toolchains/gcc-4.6.3-nolibc/m68k-linux/.'
-      - looking in '/toolchains/gcc-4.6.3-nolibc/m68k-linux/bin'
-         - found '/toolchains/gcc-4.6.3-nolibc/m68k-linux/bin/m68k-linux-gcc'
-      - looking in '/toolchains/gcc-4.6.3-nolibc/m68k-linux/usr/bin'
-Tool chain test:  OK, arch='m68k', priority 4
-Toolchain '/toolchains/gcc-4.6.3-nolibc/m68k-linux/bin/m68k-linux-gcc' at priority 4 will be ignored because another toolchain for arch 'm68k' has priority 4
-   - scanning path '/toolchains/gcc-4.6.3-nolibc/powerpc-linux'
-      - looking in '/toolchains/gcc-4.6.3-nolibc/powerpc-linux/.'
-      - looking in '/toolchains/gcc-4.6.3-nolibc/powerpc-linux/bin'
-         - found '/toolchains/gcc-4.6.3-nolibc/powerpc-linux/bin/powerpc-linux-gcc'
-      - looking in '/toolchains/gcc-4.6.3-nolibc/powerpc-linux/usr/bin'
-Tool chain test:  OK, arch='powerpc', priority 4
-Tool chain test:  OK, arch='or32', priority 4
-   - scanning path '/'
-      - looking in '/.'
-      - looking in '/bin'
-      - looking in '/usr/bin'
-         - found '/usr/bin/i586-mingw32msvc-gcc'
-         - found '/usr/bin/c89-gcc'
-         - found '/usr/bin/x86_64-linux-gnu-gcc'
-         - found '/usr/bin/gcc'
-         - found '/usr/bin/c99-gcc'
-         - found '/usr/bin/arm-linux-gnueabi-gcc'
-         - found '/usr/bin/aarch64-linux-gnu-gcc'
-         - found '/usr/bin/winegcc'
-         - found '/usr/bin/arm-linux-gnueabihf-gcc'
-Tool chain test:  OK, arch='i586', priority 11
-Tool chain test:  OK, arch='c89', priority 11
-Tool chain test:  OK, arch='x86_64', priority 4
-Toolchain '/usr/bin/x86_64-linux-gnu-gcc' at priority 4 will be ignored because another toolchain for arch 'x86_64' has priority 4
-Tool chain test:  OK, arch='sandbox', priority 11
-Tool chain test:  OK, arch='c99', priority 11
-Tool chain test:  OK, arch='arm', priority 4
-Toolchain '/usr/bin/arm-linux-gnueabi-gcc' at priority 4 will be ignored because another toolchain for arch 'arm' has priority 1
-Tool chain test:  OK, arch='aarch64', priority 4
-Toolchain '/usr/bin/aarch64-linux-gnu-gcc' at priority 4 will be ignored because another toolchain for arch 'aarch64' has priority 4
-Tool chain test:  OK, arch='sandbox', priority 11
-Toolchain '/usr/bin/winegcc' at priority 11 will be ignored because another toolchain for arch 'sandbox' has priority 11
-Tool chain test:  OK, arch='arm', priority 4
-Toolchain '/usr/bin/arm-linux-gnueabihf-gcc' at priority 4 will be ignored because another toolchain for arch 'arm' has priority 1
-List of available toolchains (34):
-aarch64   : /toolchains/gcc-4.9.0-nolibc/aarch64-linux/bin/aarch64-linux-gcc
-alpha     : /toolchains/gcc-4.9.0-nolibc/alpha-linux/bin/alpha-linux-gcc
-am33_2.0  : /toolchains/gcc-4.9.0-nolibc/am33_2.0-linux/bin/am33_2.0-linux-gcc
-arm       : /opt/arm-eabi-4.6/bin/arm-eabi-gcc
-bfin      : /toolchains/gcc-4.6.3-nolibc/bfin-uclinux/bin/bfin-uclinux-gcc
-c89       : /usr/bin/c89-gcc
-c99       : /usr/bin/c99-gcc
-frv       : /toolchains/gcc-4.9.0-nolibc/frv-linux/bin/frv-linux-gcc
-h8300     : /toolchains/gcc-4.9.0-nolibc/h8300-elf/bin/h8300-elf-gcc
-hppa      : /toolchains/gcc-4.9.0-nolibc/hppa-linux/bin/hppa-linux-gcc
-hppa64    : /toolchains/gcc-4.9.0-nolibc/hppa64-linux/bin/hppa64-linux-gcc
-i386      : /toolchains/gcc-4.9.0-nolibc/i386-linux/bin/i386-linux-gcc
-i586      : /usr/bin/i586-mingw32msvc-gcc
-ia64      : /toolchains/gcc-4.9.0-nolibc/ia64-linux/bin/ia64-linux-gcc
-m32r      : /toolchains/gcc-4.9.0-nolibc/m32r-linux/bin/m32r-linux-gcc
-m68k      : /toolchains/gcc-4.9.0-nolibc/m68k-linux/bin/m68k-linux-gcc
-microblaze: /toolchains/gcc-4.9.0-nolibc/microblaze-linux/bin/microblaze-linux-gcc
-mips      : /toolchains/gcc-4.9.0-nolibc/mips-linux/bin/mips-linux-gcc
-mips64    : /toolchains/gcc-4.9.0-nolibc/mips64-linux/bin/mips64-linux-gcc
-or32      : /toolchains/gcc-4.5.1-nolibc/or32-linux/bin/or32-linux-gcc
-powerpc   : /toolchains/gcc-4.9.0-nolibc/powerpc-linux/bin/powerpc-linux-gcc
-powerpc64 : /toolchains/gcc-4.9.0-nolibc/powerpc64-linux/bin/powerpc64-linux-gcc
-ppc64le   : /toolchains/gcc-4.9.0-nolibc/ppc64le-linux/bin/ppc64le-linux-gcc
-s390x     : /toolchains/gcc-4.9.0-nolibc/s390x-linux/bin/s390x-linux-gcc
-sandbox   : /usr/bin/gcc
-sh4       : /toolchains/gcc-4.6.3-nolibc/sh4-linux/bin/sh4-linux-gcc
-sparc     : /toolchains/gcc-4.9.0-nolibc/sparc-linux/bin/sparc-linux-gcc
-sparc64   : /toolchains/gcc-4.9.0-nolibc/sparc64-linux/bin/sparc64-linux-gcc
-tilegx    : /toolchains/gcc-4.6.2-nolibc/tilegx-linux/bin/tilegx-linux-gcc
-x86       : /opt/gcc-4.6.3-nolibc/x86_64-linux/bin/x86_64-linux-gcc
-x86_64    : /toolchains/gcc-4.9.0-nolibc/x86_64-linux/bin/x86_64-linux-gcc
-
-
-You can see that everything is covered, even some strange ones that won't
-be used (c88 and c99). This is a feature.
-
-
-5. Install new toolchains if needed
-
-You can download toolchains and update the [toolchain] section of the
-settings file to find them.
-
-To make this easier, buildman can automatically download and install
-toolchains from kernel.org. First list the available architectures:
-
-$ ./tools/buildman/buildman --fetch-arch list
-Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.6.3/
-Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.6.2/
-Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.5.1/
-Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.2.4/
-Available architectures: alpha am33_2.0 arm bfin cris crisv32 frv h8300
-hppa hppa64 i386 ia64 m32r m68k mips mips64 or32 powerpc powerpc64 s390x sh4
-sparc sparc64 tilegx x86_64 xtensa
-
-Then pick one and download it:
-
-$ ./tools/buildman/buildman --fetch-arch or32
-Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.6.3/
-Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.6.2/
-Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.5.1/
-Downloading: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.5.1//x86_64-gcc-4.5.1-nolibc_or32-linux.tar.xz
-Unpacking to: /home/sjg/.buildman-toolchains
-Testing
-      - looking in '/home/sjg/.buildman-toolchains/gcc-4.5.1-nolibc/or32-linux/.'
-      - looking in '/home/sjg/.buildman-toolchains/gcc-4.5.1-nolibc/or32-linux/bin'
-         - found '/home/sjg/.buildman-toolchains/gcc-4.5.1-nolibc/or32-linux/bin/or32-linux-gcc'
-Tool chain test:  OK
-
-Or download them all from kernel.org and move them to /toolchains directory,
-
-$ ./tools/buildman/buildman --fetch-arch all
-$ sudo mkdir -p /toolchains
-$ sudo mv ~/.buildman-toolchains/*/* /toolchains/
-
-For those not available from kernel.org, download from the following links.
-
-arc: https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain/releases/
-    download/arc-2016.09-release/arc_gnu_2016.09_prebuilt_uclibc_le_archs_linux_install.tar.gz
-blackfin: http://sourceforge.net/projects/adi-toolchain/files/
-    blackfin-toolchain-elf-gcc-4.5-2014R1_45-RC2.x86_64.tar.bz2
-nios2: http://sourcery.mentor.com/public/gnu_toolchain/nios2-linux-gnu/
-    sourceryg++-2015.11-27-nios2-linux-gnu-i686-pc-linux-gnu.tar.bz2
-sh: http://sourcery.mentor.com/public/gnu_toolchain/sh-linux-gnu/
-    renesas-4.4-200-sh-linux-gnu-i686-pc-linux-gnu.tar.bz2
-
-Note openrisc kernel.org toolchain is out of date. Download the latest one from
-http://opencores.org/or1k/OpenRISC_GNU_tool_chain#Prebuilt_versions - eg:
-ftp://ocuser:ocuser@openrisc.opencores.org/toolchain/gcc-or1k-elf-4.8.1-x86.tar.bz2.
-
-Buildman should now be set up to use your new toolchain.
-
-At the time of writing, U-Boot has these architectures:
-
-   arc, arm, blackfin, m68k, microblaze, mips, nios2, openrisc
-   powerpc, sandbox, sh, sparc, x86
-
-Of these, only arc is not available at kernel.org..
-
-
-How to run it
-=============
-
-First do a dry run using the -n flag: (replace <branch> with a real, local
-branch with a valid upstream)
-
-$ ./tools/buildman/buildman -b <branch> -n
-
-If it can't detect the upstream branch, try checking out the branch, and
-doing something like 'git branch --set-upstream-to upstream/master'
-or something similar. Buildman will try to guess a suitable upstream branch
-if it can't find one (you will see a message like" Guessing upstream as ...).
-You can also use the -c option to manually specify the number of commits to
-build.
-
-As an example:
-
-Dry run, so not doing much. But I would do this:
-
-Building 18 commits for 1059 boards (4 threads, 1 job per thread)
-Build directory: ../lcd9b
-    5bb3505 Merge branch 'master' of git://git.denx.de/u-boot-arm
-    c18f1b4 tegra: Use const for pinmux_config_pingroup/table()
-    2f043ae tegra: Add display support to funcmux
-    e349900 tegra: fdt: Add pwm binding and node
-    424a5f0 tegra: fdt: Add LCD definitions for Tegra
-    0636ccf tegra: Add support for PWM
-    a994fe7 tegra: Add SOC support for display/lcd
-    fcd7350 tegra: Add LCD driver
-    4d46e9d tegra: Add LCD support to Nvidia boards
-    991bd48 arm: Add control over cachability of memory regions
-    54e8019 lcd: Add CONFIG_LCD_ALIGNMENT to select frame buffer alignment
-    d92aff7 lcd: Add support for flushing LCD fb from dcache after update
-    dbd0677 tegra: Align LCD frame buffer to section boundary
-    0cff9b8 tegra: Support control of cache settings for LCD
-    9c56900 tegra: fdt: Add LCD definitions for Seaboard
-    5cc29db lcd: Add CONFIG_CONSOLE_SCROLL_LINES option to speed console
-    cac5a23 tegra: Enable display/lcd support on Seaboard
-    49ff541 wip
-
-Total boards to build for each commit: 1059
-
-This shows that it will build all 1059 boards, using 4 threads (because
-we have a 4-core CPU). Each thread will run with -j1, meaning that each
-make job will use a single CPU. The list of commits to be built helps you
-confirm that things look about right. Notice that buildman has chosen a
-'base' directory for you, immediately above your source tree.
-
-Buildman works entirely inside the base directory, here ../lcd9b,
-creating a working directory for each thread, and creating output
-directories for each commit and board.
-
-
-Suggested Workflow
-==================
-
-To run the build for real, take off the -n:
-
-$ ./tools/buildman/buildman -b <branch>
-
-Buildman will set up some working directories, and get started. After a
-minute or so it will settle down to a steady pace, with a display like this:
-
-Building 18 commits for 1059 boards (4 threads, 1 job per thread)
-  528   36  124 /19062    -18374  1:13:30  : SIMPC8313_SP
-
-This means that it is building 19062 board/commit combinations. So far it
-has managed to successfully build 528. Another 36 have built with warnings,
-and 124 more didn't build at all. It has 18374 builds left to complete.
-Buildman expects to complete the process in around an hour and a quarter.
-Use this time to buy a faster computer.
-
-
-To find out how the build went, ask for a summary with -s. You can do this
-either before the build completes (presumably in another terminal) or
-afterwards. Let's work through an example of how this is used:
-
-$ ./tools/buildman/buildman -b lcd9b -s
-...
-01: Merge branch 'master' of git://git.denx.de/u-boot-arm
-   powerpc:   + galaxy5200_LOWBOOT
-02: tegra: Use const for pinmux_config_pingroup/table()
-03: tegra: Add display support to funcmux
-04: tegra: fdt: Add pwm binding and node
-05: tegra: fdt: Add LCD definitions for Tegra
-06: tegra: Add support for PWM
-07: tegra: Add SOC support for display/lcd
-08: tegra: Add LCD driver
-09: tegra: Add LCD support to Nvidia boards
-10: arm: Add control over cachability of memory regions
-11: lcd: Add CONFIG_LCD_ALIGNMENT to select frame buffer alignment
-12: lcd: Add support for flushing LCD fb from dcache after update
-       arm:   + lubbock
-13: tegra: Align LCD frame buffer to section boundary
-14: tegra: Support control of cache settings for LCD
-15: tegra: fdt: Add LCD definitions for Seaboard
-16: lcd: Add CONFIG_CONSOLE_SCROLL_LINES option to speed console
-17: tegra: Enable display/lcd support on Seaboard
-18: wip
-
-This shows which commits have succeeded and which have failed. In this case
-the build is still in progress so many boards are not built yet (use -u to
-see which ones). But already we can see a few failures. The galaxy5200_LOWBOOT
-never builds correctly. This could be a problem with our toolchain, or it
-could be a bug in the upstream. The good news is that we probably don't need
-to blame our commits. The bad news is that our commits are not tested on that
-board.
-
-Commit 12 broke lubbock. That's what the '+ lubbock', in red, means. The
-failure is never fixed by a later commit, or you would see lubbock again, in
-green, without the +.
-
-To see the actual error:
-
-$ ./tools/buildman/buildman -b <branch> -se
-...
-12: lcd: Add support for flushing LCD fb from dcache after update
-       arm:   + lubbock
-+common/libcommon.o: In function `lcd_sync':
-+common/lcd.c:120: undefined reference to `flush_dcache_range'
-+arm-none-linux-gnueabi-ld: BFD (Sourcery G++ Lite 2010q1-202) 2.19.51.20090709 assertion fail /scratch/julian/2010q1-release-linux-lite/obj/binutils-src-2010q1-202-arm-none-linux-gnueabi-i686-pc-linux-gnu/bfd/elf32-arm.c:12572
-+make: *** [build/u-boot] Error 139
-13: tegra: Align LCD frame buffer to section boundary
-14: tegra: Support control of cache settings for LCD
-15: tegra: fdt: Add LCD definitions for Seaboard
-16: lcd: Add CONFIG_CONSOLE_SCROLL_LINES option to speed console
--common/lcd.c:120: undefined reference to `flush_dcache_range'
-+common/lcd.c:125: undefined reference to `flush_dcache_range'
-17: tegra: Enable display/lcd support on Seaboard
-18: wip
-
-So the problem is in lcd.c, due to missing cache operations. This information
-should be enough to work out what that commit is doing to break these
-boards. (In this case pxa did not have cache operations defined).
-
-Note that if there were other boards with errors, the above command would
-show their errors also. Each line is shown only once. So if lubbock and snow
-produce the same error, we just see:
-
-12: lcd: Add support for flushing LCD fb from dcache after update
-       arm:   + lubbock snow
-+common/libcommon.o: In function `lcd_sync':
-+common/lcd.c:120: undefined reference to `flush_dcache_range'
-+arm-none-linux-gnueabi-ld: BFD (Sourcery G++ Lite 2010q1-202) 2.19.51.20090709 assertion fail /scratch/julian/2010q1-release-linux-lite/obj/binutils-src-2010q1-202-arm-none-linux-gnueabi-i686-pc-linux-gnu/bfd/elf32-arm.c:12572
-+make: *** [build/u-boot] Error 139
-
-But if you did want to see just the errors for lubbock, use:
-
-$ ./tools/buildman/buildman -b <branch> -se lubbock
-
-If you see error lines marked with '-', that means that the errors were fixed
-by that commit. Sometimes commits can be in the wrong order, so that a
-breakage is introduced for a few commits and fixed by later commits. This
-shows up clearly with buildman. You can then reorder the commits and try
-again.
-
-At commit 16, the error moves: you can see that the old error at line 120
-is fixed, but there is a new one at line 126. This is probably only because
-we added some code and moved the broken line further down the file.
-
-As mentioned, if many boards have the same error, then -e will display the
-error only once. This makes the output as concise as possible. To see which
-boards have each error, use -l. So it is safe to omit the board name - you
-will not get lots of repeated output for every board.
-
-Buildman tries to distinguish warnings from errors, and shows warning lines
-separately with a 'w' prefix. Warnings introduced show as yellow. Warnings
-fixed show as cyan.
-
-The full build output in this case is available in:
-
-../lcd9b/12_of_18_gd92aff7_lcd--Add-support-for/lubbock/
-
-   done: Indicates the build was done, and holds the return code from make.
-         This is 0 for a good build, typically 2 for a failure.
-
-   err:  Output from stderr, if any. Errors and warnings appear here.
-
-   log:  Output from stdout. Normally there isn't any since buildman runs
-         in silent mode. Use -V to force a verbose build (this passes V=1
-         to 'make')
-
-   toolchain: Shows information about the toolchain used for the build.
-
-   sizes: Shows image size information.
-
-It is possible to get the build binary output there also. Use the -k option
-for this. In that case you will also see some output files, like:
-
-   System.map  toolchain  u-boot  u-boot.bin  u-boot.map  autoconf.mk
-   (also SPL versions u-boot-spl and u-boot-spl.bin if available)
-
-
-Checking Image Sizes
-====================
-
-A key requirement for U-Boot is that you keep code/data size to a minimum.
-Where a new feature increases this noticeably it should normally be put
-behind a CONFIG flag so that boards can leave it disabled and keep the image
-size more or less the same with each new release.
-
-To check the impact of your commits on image size, use -S. For example:
-
-$ ./tools/buildman/buildman -b us-x86 -sS
-Summary of 10 commits for 1066 boards (4 threads, 1 job per thread)
-01: MAKEALL: add support for per architecture toolchains
-02: x86: Add function to get top of usable ram
-       x86: (for 1/3 boards)  text -272.0  rodata +41.0
-03: x86: Add basic cache operations
-04: x86: Permit bootstage and timer data to be used prior to relocation
-       x86: (for 1/3 boards)  data +16.0
-05: x86: Add an __end symbol to signal the end of the U-Boot binary
-       x86: (for 1/3 boards)  text +76.0
-06: x86: Rearrange the output input to remove BSS
-       x86: (for 1/3 boards)  bss -2140.0
-07: x86: Support relocation of FDT on start-up
-       x86: +   coreboot-x86
-08: x86: Add error checking to x86 relocation code
-09: x86: Adjust link device tree include file
-10: x86: Enable CONFIG_OF_CONTROL on coreboot
-
-
-You can see that image size only changed on x86, which is good because this
-series is not supposed to change any other board. From commit 7 onwards the
-build fails so we don't get code size numbers. The numbers are fractional
-because they are an average of all boards for that architecture. The
-intention is to allow you to quickly find image size problems introduced by
-your commits.
-
-Note that the 'text' region and 'rodata' are split out. You should add the
-two together to get the total read-only size (reported as the first column
-in the output from binutil's 'size' utility).
-
-A useful option is --step which lets you skip some commits. For example
---step 2 will show the image sizes for only every 2nd commit (so it will
-compare the image sizes of the 1st, 3rd, 5th... commits). You can also use
---step 0 which will compare only the first and last commits. This is useful
-for an overview of how your entire series affects code size. It will build
-only the upstream commit and your final branch commit.
-
-You can also use -d to see a detailed size breakdown for each board. This
-list is sorted in order from largest growth to largest reduction.
-
-It is even possible to go a little further with the -B option (--bloat). This
-shows where U-Boot has bloated, breaking the size change down to the function
-level. Example output is below:
-
-$ ./tools/buildman/buildman -b us-mem4 -sSdB
-...
-19: Roll crc32 into hash infrastructure
-       arm: (for 10/10 boards)  all -143.4  bss +1.2  data -4.8  rodata -48.2 text -91.6
-            paz00          :  all +23  bss -4  rodata -29  text +56
-               u-boot: add: 1/0, grow: 3/-2 bytes: 168/-104 (64)
-                 function                                   old     new   delta
-                 hash_command                                80     160     +80
-                 crc32_wd_buf                                 -      56     +56
-                 ext4fs_read_file                           540     568     +28
-                 insert_var_value_sub                       688     692      +4
-                 run_list_real                             1996    1992      -4
-                 do_mem_crc                                 168      68    -100
-            trimslice      :  all -9  bss +16  rodata -29  text +4
-               u-boot: add: 1/0, grow: 1/-3 bytes: 136/-124 (12)
-                 function                                   old     new   delta
-                 hash_command                                80     160     +80
-                 crc32_wd_buf                                 -      56     +56
-                 ext4fs_iterate_dir                         672     668      -4
-                 ext4fs_read_file                           568     548     -20
-                 do_mem_crc                                 168      68    -100
-            whistler       :  all -9  bss +16  rodata -29  text +4
-               u-boot: add: 1/0, grow: 1/-3 bytes: 136/-124 (12)
-                 function                                   old     new   delta
-                 hash_command                                80     160     +80
-                 crc32_wd_buf                                 -      56     +56
-                 ext4fs_iterate_dir                         672     668      -4
-                 ext4fs_read_file                           568     548     -20
-                 do_mem_crc                                 168      68    -100
-            seaboard       :  all -9  bss -28  rodata -29  text +48
-               u-boot: add: 1/0, grow: 3/-2 bytes: 160/-104 (56)
-                 function                                   old     new   delta
-                 hash_command                                80     160     +80
-                 crc32_wd_buf                                 -      56     +56
-                 ext4fs_read_file                           548     568     +20
-                 run_list_real                             1996    2000      +4
-                 do_nandboot                                760     756      -4
-                 do_mem_crc                                 168      68    -100
-            colibri_t20    :  all -9  rodata -29  text +20
-               u-boot: add: 1/0, grow: 2/-3 bytes: 140/-112 (28)
-                 function                                   old     new   delta
-                 hash_command                                80     160     +80
-                 crc32_wd_buf                                 -      56     +56
-                 read_abs_bbt                               204     208      +4
-                 do_nandboot                                760     756      -4
-                 ext4fs_read_file                           576     568      -8
-                 do_mem_crc                                 168      68    -100
-            ventana        :  all -37  bss -12  rodata -29  text +4
-               u-boot: add: 1/0, grow: 1/-3 bytes: 136/-124 (12)
-                 function                                   old     new   delta
-                 hash_command                                80     160     +80
-                 crc32_wd_buf                                 -      56     +56
-                 ext4fs_iterate_dir                         672     668      -4
-                 ext4fs_read_file                           568     548     -20
-                 do_mem_crc                                 168      68    -100
-            harmony        :  all -37  bss -16  rodata -29  text +8
-               u-boot: add: 1/0, grow: 2/-3 bytes: 140/-124 (16)
-                 function                                   old     new   delta
-                 hash_command                                80     160     +80
-                 crc32_wd_buf                                 -      56     +56
-                 nand_write_oob_syndrome                    428     432      +4
-                 ext4fs_iterate_dir                         672     668      -4
-                 ext4fs_read_file                           568     548     -20
-                 do_mem_crc                                 168      68    -100
-            medcom-wide    :  all -417  bss +28  data -16  rodata -93  text -336
-               u-boot: add: 1/-1, grow: 1/-2 bytes: 88/-376 (-288)
-                 function                                   old     new   delta
-                 crc32_wd_buf                                 -      56     +56
-                 do_fat_read_at                            2872    2904     +32
-                 hash_algo                                   16       -     -16
-                 do_mem_crc                                 168      68    -100
-                 hash_command                               420     160    -260
-            tec            :  all -449  bss -4  data -16  rodata -93  text -336
-               u-boot: add: 1/-1, grow: 1/-2 bytes: 88/-376 (-288)
-                 function                                   old     new   delta
-                 crc32_wd_buf                                 -      56     +56
-                 do_fat_read_at                            2872    2904     +32
-                 hash_algo                                   16       -     -16
-                 do_mem_crc                                 168      68    -100
-                 hash_command                               420     160    -260
-            plutux         :  all -481  bss +16  data -16  rodata -93  text -388
-               u-boot: add: 1/-1, grow: 1/-3 bytes: 68/-408 (-340)
-                 function                                   old     new   delta
-                 crc32_wd_buf                                 -      56     +56
-                 do_load_serial_bin                        1688    1700     +12
-                 hash_algo                                   16       -     -16
-                 do_fat_read_at                            2904    2872     -32
-                 do_mem_crc                                 168      68    -100
-                 hash_command                               420     160    -260
-   powerpc: (for 5/5 boards)  all +37.4  data -3.2  rodata -41.8  text +82.4
-            MPC8610HPCD    :  all +55  rodata -29  text +84
-               u-boot: add: 1/0, grow: 0/-1 bytes: 176/-96 (80)
-                 function                                   old     new   delta
-                 hash_command                                 -     176    +176
-                 do_mem_crc                                 184      88     -96
-            MPC8641HPCN    :  all +55  rodata -29  text +84
-               u-boot: add: 1/0, grow: 0/-1 bytes: 176/-96 (80)
-                 function                                   old     new   delta
-                 hash_command                                 -     176    +176
-                 do_mem_crc                                 184      88     -96
-            MPC8641HPCN_36BIT:  all +55  rodata -29  text +84
-               u-boot: add: 1/0, grow: 0/-1 bytes: 176/-96 (80)
-                 function                                   old     new   delta
-                 hash_command                                 -     176    +176
-                 do_mem_crc                                 184      88     -96
-            sbc8641d       :  all +55  rodata -29  text +84
-               u-boot: add: 1/0, grow: 0/-1 bytes: 176/-96 (80)
-                 function                                   old     new   delta
-                 hash_command                                 -     176    +176
-                 do_mem_crc                                 184      88     -96
-            xpedite517x    :  all -33  data -16  rodata -93  text +76
-               u-boot: add: 1/-1, grow: 0/-1 bytes: 176/-112 (64)
-                 function                                   old     new   delta
-                 hash_command                                 -     176    +176
-                 hash_algo                                   16       -     -16
-                 do_mem_crc                                 184      88     -96
-...
-
-
-This shows that commit 19 has reduced codesize for arm slightly and increased
-it for powerpc. This increase was offset in by reductions in rodata and
-data/bss.
-
-Shown below the summary lines are the sizes for each board. Below each board
-are the sizes for each function. This information starts with:
-
-   add - number of functions added / removed
-   grow - number of functions which grew / shrunk
-   bytes - number of bytes of code added to / removed from all functions,
-            plus the total byte change in brackets
-
-The change seems to be that hash_command() has increased by more than the
-do_mem_crc() function has decreased. The function sizes typically add up to
-roughly the text area size, but note that every read-only section except
-rodata is included in 'text', so the function total does not exactly
-correspond.
-
-It is common when refactoring code for the rodata to decrease as the text size
-increases, and vice versa.
-
-
-The .buildman file
-==================
-
-The .buildman file provides information about the available toolchains and
-also allows build flags to be passed to 'make'. It consists of several
-sections, with the section name in square brackets. Within each section are
-a set of (tag, value) pairs.
-
-'[toolchain]' section
-
-    This lists the available toolchains. The tag here doesn't matter, but
-    make sure it is unique. The value is the path to the toolchain. Buildman
-    will look in that path for a file ending in 'gcc'. It will then execute
-    it to check that it is a C compiler, passing only the --version flag to
-    it. If the return code is 0, buildman assumes that it is a valid C
-    compiler. It uses the first part of the name as the architecture and
-    strips off the last part when setting the CROSS_COMPILE environment
-    variable (parts are delimited with a hyphen).
-
-    For example powerpc-linux-gcc will be noted as a toolchain for 'powerpc'
-    and CROSS_COMPILE will be set to powerpc-linux- when using it.
-
-'[toolchain-alias]' section
-
-    This converts toolchain architecture names to U-Boot names. For example,
-    if an x86 toolchains is called i386-linux-gcc it will not normally be
-    used for architecture 'x86'. Adding 'x86: i386 x86_64' to this section
-    will tell buildman that the i386 and x86_64 toolchains can be used for
-    the x86 architecture.
-
-'[make-flags]' section
-
-    U-Boot's build system supports a few flags (such as BUILD_TAG) which
-    affect the build product. These flags can be specified in the buildman
-    settings file. They can also be useful when building U-Boot against other
-    open source software.
-
-    [make-flags]
-    at91-boards=ENABLE_AT91_TEST=1
-    snapper9260=${at91-boards} BUILD_TAG=442
-    snapper9g45=${at91-boards} BUILD_TAG=443
-
-    This will use 'make ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260
-    and 'make ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45. A special
-    variable ${target} is available to access the target name (snapper9260
-    and snapper9g20 in this case). Variables are resolved recursively. Note
-    that variables can only contain the characters A-Z, a-z, 0-9, hyphen (-)
-    and underscore (_).
-
-    It is expected that any variables added are dealt with in U-Boot's
-    config.mk file and documented in the README.
-
-    Note that you can pass ad-hoc options to the build using environment
-    variables, for example:
-
-       SOME_OPTION=1234 ./tools/buildman/buildman my_board
-
-
-Quick Sanity Check
-==================
-
-If you have made changes and want to do a quick sanity check of the
-currently checked-out source, run buildman without the -b flag. This will
-build the selected boards and display build status as it runs (i.e. -v is
-enabled automatically). Use -e to see errors/warnings as well.
-
-
-Building Ranges
-===============
-
-You can build a range of commits by specifying a range instead of a branch
-when using the -b flag. For example:
-
-    upstream/master..us-buildman
-
-will build commits in us-buildman that are not in upstream/master.
-
-
-Building Faster
-===============
-
-By default, buildman doesn't execute 'make mrproper' prior to building the
-first commit for each board. This reduces the amount of work 'make' does, and
-hence speeds up the build. To force use of 'make mrproper', use -the -m flag.
-This flag will slow down any buildman invocation, since it increases the amount
-of work done on any build.
-
-One possible application of buildman is as part of a continual edit, build,
-edit, build, ... cycle; repeatedly applying buildman to the same change or
-series of changes while making small incremental modifications to the source
-each time. This provides quick feedback regarding the correctness of recent
-modifications. In this scenario, buildman's default choice of build directory
-causes more build work to be performed than strictly necessary.
-
-By default, each buildman thread uses a single directory for all builds. When a
-thread builds multiple boards, the configuration built in this directory will
-cycle through various different configurations, one per board built by the
-thread. Variations in the configuration will force a rebuild of affected source
-files when a thread switches between boards. Ideally, such buildman-induced
-rebuilds would not happen, thus allowing the build to operate as efficiently as
-the build system and source changes allow. buildman's -P flag may be used to
-enable this; -P causes each board to be built in a separate (board-specific)
-directory, thus avoiding any buildman-induced configuration changes in any
-build directory.
-
-U-Boot's build system embeds information such as a build timestamp into the
-final binary. This information varies each time U-Boot is built. This causes
-various files to be rebuilt even if no source changes are made, which in turn
-requires that the final U-Boot binary be re-linked. This unnecessary work can
-be avoided by turning off the timestamp feature. This can be achieved by
-setting the SOURCE_DATE_EPOCH environment variable to 0.
-
-Combining all of these options together yields the command-line shown below.
-This will provide the quickest possible feedback regarding the current content
-of the source tree, thus allowing rapid tested evolution of the code.
-
-    SOURCE_DATE_EPOCH=0 ./tools/buildman/buildman -P tegra
-
-
-Checking configuration
-======================
-
-A common requirement when converting CONFIG options to Kconfig is to check
-that the effective configuration has not changed due to the conversion.
-Buildman supports this with the -K option, used after a build. This shows
-differences in effective configuration between one commit and the next.
-
-For example:
-
-    $ buildman -b kc4 -sK
-    ...
-    43: Convert CONFIG_SPL_USBETH_SUPPORT to Kconfig
-    arm:
-    + u-boot.cfg: CONFIG_SPL_ENV_SUPPORT=1 CONFIG_SPL_NET=1
-    + u-boot-spl.cfg: CONFIG_SPL_MMC=1 CONFIG_SPL_NAND_SUPPORT=1
-    + all: CONFIG_SPL_ENV_SUPPORT=1 CONFIG_SPL_MMC=1 CONFIG_SPL_NAND_SUPPORT=1 CONFIG_SPL_NET=1
-    am335x_evm_usbspl :
-    + u-boot.cfg: CONFIG_SPL_ENV_SUPPORT=1 CONFIG_SPL_NET=1
-    + u-boot-spl.cfg: CONFIG_SPL_MMC=1 CONFIG_SPL_NAND_SUPPORT=1
-    + all: CONFIG_SPL_ENV_SUPPORT=1 CONFIG_SPL_MMC=1 CONFIG_SPL_NAND_SUPPORT=1 CONFIG_SPL_NET=1
-    44: Convert CONFIG_SPL_USB_HOST to Kconfig
-    ...
-
-This shows that commit 44 enabled three new options for the board
-am335x_evm_usbspl which were not enabled in commit 43. There is also a
-summary for 'arm' showing all the changes detected for that architecture.
-In this case there is only one board with changes, so 'arm' output is the
-same as 'am335x_evm_usbspl'/
-
-The -K option uses the u-boot.cfg, spl/u-boot-spl.cfg and tpl/u-boot-tpl.cfg
-files which are produced by a build. If all you want is to check the
-configuration you can in fact avoid doing a full build, using -D. This tells
-buildman to configuration U-Boot and create the .cfg files, but not actually
-build the source. This is 5-10 times faster than doing a full build.
-
-By default buildman considers the follow two configuration methods
-equivalent:
-
-   #define CONFIG_SOME_OPTION
-
-   CONFIG_SOME_OPTION=y
-
-The former would appear in a header filer and the latter in a defconfig
-file. The achieve this, buildman considers 'y' to be '1' in configuration
-variables. This avoids lots of useless output when converting a CONFIG
-option to Kconfig. To disable this behaviour, use --squash-config-y.
-
-
-Checking the environment
-========================
-
-When converting CONFIG options which manipulate the default environment,
-a common requirement is to check that the default environment has not
-changed due to the conversion. Buildman supports this with the -U option,
-used after a build. This shows differences in the default environment
-between one commit and the next.
-
-For example:
-
-$ buildman -b squash brppt1 -sU
-Summary of 2 commits for 3 boards (3 threads, 3 jobs per thread)
-01: Migrate bootlimit to Kconfig
-02: Squashed commit of the following:
-   c brppt1_mmc: altbootcmd=mmc dev 1; run mmcboot0; -> mmc dev 1; run mmcboot0
-   c brppt1_spi: altbootcmd=mmc dev 1; run mmcboot0; -> mmc dev 1; run mmcboot0
-   + brppt1_nand: altbootcmd=run usbscript
-   - brppt1_nand:  altbootcmd=run usbscript
-(no errors to report)
-
-This shows that commit 2 modified the value of 'altbootcmd' for 'brppt1_mmc'
-and 'brppt1_spi', removing a trailing semicolon. 'brppt1_nand' gained an a
-value for 'altbootcmd', but lost one for ' altbootcmd'.
-
-The -U option uses the u-boot.env files which are produced by a build.
-
-
-Building with clang
-===================
-
-To build with clang (sandbox only), use the -O option to override the
-toolchain. For example:
-
-   buildman -O clang-7 --board sandbox
-
-
-Doing a simple build
-====================
-
-In some cases you just want to build a single board and get the full output, use
-the -w option, for example:
-
-   buildman -o /tmp/build --board sandbox -w
-
-This will write the full build into /tmp/build including object files. You must
-specify the output directory with -o when using -w.
-
-
-Support for IDEs (Integrated Development Environments)
-======================================================
-
-Normally buildman summarises the output and shows information indicating the
-meaning of each line of output. For example a '+' symbol appears at the start of
-each error line. Also, buildman prints information about what it is about to do,
-along with a summary at the end.
-
-When using buildman from an IDE, it is helpful to drop this behaviour. Use the
--I/--ide option for that. You might find -W helpful also so that warnings do
-not cause the build to fail:
-
-   buildman -o /tmp/build --board sandbox -wWI
-
-
-Changing the configuration
-==========================
-
-Sometimes it is useful to change the CONFIG options for a build on the fly. This
-can be used to build a board (or multiple) with a few changes to see the impact.
-The -a option supports this:
-
-   -a <cfg>
-
-where <cfg> is a CONFIG option (with or without the CONFIG_ prefix) to enable.
-For example:
-
-    buildman -a CMD_SETEXPR_FMT
-
-will build with CONFIG_CMD_SETEXPR_FMT enabled.
-
-You can disable options by preceding them with tilde (~). You can specify the
--a option multiple times:
-
-    buildman -a CMD_SETEXPR_FMT -a ~CMDLINE
-
-Some options have values, in which case you can change them:
-
-    buildman -a 'BOOTCOMMAND="echo hello"' CONFIG_SYS_LOAD_ADDR=0x1000
-
-Note that you must put quotes around string options and the whole thing must be
-in single quotes, to make sure the shell leave it alone.
-
-If you try to set an option that does not exist, or that cannot be changed for
-some other reason (e.g. it is 'selected' by another option), then buildman
-shows an error:
-
-   buildman --board sandbox -a FRED
-   Building current source for 1 boards (1 thread, 32 jobs per thread)
-       0    0    0 /1       -1      (starting)errs
-   Some CONFIG adjustments did not take effect. This may be because
-   the request CONFIGs do not exist or conflict with others.
-
-   Failed adjustments:
-
-   FRED                  Missing expected line: CONFIG_FRED=y
-
-
-One major caveat with this feature with branches (-b) is that buildman does not
-name the output directories differently when you change the configuration, so
-doing the same build again with different configuration will not trigger a
-rebuild. You can use -f to work around that.
-
-
-Other options
-=============
-
-Buildman has various other command-line options. Try --help to see them.
-
-To find out what toolchain prefix buildman will use for a build, use the -A
-option.
-
-To request that compiler warnings be promoted to errors, use -E. This passes the
--Werror flag to the compiler. Note that the build can still produce warnings
-with -E, e.g. the migration warnings:
-
-        ===================== WARNING ======================
-        This board does not use CONFIG_DM_MMC. Please update
-        ...
-        ====================================================
-
-When doing builds, Buildman's return code will reflect the overall result:
-
-    0 (success)     No errors or warnings found
-    100             Errors found
-    101             Warnings found (only if no -W)
-
-You can use -W to tell Buildman to return 0 (success) instead of 101 when
-warnings are found. Note that it can be useful to combine -E and -W. This means
-that all compiler warnings will produce failures (code 100) and all other
-warnings will produce success (since 101 is changed to 0).
-
-If there are both warnings and errors, errors win, so buildman returns 100.
-
-The -y option is provided (for use with -s) to ignore the bountiful device-tree
-warnings. Similarly, -Y tells buildman to ignore the migration warnings.
-
-Sometimes you might get an error in a thread that is not handled by buildman,
-perhaps due to a failure of a tool that it calls. You might see the output, but
-then buildman hangs. Failing to handle any eventuality is a bug in buildman and
-should be reported. But you can use -T0 to disable threading and hopefully
-figure out the root cause of the build failure.
-
-Build summary
-=============
-
-When buildman finishes it shows a summary, something like this:
-
-    Completed: 5 total built, duration 0:00:21, rate 0.24
-
-This shows that a total of 5 builds were done across all selected boards, it
-took 21 seconds and the builds happened at the rate of 0.24 per second. The
-latter number depends on the speed of your machine and the efficiency of the
-U-Boot build.
-
-
-How to change from MAKEALL
-==========================
-
-Buildman includes most of the features of MAKEALL and is generally faster
-and easier to use. In particular it builds entire branches: if a particular
-commit introduces an error in a particular board, buildman can easily show
-you this, even if a later commit fixes that error.
-
-The reasons to deprecate MAKEALL are:
-- We don't want to maintain two build systems
-- Buildman is typically faster
-- Buildman has a lot more features
-
-But still, many people will be sad to lose MAKEALL. If you are used to
-MAKEALL, here are a few pointers.
-
-First you need to set up your tool chains - see the 'Setting up' section
-for details. Once you have your required toolchain(s) detected then you are
-ready to go.
-
-To build the current source tree, run buildman without a -b flag:
-
-   ./tools/buildman/buildman <list of things to build>
-
-This will build the current source tree for the given boards and display
-the results and errors.
-
-However buildman usually works on entire branches, and for that you must
-specify a board flag:
-
-   ./tools/buildman/buildman -b <branch_name> <list of things to build>
-
-followed by (afterwards, or perhaps concurrently in another terminal):
-
-   ./tools/buildman/buildman -b <branch_name> -s <list of things to build>
-
-to see the results of the build. Rather than showing you all the output,
-buildman just shows a summary, with red indicating that a commit introduced
-an error and green indicating that a commit fixed an error. Use the -e
-flag to see the full errors and -l to see which boards caused which errors.
-
-If you really want to see build results as they happen, use -v when doing a
-build (and -e to see the errors/warnings too).
-
-You don't need to stick around on that branch while buildman is running. It
-checks out its own copy of the source code, so you can change branches,
-add commits, etc. without affecting the build in progress.
-
-The <list of things to build> can include board names, architectures or the
-like. There are no flags to disambiguate since ambiguities are rare. Using
-the examples from MAKEALL:
-
-Examples:
-  - build all Power Architecture boards:
-      MAKEALL -a powerpc
-      MAKEALL --arch powerpc
-      MAKEALL powerpc
-          ** buildman -b <branch> powerpc
-  - build all PowerPC boards manufactured by vendor "esd":
-      MAKEALL -a powerpc -v esd
-          ** buildman -b <branch> esd
-  - build all PowerPC boards manufactured either by "keymile" or "siemens":
-      MAKEALL -a powerpc -v keymile -v siemens
-          ** buildman -b <branch> keymile siemens
-  - build all Freescale boards with MPC83xx CPUs, plus all 4xx boards:
-      MAKEALL -c mpc83xx -v freescale 4xx
-          ** buildman -b <branch> mpc83xx freescale 4xx
-
-Buildman automatically tries to use all the CPUs in your machine. If you
-are building a lot of boards it will use one thread for every CPU core
-it detects in your machine. This is like MAKEALL's BUILD_NBUILDS option.
-You can use the -T flag to change the number of threads. If you are only
-building a few boards, buildman will automatically run make with the -j
-flag to increase the number of concurrent make tasks. It isn't normally
-that helpful to fiddle with this option, but if you use the BUILD_NCPUS
-option in MAKEALL then -j is the equivalent in buildman.
-
-Buildman puts its output in ../<branch_name> by default but you can change
-this with the -o option. Buildman normally does out-of-tree builds: use -i
-to disable that if you really want to. But be careful that once you have
-used -i you pollute buildman's copies of the source tree, and you will need
-to remove the build directory (normally ../<branch_name>) to run buildman
-in normal mode (without -i).
-
-Buildman doesn't keep the output result normally, but use the -k option to
-do this.
-
-Please read 'Theory of Operation' a few times as it will make a lot of
-things clearer.
-
-Some options you might like are:
-
-   -B shows which functions are growing/shrinking in which commit - great
-        for finding code bloat.
-   -S shows image sizes for each commit (just an overall summary)
-   -u shows boards that you haven't built yet
-   --step 0 will build just the upstream commit and the last commit of your
-        branch. This is often a quick sanity check that your branch doesn't
-        break anything. But note this does not check bisectability!
-
-
-Using boards.cfg
-================
-
-This file is no-longer needed by buildman but it is still generated in the
-working directory. This helps avoid a delay on every build, since scanning all
-the Kconfig files takes a few seconds. Use the -R flag to force regeneration
-of the file - in that case buildman exits after writing the file. with exit code
-2 if there was an error in the maintainer files.
-
-You should use 'buildman -nv <criteria>' instead of greoing the boards.cfg file,
-since it may be dropped altogether in future.
-
-
-TODO
-====
-
-Many improvements have been made over the years. There is still quite a bit of
-scope for more though, e.g.:
-
-- easier access to log files
-- 'hunting' for problems, perhaps by building a few boards for each arch, or
-  checking commits for changed files and building only boards which use those
-  files
-
-
-Credits
-=======
-
-Thanks to Grant Grundler <grundler@chromium.org> for his ideas for improving
-the build speed by building all commits for a board instead of the other
-way around.
-
-
-Simon Glass
-sjg@chromium.org
-Halloween 2012
-Updated 12-12-12
-Updated 23-02-13
-Updated 09-04-20
diff --git a/tools/buildman/README.rst b/tools/buildman/README.rst
new file mode 120000
index 0000000..c359387
--- /dev/null
+++ b/tools/buildman/README.rst
@@ -0,0 +1 @@
+buildman.rst
\ No newline at end of file
diff --git a/tools/buildman/bsettings.py b/tools/buildman/bsettings.py
index 35bb2c1..0eb894a 100644
--- a/tools/buildman/bsettings.py
+++ b/tools/buildman/bsettings.py
@@ -5,6 +5,7 @@
 import os
 import io
 
+config_fname = None
 
 def Setup(fname=''):
     """Set up the buildman settings module by reading config files
@@ -46,6 +47,17 @@
     except:
         raise
 
+def GetGlobalItemValue(name):
+    """Get an item from the 'global' section of the config.
+
+    Args:
+        name: name of item to retrieve
+
+    Returns:
+        str: Value of item, or None if not present
+    """
+    return settings.get('global', name, fallback=None)
+
 def SetItem(section, tag, value):
     """Set an item and write it back to the settings file"""
     global settings
@@ -79,13 +91,14 @@
 [toolchain-prefix]
 # name = path to prefix
 # e.g. x86 = /opt/gcc-4.6.3-nolibc/x86_64-linux/bin/x86_64-linux-
+# arc = /opt/arc/arc_gnu_2021.03_prebuilt_elf32_le_linux_install/bin/arc-elf32-
 
 [toolchain-alias]
 # arch = alias
 # Indicates which toolchain should be used to build for that arch
+riscv = riscv32
+sh = sh4
 x86 = i386
-blackfin = bfin
-openrisc = or1k
 
 [make-flags]
 # Special flags to pass to 'make' for certain boards, e.g. to pass a test
diff --git a/tools/buildman/builder.py b/tools/buildman/builder.py
index 76252b9..c2a6902 100644
--- a/tools/buildman/builder.py
+++ b/tools/buildman/builder.py
@@ -252,7 +252,8 @@
                  mrproper=False, per_board_out_dir=False,
                  config_only=False, squash_config_y=False,
                  warnings_as_errors=False, work_in_output=False,
-                 test_thread_exceptions=False, adjust_cfg=None):
+                 test_thread_exceptions=False, adjust_cfg=None,
+                 allow_missing=False):
         """Create a new Builder object
 
         Args:
@@ -290,6 +291,7 @@
                     ~C to disable C
                     C=val to set the value of C (val must have quotes if C is
                         a string Kconfig
+            allow_missing: Run build with BINMAN_ALLOW_MISSING=1
 
         """
         self.toolchains = toolchains
@@ -327,6 +329,7 @@
         self.config_filenames = BASE_CONFIG_FILENAMES
         self.work_in_output = work_in_output
         self.adjust_cfg = adjust_cfg
+        self.allow_missing = allow_missing
         self._ide = False
 
         if not self.squash_config_y:
diff --git a/tools/buildman/builderthread.py b/tools/buildman/builderthread.py
index 6240e08..680efae 100644
--- a/tools/buildman/builderthread.py
+++ b/tools/buildman/builderthread.py
@@ -253,6 +253,8 @@
                     args.extend(['-j', str(self.builder.num_jobs)])
                 if self.builder.warnings_as_errors:
                     args.append('KCFLAGS=-Werror')
+                if self.builder.allow_missing:
+                    args.append('BINMAN_ALLOW_MISSING=1')
                 config_args = ['%s_defconfig' % brd.target]
                 config_out = ''
                 args.extend(self.builder.toolchains.GetMakeArguments(brd))
@@ -288,10 +290,14 @@
                         args.append('cfg')
                     result = self.Make(commit, brd, 'build', cwd, *args,
                             env=env)
+                    if (result.return_code == 2 and
+                        ('Some images are invalid' in result.stderr)):
+                        # This is handled later by the check for output in
+                        # stderr
+                        result.return_code = 0
                     if adjust_cfg:
                         errs = cfgutil.check_cfg_file(cfg_file, adjust_cfg)
                         if errs:
-                            print('errs', errs)
                             result.stderr += errs
                             result.return_code = 1
                 result.stderr = result.stderr.replace(src_dir + '/', '')
diff --git a/tools/buildman/buildman.rst b/tools/buildman/buildman.rst
new file mode 100644
index 0000000..2a83cb7
--- /dev/null
+++ b/tools/buildman/buildman.rst
@@ -0,0 +1,1328 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Buildman build tool
+===================
+
+Quick-start
+-----------
+
+If you just want to quickly set up buildman so you can build something (for
+example Raspberry Pi 2):
+
+.. code-block:: bash
+
+   cd /path/to/u-boot
+   PATH=$PATH:`pwd`/tools/buildman
+   buildman --fetch-arch arm
+   buildman -k rpi_2
+   ls ../current/rpi_2
+   # u-boot.bin is the output image
+
+
+What is this?
+-------------
+
+This tool handles building U-Boot to check that you have not broken it
+with your patch series. It can build each individual commit and report
+which boards fail on which commits, and which errors come up. It aims
+to make full use of multi-processor machines.
+
+A key feature of buildman is its output summary, which allows warnings,
+errors or image size increases in a particular commit or board to be
+quickly identified and the offending commit pinpointed. This can be a big
+help for anyone working with >10 patches at a time.
+
+
+Caveats
+-------
+
+Buildman can be stopped and restarted, in which case it will continue
+where it left off. This should happen cleanly and without side-effects.
+If not, it is a bug, for which a patch would be welcome.
+
+Buildman gets so tied up in its work that it can ignore the outside world.
+You may need to press Ctrl-C several times to quit it. Also it will print
+out various exceptions when stopped. You may have to kill it since the
+Ctrl-C handling is somewhat broken.
+
+
+Theory of Operation
+-------------------
+
+(please read this section in full twice or you will be perpetually confused)
+
+Buildman is a builder. It is not make, although it runs make. It does not
+produce any useful output on the terminal while building, except for
+progress information (but see -v below). All the output (errors, warnings and
+binaries if you ask for them) is stored in output directories, which you can
+look at from a separate 'buildman -s' instance while the build is progressing,
+or when it is finished.
+
+Buildman is designed to build entire git branches, i.e. muliple commits. It
+can be run repeatedly on the same branch after making changes to commits on
+that branch. In this case it will automatically rebuild commits which have
+changed (and remove its old results for that commit). It is possible to build
+a branch for one board, then later build it for another board. This adds to
+the output, so now you have results for two boards. If you want buildman to
+re-build a commit it has already built (e.g. because of a toolchain update),
+use the -f flag.
+
+Buildman produces a concise summary of which boards succeeded and failed.
+It shows which commit introduced which board failure using a simple
+red/green colour coding (with yellow/cyan for warnings). Full error
+information can be requested, in which case it is de-duped and displayed
+against the commit that introduced the error. An example workflow is below.
+
+Buildman stores image size information and can report changes in image size
+from commit to commit. An example of this is below.
+
+Buildman starts multiple threads, and each thread builds for one board at
+a time. A thread starts at the first commit, configures the source for your
+board and builds it. Then it checks out the next commit and does an
+incremental build (i.e. not using 'make xxx_defconfig' unless you use -C).
+Eventually the thread reaches the last commit and stops. If a commit causes
+an error or warning, buildman will try it again after reconfiguring (but see
+-Q). Thus some commits may be built twice, with the first result silently
+discarded. Lots of errors and warnings will causes lots of reconfigures and your
+build will be very slow. This is because a file that produces just a warning
+would not normally be rebuilt in an incremental build. Once a thread finishes
+building all the commits for a board, it starts on the commits for another
+board.
+
+Buildman works in an entirely separate place from your U-Boot repository.
+It creates a separate working directory for each thread, and puts the
+output files in the working directory, organised by commit name and board
+name, in a two-level hierarchy (but see -P).
+
+Buildman is invoked in your U-Boot directory, the one with the .git
+directory. It clones this repository into a copy for each thread, and the
+threads do not affect the state of your git repository. Any checkouts done
+by the thread affect only the working directory for that thread.
+
+Buildman automatically selects the correct tool chain for each board. You
+must supply suitable tool chains (see --fetch-arch), but buildman takes care
+of selecting the right one.
+
+Buildman generally builds a branch (with the -b flag), and in this case
+builds the upstream commit as well, for comparison. So even if you have one
+commit in your branch, two commits will be built. Put all your commits in a
+branch, set the branch's upstream to a valid value, and all will be well.
+Otherwise buildman will perform random actions. Use -n to check what the
+random actions might be.
+
+Buildman effectively has two modes: without -s it builds, with -s it
+summarises the results of previous (or active) builds.
+
+If you just want to build the current source tree, leave off the -b flag.
+This will display results and errors as they happen. You can still look at
+them later using -se. Note that buildman will assume that the source has
+changed, and will build all specified boards in this case.
+
+Buildman is optimised for building many commits at once, for many boards.
+On multi-core machines, Buildman is fast because it uses most of the
+available CPU power. When it gets to the end, or if you are building just
+a few commits or boards, it will be pretty slow. As a tip, if you don't
+plan to use your machine for anything else, you can use -T to increase the
+number of threads beyond the default.
+
+
+Selecting which boards to build
+-------------------------------
+
+Buildman lets you build all boards, or a subset. Specify the subset by passing
+command-line arguments that list the desired build target, architecture,
+CPU, board name, vendor, SoC or options. Multiple arguments are allowed. Each
+argument will be interpreted as a regular expression, so behaviour is a superset
+of exact or substring matching. Examples are:
+
+- 'tegra20' - all boards with a Tegra20 SoC
+- 'tegra' - all boards with any Tegra Soc (Tegra20, Tegra30, Tegra114...)
+- '^tegra[23]0$' - all boards with either Tegra20 or Tegra30 SoC
+- 'powerpc' - all PowerPC boards
+
+While the default is to OR the terms together, you can also make use of
+the '&' operator to limit the selection:
+
+- 'freescale & arm sandbox' - all Freescale boards with ARM architecture, plus
+  sandbox
+
+You can also use -x to specifically exclude some boards. For example:
+
+  buildman arm -x nvidia,freescale,.*ball$
+
+means to build all arm boards except nvidia, freescale and anything ending
+with 'ball'.
+
+For building specific boards you can use the --boards (or --bo) option, which
+takes a comma-separated list of board target names and be used multiple times
+on the command line:
+
+.. code-block:: bash
+
+  buildman --boards sandbox,snow --boards
+
+It is convenient to use the -n option to see what will be built based on
+the subset given. Use -v as well to get an actual list of boards.
+
+Buildman does not store intermediate object files. It optionally copies
+the binary output into a directory when a build is successful (-k). Size
+information is always recorded. It needs a fair bit of disk space to work,
+typically 250MB per thread.
+
+
+Setting up
+----------
+
+#. Get the U-Boot source. You probably already have it, but if not these
+   steps should get you started with a repo and some commits for testing.
+
+   .. code-block:: bash
+
+      cd /path/to/u-boot
+      git clone git://git.denx.de/u-boot.git .
+      git checkout -b my-branch origin/master
+      # Add some commits to the branch, reading for testing
+
+#. Create ~/.buildman to tell buildman where to find tool chains (see
+   buildman_settings_ for details). As an example::
+
+   # Buildman settings file
+
+   [toolchain]
+   root: /
+   rest: /toolchains/*
+   eldk: /opt/eldk-4.2
+   arm: /opt/linaro/gcc-linaro-arm-linux-gnueabihf-4.8-2013.08_linux
+   aarch64: /opt/linaro/gcc-linaro-aarch64-none-elf-4.8-2013.10_linux
+
+   [toolchain-prefix]
+   arc = /opt/arc/arc_gnu_2021.03_prebuilt_elf32_le_linux_install/bin/arc-elf32-
+
+   [toolchain-alias]
+   riscv = riscv32
+   sh = sh4
+   x86: i386
+
+
+   This selects the available toolchain paths. Add the base directory for
+   each of your toolchains here. Buildman will search inside these directories
+   and also in any '/usr' and '/usr/bin' subdirectories.
+
+   Make sure the tags (here root: rest: and eldk:) are unique.
+
+   The toolchain-alias section indicates that the i386 toolchain should be used
+   to build x86 commits.
+
+   Note that you can also specific exactly toolchain prefixes if you like::
+
+      [toolchain-prefix]
+      arm: /opt/arm-eabi-4.6/bin/arm-eabi-
+
+   or even::
+
+      [toolchain-prefix]
+      arm: /opt/arm-eabi-4.6/bin/arm-eabi-gcc
+
+   This tells buildman that you want to use this exact toolchain for the arm
+   architecture. This will override any toolchains found by searching using the
+   [toolchain] settings.
+
+   Since the toolchain prefix is an explicit request, buildman will report an
+   error if a toolchain is not found with that prefix. The current PATH will be
+   searched, so it is possible to use::
+
+      [toolchain-prefix]
+      arm: arm-none-eabi-
+
+   and buildman will find arm-none-eabi-gcc in /usr/bin if you have it
+   installed.
+
+   Another example::
+
+      [toolchain-wrapper]
+      wrapper: ccache
+
+   This tells buildman to use a compiler wrapper in front of CROSS_COMPILE. In
+   this example, ccache. It doesn't affect the toolchain scan. The wrapper is
+   added when CROSS_COMPILE environtal variable is set. The name in this
+   section is ignored. If more than one line is provided, only the last one
+   is taken.
+
+#. Make sure you have the require Python pre-requisites
+
+   Buildman uses multiprocessing, Queue, shutil, StringIO, ConfigParser and
+   urllib2. These should normally be available, but if you get an error like
+   this then you will need to obtain those modules::
+
+      ImportError: No module named multiprocessing
+
+
+#. Check the available toolchains
+
+   Run this check to make sure that you have a toolchain for every architecture::
+
+      $ ./tools/buildman/buildman --list-tool-chains
+      Scanning for tool chains
+         - scanning prefix '/opt/gcc-4.6.3-nolibc/x86_64-linux/bin/x86_64-linux-'
+      Tool chain test:  OK, arch='x86', priority 1
+         - scanning prefix '/opt/arm-eabi-4.6/bin/arm-eabi-'
+      Tool chain test:  OK, arch='arm', priority 1
+         - scanning path '/toolchains/gcc-4.9.0-nolibc/i386-linux'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/i386-linux/.'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/i386-linux/bin'
+               - found '/toolchains/gcc-4.9.0-nolibc/i386-linux/bin/i386-linux-gcc'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/i386-linux/usr/bin'
+      Tool chain test:  OK, arch='i386', priority 4
+         - scanning path '/toolchains/gcc-4.9.0-nolibc/aarch64-linux'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/aarch64-linux/.'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/aarch64-linux/bin'
+               - found '/toolchains/gcc-4.9.0-nolibc/aarch64-linux/bin/aarch64-linux-gcc'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/aarch64-linux/usr/bin'
+      Tool chain test:  OK, arch='aarch64', priority 4
+         - scanning path '/toolchains/gcc-4.9.0-nolibc/microblaze-linux'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/microblaze-linux/.'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/microblaze-linux/bin'
+               - found '/toolchains/gcc-4.9.0-nolibc/microblaze-linux/bin/microblaze-linux-gcc'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/microblaze-linux/usr/bin'
+      Tool chain test:  OK, arch='microblaze', priority 4
+         - scanning path '/toolchains/gcc-4.9.0-nolibc/mips64-linux'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/mips64-linux/.'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/mips64-linux/bin'
+               - found '/toolchains/gcc-4.9.0-nolibc/mips64-linux/bin/mips64-linux-gcc'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/mips64-linux/usr/bin'
+      Tool chain test:  OK, arch='mips64', priority 4
+         - scanning path '/toolchains/gcc-4.9.0-nolibc/sparc64-linux'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/sparc64-linux/.'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/sparc64-linux/bin'
+               - found '/toolchains/gcc-4.9.0-nolibc/sparc64-linux/bin/sparc64-linux-gcc'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/sparc64-linux/usr/bin'
+      Tool chain test:  OK, arch='sparc64', priority 4
+         - scanning path '/toolchains/gcc-4.9.0-nolibc/arm-unknown-linux-gnueabi'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/arm-unknown-linux-gnueabi/.'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/arm-unknown-linux-gnueabi/bin'
+               - found '/toolchains/gcc-4.9.0-nolibc/arm-unknown-linux-gnueabi/bin/arm-unknown-linux-gnueabi-gcc'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/arm-unknown-linux-gnueabi/usr/bin'
+      Tool chain test:  OK, arch='arm', priority 3
+      Toolchain '/toolchains/gcc-4.9.0-nolibc/arm-unknown-linux-gnueabi/bin/arm-unknown-linux-gnueabi-gcc' at priority 3 will be ignored because another toolchain for arch 'arm' has priority 1
+         - scanning path '/toolchains/gcc-4.9.0-nolibc/sparc-linux'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/sparc-linux/.'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/sparc-linux/bin'
+               - found '/toolchains/gcc-4.9.0-nolibc/sparc-linux/bin/sparc-linux-gcc'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/sparc-linux/usr/bin'
+      Tool chain test:  OK, arch='sparc', priority 4
+         - scanning path '/toolchains/gcc-4.9.0-nolibc/mips-linux'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/mips-linux/.'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/mips-linux/bin'
+               - found '/toolchains/gcc-4.9.0-nolibc/mips-linux/bin/mips-linux-gcc'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/mips-linux/usr/bin'
+      Tool chain test:  OK, arch='mips', priority 4
+         - scanning path '/toolchains/gcc-4.9.0-nolibc/x86_64-linux'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/x86_64-linux/.'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/x86_64-linux/bin'
+               - found '/toolchains/gcc-4.9.0-nolibc/x86_64-linux/bin/x86_64-linux-gcc'
+               - found '/toolchains/gcc-4.9.0-nolibc/x86_64-linux/bin/x86_64-linux-x86_64-linux-gcc'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/x86_64-linux/usr/bin'
+      Tool chain test:  OK, arch='x86_64', priority 4
+      Tool chain test:  OK, arch='x86_64', priority 4
+      Toolchain '/toolchains/gcc-4.9.0-nolibc/x86_64-linux/bin/x86_64-linux-x86_64-linux-gcc' at priority 4 will be ignored because another toolchain for arch 'x86_64' has priority 4
+         - scanning path '/toolchains/gcc-4.9.0-nolibc/m68k-linux'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/m68k-linux/.'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/m68k-linux/bin'
+               - found '/toolchains/gcc-4.9.0-nolibc/m68k-linux/bin/m68k-linux-gcc'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/m68k-linux/usr/bin'
+      Tool chain test:  OK, arch='m68k', priority 4
+         - scanning path '/toolchains/gcc-4.9.0-nolibc/powerpc-linux'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/powerpc-linux/.'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/powerpc-linux/bin'
+               - found '/toolchains/gcc-4.9.0-nolibc/powerpc-linux/bin/powerpc-linux-gcc'
+            - looking in '/toolchains/gcc-4.9.0-nolibc/powerpc-linux/usr/bin'
+      Tool chain test:  OK, arch='powerpc', priority 4
+         - scanning path '/toolchains/gcc-4.6.3-nolibc/bfin-uclinux'
+            - looking in '/toolchains/gcc-4.6.3-nolibc/bfin-uclinux/.'
+            - looking in '/toolchains/gcc-4.6.3-nolibc/bfin-uclinux/bin'
+               - found '/toolchains/gcc-4.6.3-nolibc/bfin-uclinux/bin/bfin-uclinux-gcc'
+            - looking in '/toolchains/gcc-4.6.3-nolibc/bfin-uclinux/usr/bin'
+      Tool chain test:  OK, arch='bfin', priority 6
+         - scanning path '/toolchains/gcc-4.6.3-nolibc/sparc-linux'
+            - looking in '/toolchains/gcc-4.6.3-nolibc/sparc-linux/.'
+            - looking in '/toolchains/gcc-4.6.3-nolibc/sparc-linux/bin'
+               - found '/toolchains/gcc-4.6.3-nolibc/sparc-linux/bin/sparc-linux-gcc'
+            - looking in '/toolchains/gcc-4.6.3-nolibc/sparc-linux/usr/bin'
+      Tool chain test:  OK, arch='sparc', priority 4
+      Toolchain '/toolchains/gcc-4.6.3-nolibc/sparc-linux/bin/sparc-linux-gcc' at priority 4 will be ignored because another toolchain for arch 'sparc' has priority 4
+         - scanning path '/toolchains/gcc-4.6.3-nolibc/mips-linux'
+            - looking in '/toolchains/gcc-4.6.3-nolibc/mips-linux/.'
+            - looking in '/toolchains/gcc-4.6.3-nolibc/mips-linux/bin'
+               - found '/toolchains/gcc-4.6.3-nolibc/mips-linux/bin/mips-linux-gcc'
+            - looking in '/toolchains/gcc-4.6.3-nolibc/mips-linux/usr/bin'
+      Tool chain test:  OK, arch='mips', priority 4
+      Toolchain '/toolchains/gcc-4.6.3-nolibc/mips-linux/bin/mips-linux-gcc' at priority 4 will be ignored because another toolchain for arch 'mips' has priority 4
+         - scanning path '/toolchains/gcc-4.6.3-nolibc/m68k-linux'
+            - looking in '/toolchains/gcc-4.6.3-nolibc/m68k-linux/.'
+            - looking in '/toolchains/gcc-4.6.3-nolibc/m68k-linux/bin'
+               - found '/toolchains/gcc-4.6.3-nolibc/m68k-linux/bin/m68k-linux-gcc'
+            - looking in '/toolchains/gcc-4.6.3-nolibc/m68k-linux/usr/bin'
+      Tool chain test:  OK, arch='m68k', priority 4
+      Toolchain '/toolchains/gcc-4.6.3-nolibc/m68k-linux/bin/m68k-linux-gcc' at priority 4 will be ignored because another toolchain for arch 'm68k' has priority 4
+         - scanning path '/toolchains/gcc-4.6.3-nolibc/powerpc-linux'
+            - looking in '/toolchains/gcc-4.6.3-nolibc/powerpc-linux/.'
+            - looking in '/toolchains/gcc-4.6.3-nolibc/powerpc-linux/bin'
+               - found '/toolchains/gcc-4.6.3-nolibc/powerpc-linux/bin/powerpc-linux-gcc'
+            - looking in '/toolchains/gcc-4.6.3-nolibc/powerpc-linux/usr/bin'
+      Tool chain test:  OK, arch='powerpc', priority 4
+      Tool chain test:  OK, arch='or32', priority 4
+         - scanning path '/'
+            - looking in '/.'
+            - looking in '/bin'
+            - looking in '/usr/bin'
+               - found '/usr/bin/i586-mingw32msvc-gcc'
+               - found '/usr/bin/c89-gcc'
+               - found '/usr/bin/x86_64-linux-gnu-gcc'
+               - found '/usr/bin/gcc'
+               - found '/usr/bin/c99-gcc'
+               - found '/usr/bin/arm-linux-gnueabi-gcc'
+               - found '/usr/bin/aarch64-linux-gnu-gcc'
+               - found '/usr/bin/winegcc'
+               - found '/usr/bin/arm-linux-gnueabihf-gcc'
+      Tool chain test:  OK, arch='i586', priority 11
+      Tool chain test:  OK, arch='c89', priority 11
+      Tool chain test:  OK, arch='x86_64', priority 4
+      Toolchain '/usr/bin/x86_64-linux-gnu-gcc' at priority 4 will be ignored because another toolchain for arch 'x86_64' has priority 4
+      Tool chain test:  OK, arch='sandbox', priority 11
+      Tool chain test:  OK, arch='c99', priority 11
+      Tool chain test:  OK, arch='arm', priority 4
+      Toolchain '/usr/bin/arm-linux-gnueabi-gcc' at priority 4 will be ignored because another toolchain for arch 'arm' has priority 1
+      Tool chain test:  OK, arch='aarch64', priority 4
+      Toolchain '/usr/bin/aarch64-linux-gnu-gcc' at priority 4 will be ignored because another toolchain for arch 'aarch64' has priority 4
+      Tool chain test:  OK, arch='sandbox', priority 11
+      Toolchain '/usr/bin/winegcc' at priority 11 will be ignored because another toolchain for arch 'sandbox' has priority 11
+      Tool chain test:  OK, arch='arm', priority 4
+      Toolchain '/usr/bin/arm-linux-gnueabihf-gcc' at priority 4 will be ignored because another toolchain for arch 'arm' has priority 1
+      List of available toolchains (34):
+      aarch64   : /toolchains/gcc-4.9.0-nolibc/aarch64-linux/bin/aarch64-linux-gcc
+      alpha     : /toolchains/gcc-4.9.0-nolibc/alpha-linux/bin/alpha-linux-gcc
+      am33_2.0  : /toolchains/gcc-4.9.0-nolibc/am33_2.0-linux/bin/am33_2.0-linux-gcc
+      arm       : /opt/arm-eabi-4.6/bin/arm-eabi-gcc
+      bfin      : /toolchains/gcc-4.6.3-nolibc/bfin-uclinux/bin/bfin-uclinux-gcc
+      c89       : /usr/bin/c89-gcc
+      c99       : /usr/bin/c99-gcc
+      frv       : /toolchains/gcc-4.9.0-nolibc/frv-linux/bin/frv-linux-gcc
+      h8300     : /toolchains/gcc-4.9.0-nolibc/h8300-elf/bin/h8300-elf-gcc
+      hppa      : /toolchains/gcc-4.9.0-nolibc/hppa-linux/bin/hppa-linux-gcc
+      hppa64    : /toolchains/gcc-4.9.0-nolibc/hppa64-linux/bin/hppa64-linux-gcc
+      i386      : /toolchains/gcc-4.9.0-nolibc/i386-linux/bin/i386-linux-gcc
+      i586      : /usr/bin/i586-mingw32msvc-gcc
+      ia64      : /toolchains/gcc-4.9.0-nolibc/ia64-linux/bin/ia64-linux-gcc
+      m32r      : /toolchains/gcc-4.9.0-nolibc/m32r-linux/bin/m32r-linux-gcc
+      m68k      : /toolchains/gcc-4.9.0-nolibc/m68k-linux/bin/m68k-linux-gcc
+      microblaze: /toolchains/gcc-4.9.0-nolibc/microblaze-linux/bin/microblaze-linux-gcc
+      mips      : /toolchains/gcc-4.9.0-nolibc/mips-linux/bin/mips-linux-gcc
+      mips64    : /toolchains/gcc-4.9.0-nolibc/mips64-linux/bin/mips64-linux-gcc
+      or32      : /toolchains/gcc-4.5.1-nolibc/or32-linux/bin/or32-linux-gcc
+      powerpc   : /toolchains/gcc-4.9.0-nolibc/powerpc-linux/bin/powerpc-linux-gcc
+      powerpc64 : /toolchains/gcc-4.9.0-nolibc/powerpc64-linux/bin/powerpc64-linux-gcc
+      ppc64le   : /toolchains/gcc-4.9.0-nolibc/ppc64le-linux/bin/ppc64le-linux-gcc
+      s390x     : /toolchains/gcc-4.9.0-nolibc/s390x-linux/bin/s390x-linux-gcc
+      sandbox   : /usr/bin/gcc
+      sh4       : /toolchains/gcc-4.6.3-nolibc/sh4-linux/bin/sh4-linux-gcc
+      sparc     : /toolchains/gcc-4.9.0-nolibc/sparc-linux/bin/sparc-linux-gcc
+      sparc64   : /toolchains/gcc-4.9.0-nolibc/sparc64-linux/bin/sparc64-linux-gcc
+      tilegx    : /toolchains/gcc-4.6.2-nolibc/tilegx-linux/bin/tilegx-linux-gcc
+      x86       : /opt/gcc-4.6.3-nolibc/x86_64-linux/bin/x86_64-linux-gcc
+      x86_64    : /toolchains/gcc-4.9.0-nolibc/x86_64-linux/bin/x86_64-linux-gcc
+
+
+   You can see that everything is covered, even some strange ones that won't
+   be used (c88 and c99). This is a feature.
+
+
+#. Install new toolchains if needed
+
+   You can download toolchains and update the [toolchain] section of the
+   settings file to find them.
+
+   To make this easier, buildman can automatically download and install
+   toolchains from kernel.org. First list the available architectures::
+
+      $ ./tools/buildman/buildman --fetch-arch list
+      Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.6.3/
+      Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.6.2/
+      Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.5.1/
+      Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.2.4/
+      Available architectures: alpha am33_2.0 arm bfin cris crisv32 frv h8300
+      hppa hppa64 i386 ia64 m32r m68k mips mips64 or32 powerpc powerpc64 s390x sh4
+      sparc sparc64 tilegx x86_64 xtensa
+
+   Then pick one and download it::
+
+      $ ./tools/buildman/buildman --fetch-arch or32
+      Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.6.3/
+      Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.6.2/
+      Checking: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.5.1/
+      Downloading: https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.5.1//x86_64-gcc-4.5.1-nolibc_or32-linux.tar.xz
+      Unpacking to: /home/sjg/.buildman-toolchains
+      Testing
+            - looking in '/home/sjg/.buildman-toolchains/gcc-4.5.1-nolibc/or32-linux/.'
+            - looking in '/home/sjg/.buildman-toolchains/gcc-4.5.1-nolibc/or32-linux/bin'
+               - found '/home/sjg/.buildman-toolchains/gcc-4.5.1-nolibc/or32-linux/bin/or32-linux-gcc'
+      Tool chain test:  OK
+
+   Or download them all from kernel.org and move them to /toolchains directory:
+
+   .. code-block:: bash
+
+      ./tools/buildman/buildman --fetch-arch all
+      sudo mkdir -p /toolchains
+      sudo mv ~/.buildman-toolchains/*/* /toolchains/
+
+   For those not available from kernel.org, download from the following links:
+
+   - `Arc Toolchain`_
+
+   Buildman should now be set up to use your new toolchain.
+
+   At the time of writing, U-Boot has these architectures:
+
+      arc, arm, m68k, microblaze, mips, nios2, powerpc, sandbox, sh, x86, xtensa
+
+
+How to run it
+-------------
+
+First do a dry run using the -n flag: (replace <branch> with a real, local
+branch with a valid upstream):
+
+.. code-block:: bash
+
+   ./tools/buildman/buildman -b <branch> -n
+
+If it can't detect the upstream branch, try checking out the branch, and
+doing something like 'git branch --set-upstream-to upstream/master'
+or something similar. Buildman will try to guess a suitable upstream branch
+if it can't find one (you will see a message like "Guessing upstream as ...").
+You can also use the -c option to manually specify the number of commits to
+build.
+
+As an example::
+
+   Dry run, so not doing much. But I would do this:
+
+   Building 18 commits for 1059 boards (4 threads, 1 job per thread)
+   Build directory: ../lcd9b
+       5bb3505 Merge branch 'master' of git://git.denx.de/u-boot-arm
+       c18f1b4 tegra: Use const for pinmux_config_pingroup/table()
+       2f043ae tegra: Add display support to funcmux
+       e349900 tegra: fdt: Add pwm binding and node
+       424a5f0 tegra: fdt: Add LCD definitions for Tegra
+       0636ccf tegra: Add support for PWM
+       a994fe7 tegra: Add SOC support for display/lcd
+       fcd7350 tegra: Add LCD driver
+       4d46e9d tegra: Add LCD support to Nvidia boards
+       991bd48 arm: Add control over cachability of memory regions
+       54e8019 lcd: Add CONFIG_LCD_ALIGNMENT to select frame buffer alignment
+       d92aff7 lcd: Add support for flushing LCD fb from dcache after update
+       dbd0677 tegra: Align LCD frame buffer to section boundary
+       0cff9b8 tegra: Support control of cache settings for LCD
+       9c56900 tegra: fdt: Add LCD definitions for Seaboard
+       5cc29db lcd: Add CONFIG_CONSOLE_SCROLL_LINES option to speed console
+       cac5a23 tegra: Enable display/lcd support on Seaboard
+       49ff541 wip
+
+   Total boards to build for each commit: 1059
+
+This shows that it will build all 1059 boards, using 4 threads (because
+we have a 4-core CPU). Each thread will run with -j1, meaning that each
+make job will use a single CPU. The list of commits to be built helps you
+confirm that things look about right. Notice that buildman has chosen a
+'base' directory for you, immediately above your source tree.
+
+Buildman works entirely inside the base directory, here ../lcd9b,
+creating a working directory for each thread, and creating output
+directories for each commit and board.
+
+
+Suggested Workflow
+------------------
+
+To run the build for real, take off the -n:
+
+.. code-block:: bash
+
+   ./tools/buildman/buildman -b <branch>
+
+Buildman will set up some working directories, and get started. After a
+minute or so it will settle down to a steady pace, with a display like this::
+
+   Building 18 commits for 1059 boards (4 threads, 1 job per thread)
+     528   36  124 /19062    -18374  1:13:30  : SIMPC8313_SP
+
+This means that it is building 19062 board/commit combinations. So far it
+has managed to successfully build 528. Another 36 have built with warnings,
+and 124 more didn't build at all. It has 18374 builds left to complete.
+Buildman expects to complete the process in around an hour and a quarter.
+Use this time to buy a faster computer.
+
+
+To find out how the build went, ask for a summary with -s. You can do this
+either before the build completes (presumably in another terminal) or
+afterwards. Let's work through an example of how this is used::
+
+   $ ./tools/buildman/buildman -b lcd9b -s
+   ...
+   01: Merge branch 'master' of git://git.denx.de/u-boot-arm
+      powerpc:   + galaxy5200_LOWBOOT
+   02: tegra: Use const for pinmux_config_pingroup/table()
+   03: tegra: Add display support to funcmux
+   04: tegra: fdt: Add pwm binding and node
+   05: tegra: fdt: Add LCD definitions for Tegra
+   06: tegra: Add support for PWM
+   07: tegra: Add SOC support for display/lcd
+   08: tegra: Add LCD driver
+   09: tegra: Add LCD support to Nvidia boards
+   10: arm: Add control over cachability of memory regions
+   11: lcd: Add CONFIG_LCD_ALIGNMENT to select frame buffer alignment
+   12: lcd: Add support for flushing LCD fb from dcache after update
+          arm:   + lubbock
+   13: tegra: Align LCD frame buffer to section boundary
+   14: tegra: Support control of cache settings for LCD
+   15: tegra: fdt: Add LCD definitions for Seaboard
+   16: lcd: Add CONFIG_CONSOLE_SCROLL_LINES option to speed console
+   17: tegra: Enable display/lcd support on Seaboard
+   18: wip
+
+This shows which commits have succeeded and which have failed. In this case
+the build is still in progress so many boards are not built yet (use -u to
+see which ones). But already we can see a few failures. The galaxy5200_LOWBOOT
+never builds correctly. This could be a problem with our toolchain, or it
+could be a bug in the upstream. The good news is that we probably don't need
+to blame our commits. The bad news is that our commits are not tested on that
+board.
+
+Commit 12 broke lubbock. That's what the '+ lubbock', in red, means. The
+failure is never fixed by a later commit, or you would see lubbock again, in
+green, without the +.
+
+To see the actual error::
+
+   $ ./tools/buildman/buildman -b <branch> -se
+   ...
+   12: lcd: Add support for flushing LCD fb from dcache after update
+          arm:   + lubbock
+   +common/libcommon.o: In function `lcd_sync':
+   +common/lcd.c:120: undefined reference to `flush_dcache_range'
+   +arm-none-linux-gnueabi-ld: BFD (Sourcery G++ Lite 2010q1-202) 2.19.51.20090709 assertion fail /scratch/julian/2010q1-release-linux-lite/obj/binutils-src-2010q1-202-arm-none-linux-gnueabi-i686-pc-linux-gnu/bfd/elf32-arm.c:12572
+   +make: *** [build/u-boot] Error 139
+   13: tegra: Align LCD frame buffer to section boundary
+   14: tegra: Support control of cache settings for LCD
+   15: tegra: fdt: Add LCD definitions for Seaboard
+   16: lcd: Add CONFIG_CONSOLE_SCROLL_LINES option to speed console
+   -common/lcd.c:120: undefined reference to `flush_dcache_range'
+   +common/lcd.c:125: undefined reference to `flush_dcache_range'
+   17: tegra: Enable display/lcd support on Seaboard
+   18: wip
+
+So the problem is in lcd.c, due to missing cache operations. This information
+should be enough to work out what that commit is doing to break these
+boards. (In this case pxa did not have cache operations defined).
+
+Note that if there were other boards with errors, the above command would
+show their errors also. Each line is shown only once. So if lubbock and snow
+produce the same error, we just see::
+
+   12: lcd: Add support for flushing LCD fb from dcache after update
+          arm:   + lubbock snow
+   +common/libcommon.o: In function `lcd_sync':
+   +common/lcd.c:120: undefined reference to `flush_dcache_range'
+   +arm-none-linux-gnueabi-ld: BFD (Sourcery G++ Lite 2010q1-202) 2.19.51.20090709 assertion fail /scratch/julian/2010q1-release-linux-lite/obj/binutils-src-2010q1-202-arm-none-linux-gnueabi-i686-pc-linux-gnu/bfd/elf32-arm.c:12572
+   +make: *** [build/u-boot] Error 139
+
+But if you did want to see just the errors for lubbock, use:
+
+.. code-block:: bash
+
+   ./tools/buildman/buildman -b <branch> -se lubbock
+
+If you see error lines marked with '-', that means that the errors were fixed
+by that commit. Sometimes commits can be in the wrong order, so that a
+breakage is introduced for a few commits and fixed by later commits. This
+shows up clearly with buildman. You can then reorder the commits and try
+again.
+
+At commit 16, the error moves: you can see that the old error at line 120
+is fixed, but there is a new one at line 126. This is probably only because
+we added some code and moved the broken line further down the file.
+
+As mentioned, if many boards have the same error, then -e will display the
+error only once. This makes the output as concise as possible. To see which
+boards have each error, use -l. So it is safe to omit the board name - you
+will not get lots of repeated output for every board.
+
+Buildman tries to distinguish warnings from errors, and shows warning lines
+separately with a 'w' prefix. Warnings introduced show as yellow. Warnings
+fixed show as cyan.
+
+The full build output in this case is available in::
+
+   ../lcd9b/12_of_18_gd92aff7_lcd--Add-support-for/lubbock/
+
+Files:
+
+done
+   Indicates the build was done, and holds the return code from make. This is 0
+   for a good build, typically 2 for a failure.
+
+err
+   Output from stderr, if any. Errors and warnings appear here.
+
+log
+   Output from stdout. Normally there isn't any since buildman runs in silent
+   mode. Use -V to force a verbose build (this passes V=1 to 'make')
+
+toolchain
+   Shows information about the toolchain used for the build.
+
+sizes
+   Shows image size information.
+
+It is possible to get the build binary output there also. Use the -k option
+for this. In that case you will also see some output files, like:
+
+- System.map
+- toolchain
+- u-boot
+- u-boot.bin
+- u-boot.map
+- autoconf.mk
+- SPL/TPL versions like u-boot-spl and u-boot-spl.bin if available
+
+
+Checking Image Sizes
+--------------------
+
+A key requirement for U-Boot is that you keep code/data size to a minimum.
+Where a new feature increases this noticeably it should normally be put
+behind a CONFIG flag so that boards can leave it disabled and keep the image
+size more or less the same with each new release.
+
+To check the impact of your commits on image size, use -S. For example::
+
+   $ ./tools/buildman/buildman -b us-x86 -sS
+   Summary of 10 commits for 1066 boards (4 threads, 1 job per thread)
+   01: MAKEALL: add support for per architecture toolchains
+   02: x86: Add function to get top of usable ram
+          x86: (for 1/3 boards)  text -272.0  rodata +41.0
+   03: x86: Add basic cache operations
+   04: x86: Permit bootstage and timer data to be used prior to relocation
+          x86: (for 1/3 boards)  data +16.0
+   05: x86: Add an __end symbol to signal the end of the U-Boot binary
+          x86: (for 1/3 boards)  text +76.0
+   06: x86: Rearrange the output input to remove BSS
+          x86: (for 1/3 boards)  bss -2140.0
+   07: x86: Support relocation of FDT on start-up
+          x86: +   coreboot-x86
+   08: x86: Add error checking to x86 relocation code
+   09: x86: Adjust link device tree include file
+   10: x86: Enable CONFIG_OF_CONTROL on coreboot
+
+
+You can see that image size only changed on x86, which is good because this
+series is not supposed to change any other board. From commit 7 onwards the
+build fails so we don't get code size numbers. The numbers are fractional
+because they are an average of all boards for that architecture. The
+intention is to allow you to quickly find image size problems introduced by
+your commits.
+
+Note that the 'text' region and 'rodata' are split out. You should add the
+two together to get the total read-only size (reported as the first column
+in the output from binutil's 'size' utility).
+
+A useful option is --step which lets you skip some commits. For example
+--step 2 will show the image sizes for only every 2nd commit (so it will
+compare the image sizes of the 1st, 3rd, 5th... commits). You can also use
+--step 0 which will compare only the first and last commits. This is useful
+for an overview of how your entire series affects code size. It will build
+only the upstream commit and your final branch commit.
+
+You can also use -d to see a detailed size breakdown for each board. This
+list is sorted in order from largest growth to largest reduction.
+
+It is even possible to go a little further with the -B option (--bloat). This
+shows where U-Boot has bloated, breaking the size change down to the function
+level. Example output is below::
+
+   $ ./tools/buildman/buildman -b us-mem4 -sSdB
+   ...
+   19: Roll crc32 into hash infrastructure
+          arm: (for 10/10 boards)  all -143.4  bss +1.2  data -4.8  rodata -48.2 text -91.6
+               paz00          :  all +23  bss -4  rodata -29  text +56
+                  u-boot: add: 1/0, grow: 3/-2 bytes: 168/-104 (64)
+                    function                                   old     new   delta
+                    hash_command                                80     160     +80
+                    crc32_wd_buf                                 -      56     +56
+                    ext4fs_read_file                           540     568     +28
+                    insert_var_value_sub                       688     692      +4
+                    run_list_real                             1996    1992      -4
+                    do_mem_crc                                 168      68    -100
+               trimslice      :  all -9  bss +16  rodata -29  text +4
+                  u-boot: add: 1/0, grow: 1/-3 bytes: 136/-124 (12)
+                    function                                   old     new   delta
+                    hash_command                                80     160     +80
+                    crc32_wd_buf                                 -      56     +56
+                    ext4fs_iterate_dir                         672     668      -4
+                    ext4fs_read_file                           568     548     -20
+                    do_mem_crc                                 168      68    -100
+               whistler       :  all -9  bss +16  rodata -29  text +4
+                  u-boot: add: 1/0, grow: 1/-3 bytes: 136/-124 (12)
+                    function                                   old     new   delta
+                    hash_command                                80     160     +80
+                    crc32_wd_buf                                 -      56     +56
+                    ext4fs_iterate_dir                         672     668      -4
+                    ext4fs_read_file                           568     548     -20
+                    do_mem_crc                                 168      68    -100
+               seaboard       :  all -9  bss -28  rodata -29  text +48
+                  u-boot: add: 1/0, grow: 3/-2 bytes: 160/-104 (56)
+                    function                                   old     new   delta
+                    hash_command                                80     160     +80
+                    crc32_wd_buf                                 -      56     +56
+                    ext4fs_read_file                           548     568     +20
+                    run_list_real                             1996    2000      +4
+                    do_nandboot                                760     756      -4
+                    do_mem_crc                                 168      68    -100
+               colibri_t20    :  all -9  rodata -29  text +20
+                  u-boot: add: 1/0, grow: 2/-3 bytes: 140/-112 (28)
+                    function                                   old     new   delta
+                    hash_command                                80     160     +80
+                    crc32_wd_buf                                 -      56     +56
+                    read_abs_bbt                               204     208      +4
+                    do_nandboot                                760     756      -4
+                    ext4fs_read_file                           576     568      -8
+                    do_mem_crc                                 168      68    -100
+               ventana        :  all -37  bss -12  rodata -29  text +4
+                  u-boot: add: 1/0, grow: 1/-3 bytes: 136/-124 (12)
+                    function                                   old     new   delta
+                    hash_command                                80     160     +80
+                    crc32_wd_buf                                 -      56     +56
+                    ext4fs_iterate_dir                         672     668      -4
+                    ext4fs_read_file                           568     548     -20
+                    do_mem_crc                                 168      68    -100
+               harmony        :  all -37  bss -16  rodata -29  text +8
+                  u-boot: add: 1/0, grow: 2/-3 bytes: 140/-124 (16)
+                    function                                   old     new   delta
+                    hash_command                                80     160     +80
+                    crc32_wd_buf                                 -      56     +56
+                    nand_write_oob_syndrome                    428     432      +4
+                    ext4fs_iterate_dir                         672     668      -4
+                    ext4fs_read_file                           568     548     -20
+                    do_mem_crc                                 168      68    -100
+               medcom-wide    :  all -417  bss +28  data -16  rodata -93  text -336
+                  u-boot: add: 1/-1, grow: 1/-2 bytes: 88/-376 (-288)
+                    function                                   old     new   delta
+                    crc32_wd_buf                                 -      56     +56
+                    do_fat_read_at                            2872    2904     +32
+                    hash_algo                                   16       -     -16
+                    do_mem_crc                                 168      68    -100
+                    hash_command                               420     160    -260
+               tec            :  all -449  bss -4  data -16  rodata -93  text -336
+                  u-boot: add: 1/-1, grow: 1/-2 bytes: 88/-376 (-288)
+                    function                                   old     new   delta
+                    crc32_wd_buf                                 -      56     +56
+                    do_fat_read_at                            2872    2904     +32
+                    hash_algo                                   16       -     -16
+                    do_mem_crc                                 168      68    -100
+                    hash_command                               420     160    -260
+               plutux         :  all -481  bss +16  data -16  rodata -93  text -388
+                  u-boot: add: 1/-1, grow: 1/-3 bytes: 68/-408 (-340)
+                    function                                   old     new   delta
+                    crc32_wd_buf                                 -      56     +56
+                    do_load_serial_bin                        1688    1700     +12
+                    hash_algo                                   16       -     -16
+                    do_fat_read_at                            2904    2872     -32
+                    do_mem_crc                                 168      68    -100
+                    hash_command                               420     160    -260
+      powerpc: (for 5/5 boards)  all +37.4  data -3.2  rodata -41.8  text +82.4
+               MPC8610HPCD    :  all +55  rodata -29  text +84
+                  u-boot: add: 1/0, grow: 0/-1 bytes: 176/-96 (80)
+                    function                                   old     new   delta
+                    hash_command                                 -     176    +176
+                    do_mem_crc                                 184      88     -96
+               MPC8641HPCN    :  all +55  rodata -29  text +84
+                  u-boot: add: 1/0, grow: 0/-1 bytes: 176/-96 (80)
+                    function                                   old     new   delta
+                    hash_command                                 -     176    +176
+                    do_mem_crc                                 184      88     -96
+               MPC8641HPCN_36BIT:  all +55  rodata -29  text +84
+                  u-boot: add: 1/0, grow: 0/-1 bytes: 176/-96 (80)
+                    function                                   old     new   delta
+                    hash_command                                 -     176    +176
+                    do_mem_crc                                 184      88     -96
+               sbc8641d       :  all +55  rodata -29  text +84
+                  u-boot: add: 1/0, grow: 0/-1 bytes: 176/-96 (80)
+                    function                                   old     new   delta
+                    hash_command                                 -     176    +176
+                    do_mem_crc                                 184      88     -96
+               xpedite517x    :  all -33  data -16  rodata -93  text +76
+                  u-boot: add: 1/-1, grow: 0/-1 bytes: 176/-112 (64)
+                    function                                   old     new   delta
+                    hash_command                                 -     176    +176
+                    hash_algo                                   16       -     -16
+                    do_mem_crc                                 184      88     -96
+   ...
+
+
+This shows that commit 19 has reduced codesize for arm slightly and increased
+it for powerpc. This increase was offset in by reductions in rodata and
+data/bss.
+
+Shown below the summary lines are the sizes for each board. Below each board
+are the sizes for each function. This information starts with:
+
+add
+   number of functions added / removed
+
+grow
+   number of functions which grew / shrunk
+
+bytes
+   number of bytes of code added to / removed from all functions, plus the total
+   byte change in brackets
+
+The change seems to be that hash_command() has increased by more than the
+do_mem_crc() function has decreased. The function sizes typically add up to
+roughly the text area size, but note that every read-only section except
+rodata is included in 'text', so the function total does not exactly
+correspond.
+
+It is common when refactoring code for the rodata to decrease as the text size
+increases, and vice versa.
+
+
+.. _buildman_settings:
+
+The .buildman settings file
+---------------------------
+
+The .buildman file provides information about the available toolchains and
+also allows build flags to be passed to 'make'. It consists of several
+sections, with the section name in square brackets. Within each section are
+a set of (tag, value) pairs.
+
+'[global]' section
+    allow-missing
+        Indicates the policy to use for missing blobs. Note that the flags
+        ``--allow-missing`` (``-M``) and ``--no-allow-missing`` (``--no-a``)
+        override these setting.
+
+        always
+           Run with ``-M`` by default.
+
+        multiple
+           Run with ``-M`` if more than one board is being built.
+
+        branch
+           Run with ``-M`` if a branch is being built.
+
+        Note that the last two can be given together::
+
+           allow-missing = multiple branch
+
+'[toolchain]' section
+    This lists the available toolchains. The tag here doesn't matter, but
+    make sure it is unique. The value is the path to the toolchain. Buildman
+    will look in that path for a file ending in 'gcc'. It will then execute
+    it to check that it is a C compiler, passing only the --version flag to
+    it. If the return code is 0, buildman assumes that it is a valid C
+    compiler. It uses the first part of the name as the architecture and
+    strips off the last part when setting the CROSS_COMPILE environment
+    variable (parts are delimited with a hyphen).
+
+    For example powerpc-linux-gcc will be noted as a toolchain for 'powerpc'
+    and CROSS_COMPILE will be set to powerpc-linux- when using it.
+
+'[toolchain-alias]' section
+    This converts toolchain architecture names to U-Boot names. For example,
+    if an x86 toolchains is called i386-linux-gcc it will not normally be
+    used for architecture 'x86'. Adding 'x86: i386 x86_64' to this section
+    will tell buildman that the i386 and x86_64 toolchains can be used for
+    the x86 architecture.
+
+'[make-flags]' section
+    U-Boot's build system supports a few flags (such as BUILD_TAG) which
+    affect the build product. These flags can be specified in the buildman
+    settings file. They can also be useful when building U-Boot against other
+    open source software.
+
+    [make-flags]
+    at91-boards=ENABLE_AT91_TEST=1
+    snapper9260=${at91-boards} BUILD_TAG=442
+    snapper9g45=${at91-boards} BUILD_TAG=443
+
+    This will use 'make ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260
+    and 'make ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45. A special
+    variable ${target} is available to access the target name (snapper9260
+    and snapper9g20 in this case). Variables are resolved recursively. Note
+    that variables can only contain the characters A-Z, a-z, 0-9, hyphen (-)
+    and underscore (_).
+
+    It is expected that any variables added are dealt with in U-Boot's
+    config.mk file and documented in the README.
+
+    Note that you can pass ad-hoc options to the build using environment
+    variables, for example:
+
+       SOME_OPTION=1234 ./tools/buildman/buildman my_board
+
+
+Quick Sanity Check
+------------------
+
+If you have made changes and want to do a quick sanity check of the
+currently checked-out source, run buildman without the -b flag. This will
+build the selected boards and display build status as it runs (i.e. -v is
+enabled automatically). Use -e to see errors/warnings as well.
+
+
+Building Ranges
+---------------
+
+You can build a range of commits by specifying a range instead of a branch
+when using the -b flag. For example::
+
+    buildman -b upstream/master..us-buildman
+
+will build commits in us-buildman that are not in upstream/master.
+
+
+Building Faster
+---------------
+
+By default, buildman doesn't execute 'make mrproper' prior to building the
+first commit for each board. This reduces the amount of work 'make' does, and
+hence speeds up the build. To force use of 'make mrproper', use -the -m flag.
+This flag will slow down any buildman invocation, since it increases the amount
+of work done on any build.
+
+One possible application of buildman is as part of a continual edit, build,
+edit, build, ... cycle; repeatedly applying buildman to the same change or
+series of changes while making small incremental modifications to the source
+each time. This provides quick feedback regarding the correctness of recent
+modifications. In this scenario, buildman's default choice of build directory
+causes more build work to be performed than strictly necessary.
+
+By default, each buildman thread uses a single directory for all builds. When a
+thread builds multiple boards, the configuration built in this directory will
+cycle through various different configurations, one per board built by the
+thread. Variations in the configuration will force a rebuild of affected source
+files when a thread switches between boards. Ideally, such buildman-induced
+rebuilds would not happen, thus allowing the build to operate as efficiently as
+the build system and source changes allow. buildman's -P flag may be used to
+enable this; -P causes each board to be built in a separate (board-specific)
+directory, thus avoiding any buildman-induced configuration changes in any
+build directory.
+
+U-Boot's build system embeds information such as a build timestamp into the
+final binary. This information varies each time U-Boot is built. This causes
+various files to be rebuilt even if no source changes are made, which in turn
+requires that the final U-Boot binary be re-linked. This unnecessary work can
+be avoided by turning off the timestamp feature. This can be achieved by
+setting the SOURCE_DATE_EPOCH environment variable to 0.
+
+Combining all of these options together yields the command-line shown below.
+This will provide the quickest possible feedback regarding the current content
+of the source tree, thus allowing rapid tested evolution of the code::
+
+    SOURCE_DATE_EPOCH=0 ./tools/buildman/buildman -P tegra
+
+
+Checking configuration
+----------------------
+
+A common requirement when converting CONFIG options to Kconfig is to check
+that the effective configuration has not changed due to the conversion.
+Buildman supports this with the -K option, used after a build. This shows
+differences in effective configuration between one commit and the next.
+
+For example::
+
+    $ buildman -b kc4 -sK
+    ...
+    43: Convert CONFIG_SPL_USBETH_SUPPORT to Kconfig
+    arm:
+    + u-boot.cfg: CONFIG_SPL_ENV_SUPPORT=1 CONFIG_SPL_NET=1
+    + u-boot-spl.cfg: CONFIG_SPL_MMC=1 CONFIG_SPL_NAND_SUPPORT=1
+    + all: CONFIG_SPL_ENV_SUPPORT=1 CONFIG_SPL_MMC=1 CONFIG_SPL_NAND_SUPPORT=1 CONFIG_SPL_NET=1
+    am335x_evm_usbspl :
+    + u-boot.cfg: CONFIG_SPL_ENV_SUPPORT=1 CONFIG_SPL_NET=1
+    + u-boot-spl.cfg: CONFIG_SPL_MMC=1 CONFIG_SPL_NAND_SUPPORT=1
+    + all: CONFIG_SPL_ENV_SUPPORT=1 CONFIG_SPL_MMC=1 CONFIG_SPL_NAND_SUPPORT=1 CONFIG_SPL_NET=1
+    44: Convert CONFIG_SPL_USB_HOST to Kconfig
+    ...
+
+This shows that commit 44 enabled three new options for the board
+am335x_evm_usbspl which were not enabled in commit 43. There is also a
+summary for 'arm' showing all the changes detected for that architecture.
+In this case there is only one board with changes, so 'arm' output is the
+same as 'am335x_evm_usbspl'/
+
+The -K option uses the u-boot.cfg, spl/u-boot-spl.cfg and tpl/u-boot-tpl.cfg
+files which are produced by a build. If all you want is to check the
+configuration you can in fact avoid doing a full build, using -D. This tells
+buildman to configuration U-Boot and create the .cfg files, but not actually
+build the source. This is 5-10 times faster than doing a full build.
+
+By default buildman considers the follow two configuration methods
+equivalent::
+
+   #define CONFIG_SOME_OPTION
+
+   CONFIG_SOME_OPTION=y
+
+The former would appear in a header filer and the latter in a defconfig
+file. The achieve this, buildman considers 'y' to be '1' in configuration
+variables. This avoids lots of useless output when converting a CONFIG
+option to Kconfig. To disable this behaviour, use --squash-config-y.
+
+
+Checking the environment
+------------------------
+
+When converting CONFIG options which manipulate the default environment,
+a common requirement is to check that the default environment has not
+changed due to the conversion. Buildman supports this with the -U option,
+used after a build. This shows differences in the default environment
+between one commit and the next.
+
+For example::
+
+   $ buildman -b squash brppt1 -sU
+   Summary of 2 commits for 3 boards (3 threads, 3 jobs per thread)
+   01: Migrate bootlimit to Kconfig
+   02: Squashed commit of the following:
+      c brppt1_mmc: altbootcmd=mmc dev 1; run mmcboot0; -> mmc dev 1; run mmcboot0
+      c brppt1_spi: altbootcmd=mmc dev 1; run mmcboot0; -> mmc dev 1; run mmcboot0
+      + brppt1_nand: altbootcmd=run usbscript
+      - brppt1_nand:  altbootcmd=run usbscript
+   (no errors to report)
+
+This shows that commit 2 modified the value of 'altbootcmd' for 'brppt1_mmc'
+and 'brppt1_spi', removing a trailing semicolon. 'brppt1_nand' gained an a
+value for 'altbootcmd', but lost one for ' altbootcmd'.
+
+The -U option uses the u-boot.env files which are produced by a build.
+
+
+Building with clang
+-------------------
+
+To build with clang (sandbox only), use the -O option to override the
+toolchain. For example:
+
+.. code-block:: bash
+
+   buildman -O clang-7 --board sandbox
+
+
+Doing a simple build
+--------------------
+
+In some cases you just want to build a single board and get the full output, use
+the -w option, for example:
+
+.. code-block:: bash
+
+   buildman -o /tmp/build --board sandbox -w
+
+This will write the full build into /tmp/build including object files. You must
+specify the output directory with -o when using -w.
+
+
+Support for IDEs (Integrated Development Environments)
+------------------------------------------------------
+
+Normally buildman summarises the output and shows information indicating the
+meaning of each line of output. For example a '+' symbol appears at the start of
+each error line. Also, buildman prints information about what it is about to do,
+along with a summary at the end.
+
+When using buildman from an IDE, it is helpful to drop this behaviour. Use the
+-I/--ide option for that. You might find -W helpful also so that warnings do
+not cause the build to fail:
+
+.. code-block:: bash
+
+   buildman -o /tmp/build --board sandbox -wWI
+
+
+Support for binary blobs
+------------------------
+
+U-Boot is moving to using Binman (see :doc:`../develop/package/binman`) for
+dealing with the complexities of packaging U-Boot along with binary files from
+other projects. These are called 'external blobs' by Binman.
+
+Typically a missing external blob causes a build failure. For build testing of
+a lot of boards, or boards for which you do not have the blobs, you can use the
+-M flag to allow missing blobs. This marks the build as if it succeeded,
+although with warnings shown, including 'Some images are invalid'. If any boards
+fail in this way, buildman exits with status 101.
+
+To convert warnings to errors, use -E. To make buildman return success with
+these warnings, use -W.
+
+It is generally safe to default to enabling -M for all runs of buildman, so long
+as you check the exit code. To do this, add::
+
+   allow-missing = "always"
+
+to the top of the buildman_settings_ file.
+
+
+Changing the configuration
+--------------------------
+
+Sometimes it is useful to change the CONFIG options for a build on the fly. This
+can be used to build a board (or multiple) with a few changes to see the impact.
+The -a option supports this:
+
+.. code-block:: bash
+
+   -a <cfg>
+
+where <cfg> is a CONFIG option (with or without the `CONFIG_` prefix) to enable.
+For example:
+
+.. code-block:: bash
+
+    buildman -a CMD_SETEXPR_FMT
+
+will build with CONFIG_CMD_SETEXPR_FMT enabled.
+
+You can disable options by preceding them with tilde (~). You can specify the
+-a option multiple times:
+
+.. code-block:: bash
+
+    buildman -a CMD_SETEXPR_FMT -a ~CMDLINE
+
+Some options have values, in which case you can change them:
+
+.. code-block:: bash
+
+    buildman -a 'BOOTCOMMAND="echo hello"' CONFIG_SYS_LOAD_ADDR=0x1000
+
+Note that you must put quotes around string options and the whole thing must be
+in single quotes, to make sure the shell leave it alone.
+
+If you try to set an option that does not exist, or that cannot be changed for
+some other reason (e.g. it is 'selected' by another option), then buildman
+shows an error::
+
+   $ buildman --board sandbox -a FRED
+   Building current source for 1 boards (1 thread, 32 jobs per thread)
+       0    0    0 /1       -1      (starting)errs
+   Some CONFIG adjustments did not take effect. This may be because
+   the request CONFIGs do not exist or conflict with others.
+
+   Failed adjustments:
+
+   FRED                  Missing expected line: CONFIG_FRED=y
+
+
+One major caveat with this feature with branches (-b) is that buildman does not
+name the output directories differently when you change the configuration, so
+doing the same build again with different configuration will not trigger a
+rebuild. You can use -f to work around that.
+
+
+Other options
+-------------
+
+Buildman has various other command-line options. Try --help to see them.
+
+To find out what toolchain prefix buildman will use for a build, use the -A
+option.
+
+To request that compiler warnings be promoted to errors, use -E. This passes the
+-Werror flag to the compiler. Note that the build can still produce warnings
+with -E, e.g. the migration warnings::
+
+   ===================== WARNING ======================
+   This board does not use CONFIG_DM_MMC. Please update
+   ...
+   ====================================================
+
+When doing builds, Buildman's return code will reflect the overall result::
+
+    0 (success)     No errors or warnings found
+    100             Errors found
+    101             Warnings found (only if no -W)
+
+You can use -W to tell Buildman to return 0 (success) instead of 101 when
+warnings are found. Note that it can be useful to combine -E and -W. This means
+that all compiler warnings will produce failures (code 100) and all other
+warnings will produce success (since 101 is changed to 0).
+
+If there are both warnings and errors, errors win, so buildman returns 100.
+
+The -y option is provided (for use with -s) to ignore the bountiful device-tree
+warnings. Similarly, -Y tells buildman to ignore the migration warnings.
+
+Sometimes you might get an error in a thread that is not handled by buildman,
+perhaps due to a failure of a tool that it calls. You might see the output, but
+then buildman hangs. Failing to handle any eventuality is a bug in buildman and
+should be reported. But you can use -T0 to disable threading and hopefully
+figure out the root cause of the build failure.
+
+Build summary
+-------------
+
+When buildman finishes it shows a summary, something like this::
+
+    Completed: 5 total built, duration 0:00:21, rate 0.24
+
+This shows that a total of 5 builds were done across all selected boards, it
+took 21 seconds and the builds happened at the rate of 0.24 per second. The
+latter number depends on the speed of your machine and the efficiency of the
+U-Boot build.
+
+
+Using boards.cfg
+----------------
+
+This file is no-longer needed by buildman but it is still generated in the
+working directory. This helps avoid a delay on every build, since scanning all
+the Kconfig files takes a few seconds. Use the -R flag to force regeneration
+of the file - in that case buildman exits after writing the file. with exit code
+2 if there was an error in the maintainer files.
+
+You should use 'buildman -nv <criteria>' instead of greoing the boards.cfg file,
+since it may be dropped altogether in future.
+
+
+TODO
+----
+
+Many improvements have been made over the years. There is still quite a bit of
+scope for more though, e.g.:
+
+- easier access to log files
+- 'hunting' for problems, perhaps by building a few boards for each arch, or
+  checking commits for changed files and building only boards which use those
+  files
+
+
+Credits
+-------
+
+Thanks to Grant Grundler <grundler@chromium.org> for his ideas for improving
+the build speed by building all commits for a board instead of the other
+way around.
+
+.. _`Arc Toolchain`: https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain/releases/download/arc-2021.03-release/arc_gnu_2021.03_prebuilt_elf32_le_linux_install.tar.gz
+
+.. sectionauthor:: Simon Glass
+.. sectionauthor:: Copyright (c) 2013 The Chromium OS Authors.
+.. sectionauthor:: sjg@chromium.org
+.. Halloween 2012
+.. Updated 12-12-12
+.. Updated 23-02-13
+.. Updated 09-04-20
diff --git a/tools/buildman/cmdline.py b/tools/buildman/cmdline.py
index b29c1eb..c485994 100644
--- a/tools/buildman/cmdline.py
+++ b/tools/buildman/cmdline.py
@@ -75,6 +75,12 @@
           help='List available tool chains (use -v to see probing detail)')
     parser.add_option('-m', '--mrproper', action='store_true',
           default=False, help="Run 'make mrproper before reconfiguring")
+    parser.add_option(
+          '-M', '--allow-missing', action='store_true', default=False,
+          help='Tell binman to allow missing blobs and generate fake ones as needed'),
+    parser.add_option(
+          '--no-allow-missing', action='store_true', default=False,
+          help='Disable telling binman to allow missing blobs'),
     parser.add_option('-n', '--dry-run', action='store_true', dest='dry_run',
           default=False, help="Do a dry run (describe actions, but do nothing)")
     parser.add_option('-N', '--no-subdirs', action='store_true', dest='no_subdirs',
diff --git a/tools/buildman/control.py b/tools/buildman/control.py
index 0c75466..87e7d0e 100644
--- a/tools/buildman/control.py
+++ b/tools/buildman/control.py
@@ -111,6 +111,23 @@
     print(tc.GetEnvArgs(toolchain.VAR_CROSS_COMPILE))
     return None
 
+def get_allow_missing(opt_allow, opt_no_allow, num_selected, has_branch):
+    allow_missing = False
+    am_setting = bsettings.GetGlobalItemValue('allow-missing')
+    if am_setting:
+        if am_setting == 'always':
+            allow_missing = True
+        if 'multiple' in am_setting and num_selected > 1:
+            allow_missing = True
+        if 'branch' in am_setting and has_branch:
+            allow_missing = True
+
+    if opt_allow:
+        allow_missing = True
+    if opt_no_allow:
+        allow_missing = False
+    return allow_missing
+
 def DoBuildman(options, args, toolchains=None, make_func=None, brds=None,
                clean_dir=False, test_thread_exceptions=False):
     """The main control code for buildman
@@ -136,8 +153,8 @@
 
     if options.full_help:
         tools.print_full_help(
-            os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), 'README')
-        )
+            os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
+                         'README.rst'))
         return 0
 
     gitutil.setup()
@@ -305,6 +322,10 @@
     if not gnu_make:
         sys.exit('GNU Make not found')
 
+    allow_missing = get_allow_missing(options.allow_missing,
+                                      options.no_allow_missing, len(selected),
+                                      options.branch)
+
     # Create a new builder with the selected options.
     output_dir = options.output_dir
     if options.branch:
@@ -329,7 +350,8 @@
             warnings_as_errors=options.warnings_as_errors,
             work_in_output=options.work_in_output,
             test_thread_exceptions=test_thread_exceptions,
-            adjust_cfg=adjust_cfg)
+            adjust_cfg=adjust_cfg,
+            allow_missing=allow_missing)
     builder.force_config_on_failure = not options.quick
     if make_func:
         builder.do_make = make_func
diff --git a/tools/buildman/func_test.py b/tools/buildman/func_test.py
index f12e996..559e4ed 100644
--- a/tools/buildman/func_test.py
+++ b/tools/buildman/func_test.py
@@ -22,6 +22,7 @@
 
 settings_data = '''
 # Buildman settings file
+[global]
 
 [toolchain]
 
@@ -205,13 +206,16 @@
 
         self._test_branch = TEST_BRANCH
 
+        # Set to True to report missing blobs
+        self._missing = False
+
         # Avoid sending any output and clear all terminal output
         terminal.set_print_test_mode()
         terminal.get_print_test_lines()
 
     def tearDown(self):
         shutil.rmtree(self._base_dir)
-        #shutil.rmtree(self._output_dir)
+        shutil.rmtree(self._output_dir)
 
     def setupToolchains(self):
         self._toolchains = toolchain.Toolchains()
@@ -249,7 +253,7 @@
     def testFullHelp(self):
         command.test_result = None
         result = self._RunBuildman('-H')
-        help_file = os.path.join(self._buildman_dir, 'README')
+        help_file = os.path.join(self._buildman_dir, 'README.rst')
         # Remove possible extraneous strings
         extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
         gothelp = result.stdout.replace(extra, '')
@@ -260,7 +264,7 @@
     def testHelp(self):
         command.test_result = None
         result = self._RunBuildman('-h')
-        help_file = os.path.join(self._buildman_dir, 'README')
+        help_file = os.path.join(self._buildman_dir, 'README.rst')
         self.assertTrue(len(result.stdout) > 1000)
         self.assertEqual(0, len(result.stderr))
         self.assertEqual(0, result.return_code)
@@ -424,10 +428,21 @@
                     out_dir = arg[2:]
             fname = os.path.join(cwd or '', out_dir, 'u-boot')
             tools.write_file(fname, b'U-Boot')
-            if type(commit) is not str:
+
+            # Handle missing blobs
+            if self._missing:
+                if 'BINMAN_ALLOW_MISSING=1' in args:
+                    stderr = '''+Image 'main-section' is missing external blobs and is non-functional: intel-descriptor intel-ifwi intel-fsp-m intel-fsp-s intel-vbt
+Image 'main-section' has faked external blobs and is non-functional: descriptor.bin fsp_m.bin fsp_s.bin vbt.bin
+
+Some images are invalid'''
+                else:
+                    stderr = "binman: Filename 'fsp.bin' not found in input path"
+            elif type(commit) is not str:
                 stderr = self._error.get((brd.target, commit.sequence))
+
             if stderr:
-                return command.CommandResult(return_code=1, stderr=stderr)
+                return command.CommandResult(return_code=2, stderr=stderr)
             return command.CommandResult(return_code=0)
 
         # Not handled, so abort
@@ -621,3 +636,90 @@
         self.assertIn(
             'Thread exception (use -T0 to run without threads): test exception',
             stdout.getvalue())
+
+    def testBlobs(self):
+        """Test handling of missing blobs"""
+        self._missing = True
+
+        board0_dir = os.path.join(self._output_dir, 'current', 'board0')
+        errfile = os.path.join(board0_dir, 'err')
+        logfile = os.path.join(board0_dir, 'log')
+
+        # We expect failure when there are missing blobs
+        result = self._RunControl('board0', '-o', self._output_dir)
+        self.assertEqual(100, result)
+        self.assertTrue(os.path.exists(os.path.join(board0_dir, 'done')))
+        self.assertTrue(os.path.exists(errfile))
+        self.assertIn(b"Filename 'fsp.bin' not found in input path",
+                      tools.read_file(errfile))
+
+    def testBlobsAllowMissing(self):
+        """Allow missing blobs - still failure but a different exit code"""
+        self._missing = True
+        result = self._RunControl('board0', '-o', self._output_dir, '-M',
+                                  clean_dir=True)
+        self.assertEqual(101, result)
+        board0_dir = os.path.join(self._output_dir, 'current', 'board0')
+        errfile = os.path.join(board0_dir, 'err')
+        self.assertTrue(os.path.exists(errfile))
+        self.assertIn(b'Some images are invalid', tools.read_file(errfile))
+
+    def testBlobsWarning(self):
+        """Allow missing blobs and ignore warnings"""
+        self._missing = True
+        result = self._RunControl('board0', '-o', self._output_dir, '-MW')
+        self.assertEqual(0, result)
+        board0_dir = os.path.join(self._output_dir, 'current', 'board0')
+        errfile = os.path.join(board0_dir, 'err')
+        self.assertIn(b'Some images are invalid', tools.read_file(errfile))
+
+    def testBlobSettings(self):
+        """Test with no settings"""
+        self.assertEqual(False,
+                         control.get_allow_missing(False, False, 1, False))
+        self.assertEqual(True,
+                         control.get_allow_missing(True, False, 1, False))
+        self.assertEqual(False,
+                         control.get_allow_missing(True, True, 1, False))
+
+    def testBlobSettingsAlways(self):
+        """Test the 'always' policy"""
+        bsettings.SetItem('global', 'allow-missing', 'always')
+        self.assertEqual(True,
+                         control.get_allow_missing(False, False, 1, False))
+        self.assertEqual(False,
+                         control.get_allow_missing(False, True, 1, False))
+
+    def testBlobSettingsBranch(self):
+        """Test the 'branch' policy"""
+        bsettings.SetItem('global', 'allow-missing', 'branch')
+        self.assertEqual(False,
+                         control.get_allow_missing(False, False, 1, False))
+        self.assertEqual(True,
+                         control.get_allow_missing(False, False, 1, True))
+        self.assertEqual(False,
+                         control.get_allow_missing(False, True, 1, True))
+
+    def testBlobSettingsMultiple(self):
+        """Test the 'multiple' policy"""
+        bsettings.SetItem('global', 'allow-missing', 'multiple')
+        self.assertEqual(False,
+                         control.get_allow_missing(False, False, 1, False))
+        self.assertEqual(True,
+                         control.get_allow_missing(False, False, 2, False))
+        self.assertEqual(False,
+                         control.get_allow_missing(False, True, 2, False))
+
+    def testBlobSettingsBranchMultiple(self):
+        """Test the 'branch multiple' policy"""
+        bsettings.SetItem('global', 'allow-missing', 'branch multiple')
+        self.assertEqual(False,
+                         control.get_allow_missing(False, False, 1, False))
+        self.assertEqual(True,
+                         control.get_allow_missing(False, False, 1, True))
+        self.assertEqual(True,
+                         control.get_allow_missing(False, False, 2, False))
+        self.assertEqual(True,
+                         control.get_allow_missing(False, False, 2, True))
+        self.assertEqual(False,
+                         control.get_allow_missing(False, True, 2, True))
diff --git a/tools/mkenvimage.c b/tools/mkenvimage.c
index b05f834..a8eebab 100644
--- a/tools/mkenvimage.c
+++ b/tools/mkenvimage.c
@@ -14,7 +14,6 @@
 #include <stdlib.h>
 #include <stdint.h>
 #include <string.h>
-#include <u-boot/crc.h>
 #include <unistd.h>
 #include <libgen.h>
 #include <sys/types.h>