Merge tag 'efi-2021-07-rc1-2' of https://source.denx.de/u-boot/custodians/u-boot-efi

Pull request for efi-2021-07-rc1-2

Documentation:
	man-page for fatinfo

Bug fixes:
	memory leak in efi_capsule_scan_dir()
	incorrect invocations of EFI_CALL macro creating ESRT table
	buffer overflow in tcg2_create_digest()
diff --git a/arch/arc/dts/iot_devkit.dts b/arch/arc/dts/iot_devkit.dts
index c0173fa..2122827 100644
--- a/arch/arc/dts/iot_devkit.dts
+++ b/arch/arc/dts/iot_devkit.dts
@@ -39,7 +39,7 @@
 	};
 
 	usbphy: phy {
-		compatible = "nop-phy";
+		compatible = "usb-nop-xceiv";
 		#phy-cells = <0>;
 	};
 
diff --git a/arch/arm/dts/imx8mm.dtsi b/arch/arm/dts/imx8mm.dtsi
index c824f26..b142b80 100644
--- a/arch/arm/dts/imx8mm.dtsi
+++ b/arch/arm/dts/imx8mm.dtsi
@@ -4,6 +4,8 @@
  */
 
 #include <dt-bindings/clock/imx8mm-clock.h>
+#include <dt-bindings/power/imx8mm-power.h>
+#include <dt-bindings/reset/imx8mq-reset.h>
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/input/input.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
@@ -241,6 +243,7 @@
 	};
 
 	usbphynop1: usbphynop1 {
+		#phy-cells = <0>;
 		compatible = "usb-nop-xceiv";
 		clocks = <&clk IMX8MM_CLK_USB_PHY_REF>;
 		assigned-clocks = <&clk IMX8MM_CLK_USB_PHY_REF>;
@@ -249,6 +252,7 @@
 	};
 
 	usbphynop2: usbphynop2 {
+		#phy-cells = <0>;
 		compatible = "usb-nop-xceiv";
 		clocks = <&clk IMX8MM_CLK_USB_PHY_REF>;
 		assigned-clocks = <&clk IMX8MM_CLK_USB_PHY_REF>;
@@ -590,6 +594,75 @@
 				interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
 				#reset-cells = <1>;
 			};
+
+			gpc: gpc@303a0000 {
+				compatible = "fsl,imx8mm-gpc";
+				reg = <0x303a0000 0x10000>;
+				interrupt-parent = <&gic>;
+				interrupt-controller;
+				#interrupt-cells = <3>;
+
+				pgc {
+					#address-cells = <1>;
+					#size-cells = <0>;
+
+					pgc_hsiomix: power-domain@0 {
+						#power-domain-cells = <0>;
+						reg = <IMX8MM_POWER_DOMAIN_HSIOMIX>;
+						clocks = <&clk IMX8MM_CLK_USB_BUS>;
+					};
+
+					pgc_pcie: power-domain@1 {
+						#power-domain-cells = <0>;
+						reg = <IMX8MM_POWER_DOMAIN_PCIE>;
+						power-domains = <&pgc_hsiomix>;
+					};
+
+					pgc_otg1: power-domain@2 {
+						#power-domain-cells = <0>;
+						reg = <IMX8MM_POWER_DOMAIN_OTG1>;
+						power-domains = <&pgc_hsiomix>;
+					};
+
+					pgc_otg2: power-domain@3 {
+						#power-domain-cells = <0>;
+						reg = <IMX8MM_POWER_DOMAIN_OTG2>;
+						power-domains = <&pgc_hsiomix>;
+					};
+
+					pgc_gpumix: power-domain@4 {
+						#power-domain-cells = <0>;
+						reg = <IMX8MM_POWER_DOMAIN_GPUMIX>;
+						clocks = <&clk IMX8MM_CLK_GPU_BUS_ROOT>,
+						         <&clk IMX8MM_CLK_GPU_AHB>;
+					};
+
+					pgc_gpu: power-domain@5 {
+						#power-domain-cells = <0>;
+						reg = <IMX8MM_POWER_DOMAIN_GPU>;
+						clocks = <&clk IMX8MM_CLK_GPU_AHB>,
+						         <&clk IMX8MM_CLK_GPU_BUS_ROOT>,
+						         <&clk IMX8MM_CLK_GPU2D_ROOT>,
+						         <&clk IMX8MM_CLK_GPU3D_ROOT>;
+						resets = <&src IMX8MQ_RESET_GPU_RESET>;
+						power-domains = <&pgc_gpumix>;
+					};
+
+					dispmix_pd: power-domain@10 {
+						#power-domain-cells = <0>;
+						reg = <IMX8MM_POWER_DOMAIN_DISPMIX>;
+						clocks = <&clk IMX8MM_CLK_DISP_ROOT>,
+							 <&clk IMX8MM_CLK_DISP_AXI_ROOT>,
+							 <&clk IMX8MM_CLK_DISP_APB_ROOT>;
+					};
+
+					mipi_pd: power-domain@11 {
+						#power-domain-cells = <0>;
+						reg = <IMX8MM_POWER_DOMAIN_MIPI>;
+						power-domains = <&dispmix_pd>;
+					};
+				};
+			};
 		};
 
 		aips2: bus@30400000 {
@@ -936,8 +1009,9 @@
 				clock-names = "usb1_ctrl_root_clk";
 				assigned-clocks = <&clk IMX8MM_CLK_USB_BUS>;
 				assigned-clock-parents = <&clk IMX8MM_SYS_PLL2_500M>;
-				fsl,usbphy = <&usbphynop1>;
+				phys = <&usbphynop1>;
 				fsl,usbmisc = <&usbmisc1 0>;
+				power-domains = <&pgc_otg1>;
 				status = "disabled";
 			};
 
