Merge https://source.denx.de/u-boot/custodians/u-boot-usb

This is a patchset which makes away with the .bind() controller indexing
workaround which was broken since before v2021.04, and then adds PHY
support and MX8M support on top of that. Better add it into the release
early to get as much testing as possible done, because this really does
a lot of changes to the ehci-mx6 driver.
diff --git a/arch/arm/dts/k3-j721e-main.dtsi b/arch/arm/dts/k3-j721e-main.dtsi
index e47b6c0..07b4896 100644
--- a/arch/arm/dts/k3-j721e-main.dtsi
+++ b/arch/arm/dts/k3-j721e-main.dtsi
@@ -1078,13 +1078,12 @@
 		assigned-clocks = <&k3_clks 91 1>;
 		assigned-clock-parents = <&k3_clks 91 2>;
 		bus-width = <8>;
-		mmc-hs400-1_8v;
+		mmc-hs200-1_8v;
 		mmc-ddr-1_8v;
 		ti,otap-del-sel-legacy = <0xf>;
 		ti,otap-del-sel-mmc-hs = <0xf>;
 		ti,otap-del-sel-ddr52 = <0x5>;
 		ti,otap-del-sel-hs200 = <0x6>;
-		ti,otap-del-sel-hs400 = <0x0>;
 		ti,itap-del-sel-legacy = <0x10>;
 		ti,itap-del-sel-mmc-hs = <0xa>;
 		ti,itap-del-sel-ddr52 = <0x3>;
@@ -1102,14 +1101,20 @@
 		assigned-clocks = <&k3_clks 92 0>;
 		assigned-clock-parents = <&k3_clks 92 1>;
 		ti,otap-del-sel-legacy = <0x0>;
-		ti,otap-del-sel-sd-hs = <0xf>;
+		ti,otap-del-sel-sd-hs = <0x0>;
 		ti,otap-del-sel-sdr12 = <0xf>;
 		ti,otap-del-sel-sdr25 = <0xf>;
 		ti,otap-del-sel-sdr50 = <0xc>;
 		ti,otap-del-sel-ddr50 = <0xc>;
+		ti,itap-del-sel-legacy = <0x0>;
+		ti,itap-del-sel-sd-hs = <0x0>;
+		ti,itap-del-sel-sdr12 = <0x0>;
+		ti,itap-del-sel-sdr25 = <0x0>;
+		ti,itap-del-sel-ddr50 = <0x2>;
 		ti,trm-icp = <0x8>;
 		ti,clkbuf-sel = <0x7>;
 		dma-coherent;
+		sdhci-caps-mask = <0x2 0x0>;
 	};
 
 	main_sdhci2: sdhci@4f98000 {
@@ -1122,14 +1127,20 @@
 		assigned-clocks = <&k3_clks 93 0>;
 		assigned-clock-parents = <&k3_clks 93 1>;
 		ti,otap-del-sel-legacy = <0x0>;
-		ti,otap-del-sel-sd-hs = <0xf>;
+		ti,otap-del-sel-sd-hs = <0x0>;
 		ti,otap-del-sel-sdr12 = <0xf>;
 		ti,otap-del-sel-sdr25 = <0xf>;
 		ti,otap-del-sel-sdr50 = <0xc>;
 		ti,otap-del-sel-ddr50 = <0xc>;
+		ti,itap-del-sel-legacy = <0x0>;
+		ti,itap-del-sel-sd-hs = <0x0>;
+		ti,itap-del-sel-sdr12 = <0x0>;
+		ti,itap-del-sel-sdr25 = <0x0>;
+		ti,itap-del-sel-ddr50 = <0x2>;
 		ti,trm-icp = <0x8>;
 		ti,clkbuf-sel = <0x7>;
 		dma-coherent;
+		sdhci-caps-mask = <0x2 0x0>;
 	};
 
 	usbss0: cdns-usb@4104000 {
diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi
index cd096bf..a4227a3 100644
--- a/arch/arm/dts/sunxi-u-boot.dtsi
+++ b/arch/arm/dts/sunxi-u-boot.dtsi
@@ -42,7 +42,7 @@
 					os = "u-boot";
 					arch = "arm64";
 					compression = "none";
-					load = <0x4a000000>;
+					load = <CONFIG_SYS_TEXT_BASE>;
 
 					u-boot-nodtb {
 					};
@@ -86,7 +86,7 @@
 			};
 
 			configurations {
-				default = "config-1";
+				default = "@config-DEFAULT-SEQ";
 
 				@config-SEQ {
 					description = "NAME";
diff --git a/arch/arm/include/asm/arch-sunxi/dram_sunxi_dw.h b/arch/arm/include/asm/arch-sunxi/dram_sunxi_dw.h
index a5a7ebd..e843c14 100644
--- a/arch/arm/include/asm/arch-sunxi/dram_sunxi_dw.h
+++ b/arch/arm/include/asm/arch-sunxi/dram_sunxi_dw.h
@@ -215,12 +215,17 @@
 #define NR_OF_BYTE_LANES	(32 / BITS_PER_BYTE)
 /* The eight data lines (DQn) plus DM, DQS and DQSN */
 #define LINES_PER_BYTE_LANE	(BITS_PER_BYTE + 3)
-struct dram_para {
+
+struct rank_para {
 	u16 page_size;
-	u8 bus_full_width;
-	u8 dual_rank;
 	u8 row_bits;
 	u8 bank_bits;
+};
+
+struct dram_para {
+	u8 dual_rank;
+	u8 bus_full_width;
+	struct rank_para ranks[2];
 	const u8 dx_read_delays[NR_OF_BYTE_LANES][LINES_PER_BYTE_LANE];
 	const u8 dx_write_delays[NR_OF_BYTE_LANES][LINES_PER_BYTE_LANE];
 	const u8 ac_delays[31];
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index a29d115..8e9012d 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -973,6 +973,7 @@
 	depends on SUNXI_DE2
 	select DM_VIDEO
 	select DISPLAY
+	select VIDEO_DW_HDMI
 	imply VIDEO_DT_SIMPLEFB
 	default y
 	---help---
diff --git a/arch/arm/mach-sunxi/dram_sunxi_dw.c b/arch/arm/mach-sunxi/dram_sunxi_dw.c
index d060001..9107b11 100644
--- a/arch/arm/mach-sunxi/dram_sunxi_dw.c
+++ b/arch/arm/mach-sunxi/dram_sunxi_dw.c
@@ -399,18 +399,24 @@
 #else
 #error Unsupported DRAM type!
 #endif
-	       (para->bank_bits == 3 ? MCTL_CR_EIGHT_BANKS : MCTL_CR_FOUR_BANKS) |
+	       (para->ranks[0].bank_bits == 3 ? MCTL_CR_EIGHT_BANKS : MCTL_CR_FOUR_BANKS) |
 	       MCTL_CR_BUS_FULL_WIDTH(para->bus_full_width) |
 	       (para->dual_rank ? MCTL_CR_DUAL_RANK : MCTL_CR_SINGLE_RANK) |
-	       MCTL_CR_PAGE_SIZE(para->page_size) |
-	       MCTL_CR_ROW_BITS(para->row_bits), &mctl_com->cr);
+	       MCTL_CR_PAGE_SIZE(para->ranks[0].page_size) |
+	       MCTL_CR_ROW_BITS(para->ranks[0].row_bits), &mctl_com->cr);
 
-	if (socid == SOCID_R40) {
-		if (para->dual_rank)
-			panic("Dual rank memory not supported\n");
+	if (para->dual_rank && (socid == SOCID_A64 || socid == SOCID_R40)) {
+		writel((para->ranks[1].bank_bits == 3 ? MCTL_CR_EIGHT_BANKS : MCTL_CR_FOUR_BANKS) |
+		       MCTL_CR_BUS_FULL_WIDTH(para->bus_full_width) |
+		       MCTL_CR_DUAL_RANK |
+		       MCTL_CR_PAGE_SIZE(para->ranks[1].page_size) |
+		       MCTL_CR_ROW_BITS(para->ranks[1].row_bits), &mctl_com->cr_r1);
+	}
 
+	if (socid == SOCID_R40) {
 		/* Mux pin to A15 address line for single rank memory. */
-		setbits_le32(&mctl_com->cr_r1, MCTL_CR_R1_MUX_A15);
+		if (!para->dual_rank)
+			setbits_le32(&mctl_com->cr_r1, MCTL_CR_R1_MUX_A15);
 	}
 }
 
@@ -646,35 +652,105 @@
 	return 0;
 }
 
-static void mctl_auto_detect_dram_size(uint16_t socid, struct dram_para *para)
+/*
+ * Test if memory at offset offset matches memory at a certain base
+ */
+static bool mctl_mem_matches_base(u32 offset, ulong base)
+{
+	/* Try to write different values to RAM at two addresses */
+	writel(0, base);
+	writel(0xaa55aa55, base + offset);
+	dsb();
+	/* Check if the same value is actually observed when reading back */
+	return readl(base) ==
+	       readl(base + offset);
+}
+
+static void mctl_auto_detect_dram_size_rank(uint16_t socid, struct dram_para *para, ulong base, struct rank_para *rank)
 {
 	/* detect row address bits */
-	para->page_size = 512;
-	para->row_bits = 16;
-	para->bank_bits = 2;
+	rank->page_size = 512;
+	rank->row_bits = 16;
+	rank->bank_bits = 2;
 	mctl_set_cr(socid, para);
 
-	for (para->row_bits = 11; para->row_bits < 16; para->row_bits++)
-		if (mctl_mem_matches((1 << (para->row_bits + para->bank_bits)) * para->page_size))
+	for (rank->row_bits = 11; rank->row_bits < 16; rank->row_bits++)
+		if (mctl_mem_matches_base((1 << (rank->row_bits + rank->bank_bits)) * rank->page_size, base))
 			break;
 
 	/* detect bank address bits */
-	para->bank_bits = 3;
+	rank->bank_bits = 3;
 	mctl_set_cr(socid, para);
 
-	for (para->bank_bits = 2; para->bank_bits < 3; para->bank_bits++)
-		if (mctl_mem_matches((1 << para->bank_bits) * para->page_size))
+	for (rank->bank_bits = 2; rank->bank_bits < 3; rank->bank_bits++)
+		if (mctl_mem_matches_base((1 << rank->bank_bits) * rank->page_size, base))
 			break;
 
 	/* detect page size */
-	para->page_size = 8192;
+	rank->page_size = 8192;
 	mctl_set_cr(socid, para);
 
-	for (para->page_size = 512; para->page_size < 8192; para->page_size *= 2)
-		if (mctl_mem_matches(para->page_size))
+	for (rank->page_size = 512; rank->page_size < 8192; rank->page_size *= 2)
+		if (mctl_mem_matches_base(rank->page_size, base))
 			break;
 }
 
+static unsigned long mctl_calc_rank_size(struct rank_para *rank)
+{
+	return (1UL << (rank->row_bits + rank->bank_bits)) * rank->page_size;
+}
+
+/*
+ * Because we cannot do mctl_phy_init(PIR_QSGATE) on R40 now (which leads
+ * to failure), it's needed to detect the rank count of R40 in another way.
+ *
+ * The code here is modelled after time_out_detect() in BSP, which tries to
+ * access the memory and check for error code.
+ *
+ * TODO: auto detect half DQ width here
+ */
+static void mctl_r40_detect_rank_count(struct dram_para *para)
+{
+	ulong rank1_base = (ulong) CONFIG_SYS_SDRAM_BASE +
+			   mctl_calc_rank_size(&para->ranks[0]);
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+	/* Enable read time out */
+	setbits_le32(&mctl_ctl->pgcr[0], 0x1 << 25);
+
+	(void) readl((void *) rank1_base);
+	udelay(10);
+
+	if (readl(&mctl_ctl->pgsr[0]) & (0x1 << 13)) {
+		clrsetbits_le32(&mctl_ctl->dtcr, 0xf << 24, 0x1 << 24);
+		para->dual_rank = 0;
+	}
+
+	/* Reset PHY FIFO to clear it */
+	clrbits_le32(&mctl_ctl->pgcr[0], 0x1 << 26);
+	udelay(100);
+	setbits_le32(&mctl_ctl->pgcr[0], 0x1 << 26);
+
+	/* Clear error status */
+	setbits_le32(&mctl_ctl->pgcr[0], 0x1 << 24);
+
+	/* Clear time out flag */
+	clrbits_le32(&mctl_ctl->pgsr[0], 0x1 << 13);
+
+	/* Disable read time out */
+	clrbits_le32(&mctl_ctl->pgcr[0], 0x1 << 25);
+}
+
+static void mctl_auto_detect_dram_size(uint16_t socid, struct dram_para *para)
+{
+	mctl_auto_detect_dram_size_rank(socid, para, (ulong)CONFIG_SYS_SDRAM_BASE, &para->ranks[0]);
+
+	if ((socid == SOCID_A64 || socid == SOCID_R40) && para->dual_rank) {
+		mctl_auto_detect_dram_size_rank(socid, para, (ulong)CONFIG_SYS_SDRAM_BASE + mctl_calc_rank_size(&para->ranks[0]), &para->ranks[1]);
+	}
+}
+
 /*
  * The actual values used here are taken from Allwinner provided boot0
  * binaries, though they are probably board specific, so would likely benefit
@@ -769,12 +845,23 @@
 	struct sunxi_mctl_ctl_reg * const mctl_ctl =
 			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
 
+	unsigned long size;
+
 	struct dram_para para = {
 		.dual_rank = 1,
 		.bus_full_width = 1,
-		.row_bits = 15,
-		.bank_bits = 3,
-		.page_size = 4096,
+		.ranks = {
+			{
+				.row_bits = 15,
+				.bank_bits = 3,
+				.page_size = 4096,
+			},
+			{
+				.row_bits = 15,
+				.bank_bits = 3,
+				.page_size = 4096,
+			}
+		},
 
 #if defined(CONFIG_MACH_SUN8I_H3)
 		.dx_read_delays  = SUN8I_H3_DX_READ_DELAYS,
@@ -807,8 +894,6 @@
 	uint16_t socid = SOCID_H3;
 #elif defined(CONFIG_MACH_SUN8I_R40)
 	uint16_t socid = SOCID_R40;
-	/* Currently we cannot support R40 with dual rank memory */
-	para.dual_rank = 0;
 #elif defined(CONFIG_MACH_SUN8I_V3S)
 	uint16_t socid = SOCID_V3S;
 #elif defined(CONFIG_MACH_SUN50I)
@@ -843,9 +928,21 @@
 	setbits_le32(&mctl_com->cccr, 1 << 31);
 	udelay(10);
 
+	if (socid == SOCID_R40) {
+		mctl_r40_detect_rank_count(&para);
+		mctl_set_cr(SOCID_R40, &para);
+	}
+
 	mctl_auto_detect_dram_size(socid, &para);
 	mctl_set_cr(socid, &para);
 
+	size = mctl_calc_rank_size(&para.ranks[0]);
+	if (socid == SOCID_A64 || socid == SOCID_R40) {
+		if (para.dual_rank)
+			size += mctl_calc_rank_size(&para.ranks[1]);
+	} else if (para.dual_rank) {
+		size *= 2;
+	}
+
-	return (1UL << (para.row_bits + para.bank_bits)) * para.page_size *
-	       (para.dual_rank ? 2 : 1);
+	return size;
 }
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index dde4efb..48240aa 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -634,6 +634,9 @@
 		reg = <0 1>;
 		compatible = "sandbox,i2c";
 		clock-frequency = <100000>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinmux_i2c0_pins>;
+
 		eeprom@2c {
 			reg = <0x2c>;
 			compatible = "i2c-eeprom";
@@ -714,6 +717,8 @@
 	lcd {
 		u-boot,dm-pre-reloc;
 		compatible = "sandbox,lcd-sdl";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinmux_lcd_pins>;
 		xres = <1366>;
 		yres = <768>;
 	};
@@ -964,6 +969,8 @@
 	pwm: pwm {
 		compatible = "sandbox,pwm";
 		#pwm-cells = <2>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinmux_pwm_pins>;
 	};
 
 	pwm2 {
@@ -1035,6 +1042,9 @@
 		reg = <0 1>;
 		compatible = "sandbox,spi";
 		cs-gpios = <0>, <0>, <&gpio_a 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinmux_spi0_pins>;
+
 		spi.bin@0 {
 			reg = <0>;
 			compatible = "spansion,m25p16", "jedec,spi-nor";
@@ -1124,6 +1134,8 @@
 	uart0: serial {
 		compatible = "sandbox,serial";
 		u-boot,dm-pre-reloc;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinmux_uart0_pins>;
 	};
 
 	usb_0: usb@0 {
@@ -1392,6 +1404,66 @@
 		};
 	};
 
+	pinctrl-single-no-width {
+		compatible = "pinctrl-single";
+		reg = <0x0000 0x238>;
+		#pinctrl-cells = <1>;
+		pinctrl-single,function-mask = <0x7f>;
+	};
+
+	pinctrl-single-pins {
+		compatible = "pinctrl-single";
+		reg = <0x0000 0x238>;
+		#pinctrl-cells = <1>;
+		pinctrl-single,register-width = <32>;
+		pinctrl-single,function-mask = <0x7f>;
+
+		pinmux_pwm_pins: pinmux_pwm_pins {
+			pinctrl-single,pins = < 0x48 0x06 >;
+		};
+
+		pinmux_spi0_pins: pinmux_spi0_pins {
+			pinctrl-single,pins = <
+				0x190 0x0c
+				0x194 0x0c
+				0x198 0x23
+				0x19c 0x0c
+			>;
+		};
+
+		pinmux_uart0_pins: pinmux_uart0_pins {
+			pinctrl-single,pins = <
+				0x70 0x30
+				0x74 0x00
+			>;
+		};
+	};
+
+	pinctrl-single-bits {
+		compatible = "pinctrl-single";
+		reg = <0x0000 0x50>;
+		#pinctrl-cells = <2>;
+		pinctrl-single,bit-per-mux;
+		pinctrl-single,register-width = <32>;
+		pinctrl-single,function-mask = <0xf>;
+
+		pinmux_i2c0_pins: pinmux_i2c0_pins {
+			pinctrl-single,bits = <
+				0x10 0x00002200 0x0000ff00
+			>;
+		};
+
+		pinmux_lcd_pins: pinmux_lcd_pins {
+			pinctrl-single,bits = <
+				0x40 0x22222200 0xffffff00
+				0x44 0x22222222 0xffffffff
+				0x48 0x00000022 0x000000ff
+				0x48 0x02000000 0x0f000000
+				0x4c 0x02000022 0x0f0000ff
+			>;
+		};
+	};
+
 	hwspinlock@0 {
 		compatible = "sandbox,hwspinlock";
 	};
diff --git a/configs/omap35_logic_somlv_defconfig b/configs/omap35_logic_somlv_defconfig
index e6655c7..a8eab40 100644
--- a/configs/omap35_logic_somlv_defconfig
+++ b/configs/omap35_logic_somlv_defconfig
@@ -4,6 +4,7 @@
 CONFIG_ARCH_OMAP2PLUS=y
 CONFIG_SYS_TEXT_BASE=0x80100000
 CONFIG_TI_COMMON_CMD_OPTIONS=y
+# CONFIG_SPL_GPIO_SUPPORT is not set
 CONFIG_SYS_MALLOC_F_LEN=0x4000
 CONFIG_NR_DRAM_BANKS=2
 CONFIG_SPL_TEXT_BASE=0x40200000
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index c9318d4..6038dcb 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -198,6 +198,7 @@
 CONFIG_PINCTRL=y
 CONFIG_PINCONF=y
 CONFIG_PINCTRL_SANDBOX=y
+CONFIG_PINCTRL_SINGLE=y
 CONFIG_POWER_DOMAIN=y
 CONFIG_SANDBOX_POWER_DOMAIN=y
 CONFIG_DM_PMIC=y
diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig
index b68f938..689b3a9 100644
--- a/configs/sandbox_flattree_defconfig
+++ b/configs/sandbox_flattree_defconfig
@@ -142,6 +142,7 @@
 CONFIG_PINCTRL=y
 CONFIG_PINCONF=y
 CONFIG_PINCTRL_SANDBOX=y
+CONFIG_PINCTRL_SINGLE=y
 CONFIG_POWER_DOMAIN=y
 CONFIG_SANDBOX_POWER_DOMAIN=y
 CONFIG_DM_PMIC=y
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index 0dfc059..4f9282a 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -6,6 +6,8 @@
 
 obj-$(CONFIG_CLK_SUNXI) += clk_sunxi.o
 
+obj-$(CONFIG_CLK_SUNXI) += clk_sun6i_rtc.o
+
 obj-$(CONFIG_CLK_SUN4I_A10) += clk_a10.o
 obj-$(CONFIG_CLK_SUN5I_A10S) += clk_a10s.o
 obj-$(CONFIG_CLK_SUN6I_A31) += clk_a31.o
diff --git a/drivers/clk/sunxi/clk_h6.c b/drivers/clk/sunxi/clk_h6.c
index ac8656f..df93d96 100644
--- a/drivers/clk/sunxi/clk_h6.c
+++ b/drivers/clk/sunxi/clk_h6.c
@@ -43,6 +43,7 @@
 	[CLK_BUS_OHCI0]		= GATE(0xa8c, BIT(0)),
 	[CLK_BUS_OHCI3]		= GATE(0xa8c, BIT(3)),
 	[CLK_BUS_EHCI0]		= GATE(0xa8c, BIT(4)),
+	[CLK_BUS_XHCI]		= GATE(0xa8c, BIT(5)),
 	[CLK_BUS_EHCI3]		= GATE(0xa8c, BIT(7)),
 	[CLK_BUS_OTG]		= GATE(0xa8c, BIT(8)),
 };
@@ -71,6 +72,7 @@
 	[RST_BUS_OHCI0]		= RESET(0xa8c, BIT(16)),
 	[RST_BUS_OHCI3]		= RESET(0xa8c, BIT(19)),
 	[RST_BUS_EHCI0]		= RESET(0xa8c, BIT(20)),
+	[RST_BUS_XHCI]		= RESET(0xa8c, BIT(21)),
 	[RST_BUS_EHCI3]		= RESET(0xa8c, BIT(23)),
 	[RST_BUS_OTG]		= RESET(0xa8c, BIT(24)),
 };