@@ -955,8 +1029,9 @@
 				clock-names = "usb1_ctrl_root_clk";
 				assigned-clocks = <&clk IMX8MM_CLK_USB_BUS>;
 				assigned-clock-parents = <&clk IMX8MM_SYS_PLL2_500M>;
-				fsl,usbphy = <&usbphynop2>;
+				phys = <&usbphynop2>;
 				fsl,usbmisc = <&usbmisc2 0>;
+				power-domains = <&pgc_otg2>;
 				status = "disabled";
 			};
 
diff --git a/arch/arm/dts/imx8mn.dtsi b/arch/arm/dts/imx8mn.dtsi
index 16ea500..edcb415 100644
--- a/arch/arm/dts/imx8mn.dtsi
+++ b/arch/arm/dts/imx8mn.dtsi
@@ -4,6 +4,8 @@
  */
 
 #include <dt-bindings/clock/imx8mn-clock.h>
+#include <dt-bindings/power/imx8mn-power.h>
+#include <dt-bindings/reset/imx8mq-reset.h>
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/input/input.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
@@ -612,6 +614,54 @@
 				interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
 				#reset-cells = <1>;
 			};
+
+			gpc: gpc@303a0000 {
+				compatible = "fsl,imx8mn-gpc";
+				reg = <0x303a0000 0x10000>;
+				interrupt-parent = <&gic>;
+				interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
+
+				pgc {
+					#address-cells = <1>;
+					#size-cells = <0>;
+
+					pgc_hsiomix: power-domain@0 {
+						#power-domain-cells = <0>;
+						reg = <IMX8MN_POWER_DOMAIN_HSIOMIX>;
+						clocks = <&clk IMX8MN_CLK_USB_BUS>;
+					};
+
+					pgc_otg1: power-domain@1 {
+						#power-domain-cells = <0>;
+						reg = <IMX8MN_POWER_DOMAIN_OTG1>;
+						power-domains = <&pgc_hsiomix>;
+					};
+
+					pgc_gpumix: power-domain@2 {
+						#power-domain-cells = <0>;
+						reg = <IMX8MN_POWER_DOMAIN_GPUMIX>;
+						clocks = <&clk IMX8MN_CLK_GPU_CORE_ROOT>,
+							 <&clk IMX8MN_CLK_GPU_SHADER_DIV>,
+							 <&clk IMX8MN_CLK_GPU_BUS_ROOT>,
+							 <&clk IMX8MN_CLK_GPU_AHB>;
+						resets = <&src IMX8MQ_RESET_GPU_RESET>;
+					};
+
+					dispmix_pd: power-domain@3 {
+						#power-domain-cells = <0>;
+						reg = <IMX8MN_POWER_DOMAIN_DISPMIX>;
+						clocks = <&clk IMX8MN_CLK_DISP_PIXEL_ROOT>,
+							 <&clk IMX8MN_CLK_DISP_AXI_ROOT>,
+							 <&clk IMX8MN_CLK_DISP_APB_ROOT>;
+					};
+
+					mipi_pd: power-domain@4 {
+						#power-domain-cells = <0>;
+						reg = <IMX8MN_POWER_DOMAIN_MIPI>;
+						power-domains = <&dispmix_pd>;
+					};
+				};
+			};
 		};
 
 		aips2: bus@30400000 {
@@ -962,8 +1012,9 @@
 				clock-names = "usb1_ctrl_root_clk";
 				assigned-clocks = <&clk IMX8MN_CLK_USB_BUS>;
 				assigned-clock-parents = <&clk IMX8MN_SYS_PLL2_500M>;
-				fsl,usbphy = <&usbphynop1>;
+				phys = <&usbphynop1>;
 				fsl,usbmisc = <&usbmisc1 0>;
+				power-domains = <&pgc_otg1>;
 				status = "disabled";
 			};
 
@@ -1030,6 +1081,7 @@
 	};
 
 	usbphynop1: usbphynop1 {
+		#phy-cells = <0>;
 		compatible = "usb-nop-xceiv";
 		clocks = <&clk IMX8MN_CLK_USB_PHY_REF>;
 		assigned-clocks = <&clk IMX8MN_CLK_USB_PHY_REF>;
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/keystone-k2g-evm.dts b/arch/arm/dts/keystone-k2g-evm.dts
index 7c5deef..b5b511c 100644
--- a/arch/arm/dts/keystone-k2g-evm.dts
+++ b/arch/arm/dts/keystone-k2g-evm.dts
@@ -38,7 +38,6 @@
 
 &usb0_phy {
 	status = "okay";
-	compatible = "nop-phy";
 };
 
 &usb0 {
@@ -51,7 +50,6 @@
 };
 
 &usb1_phy {
-	compatible = "nop-phy";
 	status = "okay";
 };
 
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/configs/verdin-imx8mm_defconfig b/configs/verdin-imx8mm_defconfig
index ea0b597..c8c3420 100644
--- a/configs/verdin-imx8mm_defconfig
+++ b/configs/verdin-imx8mm_defconfig
@@ -37,7 +37,6 @@
 CONFIG_SPL_SEPARATE_BSS=y
 CONFIG_SPL_I2C_SUPPORT=y
 CONFIG_SPL_POWER_SUPPORT=y
-CONFIG_SPL_USB_HOST_SUPPORT=y
 CONFIG_SPL_WATCHDOG_SUPPORT=y
 CONFIG_SYS_PROMPT="Verdin iMX8MM # "
 # CONFIG_BOOTM_NETBSD is not set
@@ -50,6 +49,7 @@
 CONFIG_CMD_GPIO=y
 CONFIG_CMD_I2C=y
 CONFIG_CMD_MMC=y
+CONFIG_CMD_USB=y
 CONFIG_CMD_CACHE=y
 CONFIG_CMD_UUID=y
 CONFIG_CMD_REGULATOR=y
@@ -89,6 +89,8 @@
 CONFIG_PINCTRL=y
 CONFIG_SPL_PINCTRL=y
 CONFIG_PINCTRL_IMX8M=y
+CONFIG_POWER_DOMAIN=y
+CONFIG_IMX8M_POWER_DOMAIN=y
 CONFIG_DM_PMIC=y
 CONFIG_SPL_DM_PMIC_PCA9450=y
 CONFIG_DM_PMIC_PFUZE100=y
@@ -101,5 +103,9 @@
 CONFIG_SYSRESET_PSCI=y
 CONFIG_SYSRESET_WATCHDOG=y
 CONFIG_DM_THERMAL=y
+CONFIG_USB=y
+CONFIG_DM_USB=y
+# CONFIG_SPL_DM_USB is not set
+CONFIG_USB_EHCI_HCD=y
 CONFIG_IMX_WATCHDOG=y
 CONFIG_OF_LIBFDT_OVERLAY=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/phy/nop-phy.c b/drivers/phy/nop-phy.c
index 84aac80..9f12ebc 100644
--- a/drivers/phy/nop-phy.c
+++ b/drivers/phy/nop-phy.c
@@ -43,6 +43,7 @@
 
 static const struct udevice_id nop_phy_ids[] = {
 	{ .compatible = "nop-phy" },
+	{ .compatible = "usb-nop-xceiv" },
 	{ }
 };
 
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/power/domain/imx8m-power-domain.c b/drivers/power/domain/imx8m-power-domain.c
index c4cd07f..5d34bc1 100644
--- a/drivers/power/domain/imx8m-power-domain.c
+++ b/drivers/power/domain/imx8m-power-domain.c
@@ -120,6 +120,8 @@
 
 static const struct udevice_id imx8m_power_domain_ids[] = {
 	{ .compatible = "fsl,imx8mq-gpc" },
+	{ .compatible = "fsl,imx8mm-gpc" },
+	{ .compatible = "fsl,imx8mn-gpc" },
 	{ }
 };
 
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 0971a7c..bf5d82f 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -156,7 +156,9 @@
 
 config USB_EHCI_MX7
 	bool "Support for i.MX7 on-chip EHCI USB controller"
-	depends on ARCH_MX7
+	depends on ARCH_MX7 || IMX8M
+	select PHY if IMX8M
+	select NOP_PHY if IMX8M
 	default y
 	---help---
 	  Enables support for the on-chip EHCI controller on i.MX7 SoCs.
diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c
index aeea539..7642a31 100644
--- a/drivers/usb/host/ehci-mx6.c
+++ b/drivers/usb/host/ehci-mx6.c
@@ -5,6 +5,7 @@
  */
 
 #include <common.h>
+#include <clk.h>
 #include <log.h>
 #include <usb.h>
 #include <errno.h>
@@ -67,49 +68,41 @@
 #define UCMD_RUN_STOP           (1 << 0) /* controller run/stop */
 #define UCMD_RESET		(1 << 1) /* controller reset */
 
-#if defined(CONFIG_MX6) || defined(CONFIG_MX7ULP)
-static const unsigned phy_bases[] = {
-	USB_PHY0_BASE_ADDR,
-#if defined(USB_PHY1_BASE_ADDR)
-	USB_PHY1_BASE_ADDR,
+/* If this is not defined, assume MX6/MX7/MX8M SoC default */
+#ifndef CONFIG_MXC_USB_PORTSC
+#define CONFIG_MXC_USB_PORTSC	(PORT_PTS_UTMI | PORT_PTS_PTW)
 #endif
-};
 
-static void usb_internal_phy_clock_gate(int index, int on)
-{
-	void __iomem *phy_reg;
-
-	if (index >= ARRAY_SIZE(phy_bases))
-		return;
-
-	phy_reg = (void __iomem *)phy_bases[index];
-	phy_reg += on ? USBPHY_CTRL_CLR : USBPHY_CTRL_SET;
-	writel(USBPHY_CTRL_CLKGATE, phy_reg);
-}
+/* Base address for this IP block is 0x02184800 */
+struct usbnc_regs {
+	u32 ctrl[4]; /* otg/host1-3 */
+	u32 uh2_hsic_ctrl;
+	u32 uh3_hsic_ctrl;
+	u32 otg_phy_ctrl_0;
+	u32 uh1_phy_ctrl_0;
+	u32 reserve1[4];
+	u32 phy_cfg1;
+	u32 phy_cfg2;
+	u32 reserve2;
+	u32 phy_status;
+	u32 reserve3[4];
+	u32 adp_cfg1;
+	u32 adp_cfg2;
+	u32 adp_status;
+};
 
-static void usb_power_config(int index)
+#if defined(CONFIG_MX6) && !defined(CONFIG_PHY)
+static void usb_power_config_mx6(struct anatop_regs __iomem *anatop,
+				 int anatop_bits_index)
 {
-#if defined(CONFIG_MX7ULP)
-	struct usbphy_regs __iomem *usbphy =
-		(struct usbphy_regs __iomem *)USB_PHY0_BASE_ADDR;
-
-	if (index > 0)
-		return;
-
-	writel(ANADIG_USB2_CHRG_DETECT_EN_B |
-		   ANADIG_USB2_CHRG_DETECT_CHK_CHRG_B,
-		   &usbphy->usb1_chrg_detect);
-
-	scg_enable_usb_pll(true);
-
-#else
-	struct anatop_regs __iomem *anatop =
-		(struct anatop_regs __iomem *)ANATOP_BASE_ADDR;
 	void __iomem *chrg_detect;
 	void __iomem *pll_480_ctrl_clr;
 	void __iomem *pll_480_ctrl_set;
 
-	switch (index) {
+	if (!is_mx6())
+		return;
+
+	switch (anatop_bits_index) {
 	case 0:
 		chrg_detect = &anatop->usb1_chrg_detect;
 		pll_480_ctrl_clr = &anatop->usb1_pll_480_ctrl_clr;
@@ -142,22 +135,70 @@
 		     ANADIG_USB2_PLL_480_CTRL_POWER |
 		     ANADIG_USB2_PLL_480_CTRL_EN_USB_CLKS,
 		     pll_480_ctrl_set);
+}
+#else
+static void __maybe_unused
+usb_power_config_mx6(void *anatop, int anatop_bits_index) { }
+#endif
+
+#if defined(CONFIG_MX7) && !defined(CONFIG_PHY)
+static void usb_power_config_mx7(struct usbnc_regs *usbnc)
+{
+	void __iomem *phy_cfg2 = (void __iomem *)(&usbnc->phy_cfg2);
+
+	if (!is_mx7())
+		return;
+
+	/*
+	 * Clear the ACAENB to enable usb_otg_id detection,
+	 * otherwise it is the ACA detection enabled.
+	 */
+	clrbits_le32(phy_cfg2, USBNC_PHYCFG2_ACAENB);
+}
+#else
+static void __maybe_unused
+usb_power_config_mx7(void *usbnc) { }
+#endif
+
+#if defined(CONFIG_MX7ULP) && !defined(CONFIG_PHY)
+static void usb_power_config_mx7ulp(struct usbphy_regs __iomem *usbphy)
+{
+	if (!is_mx7ulp())
+		return;
+
+	writel(ANADIG_USB2_CHRG_DETECT_EN_B |
+	       ANADIG_USB2_CHRG_DETECT_CHK_CHRG_B,
+	       &usbphy->usb1_chrg_detect);
 
+	scg_enable_usb_pll(true);
+}
+#else
+static void __maybe_unused
+usb_power_config_mx7ulp(void *usbphy) { }
 #endif
+
+#if defined(CONFIG_MX6) || defined(CONFIG_MX7ULP)
+static const unsigned phy_bases[] = {
+	USB_PHY0_BASE_ADDR,
+#if defined(USB_PHY1_BASE_ADDR)
+	USB_PHY1_BASE_ADDR,
+#endif
+};
+
+#if !defined(CONFIG_PHY)
+static void usb_internal_phy_clock_gate(void __iomem *phy_reg, int on)
+{
+	phy_reg += on ? USBPHY_CTRL_CLR : USBPHY_CTRL_SET;
+	writel(USBPHY_CTRL_CLKGATE, phy_reg);
 }
 
 /* Return 0 : host node, <>0 : device mode */
-static int usb_phy_enable(int index, struct usb_ehci *ehci)
+static int usb_phy_enable(struct usb_ehci *ehci, void __iomem *phy_reg)
 {
-	void __iomem *phy_reg;
 	void __iomem *phy_ctrl;
 	void __iomem *usb_cmd;
 	int ret;
 
-	if (index >= ARRAY_SIZE(phy_bases))
-		return 0;
-
-	phy_reg = (void __iomem *)phy_bases[index];
 	phy_ctrl = (void __iomem *)(phy_reg + USBPHY_CTRL);
 	usb_cmd = (void __iomem *)&ehci->usbcmd;
 
@@ -188,6 +229,7 @@
 
 	return 0;
 }
+#endif
 
 int usb_phy_mode(int port)
 {
@@ -206,52 +248,7 @@
 		return USB_INIT_HOST;
 }
 
-#if defined(CONFIG_MX7ULP)
-struct usbnc_regs {
-	u32 ctrl1;
-	u32 ctrl2;
-	u32 reserve0[2];
-	u32 hsic_ctrl;
-};
-#else
-/* Base address for this IP block is 0x02184800 */
-struct usbnc_regs {
-	u32	ctrl[4];	/* otg/host1-3 */
-	u32	uh2_hsic_ctrl;
-	u32	uh3_hsic_ctrl;
-	u32	otg_phy_ctrl_0;
-	u32	uh1_phy_ctrl_0;
-};
-#endif
-
 #elif defined(CONFIG_MX7)
-struct usbnc_regs {
-	u32 ctrl1;
-	u32 ctrl2;
-	u32 reserve1[10];
-	u32 phy_cfg1;
-	u32 phy_cfg2;
-	u32 reserve2;
-	u32 phy_status;
-	u32 reserve3[4];
-	u32 adp_cfg1;
-	u32 adp_cfg2;
-	u32 adp_status;
-};
-
-static void usb_power_config(int index)
-{
-	struct usbnc_regs *usbnc = (struct usbnc_regs *)(USB_BASE_ADDR +
-			(0x10000 * index) + USBNC_OFFSET);
-	void __iomem *phy_cfg2 = (void __iomem *)(&usbnc->phy_cfg2);
-
-	/*
-	 * Clear the ACAENB to enable usb_otg_id detection,
-	 * otherwise it is the ACA detection enabled.
-	 */
-	clrbits_le32(phy_cfg2, USBNC_PHYCFG2_ACAENB);
-}
-
 int usb_phy_mode(int port)
 {
 	struct usbnc_regs *usbnc = (struct usbnc_regs *)(USB_BASE_ADDR +
@@ -268,17 +265,9 @@
 }
 #endif
 
-static void usb_oc_config(int index)
+static void usb_oc_config(struct usbnc_regs *usbnc, int index)
 {
-#if defined(CONFIG_MX6)
-	struct usbnc_regs *usbnc = (struct usbnc_regs *)(USB_BASE_ADDR +
-			USB_OTHERREGS_OFFSET);
 	void __iomem *ctrl = (void __iomem *)(&usbnc->ctrl[index]);
-#elif defined(CONFIG_MX7) || defined(CONFIG_MX7ULP)
-	struct usbnc_regs *usbnc = (struct usbnc_regs *)(USB_BASE_ADDR +
-			(0x10000 * index) + USBNC_OFFSET);
-	void __iomem *ctrl = (void __iomem *)(&usbnc->ctrl1);
-#endif
 
 #if CONFIG_MACH_TYPE == MACH_TYPE_MX6Q_ARM2
 	/* mx6qarm2 seems to required a different setting*/
@@ -297,6 +286,7 @@
 #endif
 }
 
+#if !CONFIG_IS_ENABLED(DM_USB)
 /**
  * board_usb_phy_mode - override usb phy mode
  * @port:	usb host/otg port
@@ -340,41 +330,29 @@
  */
 int __weak board_ehci_power(int port, int on)
 {
-	return 0;
-}
-
-int ehci_mx6_common_init(struct usb_ehci *ehci, int index)
-{
-	int ret;
-
-	enable_usboh3_clk(1);
-	mdelay(1);
-
-	/* Do board specific initialization */
-	ret = board_ehci_hcd_init(index);
-	if (ret)
-		return ret;
-
-	usb_power_config(index);
-	usb_oc_config(index);
-
-#if defined(CONFIG_MX6) || defined(CONFIG_MX7ULP)
-	usb_internal_phy_clock_gate(index, 1);
-	usb_phy_enable(index, ehci);
-#endif
-
 	return 0;
 }
 
-#if !CONFIG_IS_ENABLED(DM_USB)
 int ehci_hcd_init(int index, enum usb_init_type init,
 		struct ehci_hccr **hccr, struct ehci_hcor **hcor)
 {
 	enum usb_init_type type;
 #if defined(CONFIG_MX6)
 	u32 controller_spacing = 0x200;
-#elif defined(CONFIG_MX7) || defined(CONFIG_MX7ULP)
+	struct anatop_regs __iomem *anatop =
+		(struct anatop_regs __iomem *)ANATOP_BASE_ADDR;
+	struct usbnc_regs *usbnc = (struct usbnc_regs *)(USB_BASE_ADDR +
+			USB_OTHERREGS_OFFSET);
+#elif defined(CONFIG_MX7)
 	u32 controller_spacing = 0x10000;
+	struct usbnc_regs *usbnc = (struct usbnc_regs *)(USB_BASE_ADDR +
+			(0x10000 * index) + USBNC_OFFSET);
+#elif defined(CONFIG_MX7ULP)
+	u32 controller_spacing = 0x10000;
+	struct usbphy_regs __iomem *usbphy =
+		(struct usbphy_regs __iomem *)USB_PHY0_BASE_ADDR;
+	struct usbnc_regs *usbnc = (struct usbnc_regs *)(USB_BASE_ADDR +
+			(0x10000 * index) + USBNC_OFFSET);
 #endif
 	struct usb_ehci *ehci = (struct usb_ehci *)(USB_BASE_ADDR +
 		(controller_spacing * index));
@@ -391,15 +369,38 @@
 		}
 	}
 
-	ret = ehci_mx6_common_init(ehci, index);
-	if (ret)
+	enable_usboh3_clk(1);
+	mdelay(1);
+
+	/* Do board specific initialization */
+	ret = board_ehci_hcd_init(index);
+	if (ret) {
+		enable_usboh3_clk(0);
 		return ret;
+	}
+
+#if defined(CONFIG_MX6)
+	usb_power_config_mx6(anatop, index);
+#elif defined (CONFIG_MX7)
+	usb_power_config_mx7(usbnc);
+#elif defined (CONFIG_MX7ULP)
+	usb_power_config_mx7ulp(usbphy);
+#endif
+
+	usb_oc_config(usbnc, index);
+
+#if defined(CONFIG_MX6) || defined(CONFIG_MX7ULP)
+	if (index < ARRAY_SIZE(phy_bases)) {
+		usb_internal_phy_clock_gate((void __iomem *)phy_bases[index], 1);
+		usb_phy_enable(ehci, (void __iomem *)phy_bases[index]);
+	}
+#endif
 
 	type = board_usb_phy_mode(index);
 
 	if (hccr && hcor) {
-		*hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
-		*hcor = (struct ehci_hcor *)((uint32_t)*hccr +
+		*hccr = (struct ehci_hccr *)((uintptr_t)&ehci->caplength);
+		*hcor = (struct ehci_hcor *)((uintptr_t)*hccr +
 				HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
 	}
 
@@ -428,8 +429,13 @@
 	struct ehci_ctrl ctrl;
 	struct usb_ehci *ehci;
 	struct udevice *vbus_supply;
+	struct clk clk;
+	struct phy phy;
 	enum usb_init_type init_type;
 	int portnr;
+	void __iomem *phy_addr;
+	void __iomem *misc_addr;
+	void __iomem *anatop_addr;
 };
 
 static int mx6_init_after_reset(struct ehci_ctrl *dev)
@@ -437,14 +443,23 @@
 	struct ehci_mx6_priv_data *priv = dev->priv;
 	enum usb_init_type type = priv->init_type;
 	struct usb_ehci *ehci = priv->ehci;
-	int ret;
 
-	ret = ehci_mx6_common_init(priv->ehci, priv->portnr);
-	if (ret)
-		return ret;
+#if !defined(CONFIG_PHY)
+	usb_power_config_mx6(priv->anatop_addr, priv->portnr);
+	usb_power_config_mx7(priv->misc_addr);
+	usb_power_config_mx7ulp(priv->phy_addr);
+#endif
+
+	usb_oc_config(priv->misc_addr, priv->portnr);
+
+#if !defined(CONFIG_PHY) && (defined(CONFIG_MX6) || defined(CONFIG_MX7ULP))
+	usb_internal_phy_clock_gate(priv->phy_addr, 1);
+	usb_phy_enable(ehci, priv->phy_addr);
+#endif
 
 #if CONFIG_IS_ENABLED(DM_REGULATOR)
 	if (priv->vbus_supply) {
+		int ret;
 		ret = regulator_set_enable(priv->vbus_supply,
 					   (type == USB_INIT_DEVICE) ?
 					   false : true);
@@ -541,46 +556,58 @@
 	return 0;
 }
 
-static int ehci_usb_bind(struct udevice *dev)
+static int mx6_parse_dt_addrs(struct udevice *dev)
 {
-	/*
-	 * TODO:
-	 * This driver is only partly converted to DT probing and still uses
-	 * a tremendous amount of hard-coded addresses. To make things worse,
-	 * the driver depends on specific sequential indexing of controllers,
-	 * from which it derives offsets in the PHY and ANATOP register sets.
-	 *
-	 * Here we attempt to calculate these indexes from DT information as
-	 * well as we can. The USB controllers on all existing iMX6 SoCs
-	 * are placed next to each other, at addresses incremented by 0x200,
-	 * and iMX7 their addresses are shifted by 0x10000.
-	 * Thus, the index is derived from the multiple of 0x200 (0x10000 for
-	 * iMX7) offset from the first controller address.
-	 *
-	 * However, to complete conversion of this driver to DT probing, the
-	 * following has to be done:
-	 * - DM clock framework support for iMX must be implemented
-	 * - usb_power_config() has to be converted to clock framework
-	 *   -> Thus, the ad-hoc "index" variable goes away.
-	 * - USB PHY handling has to be factored out into separate driver
-	 *   -> Thus, the ad-hoc "index" variable goes away from the PHY
-	 *      code, the PHY driver must parse it's address from DT. This
-	 *      USB driver must find the PHY driver via DT phandle.
-	 *   -> usb_power_config() shall be moved to PHY driver
-	 * With these changes in place, the ad-hoc indexing goes away and
-	 * the driver is fully converted to DT probing.
-	 */
+	struct ehci_mx6_priv_data *priv = dev_get_priv(dev);
+	int phy_off, misc_off;
+	const void *blob = gd->fdt_blob;
+	int offset = dev_of_offset(dev);
+	void *__iomem addr;
+	int ret, devnump;
 
-	/*
-	 * FIXME: This cannot work with the new sequence numbers.
-	 * Please complete the DM conversion.
-	 *
-	 * u32 controller_spacing = is_mx7() ? 0x10000 : 0x200;
-	 * fdt_addr_t addr = devfdt_get_addr_index(dev, 0);
-	 *
-	 * dev->req_seq = (addr - USB_BASE_ADDR) / controller_spacing;
-	 */
+	phy_off = fdtdec_lookup_phandle(blob, offset, "fsl,usbphy");
+	if (phy_off < 0) {
+		phy_off = fdtdec_lookup_phandle(blob, offset, "phys");
+		if (phy_off < 0)
+			return -EINVAL;
+	}
 
+	ret = fdtdec_get_alias_seq(blob, dev->uclass->uc_drv->name,
+				   phy_off, &devnump);
+	if (ret < 0)
+		return ret;
+
+	misc_off = fdtdec_lookup_phandle(blob, offset, "fsl,usbmisc");
+	if (misc_off < 0)
+		return -EINVAL;
+
+	addr = (void __iomem *)fdtdec_get_addr(blob, phy_off, "reg");
+	if ((fdt_addr_t)addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	priv->phy_addr = addr;
+	priv->portnr = devnump;
+
+	addr = (void __iomem *)fdtdec_get_addr(blob, misc_off, "reg");
+	if ((fdt_addr_t)addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	priv->misc_addr = addr;
+
+#if !defined(CONFIG_PHY) && defined(CONFIG_MX6)
+	int anatop_off;
+
+	/* Resolve ANATOP offset through USB PHY node */
+	anatop_off = fdtdec_lookup_phandle(blob, phy_off, "fsl,anatop");
+	if (anatop_off < 0)
+		return -EINVAL;
+
+	addr = (void __iomem *)fdtdec_get_addr(blob, anatop_off, "reg");
+	if ((fdt_addr_t)addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	priv->anatop_addr = addr;
+#endif
 	return 0;
 }
 
@@ -602,19 +629,46 @@
 		}
 	}
 
+	ret = mx6_parse_dt_addrs(dev);
+	if (ret)
+		return ret;
+
 	priv->ehci = ehci;
-	priv->portnr = dev_seq(dev);
 	priv->init_type = type;
 
+#if CONFIG_IS_ENABLED(CLK)
+	ret = clk_get_by_index(dev, 0, &priv->clk);
+	if (ret < 0)
+		return ret;
+
+	ret = clk_enable(&priv->clk);
+	if (ret)
+		return ret;
+#else
+	/* Compatibility with DM_USB and !CLK */
+	enable_usboh3_clk(1);
+	mdelay(1);
+#endif
+
 #if CONFIG_IS_ENABLED(DM_REGULATOR)
 	ret = device_get_supply_regulator(dev, "vbus-supply",
 					  &priv->vbus_supply);
 	if (ret)
 		debug("%s: No vbus supply\n", dev->name);
 #endif
-	ret = ehci_mx6_common_init(ehci, priv->portnr);
-	if (ret)
-		return ret;
+
+#if !defined(CONFIG_PHY)
+	usb_power_config_mx6(priv->anatop_addr, priv->portnr);
+	usb_power_config_mx7(priv->misc_addr);
+	usb_power_config_mx7ulp(priv->phy_addr);
+#endif
+
+	usb_oc_config(priv->misc_addr, priv->portnr);
+
+#if !defined(CONFIG_PHY) && (defined(CONFIG_MX6) || defined(CONFIG_MX7ULP))
+	usb_internal_phy_clock_gate(priv->phy_addr, 1);
+	usb_phy_enable(ehci, priv->phy_addr);
+#endif
 
 #if CONFIG_IS_ENABLED(DM_REGULATOR)
 	if (priv->vbus_supply) {
@@ -623,7 +677,7 @@
 					   false : true);
 		if (ret && ret != -ENOSYS) {
 			printf("Error enabling VBUS supply (ret=%i)\n", ret);
-			return ret;
+			goto err_clk;
 		}
 	}
 #endif
@@ -636,15 +690,66 @@
 
 	mdelay(10);
 
-	hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
-	hcor = (struct ehci_hcor *)((uint32_t)hccr +
+#if defined(CONFIG_PHY)
+	ret = ehci_setup_phy(dev, &priv->phy, 0);
+	if (ret)
+		goto err_regulator;
+#endif
+
+	hccr = (struct ehci_hccr *)((uintptr_t)&ehci->caplength);
+	hcor = (struct ehci_hcor *)((uintptr_t)hccr +
 			HC_LENGTH(ehci_readl(&(hccr)->cr_capbase)));
 
+	ret = ehci_register(dev, hccr, hcor, &mx6_ehci_ops, 0, priv->init_type);
+	if (ret)
+		goto err_phy;
+
+	return ret;
+
+err_phy:
+#if defined(CONFIG_PHY)
+	ehci_shutdown_phy(dev, &priv->phy);
+err_regulator:
+#endif
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+	if (priv->vbus_supply)
+		regulator_set_enable(priv->vbus_supply, false);
+err_clk:
+#endif
+#if CONFIG_IS_ENABLED(CLK)
+	clk_disable(&priv->clk);
+#else
+	/* Compatibility with DM_USB and !CLK */
+	enable_usboh3_clk(0);
+#endif
+	return ret;
+}
+
-	return ehci_register(dev, hccr, hcor, &mx6_ehci_ops, 0, priv->init_type);
+int ehci_usb_remove(struct udevice *dev)
+{
+	struct ehci_mx6_priv_data *priv __maybe_unused = dev_get_priv(dev);
+
+	ehci_deregister(dev);
+
+#if defined(CONFIG_PHY)
+	ehci_shutdown_phy(dev, &priv->phy);
+#endif
+
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+	if (priv->vbus_supply)
+		regulator_set_enable(priv->vbus_supply, false);
+#endif
+
+#if CONFIG_IS_ENABLED(CLK)
+	clk_disable(&priv->clk);
+#endif
+
+	return 0;
 }
 
 static const struct udevice_id mx6_usb_ids[] = {
 	{ .compatible = "fsl,imx27-usb" },
+	{ .compatible = "fsl,imx7d-usb" },
 	{ }
 };
 
@@ -653,9 +758,8 @@
 	.id	= UCLASS_USB,
 	.of_match = mx6_usb_ids,
 	.of_to_plat = ehci_usb_of_to_plat,
-	.bind	= ehci_usb_bind,
 	.probe	= ehci_usb_probe,
-	.remove = ehci_deregister,
+	.remove = ehci_usb_remove,
 	.ops	= &ehci_usb_ops,
 	.plat_auto	= sizeof(struct usb_plat),
 	.priv_auto	= sizeof(struct ehci_mx6_priv_data),
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/configs/verdin-imx8mm.h b/include/configs/verdin-imx8mm.h
index 4751bf5..e2a8178 100644
--- a/include/configs/verdin-imx8mm.h
+++ b/include/configs/verdin-imx8mm.h
@@ -117,5 +117,10 @@
 #define FEC_QUIRK_ENET_MAC
 #define IMX_FEC_BASE			0x30BE0000
 
+/* USB Configs */
+#define CONFIG_EHCI_HCD_INIT_AFTER_RESET
+#define CONFIG_MXC_USB_PORTSC	(PORT_PTS_UTMI | PORT_PTS_PTW)
+#define CONFIG_USB_MAX_CONTROLLER_COUNT 2
+
 #endif /*_VERDIN_IMX8MM_H */
 
diff --git a/include/dt-bindings/power/imx8mm-power.h b/include/dt-bindings/power/imx8mm-power.h
new file mode 100644
index 0000000..fc9c2e1
--- /dev/null
+++ b/include/dt-bindings/power/imx8mm-power.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ *  Copyright (C) 2020 Pengutronix, Lucas Stach <kernel@pengutronix.de>
+ */
+
+#ifndef __DT_BINDINGS_IMX8MM_POWER_H__
+#define __DT_BINDINGS_IMX8MM_POWER_H__
+
+#define IMX8MM_POWER_DOMAIN_HSIOMIX	0
+#define IMX8MM_POWER_DOMAIN_PCIE	1
+#define IMX8MM_POWER_DOMAIN_OTG1	2
+#define IMX8MM_POWER_DOMAIN_OTG2	3
+#define IMX8MM_POWER_DOMAIN_GPUMIX	4
+#define IMX8MM_POWER_DOMAIN_GPU		5
+#define IMX8MM_POWER_DOMAIN_VPUMIX	6
+#define IMX8MM_POWER_DOMAIN_VPUG1	7
+#define IMX8MM_POWER_DOMAIN_VPUG2	8
+#define IMX8MM_POWER_DOMAIN_VPUH1	9
+#define IMX8MM_POWER_DOMAIN_DISPMIX	10
+#define IMX8MM_POWER_DOMAIN_MIPI	11
+
+#endif
diff --git a/include/dt-bindings/power/imx8mn-power.h b/include/dt-bindings/power/imx8mn-power.h
new file mode 100644
index 0000000..102ee85
--- /dev/null
+++ b/include/dt-bindings/power/imx8mn-power.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ *  Copyright (C) 2020 Compass Electronics Group, LLC
+ */
+
+#ifndef __DT_BINDINGS_IMX8MN_POWER_H__
+#define __DT_BINDINGS_IMX8MN_POWER_H__
+
+#define IMX8MN_POWER_DOMAIN_HSIOMIX	0
+#define IMX8MN_POWER_DOMAIN_OTG1	1
+#define IMX8MN_POWER_DOMAIN_GPUMIX	2
+#define IMX8MN_POWER_DOMAIN_DISPMIX	3
+#define IMX8MN_POWER_DOMAIN_MIPI	4
+
+#endif
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);