diff --git a/drivers/clk/sunxi/clk_sun6i_rtc.c b/drivers/clk/sunxi/clk_sun6i_rtc.c
new file mode 100644
index 0000000..0c280d2
--- /dev/null
+++ b/drivers/clk/sunxi/clk_sun6i_rtc.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (C) 2018 Amarula Solutions.
+ * Copyright (C) 2020 Samuel Holland <samuel@sholland.org>
+ */
+
+#include <clk-uclass.h>
+#include <dm.h>
+
+static int clk_sun6i_rtc_enable(struct clk *clk)
+{
+	return 0;
+}
+
+static const struct clk_ops clk_sun6i_rtc_ops = {
+	.enable = clk_sun6i_rtc_enable,
+};
+
+static const struct udevice_id sun6i_rtc_ids[] = {
+	{ .compatible = "allwinner,sun6i-a31-rtc" },
+	{ .compatible = "allwinner,sun8i-a23-rtc" },
+	{ .compatible = "allwinner,sun8i-h3-rtc" },
+	{ .compatible = "allwinner,sun8i-r40-rtc" },
+	{ .compatible = "allwinner,sun8i-v3-rtc" },
+	{ .compatible = "allwinner,sun50i-h5-rtc" },
+	{ .compatible = "allwinner,sun50i-h6-rtc" },
+	{ }
+};
+
+U_BOOT_DRIVER(clk_sun6i_rtc) = {
+	.name		= "clk_sun6i_rtc",
+	.id		= UCLASS_CLK,
+	.of_match	= sun6i_rtc_ids,
+	.ops		= &clk_sun6i_rtc_ops,
+};
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
index 20c3c82..48bdd0f 100644
--- a/drivers/pinctrl/pinctrl-single.c
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -1,34 +1,262 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (C) EETS GmbH, 2017, Felix Brack <f.brack@eets.ch>
+ * Copyright (C) 2021 Dario Binacchi <dariobin@libero.it>
  */
 
 #include <common.h>
 #include <dm.h>
 #include <dm/device_compat.h>
+#include <dm/devres.h>
 #include <dm/pinctrl.h>
 #include <linux/libfdt.h>
+#include <linux/list.h>
 #include <asm/io.h>
+#include <sort.h>
 
+/**
+ * struct single_pdata - platform data
+ * @base: first configuration register
+ * @offset: index of last configuration register
+ * @mask: configuration-value mask bits
+ * @width: configuration register bit width
+ * @bits_per_mux: true if one register controls more than one pin
+ */
 struct single_pdata {
-	fdt_addr_t base;	/* first configuration register */
-	int offset;		/* index of last configuration register */
-	u32 mask;		/* configuration-value mask bits */
-	int width;		/* configuration register bit width */
+	fdt_addr_t base;
+	int offset;
+	u32 mask;
+	u32 width;
 	bool bits_per_mux;
 };
 
+/**
+ * struct single_func - pinctrl function
+ * @node: list node
+ * @name: pinctrl function name
+ * @npins: number of entries in pins array
+ * @pins: pins array
+ */
+struct single_func {
+	struct list_head node;
+	const char *name;
+	unsigned int npins;
+	unsigned int *pins;
+};
+
+/**
+ * struct single_priv - private data
+ * @bits_per_pin: number of bits per pin
+ * @npins: number of selectable pins
+ * @pin_name: temporary buffer to store the pin name
+ */
+struct single_priv {
+#if (IS_ENABLED(CONFIG_SANDBOX))
+	u32 *sandbox_regs;
+#endif
+	unsigned int bits_per_pin;
+	unsigned int npins;
+	char pin_name[PINNAME_SIZE];
+	struct list_head functions;
+};
+
+/**
+ * struct single_fdt_pin_cfg - pin configuration
+ *
+ * This structure is used for the pin configuration parameters in case
+ * the register controls only one pin.
+ *
+ * @reg: configuration register offset
+ * @val: configuration register value
+ */
 struct single_fdt_pin_cfg {
-	fdt32_t reg;		/* configuration register offset */
-	fdt32_t val;		/* configuration register value */
+	fdt32_t reg;
+	fdt32_t val;
 };
 
+/**
+ * struct single_fdt_bits_cfg - pin configuration
+ *
+ * This structure is used for the pin configuration parameters in case
+ * the register controls more than one pin.
+ *
+ * @reg: configuration register offset
+ * @val: configuration register value
+ * @mask: configuration register mask
+ */
 struct single_fdt_bits_cfg {
-	fdt32_t reg;		/* configuration register offset */
-	fdt32_t val;		/* configuration register value */
-	fdt32_t mask;		/* configuration register mask */
+	fdt32_t reg;
+	fdt32_t val;
+	fdt32_t mask;
 };
 
+#if (!IS_ENABLED(CONFIG_SANDBOX))
+
+static unsigned int single_read(struct udevice *dev, fdt_addr_t reg)
+{
+	struct single_pdata *pdata = dev_get_plat(dev);
+
+	switch (pdata->width) {
+	case 8:
+		return readb(reg);
+	case 16:
+		return readw(reg);
+	default: /* 32 bits */
+		return readl(reg);
+	}
+
+	return readb(reg);
+}
+
+static void single_write(struct udevice *dev, unsigned int val, fdt_addr_t reg)
+{
+	struct single_pdata *pdata = dev_get_plat(dev);
+
+	switch (pdata->width) {
+	case 8:
+		writeb(val, reg);
+		break;
+	case 16:
+		writew(val, reg);
+		break;
+	default: /* 32 bits */
+		writel(val, reg);
+	}
+}
+
+#else /* CONFIG_SANDBOX  */
+
+static unsigned int single_read(struct udevice *dev, fdt_addr_t reg)
+{
+	struct single_priv *priv = dev_get_priv(dev);
+
+	return priv->sandbox_regs[reg];
+}
+
+static void single_write(struct udevice *dev, unsigned int val, fdt_addr_t reg)
+{
+	struct single_priv *priv = dev_get_priv(dev);
+
+	priv->sandbox_regs[reg] = val;
+}
+
+#endif /* CONFIG_SANDBOX  */
+
+/**
+ * single_get_pin_by_offset() - get a pin based on the register offset
+ * @dev: single driver instance
+ * @offset: register offset from the base
+ */
+static int single_get_pin_by_offset(struct udevice *dev, unsigned int offset)
+{
+	struct single_pdata *pdata = dev_get_plat(dev);
+	struct single_priv *priv = dev_get_priv(dev);
+
+	if (offset > pdata->offset) {
+		dev_err(dev, "mux offset out of range: 0x%x (0x%x)\n",
+			offset, pdata->offset);
+		return -EINVAL;
+	}
+
+	if (pdata->bits_per_mux)
+		return (offset * BITS_PER_BYTE) / priv->bits_per_pin;
+
+	return offset / (pdata->width / BITS_PER_BYTE);
+}
+
+static int single_get_offset_by_pin(struct udevice *dev, unsigned int pin)
+{
+	struct single_pdata *pdata = dev_get_plat(dev);
+	struct single_priv *priv = dev_get_priv(dev);
+	unsigned int mux_bytes;
+
+	if (pin >= priv->npins)
+		return -EINVAL;
+
+	mux_bytes = pdata->width / BITS_PER_BYTE;
+	if (pdata->bits_per_mux) {
+		int byte_num;
+
+		byte_num = (priv->bits_per_pin * pin) / BITS_PER_BYTE;
+		return (byte_num / mux_bytes) * mux_bytes;
+	}
+
+	return pin * mux_bytes;
+}
+
+static const char *single_get_pin_function(struct udevice *dev,
+					   unsigned int pin)
+{
+	struct single_priv *priv = dev_get_priv(dev);
+	struct single_func *func;
+	int i;
+
+	list_for_each_entry(func, &priv->functions, node) {
+		for (i = 0; i < func->npins; i++) {
+			if (pin == func->pins[i])
+				return func->name;
+
+			if (pin < func->pins[i])
+				break;
+		}
+	}
+
+	return NULL;
+}
+
+static int single_get_pin_muxing(struct udevice *dev, unsigned int pin,
+				 char *buf, int size)
+{
+	struct single_pdata *pdata = dev_get_plat(dev);
+	struct single_priv *priv = dev_get_priv(dev);
+	fdt_addr_t reg;
+	const char *fname;
+	unsigned int val;
+	int offset, pin_shift = 0;
+
+	offset = single_get_offset_by_pin(dev, pin);
+	if (offset < 0)
+		return offset;
+
+	reg = pdata->base + offset;
+	val = single_read(dev, reg);
+
+	if (pdata->bits_per_mux)
+		pin_shift = pin % (pdata->width / priv->bits_per_pin) *
+			priv->bits_per_pin;
+
+	val &= (pdata->mask << pin_shift);
+	fname = single_get_pin_function(dev, pin);
+	snprintf(buf, size, "%pa 0x%08x %s", &reg, val,
+		 fname ? fname : "UNCLAIMED");
+	return 0;
+}
+
+static struct single_func *single_allocate_function(struct udevice *dev,
+						    unsigned int group_pins)
+{
+	struct single_func *func;
+
+	func = devm_kmalloc(dev, sizeof(*func), GFP_KERNEL);
+	if (!func)
+		return ERR_PTR(-ENOMEM);
+
+	func->pins = devm_kmalloc(dev, sizeof(unsigned int) * group_pins,
+				  GFP_KERNEL);
+	if (!func->pins)
+		return ERR_PTR(-ENOMEM);
+
+	return func;
+}
+
+static int single_pin_compare(const void *s1, const void *s2)
+{
+	int pin1 = *(const unsigned int *)s1;
+	int pin2 = *(const unsigned int *)s2;
+
+	return pin1 - pin2;
+}
+
 /**
  * single_configure_pins() - Configure pins based on FDT data
  *
@@ -42,75 +270,129 @@
  * @size: Size of the 'pins' array in bytes.
  *        The number of register/value pairs in the 'pins' array therefore
  *        equals to 'size / sizeof(struct single_fdt_pin_cfg)'.
+ * @fname: Function name.
  */
 static int single_configure_pins(struct udevice *dev,
 				 const struct single_fdt_pin_cfg *pins,
-				 int size)
+				 int size, const char *fname)
 {
 	struct single_pdata *pdata = dev_get_plat(dev);
-	int count = size / sizeof(struct single_fdt_pin_cfg);
-	phys_addr_t n, reg;
-	u32 val;
+	struct single_priv *priv = dev_get_priv(dev);
+	int n, pin, count = size / sizeof(struct single_fdt_pin_cfg);
+	struct single_func *func;
+	phys_addr_t reg;
+	u32 offset, val;
 
+	/* If function mask is null, needn't enable it. */
+	if (!pdata->mask)
+		return 0;
+
+	func = single_allocate_function(dev, count);
+	if (IS_ERR(func))
+		return PTR_ERR(func);
+
+	func->name = fname;
+	func->npins = 0;
 	for (n = 0; n < count; n++, pins++) {
-		reg = fdt32_to_cpu(pins->reg);
-		if ((reg < 0) || (reg > pdata->offset)) {
-			dev_dbg(dev, "  invalid register offset 0x%pa\n", &reg);
+		offset = fdt32_to_cpu(pins->reg);
+		if (offset < 0 || offset > pdata->offset) {
+			dev_err(dev, "  invalid register offset 0x%x\n",
+				offset);
 			continue;
 		}
-		reg += pdata->base;
+
+		reg = pdata->base + offset;
 		val = fdt32_to_cpu(pins->val) & pdata->mask;
-		switch (pdata->width) {
-		case 16:
-			writew((readw(reg) & ~pdata->mask) | val, reg);
-			break;
-		case 32:
-			writel((readl(reg) & ~pdata->mask) | val, reg);
-			break;
-		default:
-			dev_warn(dev, "unsupported register width %i\n",
-				 pdata->width);
+		pin = single_get_pin_by_offset(dev, offset);
+		if (pin < 0) {
+			dev_err(dev, "  failed to get pin by offset %x\n",
+				offset);
 			continue;
 		}
-		dev_dbg(dev, "  reg/val 0x%pa/0x%08x\n", &reg, val);
+
+		single_write(dev, (single_read(dev, reg) & ~pdata->mask) | val,
+			     reg);
+		dev_dbg(dev, "  reg/val %pa/0x%08x\n", &reg, val);
+		func->pins[func->npins] = pin;
+		func->npins++;
 	}
+
+	qsort(func->pins, func->npins, sizeof(func->pins[0]),
+	      single_pin_compare);
+	list_add(&func->node, &priv->functions);
 	return 0;
 }
 
 static int single_configure_bits(struct udevice *dev,
 				 const struct single_fdt_bits_cfg *pins,
-				 int size)
+				 int size, const char *fname)
 {
 	struct single_pdata *pdata = dev_get_plat(dev);
-	int count = size / sizeof(struct single_fdt_bits_cfg);
-	phys_addr_t n, reg;
-	u32 val, mask;
+	struct single_priv *priv = dev_get_priv(dev);
+	int n, pin, count = size / sizeof(struct single_fdt_bits_cfg);
+	int npins_in_reg, pin_num_from_lsb;
+	struct single_func *func;
+	phys_addr_t reg;
+	u32 offset, val, mask, bit_pos, val_pos, mask_pos, submask;
 
+	npins_in_reg = pdata->width / priv->bits_per_pin;
+	func = single_allocate_function(dev, count * npins_in_reg);
+	if (IS_ERR(func))
+		return PTR_ERR(func);
+
+	func->name = fname;
+	func->npins = 0;
 	for (n = 0; n < count; n++, pins++) {
-		reg = fdt32_to_cpu(pins->reg);
-		if ((reg < 0) || (reg > pdata->offset)) {
-			dev_dbg(dev, "  invalid register offset 0x%pa\n", &reg);
+		offset = fdt32_to_cpu(pins->reg);
+		if (offset < 0 || offset > pdata->offset) {
+			dev_dbg(dev, "  invalid register offset 0x%x\n",
+				offset);
+			continue;
+		}
+
+		reg = pdata->base + offset;
+
+		pin = single_get_pin_by_offset(dev, offset);
+		if (pin < 0) {
+			dev_err(dev, "  failed to get pin by offset 0x%pa\n",
+				&reg);
 			continue;
 		}
-		reg += pdata->base;
 
 		mask = fdt32_to_cpu(pins->mask);
 		val = fdt32_to_cpu(pins->val) & mask;
+		single_write(dev, (single_read(dev, reg) & ~mask) | val, reg);
+		dev_dbg(dev, "  reg/val %pa/0x%08x\n", &reg, val);
 
-		switch (pdata->width) {
-		case 16:
-			writew((readw(reg) & ~mask) | val, reg);
-			break;
-		case 32:
-			writel((readl(reg) & ~mask) | val, reg);
-			break;
-		default:
-			dev_warn(dev, "unsupported register width %i\n",
-				 pdata->width);
-			continue;
+		while (mask) {
+			bit_pos = __ffs(mask);
+			pin_num_from_lsb = bit_pos / priv->bits_per_pin;
+			mask_pos = pdata->mask << bit_pos;
+			val_pos = val & mask_pos;
+			submask = mask & mask_pos;
+
+			if ((mask & mask_pos) == 0) {
+				dev_err(dev, "Invalid mask at 0x%x\n", offset);
+				break;
+			}
+
+			mask &= ~mask_pos;
+
+			if (submask != mask_pos) {
+				dev_warn(dev,
+					 "Invalid submask 0x%x at 0x%x\n",
+					 submask, offset);
+				continue;
+			}
+
+			func->pins[func->npins] = pin + pin_num_from_lsb;
+			func->npins++;
 		}
-		dev_dbg(dev, "  reg/val 0x%pa/0x%08x\n", &reg, val);
 	}
+
+	qsort(func->pins, func->npins, sizeof(func->pins[0]),
+	      single_pin_compare);
+	list_add(&func->node, &priv->functions);
 	return 0;
 }
 static int single_set_state(struct udevice *dev,
@@ -128,7 +410,7 @@
 			dev_dbg(dev, "  invalid pin configuration in fdt\n");
 			return -FDT_ERR_BADSTRUCTURE;
 		}
-		single_configure_pins(dev, prop, len);
+		single_configure_pins(dev, prop, len, config->name);
 		return 0;
 	}
 
@@ -140,7 +422,7 @@
 			dev_dbg(dev, "  invalid bits configuration in fdt\n");
 			return -FDT_ERR_BADSTRUCTURE;
 		}
-		single_configure_bits(dev, prop_bits, len);
+		single_configure_bits(dev, prop_bits, len, config->name);
 		return 0;
 	}
 
@@ -148,20 +430,83 @@
 	return len;
 }
 
+static const char *single_get_pin_name(struct udevice *dev,
+				       unsigned int selector)
+{
+	struct single_priv *priv = dev_get_priv(dev);
+
+	if (selector >= priv->npins)
+		snprintf(priv->pin_name, PINNAME_SIZE, "Error");
+	else
+		snprintf(priv->pin_name, PINNAME_SIZE, "PIN%u", selector);
+
+	return priv->pin_name;
+}
+
+static int single_get_pins_count(struct udevice *dev)
+{
+	struct single_priv *priv = dev_get_priv(dev);
+
+	return priv->npins;
+}
+
+static int single_probe(struct udevice *dev)
+{
+	struct single_pdata *pdata = dev_get_plat(dev);
+	struct single_priv *priv = dev_get_priv(dev);
+	u32 size;
+
+	INIT_LIST_HEAD(&priv->functions);
+
+	size = pdata->offset + pdata->width / BITS_PER_BYTE;
+	#if (CONFIG_IS_ENABLED(SANDBOX))
+	priv->sandbox_regs =
+		devm_kzalloc(dev, size * sizeof(*priv->sandbox_regs),
+			     GFP_KERNEL);
+	if (!priv->sandbox_regs)
+		return -ENOMEM;
+	#endif
+
+	priv->npins = size / (pdata->width / BITS_PER_BYTE);
+	if (pdata->bits_per_mux) {
+		priv->bits_per_pin = fls(pdata->mask);
+		priv->npins *= (pdata->width / priv->bits_per_pin);
+	}
+
+	dev_dbg(dev, "%d pins\n", priv->npins);
+	return 0;
+}
+
 static int single_of_to_plat(struct udevice *dev)
 {
 	fdt_addr_t addr;
-	u32 of_reg[2];
-	int res;
+	fdt_size_t size;
 	struct single_pdata *pdata = dev_get_plat(dev);
+	int ret;
 
-	pdata->width =
-		dev_read_u32_default(dev, "pinctrl-single,register-width", 0);
+	ret = dev_read_u32(dev, "pinctrl-single,register-width", &pdata->width);
+	if (ret) {
+		dev_err(dev, "missing register width\n");
+		return ret;
+	}
 
-	res = dev_read_u32_array(dev, "reg", of_reg, 2);
-	if (res)
-		return res;
-	pdata->offset = of_reg[1] - pdata->width / 8;
+	switch (pdata->width) {
+	case 8:
+	case 16:
+	case 32:
+		break;
+	default:
+		dev_err(dev, "wrong register width\n");
+		return -EINVAL;
+	}
+
+	addr = dev_read_addr_size(dev, "reg", &size);
+	if (addr == FDT_ADDR_T_NONE) {
+		dev_err(dev, "failed to get base register size\n");
+		return -EINVAL;
+	}
+
+	pdata->offset = size - pdata->width / BITS_PER_BYTE;
 
 	addr = dev_read_addr(dev);
 	if (addr == FDT_ADDR_T_NONE) {
@@ -170,15 +515,22 @@
 	}
 	pdata->base = addr;
 
-	pdata->mask = dev_read_u32_default(dev, "pinctrl-single,function-mask",
-					   0xffffffff);
+	ret = dev_read_u32(dev, "pinctrl-single,function-mask", &pdata->mask);
+	if (ret) {
+		pdata->mask = 0;
+		dev_warn(dev, "missing function register mask\n");
+	}
+
 	pdata->bits_per_mux = dev_read_bool(dev, "pinctrl-single,bit-per-mux");
 
 	return 0;
 }
 
 const struct pinctrl_ops single_pinctrl_ops = {
+	.get_pins_count	= single_get_pins_count,
+	.get_pin_name = single_get_pin_name,
 	.set_state = single_set_state,
+	.get_pin_muxing	= single_get_pin_muxing,
 };
 
 static const struct udevice_id single_pinctrl_match[] = {
@@ -192,5 +544,7 @@
 	.of_match = single_pinctrl_match,
 	.ops = &single_pinctrl_ops,
 	.plat_auto	= sizeof(struct single_pdata),
+	.priv_auto = sizeof(struct single_priv),
 	.of_to_plat = single_of_to_plat,
+	.probe = single_probe,
 };
diff --git a/drivers/video/sunxi/Makefile b/drivers/video/sunxi/Makefile
index 147e187..4321673 100644
--- a/drivers/video/sunxi/Makefile
+++ b/drivers/video/sunxi/Makefile
@@ -4,4 +4,4 @@
 # Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 
 obj-$(CONFIG_VIDEO_SUNXI) += sunxi_display.o simplefb_common.o lcdc.o tve_common.o ../videomodes.o
-obj-$(CONFIG_VIDEO_DE2) += sunxi_de2.o sunxi_dw_hdmi.o simplefb_common.o lcdc.o ../dw_hdmi.o sunxi_lcd.o
+obj-$(CONFIG_VIDEO_DE2) += sunxi_de2.o sunxi_dw_hdmi.o simplefb_common.o lcdc.o sunxi_lcd.o
diff --git a/env/Kconfig b/env/Kconfig
index b473d7c..08e49c2 100644
--- a/env/Kconfig
+++ b/env/Kconfig
@@ -324,6 +324,20 @@
 	  during a "saveenv" operation. CONFIG_ENV_OFFSET_REDUND must be
 	  aligned to an erase sector boundary.
 
+config ENV_SECT_SIZE_AUTO
+	bool "Use automatically detected sector size"
+	depends on ENV_IS_IN_SPI_FLASH
+	help
+	  Some boards exist in multiple variants, with different
+	  flashes having different sector sizes. In such cases, you
+	  can select this option to make U-Boot use the actual sector
+	  size when figuring out how much to erase, which can thus be
+	  more efficient on the flashes with smaller erase size. Since
+	  the environment must always be aligned on a sector boundary,
+	  CONFIG_ENV_OFFSET must be aligned to the largest of the
+	  different sector sizes, and CONFIG_ENV_SECT_SIZE should be
+	  set to that value.
+
 config USE_ENV_SPI_BUS
 	bool "SPI flash bus for environment"
 	depends on ENV_IS_IN_SPI_FLASH
@@ -462,6 +476,14 @@
 	  It's a string of the FAT file name. This file use to store the
 	  environment.
 
+config ENV_FAT_FILE_REDUND
+	string "Name of the FAT file to use for the environment"
+	depends on ENV_IS_IN_FAT && SYS_REDUNDAND_ENVIRONMENT
+	default "uboot-redund.env"
+	help
+	  It's a string of the FAT file name. This file use to store the
+	  redundant environment.
+
 config ENV_EXT4_INTERFACE
 	string "Name of the block device for the environment"
 	depends on ENV_IS_IN_EXT4
diff --git a/env/common.c b/env/common.c
index 2ee423b..49bbb05 100644
--- a/env/common.c
+++ b/env/common.c
@@ -145,7 +145,7 @@
 int env_check_redund(const char *buf1, int buf1_read_fail,
 		     const char *buf2, int buf2_read_fail)
 {
-	int crc1_ok, crc2_ok;
+	int crc1_ok = 0, crc2_ok = 0;
 	env_t *tmp_env1, *tmp_env2;
 
 	tmp_env1 = (env_t *)buf1;
@@ -153,25 +153,18 @@
 
 	if (buf1_read_fail && buf2_read_fail) {
 		puts("*** Error - No Valid Environment Area found\n");
+		return -EIO;
 	} else if (buf1_read_fail || buf2_read_fail) {
 		puts("*** Warning - some problems detected ");
 		puts("reading environment; recovered successfully\n");
 	}
 
-	if (buf1_read_fail && buf2_read_fail) {
-		return -EIO;
-	} else if (!buf1_read_fail && buf2_read_fail) {
-		gd->env_valid = ENV_VALID;
-		return -EINVAL;
-	} else if (buf1_read_fail && !buf2_read_fail) {
-		gd->env_valid = ENV_REDUND;
-		return -ENOENT;
-	}
-
-	crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) ==
-			tmp_env1->crc;
-	crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) ==
-			tmp_env2->crc;
+	if (!buf1_read_fail)
+		crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) ==
+				tmp_env1->crc;
+	if (!buf2_read_fail)
+		crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) ==
+				tmp_env2->crc;
 
 	if (!crc1_ok && !crc2_ok) {
 		return -ENOMSG; /* needed for env_load() */
@@ -208,10 +201,6 @@
 	if (ret == -EIO) {
 		env_set_default("bad env area", 0);
 		return -EIO;
-	} else if (ret == -EINVAL) {
-		return env_import((char *)buf1, 1, flags);
-	} else if (ret == -ENOENT) {
-		return env_import((char *)buf2, 1, flags);
 	} else if (ret == -ENOMSG) {
 		env_set_default("bad CRC", 0);
 		return -ENOMSG;
diff --git a/env/env.c b/env/env.c
index caefa33..e534008 100644
--- a/env/env.c
+++ b/env/env.c
@@ -335,6 +335,9 @@
 
 		debug("%s: Environment %s init done (ret=%d)\n", __func__,
 		      drv->name, ret);
+
+		if (gd->env_valid == ENV_INVALID)
+			ret = -ENOENT;
 	}
 
 	if (!prio)
diff --git a/env/ext4.c b/env/ext4.c
index ec643f2..9f65afb 100644
--- a/env/ext4.c
+++ b/env/ext4.c
@@ -188,6 +188,5 @@
 	ENV_NAME("EXT4")
 	.load		= env_ext4_load,
 	.save		= ENV_SAVE_PTR(env_ext4_save),
-	.erase		= CONFIG_IS_ENABLED(CMD_ERASEENV) ? env_ext4_erase :
-							    NULL,
+	.erase		= ENV_ERASE_PTR(env_ext4_erase),
 };
diff --git a/env/fat.c b/env/fat.c
index 653a38f..9d37d26 100644
--- a/env/fat.c
+++ b/env/fat.c
@@ -18,6 +18,7 @@
 #include <fat.h>
 #include <mmc.h>
 #include <asm/cache.h>
+#include <asm/global_data.h>
 #include <linux/stddef.h>
 
 #ifdef CONFIG_SPL_BUILD
@@ -29,6 +30,8 @@
 # define LOADENV
 #endif
 
+DECLARE_GLOBAL_DATA_PTR;
+
 static char *env_fat_device_and_part(void)
 {
 #ifdef CONFIG_MMC
@@ -53,6 +56,7 @@
 	env_t __aligned(ARCH_DMA_MINALIGN) env_new;
 	struct blk_desc *dev_desc = NULL;
 	struct disk_partition info;
+	const char *file = CONFIG_ENV_FAT_FILE;
 	int dev, part;
 	int err;
 	loff_t size;
@@ -78,29 +82,41 @@
 		return 1;
 	}
 
+#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
+	if (gd->env_valid == ENV_VALID)
+		file = CONFIG_ENV_FAT_FILE_REDUND;
+#endif
+
-	err = file_fat_write(CONFIG_ENV_FAT_FILE, (void *)&env_new, 0, sizeof(env_t),
-			     &size);
+	err = file_fat_write(file, (void *)&env_new, 0, sizeof(env_t), &size);
 	if (err == -1) {
 		/*
 		 * This printf is embedded in the messages from env_save that
 		 * will calling it. The missing \n is intentional.
 		 */
 		printf("Unable to write \"%s\" from %s%d:%d... ",
-			CONFIG_ENV_FAT_FILE, CONFIG_ENV_FAT_INTERFACE, dev, part);
+			file, CONFIG_ENV_FAT_INTERFACE, dev, part);
 		return 1;
 	}
 
+#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
+	gd->env_valid = (gd->env_valid == ENV_REDUND) ? ENV_VALID : ENV_REDUND;
+#endif
+
 	return 0;
 }
 
 #ifdef LOADENV
 static int env_fat_load(void)
 {
-	ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
+	ALLOC_CACHE_ALIGN_BUFFER(char, buf1, CONFIG_ENV_SIZE);
+#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
+	ALLOC_CACHE_ALIGN_BUFFER(char, buf2, CONFIG_ENV_SIZE);
+	int err2;
+#endif
 	struct blk_desc *dev_desc = NULL;
 	struct disk_partition info;
 	int dev, part;
-	int err;
+	int err1;
 
 #ifdef CONFIG_MMC
 	if (!strcmp(CONFIG_ENV_FAT_INTERFACE, "mmc"))
@@ -124,8 +140,15 @@
 		goto err_env_relocate;
 	}
 
-	err = file_fat_read(CONFIG_ENV_FAT_FILE, buf, CONFIG_ENV_SIZE);
-	if (err == -1) {
+	err1 = file_fat_read(CONFIG_ENV_FAT_FILE, buf1, CONFIG_ENV_SIZE);
+#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
+	err2 = file_fat_read(CONFIG_ENV_FAT_FILE_REDUND, buf2, CONFIG_ENV_SIZE);
+
+	err1 = (err1 >= 0) ? 0 : -1;
+	err2 = (err2 >= 0) ? 0 : -1;
+	return env_import_redund(buf1, err1, buf2, err2, H_EXTERNAL);
+#else
+	if (err1 < 0) {
 		/*
 		 * This printf is embedded in the messages from env_save that
 		 * will calling it. The missing \n is intentional.
@@ -135,7 +158,8 @@
 		goto err_env_relocate;
 	}
 
-	return env_import(buf, 1, H_EXTERNAL);
+	return env_import(buf1, 1, H_EXTERNAL);
+#endif
 
 err_env_relocate:
 	env_set_default(NULL, 0);
diff --git a/env/flags.c b/env/flags.c
index df4aed2..e3e833c 100644
--- a/env/flags.c
+++ b/env/flags.c
@@ -563,12 +563,13 @@
 		return 1;
 #endif
 
-#ifndef CONFIG_ENV_ACCESS_IGNORE_FORCE
 	if (flag & H_FORCE) {
+#ifdef CONFIG_ENV_ACCESS_IGNORE_FORCE
 		printf("## Error: Can't force access to \"%s\"\n", name);
+#else
 		return 0;
-	}
 #endif
+	}
 	switch (op) {
 	case env_op_delete:
 		if (item->flags & ENV_FLAGS_VARACCESS_PREVENT_DELETE) {
diff --git a/env/mmc.c b/env/mmc.c
index 9b226be..09e94f0 100644
--- a/env/mmc.c
+++ b/env/mmc.c
@@ -233,7 +233,6 @@
 	return ret;
 }
 
-#if defined(CONFIG_CMD_ERASEENV)
 static inline int erase_env(struct mmc *mmc, unsigned long size,
 			    unsigned long offset)
 {
@@ -279,7 +278,6 @@
 
 	return ret;
 }
-#endif /* CONFIG_CMD_ERASEENV */
 #endif /* CONFIG_CMD_SAVEENV && !CONFIG_SPL_BUILD */
 
 static inline int read_env(struct mmc *mmc, unsigned long size,
@@ -394,8 +392,6 @@
 	.load		= env_mmc_load,
 #ifndef CONFIG_SPL_BUILD
 	.save		= env_save_ptr(env_mmc_save),
-#if defined(CONFIG_CMD_ERASEENV)
-	.erase		= env_mmc_erase,
-#endif
+	.erase		= ENV_ERASE_PTR(env_mmc_erase)
 #endif
 };
diff --git a/env/sf.c b/env/sf.c
index 88ec110..e4b7ca9 100644
--- a/env/sf.c
+++ b/env/sf.c
@@ -28,16 +28,23 @@
 #define INITENV
 #endif
 
+#define	OFFSET_INVALID		(~(u32)0)
+
 #ifdef CONFIG_ENV_OFFSET_REDUND
+#define ENV_OFFSET_REDUND	CONFIG_ENV_OFFSET_REDUND
+
 static ulong env_offset		= CONFIG_ENV_OFFSET;
 static ulong env_new_offset	= CONFIG_ENV_OFFSET_REDUND;
+
+#else
+
+#define ENV_OFFSET_REDUND	OFFSET_INVALID
+
 #endif /* CONFIG_ENV_OFFSET_REDUND */
 
 DECLARE_GLOBAL_DATA_PTR;
 
-static struct spi_flash *env_flash;
-
-static int setup_flash_device(void)
+static int setup_flash_device(struct spi_flash **env_flash)
 {
 #if CONFIG_IS_ENABLED(DM_SPI_FLASH)
 	struct udevice *new;
@@ -52,14 +59,11 @@
 		return ret;
 	}
 
-	env_flash = dev_get_uclass_priv(new);
+	*env_flash = dev_get_uclass_priv(new);
 #else
-	if (env_flash)
-		spi_flash_free(env_flash);
-
-	env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
-				    CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
-	if (!env_flash) {
+	*env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
+				     CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
+	if (!*env_flash) {
 		env_set_default("spi_flash_probe() failed", 0);
 		return -EIO;
 	}
@@ -72,13 +76,18 @@
 {
 	env_t	env_new;
 	char	*saved_buffer = NULL, flag = ENV_REDUND_OBSOLETE;
-	u32	saved_size, saved_offset, sector;
+	u32	saved_size = 0, saved_offset = 0, sector;
+	u32	sect_size = CONFIG_ENV_SECT_SIZE;
 	int	ret;
+	struct spi_flash *env_flash;
 
-	ret = setup_flash_device();
+	ret = setup_flash_device(&env_flash);
 	if (ret)
 		return ret;
 
+	if (IS_ENABLED(CONFIG_ENV_SECT_SIZE_AUTO))
+		sect_size = env_flash->mtd.erasesize;
+
 	ret = env_export(&env_new);
 	if (ret)
 		return -EIO;
@@ -93,8 +102,8 @@
 	}
 
 	/* Is the sector larger than the env (i.e. embedded) */
-	if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
-		saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE;
+	if (sect_size > CONFIG_ENV_SIZE) {
+		saved_size = sect_size - CONFIG_ENV_SIZE;
 		saved_offset = env_new_offset + CONFIG_ENV_SIZE;
 		saved_buffer = memalign(ARCH_DMA_MINALIGN, saved_size);
 		if (!saved_buffer) {
@@ -107,11 +116,11 @@
 			goto done;
 	}
 
-	sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, CONFIG_ENV_SECT_SIZE);
+	sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, sect_size);
 
 	puts("Erasing SPI flash...");
 	ret = spi_flash_erase(env_flash, env_new_offset,
-				sector * CONFIG_ENV_SECT_SIZE);
+				sector * sect_size);
 	if (ret)
 		goto done;
 
@@ -122,7 +131,7 @@
 	if (ret)
 		goto done;
 
-	if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
+	if (sect_size > CONFIG_ENV_SIZE) {
 		ret = spi_flash_write(env_flash, saved_offset,
 					saved_size, saved_buffer);
 		if (ret)
@@ -141,6 +150,8 @@
 	printf("Valid environment: %d\n", (int)gd->env_valid);
 
 done:
+	spi_flash_free(env_flash);
+
 	if (saved_buffer)
 		free(saved_buffer);
 
@@ -152,6 +163,7 @@
 	int ret;
 	int read1_fail, read2_fail;
 	env_t *tmp_env1, *tmp_env2;
+	struct spi_flash *env_flash;
 
 	tmp_env1 = (env_t *)memalign(ARCH_DMA_MINALIGN,
 			CONFIG_ENV_SIZE);
@@ -163,7 +175,7 @@
 		goto out;
 	}
 
-	ret = setup_flash_device();
+	ret = setup_flash_device(&env_flash);
 	if (ret)
 		goto out;
 
@@ -176,7 +188,6 @@
 				read2_fail, H_EXTERNAL);
 
 	spi_flash_free(env_flash);
-	env_flash = NULL;
 out:
 	free(tmp_env1);
 	free(tmp_env2);
@@ -186,18 +197,23 @@
 #else
 static int env_sf_save(void)
 {
-	u32	saved_size, saved_offset, sector;
+	u32	saved_size = 0, saved_offset = 0, sector;
+	u32	sect_size = CONFIG_ENV_SECT_SIZE;
 	char	*saved_buffer = NULL;
 	int	ret = 1;
 	env_t	env_new;
+	struct spi_flash *env_flash;
 
-	ret = setup_flash_device();
+	ret = setup_flash_device(&env_flash);
 	if (ret)
 		return ret;
 
+	if (IS_ENABLED(CONFIG_ENV_SECT_SIZE_AUTO))
+		sect_size = env_flash->mtd.erasesize;
+
 	/* Is the sector larger than the env (i.e. embedded) */
-	if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
-		saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE;
+	if (sect_size > CONFIG_ENV_SIZE) {
+		saved_size = sect_size - CONFIG_ENV_SIZE;
 		saved_offset = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
 		saved_buffer = malloc(saved_size);
 		if (!saved_buffer)
@@ -213,11 +229,11 @@
 	if (ret)
 		goto done;
 
-	sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, CONFIG_ENV_SECT_SIZE);
+	sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, sect_size);
 
 	puts("Erasing SPI flash...");
 	ret = spi_flash_erase(env_flash, CONFIG_ENV_OFFSET,
-		sector * CONFIG_ENV_SECT_SIZE);
+		sector * sect_size);
 	if (ret)
 		goto done;
 
@@ -227,7 +243,7 @@
 	if (ret)
 		goto done;
 
-	if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
+	if (sect_size > CONFIG_ENV_SIZE) {
 		ret = spi_flash_write(env_flash, saved_offset,
 			saved_size, saved_buffer);
 		if (ret)
@@ -238,6 +254,8 @@
 	puts("done\n");
 
 done:
+	spi_flash_free(env_flash);
+
 	if (saved_buffer)
 		free(saved_buffer);
 
@@ -248,6 +266,7 @@
 {
 	int ret;
 	char *buf = NULL;
+	struct spi_flash *env_flash;
 
 	buf = (char *)memalign(ARCH_DMA_MINALIGN, CONFIG_ENV_SIZE);
 	if (!buf) {
@@ -255,7 +274,7 @@
 		return -EIO;
 	}
 
-	ret = setup_flash_device();
+	ret = setup_flash_device(&env_flash);
 	if (ret)
 		goto out;
 
@@ -272,7 +291,6 @@
 
 err_read:
 	spi_flash_free(env_flash);
-	env_flash = NULL;
 out:
 	free(buf);
 
@@ -280,6 +298,30 @@
 }
 #endif
 
+static int env_sf_erase(void)
+{
+	int ret;
+	env_t env;
+	struct spi_flash *env_flash;
+
+	ret = setup_flash_device(&env_flash);
+	if (ret)
+		return ret;
+
+	memset(&env, 0, sizeof(env_t));
+	ret = spi_flash_write(env_flash, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, &env);
+	if (ret)
+		goto done;
+
+	if (ENV_OFFSET_REDUND != OFFSET_INVALID)
+		ret = spi_flash_write(env_flash, ENV_OFFSET_REDUND, CONFIG_ENV_SIZE, &env);
+
+done:
+	spi_flash_free(env_flash);
+
+	return ret;
+}
+
 #if CONFIG_ENV_ADDR != 0x0
 __weak void *env_sf_get_env_addr(void)
 {
@@ -320,6 +362,7 @@
 	int crc1_ok;
 	env_t *tmp_env2 = NULL;
 	env_t *tmp_env1;
+	struct spi_flash *env_flash;
 
 	/*
 	 * if malloc is not ready yet, we cannot use
@@ -337,7 +380,7 @@
 	if (!tmp_env1 || !tmp_env2)
 		goto out;
 
-	ret = setup_flash_device();
+	ret = setup_flash_device(&env_flash);
 	if (ret)
 		goto out;
 
@@ -351,7 +394,7 @@
 		ret = env_check_redund((char *)tmp_env1, read1_fail,
 				       (char *)tmp_env2, read2_fail);
 
-		if (ret == -EIO || ret == -ENOMSG)
+		if (ret < 0)
 			goto err_read;
 
 		if (gd->env_valid == ENV_VALID)
@@ -372,10 +415,12 @@
 		gd->env_addr = (unsigned long)&tmp_env1->data;
 	}
 
+	spi_flash_free(env_flash);
+
 	return 0;
 err_read:
 	spi_flash_free(env_flash);
-	env_flash = NULL;
+
 	free(tmp_env1);
 	if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT))
 		free(tmp_env2);
@@ -406,6 +451,7 @@
 	.location	= ENVL_SPI_FLASH,
 	ENV_NAME("SPIFlash")
 	.load		= env_sf_load,
-	.save		= CONFIG_IS_ENABLED(SAVEENV) ? ENV_SAVE_PTR(env_sf_save) : NULL,
+	.save		= ENV_SAVE_PTR(env_sf_save),
+	.erase		= ENV_ERASE_PTR(env_sf_erase),
 	.init		= env_sf_init,
 };
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h
index 33a4d7b..9e37e99 100644
--- a/include/configs/sunxi-common.h
+++ b/include/configs/sunxi-common.h
@@ -62,7 +62,7 @@
 #define SDRAM_OFFSET(x) 0x2##x
 #define CONFIG_SYS_SDRAM_BASE		0x20000000
 #define CONFIG_SYS_LOAD_ADDR		0x22000000 /* default load address */
-/* Note SPL_STACK_R_ADDR is set through Kconfig, we include it here 
+/* Note SPL_STACK_R_ADDR is set through Kconfig, we include it here
  * since it needs to fit in with the other values. By also #defining it
  * we get warnings if the Kconfig value mismatches. */
 #define CONFIG_SPL_STACK_R_ADDR		0x2fe00000
@@ -72,7 +72,7 @@
 #define CONFIG_SYS_SDRAM_BASE		0x40000000
 #define CONFIG_SYS_LOAD_ADDR		0x42000000 /* default load address */
 /* V3s do not have enough memory to place code at 0x4a000000 */
-/* Note SPL_STACK_R_ADDR is set through Kconfig, we include it here 
+/* Note SPL_STACK_R_ADDR is set through Kconfig, we include it here
  * since it needs to fit in with the other values. By also #defining it
  * we get warnings if the Kconfig value mismatches. */
 #define CONFIG_SPL_STACK_R_ADDR		0x4fe00000
@@ -240,40 +240,44 @@
  * There is no compression for arm64 kernels (yet), so leave some space
  * for really big kernels, say 256MB for now.
  * Scripts, PXE and DTBs should go afterwards, leaving the rest for the initrd.
- * Align the initrd to a 2MB page.
  */
-#define BOOTM_SIZE	__stringify(0xa000000)
-#define KERNEL_ADDR_R	__stringify(SDRAM_OFFSET(0080000))
-#define FDT_ADDR_R	__stringify(SDRAM_OFFSET(FA00000))
-#define SCRIPT_ADDR_R	__stringify(SDRAM_OFFSET(FC00000))
-#define PXEFILE_ADDR_R	__stringify(SDRAM_OFFSET(FD00000))
-#define RAMDISK_ADDR_R	__stringify(SDRAM_OFFSET(FE00000))
+#define BOOTM_SIZE        __stringify(0xa000000)
+#define KERNEL_ADDR_R     __stringify(SDRAM_OFFSET(0080000))
+#define KERNEL_COMP_ADDR_R __stringify(SDRAM_OFFSET(4000000))
+#define KERNEL_COMP_SIZE  __stringify(0xb000000)
+#define FDT_ADDR_R        __stringify(SDRAM_OFFSET(FA00000))
+#define SCRIPT_ADDR_R     __stringify(SDRAM_OFFSET(FC00000))
+#define PXEFILE_ADDR_R    __stringify(SDRAM_OFFSET(FD00000))
+#define FDTOVERLAY_ADDR_R __stringify(SDRAM_OFFSET(FE00000))
+#define RAMDISK_ADDR_R    __stringify(SDRAM_OFFSET(FF00000))
 
 #else
 /*
  * 160M RAM (256M minimum minus 64MB heap + 32MB for u-boot, stack, fb, etc.
  * 32M uncompressed kernel, 16M compressed kernel, 1M fdt,
- * 1M script, 1M pxe and the ramdisk at the end.
+ * 1M script, 1M pxe, 1M dt overlay and the ramdisk at the end.
  */
 #ifndef CONFIG_MACH_SUN8I_V3S
-#define BOOTM_SIZE     __stringify(0xa000000)
-#define KERNEL_ADDR_R  __stringify(SDRAM_OFFSET(2000000))
-#define FDT_ADDR_R     __stringify(SDRAM_OFFSET(3000000))
-#define SCRIPT_ADDR_R  __stringify(SDRAM_OFFSET(3100000))
-#define PXEFILE_ADDR_R __stringify(SDRAM_OFFSET(3200000))
-#define RAMDISK_ADDR_R __stringify(SDRAM_OFFSET(3300000))
+#define BOOTM_SIZE        __stringify(0xa000000)
+#define KERNEL_ADDR_R     __stringify(SDRAM_OFFSET(2000000))
+#define FDT_ADDR_R        __stringify(SDRAM_OFFSET(3000000))
+#define SCRIPT_ADDR_R     __stringify(SDRAM_OFFSET(3100000))
+#define PXEFILE_ADDR_R    __stringify(SDRAM_OFFSET(3200000))
+#define FDTOVERLAY_ADDR_R __stringify(SDRAM_OFFSET(3300000))
+#define RAMDISK_ADDR_R    __stringify(SDRAM_OFFSET(3400000))
 #else
 /*
  * 64M RAM minus 2MB heap + 16MB for u-boot, stack, fb, etc.
  * 16M uncompressed kernel, 8M compressed kernel, 1M fdt,
- * 1M script, 1M pxe and the ramdisk at the end.
+ * 1M script, 1M pxe, 1M dt overlay and the ramdisk at the end.
  */
-#define BOOTM_SIZE     __stringify(0x2e00000)
-#define KERNEL_ADDR_R  __stringify(SDRAM_OFFSET(1000000))
-#define FDT_ADDR_R     __stringify(SDRAM_OFFSET(1800000))
-#define SCRIPT_ADDR_R  __stringify(SDRAM_OFFSET(1900000))
-#define PXEFILE_ADDR_R __stringify(SDRAM_OFFSET(1A00000))
-#define RAMDISK_ADDR_R __stringify(SDRAM_OFFSET(1B00000))
+#define BOOTM_SIZE        __stringify(0x2e00000)
+#define KERNEL_ADDR_R     __stringify(SDRAM_OFFSET(1000000))
+#define FDT_ADDR_R        __stringify(SDRAM_OFFSET(1800000))
+#define SCRIPT_ADDR_R     __stringify(SDRAM_OFFSET(1900000))
+#define PXEFILE_ADDR_R    __stringify(SDRAM_OFFSET(1A00000))
+#define FDTOVERLAY_ADDR_R __stringify(SDRAM_OFFSET(1B00000))
+#define RAMDISK_ADDR_R    __stringify(SDRAM_OFFSET(1C00000))
 #endif
 #endif
 
@@ -283,8 +287,21 @@
 	"fdt_addr_r=" FDT_ADDR_R "\0" \
 	"scriptaddr=" SCRIPT_ADDR_R "\0" \
 	"pxefile_addr_r=" PXEFILE_ADDR_R "\0" \
+	"fdtoverlay_addr_r=" FDTOVERLAY_ADDR_R "\0" \
 	"ramdisk_addr_r=" RAMDISK_ADDR_R "\0"
 
+#ifdef CONFIG_ARM64
+
+#define MEM_LAYOUT_ENV_EXTRA_SETTINGS \
+	"kernel_comp_addr_r=" KERNEL_COMP_ADDR_R "\0" \
+	"kernel_comp_size=" KERNEL_COMP_SIZE "\0"
+
+#else
+
+#define MEM_LAYOUT_ENV_EXTRA_SETTINGS ""
+
+#endif
+
 #define DFU_ALT_INFO_RAM \
 	"dfu_alt_info_ram=" \
 	"kernel ram " KERNEL_ADDR_R " 0x1000000;" \
@@ -435,6 +452,7 @@
 #define CONFIG_EXTRA_ENV_SETTINGS \
 	CONSOLE_ENV_SETTINGS \
 	MEM_LAYOUT_ENV_SETTINGS \
+	MEM_LAYOUT_ENV_EXTRA_SETTINGS \
 	DFU_ALT_INFO_RAM \
 	"fdtfile=" FDTFILE "\0" \
 	"console=ttyS0,115200\0" \
diff --git a/include/env.h b/include/env.h
index c15339a..b5731e4 100644
--- a/include/env.h
+++ b/include/env.h
@@ -328,8 +328,6 @@
  * @buf2_read_fail: 0 if buf2 is valid, non-zero if invalid
  * @return 0 if OK,
  *	-EIO if no environment is valid,
- *	-EINVAL if read of second entry is good
- *	-ENOENT if read of first entry is good
  *	-ENOMSG if the CRC was bad
  */
 
diff --git a/include/env_internal.h b/include/env_internal.h
index 708c833..b7bddcb 100644
--- a/include/env_internal.h
+++ b/include/env_internal.h
@@ -211,6 +211,7 @@
 #endif
 
 #define ENV_SAVE_PTR(x) (CONFIG_IS_ENABLED(SAVEENV) ? (x) : NULL)
+#define ENV_ERASE_PTR(x) (CONFIG_IS_ENABLED(CMD_ERASEENV) ? (x) : NULL)
 
 extern struct hsearch_data env_htab;
 
diff --git a/test/dm/pinmux.c b/test/dm/pinmux.c
index 047184d..265df4c 100644
--- a/test/dm/pinmux.c
+++ b/test/dm/pinmux.c
@@ -9,16 +9,21 @@
 #include <dm/test.h>
 #include <test/ut.h>
 
-static int dm_test_pinmux(struct unit_test_state *uts)
-{
-	char buf[64];
-	struct udevice *dev;
-
+static char buf[64];
 #define test_muxing(selector, expected) do { \
 	ut_assertok(pinctrl_get_pin_muxing(dev, selector, buf, sizeof(buf))); \
 	ut_asserteq_str(expected, (char *)&buf); \
 } while (0)
 
+#define test_name(selector, expected) do { \
+	ut_assertok(pinctrl_get_pin_name(dev, selector, buf, sizeof(buf))); \
+	ut_asserteq_str(expected, (char *)&buf); \
+} while (0)
+
+static int dm_test_pinmux(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+
 	ut_assertok(uclass_get_device_by_name(UCLASS_PINCTRL, "pinctrl", &dev));
 	test_muxing(0, "UART TX.");
 	test_muxing(1, "UART RX.");
@@ -54,4 +59,80 @@
 
 	return 0;
 }
+
 DM_TEST(dm_test_pinmux, UT_TESTF_SCAN_FDT);
+
+static int dm_test_pinctrl_single(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	int ret;
+
+	ret = uclass_get_device_by_name(UCLASS_PINCTRL,
+					"pinctrl-single-no-width", &dev);
+	ut_asserteq(-EINVAL, ret);
+	ut_assertok(uclass_get_device_by_name(UCLASS_PWM, "pwm", &dev));
+	ut_assertok(uclass_get_device_by_name(UCLASS_SERIAL, "serial", &dev));
+	ut_assertok(uclass_get_device_by_name(UCLASS_SPI, "spi@0", &dev));
+	ut_assertok(uclass_get_device_by_name(UCLASS_PINCTRL,
+					      "pinctrl-single-pins", &dev));
+	ut_asserteq(142, pinctrl_get_pins_count(dev));
+	test_name(0, "PIN0");
+	test_name(141, "PIN141");
+	test_name(142, "Error");
+	test_muxing(0, "0x00000000 0x00000000 UNCLAIMED");
+	test_muxing(18, "0x00000048 0x00000006 pinmux_pwm_pins");
+	test_muxing(28, "0x00000070 0x00000030 pinmux_uart0_pins");
+	test_muxing(29, "0x00000074 0x00000000 pinmux_uart0_pins");
+	test_muxing(100, "0x00000190 0x0000000c pinmux_spi0_pins");
+	test_muxing(101, "0x00000194 0x0000000c pinmux_spi0_pins");
+	test_muxing(102, "0x00000198 0x00000023 pinmux_spi0_pins");
+	test_muxing(103, "0x0000019c 0x0000000c pinmux_spi0_pins");
+	ret = pinctrl_get_pin_muxing(dev, 142, buf, sizeof(buf));
+	ut_asserteq(-EINVAL, ret);
+	ut_assertok(uclass_get_device_by_name(UCLASS_I2C, "i2c@0", &dev));
+	ut_assertok(uclass_get_device_by_name(UCLASS_VIDEO, "lcd", &dev));
+	ut_assertok(uclass_get_device_by_name(UCLASS_PINCTRL,
+					      "pinctrl-single-bits", &dev));
+	ut_asserteq(160, pinctrl_get_pins_count(dev));
+	test_name(0, "PIN0");
+	test_name(159, "PIN159");
+	test_name(160, "Error");
+	test_muxing(0, "0x00000000 0x00000000 UNCLAIMED");
+	test_muxing(34, "0x00000010 0x00000200 pinmux_i2c0_pins");
+	test_muxing(35, "0x00000010 0x00002000 pinmux_i2c0_pins");
+	test_muxing(130, "0x00000040 0x00000200 pinmux_lcd_pins");
+	test_muxing(131, "0x00000040 0x00002000 pinmux_lcd_pins");
+	test_muxing(132, "0x00000040 0x00020000 pinmux_lcd_pins");
+	test_muxing(133, "0x00000040 0x00200000 pinmux_lcd_pins");
+	test_muxing(134, "0x00000040 0x02000000 pinmux_lcd_pins");
+	test_muxing(135, "0x00000040 0x20000000 pinmux_lcd_pins");
+	test_muxing(136, "0x00000044 0x00000002 pinmux_lcd_pins");
+	test_muxing(137, "0x00000044 0x00000020 pinmux_lcd_pins");
+	test_muxing(138, "0x00000044 0x00000200 pinmux_lcd_pins");
+	test_muxing(139, "0x00000044 0x00002000 pinmux_lcd_pins");
+	test_muxing(140, "0x00000044 0x00020000 pinmux_lcd_pins");
+	test_muxing(141, "0x00000044 0x00200000 pinmux_lcd_pins");
+	test_muxing(142, "0x00000044 0x02000000 pinmux_lcd_pins");
+	test_muxing(143, "0x00000044 0x20000000 pinmux_lcd_pins");
+	test_muxing(144, "0x00000048 0x00000002 pinmux_lcd_pins");
+	test_muxing(145, "0x00000048 0x00000020 pinmux_lcd_pins");
+	test_muxing(146, "0x00000048 0x00000000 UNCLAIMED");
+	test_muxing(147, "0x00000048 0x00000000 UNCLAIMED");
+	test_muxing(148, "0x00000048 0x00000000 UNCLAIMED");
+	test_muxing(149, "0x00000048 0x00000000 UNCLAIMED");
+	test_muxing(150, "0x00000048 0x02000000 pinmux_lcd_pins");
+	test_muxing(151, "0x00000048 0x00000000 UNCLAIMED");
+	test_muxing(152, "0x0000004c 0x00000002 pinmux_lcd_pins");
+	test_muxing(153, "0x0000004c 0x00000020 pinmux_lcd_pins");
+	test_muxing(154, "0x0000004c 0x00000000 UNCLAIMED");
+	test_muxing(155, "0x0000004c 0x00000000 UNCLAIMED");
+	test_muxing(156, "0x0000004c 0x00000000 UNCLAIMED");
+	test_muxing(157, "0x0000004c 0x00000000 UNCLAIMED");
+	test_muxing(158, "0x0000004c 0x02000000 pinmux_lcd_pins");
+	test_muxing(159, "0x0000004c 0x00000000 UNCLAIMED");
+	ret = pinctrl_get_pin_muxing(dev, 160, buf, sizeof(buf));
+	ut_asserteq(-EINVAL, ret);
+	return 0;
+}
+
+DM_TEST(dm_test_pinctrl_single, UT_TESTF_SCAN_FDT);