Merge branch '2022-04-04-platform-updates'

- Updates for exynos78x0 and TI K3 platforms
diff --git a/MAINTAINERS b/MAINTAINERS
index aca97cd..0fc034f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -521,7 +521,9 @@
 F:	board/sunxi/
 F:	drivers/clk/sunxi/
 F:	drivers/phy/allwinner/
+F:	drivers/pinctrl/sunxi/
 F:	drivers/video/sunxi/
+F:	tools/sunxi*
 
 ARM TEGRA
 M:	Tom Warren <twarren@nvidia.com>
@@ -656,6 +658,7 @@
 F:	drivers/net/zynq_gem.c
 F:	drivers/phy/phy-zynqmp.c
 F:	drivers/power/domain/zynqmp-power-domain.c
+F:	drivers/pwm/pwm-cadence-ttc.c
 F:	drivers/serial/serial_zynq.c
 F:	drivers/reset/reset-zynqmp.c
 F:	drivers/rtc/zynqmp_rtc.c
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index f277929..6771f14 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1124,6 +1124,7 @@
 	select OF_BOARD_SETUP
 	select OF_CONTROL
 	select OF_SEPARATE
+	select PINCTRL
 	select SPECIFY_CONSOLE_INDEX
 	select SPL_SEPARATE_BSS if SPL
 	select SPL_STACK_R if SPL
diff --git a/arch/arm/dts/sun8i-h3-nanopi-neo.dts b/arch/arm/dts/sun8i-h3-nanopi-neo.dts
index 9f33f6f..df71fab 100644
--- a/arch/arm/dts/sun8i-h3-nanopi-neo.dts
+++ b/arch/arm/dts/sun8i-h3-nanopi-neo.dts
@@ -45,6 +45,10 @@
 / {
 	model = "FriendlyARM NanoPi NEO";
 	compatible = "friendlyarm,nanopi-neo", "allwinner,sun8i-h3";
+
+	aliases {
+		ethernet0 = &emac;
+	};
 };
 
 &ehci0 {
diff --git a/arch/arm/dts/zynqmp.dtsi b/arch/arm/dts/zynqmp.dtsi
index 755a4ed..c442608 100644
--- a/arch/arm/dts/zynqmp.dtsi
+++ b/arch/arm/dts/zynqmp.dtsi
@@ -742,6 +742,7 @@
 			#clock-cells = <1>;
 			clock-output-names = "clk_out_sd0", "clk_in_sd0";
 			power-domains = <&zynqmp_firmware PD_SD_0>;
+			resets = <&zynqmp_reset ZYNQMP_RESET_SDIO0>;
 		};
 
 		sdhci1: mmc@ff170000 {
@@ -758,6 +759,7 @@
 			#clock-cells = <1>;
 			clock-output-names = "clk_out_sd1", "clk_in_sd1";
 			power-domains = <&zynqmp_firmware PD_SD_1>;
+			resets = <&zynqmp_reset ZYNQMP_RESET_SDIO1>;
 		};
 
 		smmu: iommu@fd800000 {
diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h b/arch/arm/include/asm/arch-sunxi/gpio.h
index edd0fbf..437e864 100644
--- a/arch/arm/include/asm/arch-sunxi/gpio.h
+++ b/arch/arm/include/asm/arch-sunxi/gpio.h
@@ -135,17 +135,13 @@
 #define SUNXI_GPIO_OUTPUT	1
 #define SUNXI_GPIO_DISABLE	7
 
-#define SUNXI_GPA_EMAC		2
-#define SUN6I_GPA_GMAC		2
-#define SUN7I_GPA_GMAC		5
 #define SUN8I_H3_GPA_UART0	2
+#define SUN8I_H3_GPA_UART2	2
 
 #define SUN4I_GPB_PWM		2
 #define SUN4I_GPB_TWI0		2
 #define SUN4I_GPB_TWI1		2
 #define SUN5I_GPB_TWI1		2
-#define SUN4I_GPB_TWI2		2
-#define SUN5I_GPB_TWI2		2
 #define SUN8I_V3S_GPB_TWI0	2
 #define SUN4I_GPB_UART0		2
 #define SUN5I_GPB_UART0		2
@@ -164,11 +160,8 @@
 
 #define SUNXI_GPD_LCD0		2
 #define SUNXI_GPD_LVDS0		3
-#define SUNXI_GPD_PWM		2
 
 #define SUNIV_GPE_UART0		5
-#define SUN8I_GPE_TWI2		3
-#define SUN50I_GPE_TWI2		3
 
 #define SUNXI_GPF_SDC0		2
 #define SUNXI_GPF_UART0		4
@@ -179,7 +172,6 @@
 #define SUN6I_GPG_SDC1		2
 #define SUN8I_GPG_SDC1		2
 #define SUN8I_GPG_UART1		2
-#define SUN6I_GPG_TWI3		2
 #define SUN5I_GPG_UART1		4
 
 #define SUN6I_GPH_PWM		2
@@ -191,15 +183,12 @@
 #define SUN6I_GPH_TWI1		2
 #define SUN8I_GPH_TWI1		2
 #define SUN50I_GPH_TWI1		2
-#define SUN6I_GPH_TWI2		2
 #define SUN6I_GPH_UART0		2
 #define SUN9I_GPH_UART0		2
 #define SUN50I_H6_GPH_UART0	2
 #define SUN50I_H616_GPH_UART0	2
 
 #define SUNXI_GPI_SDC3		2
-#define SUN7I_GPI_TWI3		3
-#define SUN7I_GPI_TWI4		3
 
 #define SUN6I_GPL0_R_P2WI_SCK	3
 #define SUN6I_GPL1_R_P2WI_SDA	3
@@ -224,6 +213,11 @@
 #define SUNXI_GPIO_AXP0_VBUS_ENABLE	5
 #define SUNXI_GPIO_AXP0_GPIO_COUNT	6
 
+struct sunxi_gpio_plat {
+	struct sunxi_gpio	*regs;
+	char			bank_name[3];
+};
+
 void sunxi_gpio_set_cfgbank(struct sunxi_gpio *pio, int bank_offset, u32 val);
 void sunxi_gpio_set_cfgpin(u32 pin, u32 val);
 int sunxi_gpio_get_cfgbank(struct sunxi_gpio *pio, int bank_offset);
diff --git a/arch/arm/include/asm/arch-sunxi/i2c.h b/arch/arm/include/asm/arch-sunxi/i2c.h
index 1cb2ba6..3525f22 100644
--- a/arch/arm/include/asm/arch-sunxi/i2c.h
+++ b/arch/arm/include/asm/arch-sunxi/i2c.h
@@ -13,17 +13,8 @@
 #ifdef CONFIG_I2C1_ENABLE
 #define CONFIG_I2C_MVTWSI_BASE1	SUNXI_TWI1_BASE
 #endif
-#ifdef CONFIG_I2C2_ENABLE
-#define CONFIG_I2C_MVTWSI_BASE2	SUNXI_TWI2_BASE
-#endif
-#ifdef CONFIG_I2C3_ENABLE
-#define CONFIG_I2C_MVTWSI_BASE3	SUNXI_TWI3_BASE
-#endif
-#ifdef CONFIG_I2C4_ENABLE
-#define CONFIG_I2C_MVTWSI_BASE4	SUNXI_TWI4_BASE
-#endif
 #ifdef CONFIG_R_I2C_ENABLE
-#define CONFIG_I2C_MVTWSI_BASE5 SUNXI_R_TWI_BASE
+#define CONFIG_I2C_MVTWSI_BASE2 SUNXI_R_TWI_BASE
 #endif
 
 /* This is abp0-clk on sun4i/5i/7i / abp1-clk on sun6i/sun8i which is 24MHz */
diff --git a/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h b/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h
index 5f636e8..fd63d3a 100644
--- a/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h
+++ b/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h
@@ -37,8 +37,18 @@
 	u32 w1_gate_reset;	/* 0x1ec */
 	u8 res10[0x1c];		/* 0x1f0 */
 	u32 rtc_gate_reset;	/* 0x20c */
+	u8 res11[0x34];		/* 0x210 */
+	u32 pll_ldo_cfg;	/* 0x244 */
+	u8 res12[0x8];		/* 0x248 */
+	u32 sys_pwroff_gating;	/* 0x250 */
+	u8 res13[0xbc];		/* 0x254 */
+	u32 res_cal_ctrl;	/* 0x310 */
+	u32 ohms200;		/* 0x314 */
+	u32 ohms240;		/* 0x318 */
+	u32 res_cal_status;	/* 0x31c */
 };
 check_member(sunxi_prcm_reg, rtc_gate_reset, 0x20c);
+check_member(sunxi_prcm_reg, res_cal_status, 0x31c);
 
 #define PRCM_TWI_GATE		(1 << 0)
 #define PRCM_TWI_RESET		(1 << 16)
diff --git a/arch/arm/include/asm/arch-sunxi/spl.h b/arch/arm/include/asm/arch-sunxi/spl.h
index b543d24..14944a2 100644
--- a/arch/arm/include/asm/arch-sunxi/spl.h
+++ b/arch/arm/include/asm/arch-sunxi/spl.h
@@ -28,8 +28,7 @@
 #define SUNIV_BOOTED_FROM_SPI	0xffff4130
 #define SUNIV_BOOTED_FROM_MMC1	0xffff4150
 
-#define is_boot0_magic(addr)	(memcmp((void *)(addr), BOOT0_MAGIC, 8) == 0)
-
 uint32_t sunxi_get_boot_device(void);
+uint32_t sunxi_get_spl_size(void);
 
 #endif
diff --git a/arch/arm/mach-imx/spl.c b/arch/arm/mach-imx/spl.c
index 2832b73..64ca296 100644
--- a/arch/arm/mach-imx/spl.c
+++ b/arch/arm/mach-imx/spl.c
@@ -201,7 +201,7 @@
 
 #if defined(CONFIG_SPL_MMC)
 /* called from spl_mmc to see type of boot mode for storage (RAW or FAT) */
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
 {
 #if defined(CONFIG_MX7) || defined(CONFIG_IMX8M) || defined(CONFIG_IMX8)
 	switch (get_boot_device()) {
diff --git a/arch/arm/mach-k3/am642_init.c b/arch/arm/mach-k3/am642_init.c
index 3a75dce..add7ea8 100644
--- a/arch/arm/mach-k3/am642_init.c
+++ b/arch/arm/mach-k3/am642_init.c
@@ -240,7 +240,7 @@
 	}
 }
 
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
 {
 	switch (boot_device) {
 	case BOOT_DEVICE_MMC1:
diff --git a/arch/arm/mach-k3/am6_init.c b/arch/arm/mach-k3/am6_init.c
index 8a6b1de..86c1a34 100644
--- a/arch/arm/mach-k3/am6_init.c
+++ b/arch/arm/mach-k3/am6_init.c
@@ -269,7 +269,7 @@
 	spl_enable_dcache();
 }
 
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
 {
 #if defined(CONFIG_SUPPORT_EMMC_BOOT)
 	u32 devstat = readl(CTRLMMR_MAIN_DEVSTAT);
diff --git a/arch/arm/mach-k3/j721e_init.c b/arch/arm/mach-k3/j721e_init.c
index c4b6b18..f503f15 100644
--- a/arch/arm/mach-k3/j721e_init.c
+++ b/arch/arm/mach-k3/j721e_init.c
@@ -291,7 +291,7 @@
 	spl_enable_dcache();
 }
 
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
 {
 	switch (boot_device) {
 	case BOOT_DEVICE_MMC1:
diff --git a/arch/arm/mach-k3/j721s2_init.c b/arch/arm/mach-k3/j721s2_init.c
index 58a8654..2e64e44 100644
--- a/arch/arm/mach-k3/j721s2_init.c
+++ b/arch/arm/mach-k3/j721s2_init.c
@@ -173,7 +173,7 @@
 	spl_enable_dcache();
 }
 
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
 {
 	switch (boot_device) {
 	case BOOT_DEVICE_MMC1:
diff --git a/arch/arm/mach-mvebu/spl.c b/arch/arm/mach-mvebu/spl.c
index 5ad323f..fa9a1d7 100644
--- a/arch/arm/mach-mvebu/spl.c
+++ b/arch/arm/mach-mvebu/spl.c
@@ -96,7 +96,7 @@
 } __packed;
 
 #ifdef CONFIG_SPL_MMC
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
 {
 	return MMCSD_MODE_RAW;
 }
diff --git a/arch/arm/mach-omap2/boot-common.c b/arch/arm/mach-omap2/boot-common.c
index afc3585..c463c96 100644
--- a/arch/arm/mach-omap2/boot-common.c
+++ b/arch/arm/mach-omap2/boot-common.c
@@ -196,7 +196,7 @@
 	return gd->arch.omap_boot_device;
 }
 
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
 {
 	return gd->arch.omap_boot_mode;
 }
diff --git a/arch/arm/mach-rockchip/spl.c b/arch/arm/mach-rockchip/spl.c
index 7a8db63..d51a072 100644
--- a/arch/arm/mach-rockchip/spl.c
+++ b/arch/arm/mach-rockchip/spl.c
@@ -66,7 +66,7 @@
 	return boot_device;
 }
 
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
 {
 	return MMCSD_MODE_RAW;
 }
diff --git a/arch/arm/mach-socfpga/spl_a10.c b/arch/arm/mach-socfpga/spl_a10.c
index d2f454c..ec67a5b 100644
--- a/arch/arm/mach-socfpga/spl_a10.c
+++ b/arch/arm/mach-socfpga/spl_a10.c
@@ -99,7 +99,7 @@
 }
 
 #ifdef CONFIG_SPL_MMC
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
 {
 #if defined(CONFIG_SPL_FS_FAT) || defined(CONFIG_SPL_FS_EXT4)
 	return MMCSD_MODE_FS;
diff --git a/arch/arm/mach-socfpga/spl_gen5.c b/arch/arm/mach-socfpga/spl_gen5.c
index 441d893..287fbd1 100644
--- a/arch/arm/mach-socfpga/spl_gen5.c
+++ b/arch/arm/mach-socfpga/spl_gen5.c
@@ -53,7 +53,7 @@
 }
 
 #ifdef CONFIG_SPL_MMC
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
 {
 #if defined(CONFIG_SPL_FS_FAT) || defined(CONFIG_SPL_FS_EXT4)
 	return MMCSD_MODE_FS;
diff --git a/arch/arm/mach-stm32mp/spl.c b/arch/arm/mach-stm32mp/spl.c
index 51fe069..78fa9d7 100644
--- a/arch/arm/mach-stm32mp/spl.c
+++ b/arch/arm/mach-stm32mp/spl.c
@@ -55,7 +55,7 @@
 	return BOOT_DEVICE_MMC1;
 }
 
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
 {
 	return MMCSD_MODE_RAW;
 }
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 73da6b8..1f43b25 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -755,20 +755,6 @@
 	---help---
 	See I2C0_ENABLE help text.
 
-config I2C2_ENABLE
-	bool "Enable I2C/TWI controller 2"
-	select CMD_I2C
-	---help---
-	See I2C0_ENABLE help text.
-
-if MACH_SUN6I || MACH_SUN7I
-config I2C3_ENABLE
-	bool "Enable I2C/TWI controller 3"
-	select CMD_I2C
-	---help---
-	See I2C0_ENABLE help text.
-endif
-
 if SUNXI_GEN_SUN6I || SUN50I_GEN_H6
 config R_I2C_ENABLE
 	bool "Enable the PRCM I2C/TWI controller"
@@ -779,14 +765,6 @@
 	Set this to y to enable the I2C controller which is part of the PRCM.
 endif
 
-if MACH_SUN7I
-config I2C4_ENABLE
-	bool "Enable I2C/TWI controller 4"
-	select CMD_I2C
-	---help---
-	See I2C0_ENABLE help text.
-endif
-
 config AXP_GPIO
 	bool "Enable support for gpio-s on axp PMICs"
 	depends on AXP_PMIC_BUS
@@ -1069,6 +1047,8 @@
 	  The used address is "bdaddr" if set, and "ethaddr" with the LSB
 	  flipped elsewise.
 
+source "board/sunxi/Kconfig"
+
 endif
 
 config CHIP_DIP_SCAN
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
index 9a7673d..173e946 100644
--- a/arch/arm/mach-sunxi/board.c
+++ b/arch/arm/mach-sunxi/board.c
@@ -150,6 +150,10 @@
 	sunxi_gpio_set_cfgpin(SUNXI_GPG(3), SUN5I_GPG_UART1);
 	sunxi_gpio_set_cfgpin(SUNXI_GPG(4), SUN5I_GPG_UART1);
 	sunxi_gpio_set_pull(SUNXI_GPG(4), SUNXI_GPIO_PULL_UP);
+#elif CONFIG_CONS_INDEX == 3 && defined(CONFIG_MACH_SUN8I_H3)
+	sunxi_gpio_set_cfgpin(SUNXI_GPA(0), SUN8I_H3_GPA_UART2);
+	sunxi_gpio_set_cfgpin(SUNXI_GPA(1), SUN8I_H3_GPA_UART2);
+	sunxi_gpio_set_pull(SUNXI_GPA(1), SUNXI_GPIO_PULL_UP);
 #elif CONFIG_CONS_INDEX == 3 && defined(CONFIG_MACH_SUN8I)
 	sunxi_gpio_set_cfgpin(SUNXI_GPB(0), SUN8I_GPB_UART2);
 	sunxi_gpio_set_cfgpin(SUNXI_GPB(1), SUN8I_GPB_UART2);
@@ -213,8 +217,21 @@
 	return SUNXI_INVALID_BOOT_SOURCE;
 }
 
+static int sunxi_egon_valid(struct boot_file_head *egon_head)
+{
+	return !memcmp(egon_head->magic, BOOT0_MAGIC, 8); /* eGON.BT0 */
+}
+
+static int sunxi_toc0_valid(struct toc0_main_info *toc0_info)
+{
+	return !memcmp(toc0_info->name, TOC0_MAIN_INFO_NAME, 8); /* TOC0.GLH */
+}
+
 static int sunxi_get_boot_source(void)
 {
+	struct boot_file_head *egon_head = (void *)SPL_ADDR;
+	struct toc0_main_info *toc0_info = (void *)SPL_ADDR;
+
 	/*
 	 * On the ARMv5 SoCs, the SPL header in SRAM is overwritten by the
 	 * exception vectors in U-Boot proper, so we won't find any
@@ -226,13 +243,15 @@
 	    !IS_ENABLED(CONFIG_SPL_BUILD))
 		return SUNXI_BOOTED_FROM_MMC0;
 
-	if (!is_boot0_magic(SPL_ADDR + 4)) /* eGON.BT0 */
-		return SUNXI_INVALID_BOOT_SOURCE;
-
 	if (IS_ENABLED(CONFIG_MACH_SUNIV))
 		return suniv_get_boot_source();
-	else
-		return readb(SPL_ADDR + 0x28);
+	if (sunxi_egon_valid(egon_head))
+		return readb(&egon_head->boot_media);
+	if (sunxi_toc0_valid(toc0_info))
+		return readb(&toc0_info->platform[0]);
+
+	/* Not a valid image, so we must have been booted via FEL. */
+	return SUNXI_INVALID_BOOT_SOURCE;
 }
 
 /* The sunxi internal brom will try to loader external bootloader
@@ -278,12 +297,18 @@
 }
 
 #ifdef CONFIG_SPL_BUILD
-static u32 sunxi_get_spl_size(void)
+uint32_t sunxi_get_spl_size(void)
 {
-	if (!is_boot0_magic(SPL_ADDR + 4)) /* eGON.BT0 */
-		return 0;
+	struct boot_file_head *egon_head = (void *)SPL_ADDR;
+	struct toc0_main_info *toc0_info = (void *)SPL_ADDR;
 
-	return readl(SPL_ADDR + 0x10);
+	if (sunxi_egon_valid(egon_head))
+		return readl(&egon_head->length);
+	if (sunxi_toc0_valid(toc0_info))
+		return readl(&toc0_info->length);
+
+	/* Not a valid image, so use the default U-Boot offset. */
+	return 0;
 }
 
 /*
@@ -319,6 +344,89 @@
 
 __weak void sunxi_sram_init(void)
 {
+}
+
+/*
+ * When booting from an eMMC boot partition, the SPL puts the same boot
+ * source code into SRAM A1 as when loading the SPL from the normal
+ * eMMC user data partition: 0x2. So to know where we have been loaded
+ * from, we repeat the BROM algorithm here: checking for a valid eGON boot
+ * image at offset 0 of a (potentially) selected boot partition.
+ * If any of the conditions is not met, it must have been the eMMC user
+ * data partition.
+ */
+static bool sunxi_valid_emmc_boot(struct mmc *mmc)
+{
+	struct blk_desc *bd = mmc_get_blk_desc(mmc);
+	uint32_t *buffer = (void *)(uintptr_t)CONFIG_SYS_TEXT_BASE;
+	struct boot_file_head *egon_head = (void *)buffer;
+	int bootpart = EXT_CSD_EXTRACT_BOOT_PART(mmc->part_config);
+	uint32_t spl_size, emmc_checksum, chksum = 0;
+	ulong count;
+
+	/* The BROM requires BOOT_ACK to be enabled. */
+	if (!EXT_CSD_EXTRACT_BOOT_ACK(mmc->part_config))
+		return false;
+
+	/*
+	 * The BOOT_BUS_CONDITION register must be 4-bit SDR, with (0x09)
+	 * or without (0x01) high speed timings.
+	 */
+	if ((mmc->ext_csd[EXT_CSD_BOOT_BUS_WIDTH] & 0x1b) != 0x01 &&
+	    (mmc->ext_csd[EXT_CSD_BOOT_BUS_WIDTH] & 0x1b) != 0x09)
+		return false;
+
+	/* Partition 0 is the user data partition, bootpart must be 1 or 2. */
+	if (bootpart != 1 && bootpart != 2)
+		return false;
+
+	/* Failure to switch to the boot partition is fatal. */
+	if (mmc_switch_part(mmc, bootpart))
+		return false;
+
+	/* Read the first block to do some sanity checks on the eGON header. */
+	count = blk_dread(bd, 0, 1, buffer);
+	if (count != 1 || !sunxi_egon_valid(egon_head))
+		return false;
+
+	/* Read the rest of the SPL now we know it's halfway sane. */
+	spl_size = buffer[4];
+	count = blk_dread(bd, 1, DIV_ROUND_UP(spl_size, bd->blksz) - 1,
+			  buffer + bd->blksz / 4);
+
+	/* Save the checksum and replace it with the "stamp value". */
+	emmc_checksum = buffer[3];
+	buffer[3] = 0x5f0a6c39;
+
+	/* The checksum is a simple ignore-carry addition of all words. */
+	for (count = 0; count < spl_size / 4; count++)
+		chksum += buffer[count];
+
+	debug("eMMC boot part SPL checksum: stored: 0x%08x, computed: 0x%08x\n",
+	       emmc_checksum, chksum);
+
+	return emmc_checksum == chksum;
+}
+
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
+{
+	static u32 result = ~0;
+
+	if (result != ~0)
+		return result;
+
+	result = MMCSD_MODE_RAW;
+	if (!IS_SD(mmc) && IS_ENABLED(CONFIG_SUPPORT_EMMC_BOOT)) {
+		if (sunxi_valid_emmc_boot(mmc))
+			result = MMCSD_MODE_EMMCBOOT;
+		else
+			mmc_switch_part(mmc, 0);
+	}
+
+	debug("%s(): %s part\n", __func__,
+	      result == MMCSD_MODE_RAW ? "user" : "boot");
+
+	return result;
 }
 
 void board_init_f(ulong dummy)
diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
index a947463..7926394 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -9,10 +9,24 @@
 {
 	struct sunxi_ccm_reg *const ccm =
 		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+	struct sunxi_prcm_reg *const prcm =
+		(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
+
+	if (IS_ENABLED(CONFIG_MACH_SUN50I_H616)) {
+		/* this seems to enable PLLs on H616 */
+		setbits_le32(&prcm->sys_pwroff_gating, 0x10);
+		setbits_le32(&prcm->res_cal_ctrl, 2);
+	}
+
+	clrbits_le32(&prcm->res_cal_ctrl, 1);
+	setbits_le32(&prcm->res_cal_ctrl, 1);
 
-	/* this seems to enable PLLs on H616 */
-	if (IS_ENABLED(CONFIG_MACH_SUN50I_H616))
-		setbits_le32(SUNXI_PRCM_BASE + 0x250, 0x10);
+	if (IS_ENABLED(CONFIG_MACH_SUN50I_H6)) {
+		/* set key field for ldo enable */
+		setbits_le32(&prcm->pll_ldo_cfg, 0xA7000000);
+		/* set PLL VDD LDO output to 1.14 V */
+		setbits_le32(&prcm->pll_ldo_cfg, 0x60000);
+	}
 
 	clock_set_pll1(408000000);
 
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c
index d05375c..b332f3a 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c
@@ -12,6 +12,7 @@
 #include <asm/arch/clock.h>
 #include <asm/arch/dram.h>
 #include <asm/arch/cpu.h>
+#include <asm/arch/prcm.h>
 #include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/kconfig.h>
@@ -665,6 +666,8 @@
 {
 	struct sunxi_mctl_com_reg * const mctl_com =
 			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+	struct sunxi_prcm_reg *const prcm =
+		(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
 	struct dram_para para = {
 		.clk = CONFIG_DRAM_CLK,
 #ifdef CONFIG_SUNXI_DRAM_H6_LPDDR3
@@ -680,9 +683,8 @@
 
 	unsigned long size;
 
-	/* RES_CAL_CTRL_REG in BSP U-boot*/
-	setbits_le32(0x7010310, BIT(8));
-	clrbits_le32(0x7010318, 0x3f);
+	setbits_le32(&prcm->res_cal_ctrl, BIT(8));
+	clrbits_le32(&prcm->ohms240, 0x3f);
 
 	mctl_auto_detect_rank_width(&para);
 	mctl_auto_detect_dram_size(&para);
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h616.c b/arch/arm/mach-sunxi/dram_sun50i_h616.c
index 83e8abc..454c845 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_h616.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c
@@ -19,6 +19,7 @@
 #include <asm/arch/clock.h>
 #include <asm/arch/dram.h>
 #include <asm/arch/cpu.h>
+#include <asm/arch/prcm.h>
 #include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/kconfig.h>
@@ -1001,14 +1002,16 @@
 
 unsigned long sunxi_dram_init(void)
 {
+	struct sunxi_prcm_reg *const prcm =
+		(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
 	struct dram_para para = {
 		.clk = CONFIG_DRAM_CLK,
 		.type = SUNXI_DRAM_TYPE_DDR3,
 	};
 	unsigned long size;
 
-	setbits_le32(0x7010310, BIT(8));
-	clrbits_le32(0x7010318, 0x3f);
+	setbits_le32(&prcm->res_cal_ctrl, BIT(8));
+	clrbits_le32(&prcm->ohms240, 0x3f);
 
 	mctl_auto_detect_rank_width(&para);
 	mctl_auto_detect_dram_size(&para);
diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c b/arch/arm/mach-sunxi/spl_spi_sunxi.c
index 734c165..de9aa68 100644
--- a/arch/arm/mach-sunxi/spl_spi_sunxi.c
+++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c
@@ -337,9 +337,9 @@
 	int ret = 0;
 	struct image_header *header;
 	header = (struct image_header *)(CONFIG_SYS_TEXT_BASE);
-	int load_offset = readl(SPL_ADDR + 0x10);
+	uint32_t load_offset = sunxi_get_spl_size();
 
-	load_offset = max(load_offset, CONFIG_SYS_SPI_U_BOOT_OFFS);
+	load_offset = max_t(uint32_t, load_offset, CONFIG_SYS_SPI_U_BOOT_OFFS);
 
 	spi0_init();
 
diff --git a/arch/arm/mach-uniphier/mmc-boot-mode.c b/arch/arm/mach-uniphier/mmc-boot-mode.c
index e47e5df..09cad74 100644
--- a/arch/arm/mach-uniphier/mmc-boot-mode.c
+++ b/arch/arm/mach-uniphier/mmc-boot-mode.c
@@ -7,10 +7,8 @@
 #include <mmc.h>
 #include <spl.h>
 
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
 {
-	struct mmc *mmc;
-
 	/*
 	 * work around a bug in the Boot ROM of LD4, Pro4, and sLD8:
 	 *
@@ -24,7 +22,6 @@
 	 * Fixup mmc->part_config here because it is used to determine the
 	 * partition which the U-Boot image is read from.
 	 */
-	mmc = find_mmc_device(0);
 	mmc->part_config &= ~EXT_CSD_BOOT_PART_NUM(PART_ACCESS_MASK);
 	mmc->part_config |= EXT_CSD_BOOT_PARTITION_ENABLE;
 
diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig
new file mode 100644
index 0000000..084a8b0
--- /dev/null
+++ b/board/sunxi/Kconfig
@@ -0,0 +1,24 @@
+choice
+	prompt "SPL Image Type"
+	default SPL_IMAGE_TYPE_SUNXI_EGON
+
+config SPL_IMAGE_TYPE_SUNXI_EGON
+	bool "eGON (normal)"
+	help
+	  Select this option to embed the SPL binary in an eGON.BT0 image,
+	  which is compatible with the normal boot ROM (NBROM).
+
+	  This is usually the correct option to choose.
+
+config SPL_IMAGE_TYPE_SUNXI_TOC0
+	bool "TOC0 (secure)"
+	help
+	  Select this option to embed the SPL binary in a TOC0 image,
+	  which is compatible with the secure boot ROM (SBROM).
+
+endchoice
+
+config SPL_IMAGE_TYPE
+	string
+	default "sunxi_egon" if SPL_IMAGE_TYPE_SUNXI_EGON
+	default "sunxi_toc0" if SPL_IMAGE_TYPE_SUNXI_TOC0
diff --git a/board/sunxi/board.c b/board/sunxi/board.c
index 28f702b..8932415 100644
--- a/board/sunxi/board.c
+++ b/board/sunxi/board.c
@@ -107,54 +107,6 @@
 #endif
 #endif
 
-#ifdef CONFIG_I2C2_ENABLE
-#if defined(CONFIG_MACH_SUN4I) || \
-    defined(CONFIG_MACH_SUN7I) || \
-    defined(CONFIG_MACH_SUN8I_R40)
-	sunxi_gpio_set_cfgpin(SUNXI_GPB(20), SUN4I_GPB_TWI2);
-	sunxi_gpio_set_cfgpin(SUNXI_GPB(21), SUN4I_GPB_TWI2);
-	clock_twi_onoff(2, 1);
-#elif defined(CONFIG_MACH_SUN5I)
-	sunxi_gpio_set_cfgpin(SUNXI_GPB(17), SUN5I_GPB_TWI2);
-	sunxi_gpio_set_cfgpin(SUNXI_GPB(18), SUN5I_GPB_TWI2);
-	clock_twi_onoff(2, 1);
-#elif defined(CONFIG_MACH_SUN6I)
-	sunxi_gpio_set_cfgpin(SUNXI_GPH(18), SUN6I_GPH_TWI2);
-	sunxi_gpio_set_cfgpin(SUNXI_GPH(19), SUN6I_GPH_TWI2);
-	clock_twi_onoff(2, 1);
-#elif defined(CONFIG_MACH_SUN8I)
-	sunxi_gpio_set_cfgpin(SUNXI_GPE(12), SUN8I_GPE_TWI2);
-	sunxi_gpio_set_cfgpin(SUNXI_GPE(13), SUN8I_GPE_TWI2);
-	clock_twi_onoff(2, 1);
-#elif defined(CONFIG_MACH_SUN50I)
-	sunxi_gpio_set_cfgpin(SUNXI_GPE(14), SUN50I_GPE_TWI2);
-	sunxi_gpio_set_cfgpin(SUNXI_GPE(15), SUN50I_GPE_TWI2);
-	clock_twi_onoff(2, 1);
-#endif
-#endif
-
-#ifdef CONFIG_I2C3_ENABLE
-#if defined(CONFIG_MACH_SUN6I)
-	sunxi_gpio_set_cfgpin(SUNXI_GPG(10), SUN6I_GPG_TWI3);
-	sunxi_gpio_set_cfgpin(SUNXI_GPG(11), SUN6I_GPG_TWI3);
-	clock_twi_onoff(3, 1);
-#elif defined(CONFIG_MACH_SUN7I) || \
-      defined(CONFIG_MACH_SUN8I_R40)
-	sunxi_gpio_set_cfgpin(SUNXI_GPI(0), SUN7I_GPI_TWI3);
-	sunxi_gpio_set_cfgpin(SUNXI_GPI(1), SUN7I_GPI_TWI3);
-	clock_twi_onoff(3, 1);
-#endif
-#endif
-
-#ifdef CONFIG_I2C4_ENABLE
-#if defined(CONFIG_MACH_SUN7I) || \
-    defined(CONFIG_MACH_SUN8I_R40)
-	sunxi_gpio_set_cfgpin(SUNXI_GPI(2), SUN7I_GPI_TWI4);
-	sunxi_gpio_set_cfgpin(SUNXI_GPI(3), SUN7I_GPI_TWI4);
-	clock_twi_onoff(4, 1);
-#endif
-#endif
-
 #ifdef CONFIG_R_I2C_ENABLE
 #ifdef CONFIG_MACH_SUN50I
 	clock_twi_onoff(5, 1);
@@ -297,17 +249,6 @@
 	 */
 	i2c_init_board();
 #endif
-
-#ifdef CONFIG_DM_MMC
-	/*
-	 * Temporary workaround for enabling MMC clocks until a sunxi DM
-	 * pinctrl driver lands.
-	 */
-	mmc_pinmux_setup(CONFIG_MMC_SUNXI_SLOT);
-#if CONFIG_MMC_SUNXI_SLOT_EXTRA != -1
-	mmc_pinmux_setup(CONFIG_MMC_SUNXI_SLOT_EXTRA);
-#endif
-#endif	/* CONFIG_DM_MMC */
 
 	eth_init_board();
 
diff --git a/board/sunxi/gmac.c b/board/sunxi/gmac.c
index 1fa54ed..2a88530 100644
--- a/board/sunxi/gmac.c
+++ b/board/sunxi/gmac.c
@@ -1,13 +1,11 @@
 #include <common.h>
 #include <netdev.h>
 #include <miiphy.h>
-#include <asm/gpio.h>
 #include <asm/io.h>
 #include <asm/arch/clock.h>
 
 void eth_init_board(void)
 {
-	int pin;
 	struct sunxi_ccm_reg *const ccm =
 		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
 
@@ -21,57 +19,4 @@
 	setbits_le32(&ccm->gmac_clk_cfg, CCM_GMAC_CTRL_TX_CLK_SRC_MII |
 		CCM_GMAC_CTRL_GPIT_MII);
 #endif
-
-#ifndef CONFIG_MACH_SUN6I
-	/* Configure pin mux settings for GMAC */
-#ifdef CONFIG_SUN7I_GMAC_FORCE_TXERR
-	for (pin = SUNXI_GPA(0); pin <= SUNXI_GPA(17); pin++) {
-#else
-	for (pin = SUNXI_GPA(0); pin <= SUNXI_GPA(16); pin++) {
-#endif
-#ifdef CONFIG_RGMII
-		/* skip unused pins in RGMII mode */
-		if (pin == SUNXI_GPA(9) || pin == SUNXI_GPA(14))
-			continue;
-#endif
-		sunxi_gpio_set_cfgpin(pin, SUN7I_GPA_GMAC);
-		sunxi_gpio_set_drv(pin, 3);
-	}
-#elif defined CONFIG_RGMII
-	/* Configure sun6i RGMII mode pin mux settings */
-	for (pin = SUNXI_GPA(0); pin <= SUNXI_GPA(3); pin++) {
-		sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
-		sunxi_gpio_set_drv(pin, 3);
-	}
-	for (pin = SUNXI_GPA(9); pin <= SUNXI_GPA(14); pin++) {
-		sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
-		sunxi_gpio_set_drv(pin, 3);
-	}
-	for (pin = SUNXI_GPA(19); pin <= SUNXI_GPA(20); pin++) {
-		sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
-		sunxi_gpio_set_drv(pin, 3);
-	}
-	for (pin = SUNXI_GPA(25); pin <= SUNXI_GPA(27); pin++) {
-		sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
-		sunxi_gpio_set_drv(pin, 3);
-	}
-#elif defined CONFIG_GMII
-	/* Configure sun6i GMII mode pin mux settings */
-	for (pin = SUNXI_GPA(0); pin <= SUNXI_GPA(27); pin++) {
-		sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
-		sunxi_gpio_set_drv(pin, 2);
-	}
-#else
-	/* Configure sun6i MII mode pin mux settings */
-	for (pin = SUNXI_GPA(0); pin <= SUNXI_GPA(3); pin++)
-		sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
-	for (pin = SUNXI_GPA(8); pin <= SUNXI_GPA(9); pin++)
-		sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
-	for (pin = SUNXI_GPA(11); pin <= SUNXI_GPA(14); pin++)
-		sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
-	for (pin = SUNXI_GPA(19); pin <= SUNXI_GPA(24); pin++)
-		sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
-	for (pin = SUNXI_GPA(26); pin <= SUNXI_GPA(27); pin++)
-		sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
-#endif
 }
diff --git a/board/xilinx/versal/board.c b/board/xilinx/versal/board.c
index 299e128..9940f2a 100644
--- a/board/xilinx/versal/board.c
+++ b/board/xilinx/versal/board.c
@@ -9,6 +9,7 @@
 #include <env.h>
 #include <fdtdec.h>
 #include <init.h>
+#include <image.h>
 #include <env_internal.h>
 #include <log.h>
 #include <malloc.h>
@@ -249,6 +250,25 @@
 	return 0;
 }
 
+ulong board_get_usable_ram_top(ulong total_size)
+{
+	phys_size_t size;
+	phys_addr_t reg;
+	struct lmb lmb;
+
+	/* found enough not-reserved memory to relocated U-Boot */
+	lmb_init(&lmb);
+	lmb_add(&lmb, gd->ram_base, gd->ram_size);
+	boot_fdt_add_mem_rsv_regions(&lmb, (void *)gd->fdt_blob);
+	size = ALIGN(CONFIG_SYS_MALLOC_LEN + total_size, MMU_SECTION_SIZE);
+	reg = lmb_alloc(&lmb, size, MMU_SECTION_SIZE);
+
+	if (!reg)
+		reg = gd->ram_top - size;
+
+	return reg + size;
+}
+
 void reset_cpu(void)
 {
 }
diff --git a/board/xilinx/zynqmp/zynqmp-dlc21-revA/psu_init_gpl.c b/board/xilinx/zynqmp/zynqmp-dlc21-revA/psu_init_gpl.c
index 528958d..dae81e6 100644
--- a/board/xilinx/zynqmp/zynqmp-dlc21-revA/psu_init_gpl.c
+++ b/board/xilinx/zynqmp/zynqmp-dlc21-revA/psu_init_gpl.c
@@ -509,10 +509,6 @@
 	psu_mask_write(0xFF5E0238, 0x00000008U, 0x00000000U);
 	psu_mask_write(0xFF5E0238, 0x00007800U, 0x00000000U);
 	psu_mask_write(0xFF5E0238, 0x00000002U, 0x00000000U);
-	psu_mask_write(0xFF000034, 0x000000FFU, 0x00000005U);
-	psu_mask_write(0xFF000018, 0x0000FFFFU, 0x0000008FU);
-	psu_mask_write(0xFF000000, 0x000001FFU, 0x00000017U);
-	psu_mask_write(0xFF000004, 0x000003FFU, 0x00000020U);
 	psu_mask_write(0xFF5E0238, 0x00040000U, 0x00000000U);
 	psu_mask_write(0xFF4B0024, 0x000000FFU, 0x000000FFU);
 	psu_mask_write(0xFFCA5000, 0x00001FFFU, 0x00000000U);
diff --git a/board/xilinx/zynqmp/zynqmp-e-a2197-00-revA/psu_init_gpl.c b/board/xilinx/zynqmp/zynqmp-e-a2197-00-revA/psu_init_gpl.c
index 348f0e7..40d9279 100644
--- a/board/xilinx/zynqmp/zynqmp-e-a2197-00-revA/psu_init_gpl.c
+++ b/board/xilinx/zynqmp/zynqmp-e-a2197-00-revA/psu_init_gpl.c
@@ -521,10 +521,6 @@
 	psu_mask_write(0xFF180324, 0x03C00000U, 0x00000000U);
 	psu_mask_write(0xFF5E0238, 0x00000600U, 0x00000000U);
 	psu_mask_write(0xFF5E0238, 0x00000002U, 0x00000000U);
-	psu_mask_write(0xFF000034, 0x000000FFU, 0x00000006U);
-	psu_mask_write(0xFF000018, 0x0000FFFFU, 0x0000007CU);
-	psu_mask_write(0xFF000000, 0x000001FFU, 0x00000017U);
-	psu_mask_write(0xFF000004, 0x000003FFU, 0x00000020U);
 	psu_mask_write(0xFF5E0238, 0x00040000U, 0x00000000U);
 	psu_mask_write(0xFF4B0024, 0x000000FFU, 0x000000FFU);
 	psu_mask_write(0xFFCA5000, 0x00001FFFU, 0x00000000U);
diff --git a/board/xilinx/zynqmp/zynqmp-topic-miamimp-xilinx-xdp-v1r1/psu_init_gpl.c b/board/xilinx/zynqmp/zynqmp-topic-miamimp-xilinx-xdp-v1r1/psu_init_gpl.c
index dbed7b7..333510b 100644
--- a/board/xilinx/zynqmp/zynqmp-topic-miamimp-xilinx-xdp-v1r1/psu_init_gpl.c
+++ b/board/xilinx/zynqmp/zynqmp-topic-miamimp-xilinx-xdp-v1r1/psu_init_gpl.c
@@ -522,14 +522,6 @@
 	psu_mask_write(0xFF5E0238, 0x00000018U, 0x00000000U);
 	psu_mask_write(0xFF5E0238, 0x00007800U, 0x00000000U);
 	psu_mask_write(0xFF5E0238, 0x00000006U, 0x00000000U);
-	psu_mask_write(0xFF000034, 0x000000FFU, 0x00000005U);
-	psu_mask_write(0xFF000018, 0x0000FFFFU, 0x0000008FU);
-	psu_mask_write(0xFF000000, 0x000001FFU, 0x00000017U);
-	psu_mask_write(0xFF000004, 0x000003FFU, 0x00000020U);
-	psu_mask_write(0xFF010034, 0x000000FFU, 0x00000005U);
-	psu_mask_write(0xFF010018, 0x0000FFFFU, 0x0000008FU);
-	psu_mask_write(0xFF010000, 0x000001FFU, 0x00000017U);
-	psu_mask_write(0xFF010004, 0x000003FFU, 0x00000020U);
 	psu_mask_write(0xFF5E0238, 0x00040000U, 0x00000000U);
 	psu_mask_write(0xFF4B0024, 0x000000FFU, 0x000000FFU);
 	psu_mask_write(0xFFCA5000, 0x00001FFFU, 0x00000000U);
diff --git a/board/xilinx/zynqmp/zynqmp-zcu102-rev1.1/psu_init_gpl.c b/board/xilinx/zynqmp/zynqmp-zcu102-rev1.1/psu_init_gpl.c
index 1f3f2e6..f1fdc7d 100644
--- a/board/xilinx/zynqmp/zynqmp-zcu102-rev1.1/psu_init_gpl.c
+++ b/board/xilinx/zynqmp/zynqmp-zcu102-rev1.1/psu_init_gpl.c
@@ -516,14 +516,6 @@
 	psu_mask_write(0xFF5E0238, 0x00008000U, 0x00000000U);
 	psu_mask_write(0xFF5E0238, 0x00007800U, 0x00000000U);
 	psu_mask_write(0xFF5E0238, 0x00000006U, 0x00000000U);
-	psu_mask_write(0xFF000034, 0x000000FFU, 0x0000000CU);
-	psu_mask_write(0xFF000018, 0x0000FFFFU, 0x0000003EU);
-	psu_mask_write(0xFF000000, 0x000001FFU, 0x00000017U);
-	psu_mask_write(0xFF000004, 0x000003FFU, 0x00000020U);
-	psu_mask_write(0xFF010034, 0x000000FFU, 0x0000000CU);
-	psu_mask_write(0xFF010018, 0x0000FFFFU, 0x0000003EU);
-	psu_mask_write(0xFF010000, 0x000001FFU, 0x00000017U);
-	psu_mask_write(0xFF010004, 0x000003FFU, 0x00000020U);
 	psu_mask_write(0xFF5E0238, 0x00040000U, 0x00000000U);
 	psu_mask_write(0xFF4B0024, 0x000000FFU, 0x000000FFU);
 	psu_mask_write(0xFFCA5000, 0x00001FFFU, 0x00000000U);
diff --git a/board/xilinx/zynqmp/zynqmp-zcu111-revA/psu_init_gpl.c b/board/xilinx/zynqmp/zynqmp-zcu111-revA/psu_init_gpl.c
index 7c6664d..8963aa4 100644
--- a/board/xilinx/zynqmp/zynqmp-zcu111-revA/psu_init_gpl.c
+++ b/board/xilinx/zynqmp/zynqmp-zcu111-revA/psu_init_gpl.c
@@ -512,14 +512,6 @@
 	psu_mask_write(0xFF5E0238, 0x00008000U, 0x00000000U);
 	psu_mask_write(0xFF5E0238, 0x00007800U, 0x00000000U);
 	psu_mask_write(0xFF5E0238, 0x00000006U, 0x00000000U);
-	psu_mask_write(0xFF000034, 0x000000FFU, 0x00000005U);
-	psu_mask_write(0xFF000018, 0x0000FFFFU, 0x0000008FU);
-	psu_mask_write(0xFF000000, 0x000001FFU, 0x00000017U);
-	psu_mask_write(0xFF000004, 0x000003FFU, 0x00000020U);
-	psu_mask_write(0xFF010034, 0x000000FFU, 0x00000005U);
-	psu_mask_write(0xFF010018, 0x0000FFFFU, 0x0000008FU);
-	psu_mask_write(0xFF010000, 0x000001FFU, 0x00000017U);
-	psu_mask_write(0xFF010004, 0x000003FFU, 0x00000020U);
 	psu_mask_write(0xFF5E0238, 0x00040000U, 0x00000000U);
 	psu_mask_write(0xFF4B0024, 0x000000FFU, 0x000000FFU);
 	psu_mask_write(0xFFCA5000, 0x00001FFFU, 0x00000000U);
diff --git a/board/xilinx/zynqmp/zynqmp-zcu208-revA/psu_init_gpl.c b/board/xilinx/zynqmp/zynqmp-zcu208-revA/psu_init_gpl.c
index f07e60a..2adcad0 100644
--- a/board/xilinx/zynqmp/zynqmp-zcu208-revA/psu_init_gpl.c
+++ b/board/xilinx/zynqmp/zynqmp-zcu208-revA/psu_init_gpl.c
@@ -513,10 +513,6 @@
 	psu_mask_write(0xFF5E0238, 0x00008000U, 0x00000000U);
 	psu_mask_write(0xFF5E0238, 0x00007800U, 0x00000000U);
 	psu_mask_write(0xFF5E0238, 0x00000002U, 0x00000000U);
-	psu_mask_write(0xFF000034, 0x000000FFU, 0x00000006U);
-	psu_mask_write(0xFF000018, 0x0000FFFFU, 0x0000007CU);
-	psu_mask_write(0xFF000000, 0x000001FFU, 0x00000017U);
-	psu_mask_write(0xFF000004, 0x000003FFU, 0x00000020U);
 	psu_mask_write(0xFF5E0238, 0x00040000U, 0x00000000U);
 	psu_mask_write(0xFF4B0024, 0x000000FFU, 0x000000FFU);
 	psu_mask_write(0xFFCA5000, 0x00001FFFU, 0x00000000U);
diff --git a/board/xilinx/zynqmp/zynqmp-zcu216-revA/psu_init_gpl.c b/board/xilinx/zynqmp/zynqmp-zcu216-revA/psu_init_gpl.c
index fc3605d..bd31687 100644
--- a/board/xilinx/zynqmp/zynqmp-zcu216-revA/psu_init_gpl.c
+++ b/board/xilinx/zynqmp/zynqmp-zcu216-revA/psu_init_gpl.c
@@ -513,10 +513,6 @@
 	psu_mask_write(0xFF5E0238, 0x00008000U, 0x00000000U);
 	psu_mask_write(0xFF5E0238, 0x00007800U, 0x00000000U);
 	psu_mask_write(0xFF5E0238, 0x00000002U, 0x00000000U);
-	psu_mask_write(0xFF000034, 0x000000FFU, 0x00000006U);
-	psu_mask_write(0xFF000018, 0x0000FFFFU, 0x0000007CU);
-	psu_mask_write(0xFF000000, 0x000001FFU, 0x00000017U);
-	psu_mask_write(0xFF000004, 0x000003FFU, 0x00000020U);
 	psu_mask_write(0xFF5E0238, 0x00040000U, 0x00000000U);
 	psu_mask_write(0xFF4B0024, 0x000000FFU, 0x000000FFU);
 	psu_mask_write(0xFFCA5000, 0x00001FFFU, 0x00000000U);
diff --git a/board/xilinx/zynqmp/zynqmp.c b/board/xilinx/zynqmp/zynqmp.c
index bc20909..e7e8e91 100644
--- a/board/xilinx/zynqmp/zynqmp.c
+++ b/board/xilinx/zynqmp/zynqmp.c
@@ -70,6 +70,11 @@
 	u8 variants;
 } zynqmp_devices[] = {
 	{
+		.id = 0x04688093,
+		.device = 1,
+		.variants = ZYNQMP_VARIANT_EG,
+	},
+	{
 		.id = 0x04711093,
 		.device = 2,
 		.variants = ZYNQMP_VARIANT_EG | ZYNQMP_VARIANT_CG,
diff --git a/boot/image.c b/boot/image.c
index 121df0c..5dcb55b 100644
--- a/boot/image.c
+++ b/boot/image.c
@@ -178,6 +178,7 @@
 	{	IH_TYPE_MTKIMAGE,   "mtk_image",   "MediaTek BootROM loadable Image" },
 	{	IH_TYPE_COPRO, "copro", "Coprocessor Image"},
 	{	IH_TYPE_SUNXI_EGON, "sunxi_egon",  "Allwinner eGON Boot Image" },
+	{	IH_TYPE_SUNXI_TOC0, "sunxi_toc0",  "Allwinner TOC0 Boot Image" },
 	{	-1,		    "",		  "",			},
 };
 
diff --git a/common/spl/spl_mmc.c b/common/spl/spl_mmc.c
index 1c41d24..1bb785a 100644
--- a/common/spl/spl_mmc.c
+++ b/common/spl/spl_mmc.c
@@ -327,7 +327,7 @@
 }
 #endif
 
-u32 __weak spl_mmc_boot_mode(const u32 boot_device)
+u32 __weak spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
 {
 #if defined(CONFIG_SPL_FS_FAT) || defined(CONFIG_SPL_FS_EXT4)
 	return MMCSD_MODE_FS;
@@ -401,7 +401,7 @@
 		}
 	}
 
-	boot_mode = spl_mmc_boot_mode(bootdev->boot_device);
+	boot_mode = spl_mmc_boot_mode(mmc, bootdev->boot_device);
 	err = -EINVAL;
 	switch (boot_mode) {
 	case MMCSD_MODE_EMMCBOOT:
diff --git a/configs/bananapi_m64_defconfig b/configs/bananapi_m64_defconfig
index 292044d..5463b04 100644
--- a/configs/bananapi_m64_defconfig
+++ b/configs/bananapi_m64_defconfig
@@ -7,6 +7,7 @@
 CONFIG_MMC0_CD_PIN="PH13"
 CONFIG_MMC_SUNXI_SLOT_EXTRA=2
 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_SUPPORT_EMMC_BOOT=y
 CONFIG_SUN8I_EMAC=y
 CONFIG_USB_EHCI_HCD=y
 CONFIG_USB_OHCI_HCD=y
diff --git a/configs/emlid_neutis_n5_devboard_defconfig b/configs/emlid_neutis_n5_devboard_defconfig
index e2d3b13..a3b43df 100644
--- a/configs/emlid_neutis_n5_devboard_defconfig
+++ b/configs/emlid_neutis_n5_devboard_defconfig
@@ -8,3 +8,4 @@
 # CONFIG_DRAM_ODT_EN is not set
 CONFIG_MMC_SUNXI_SLOT_EXTRA=2
 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_SUPPORT_EMMC_BOOT=y
diff --git a/configs/pine64-lts_defconfig b/configs/pine64-lts_defconfig
index 45a9e77..7e7c2d7 100644
--- a/configs/pine64-lts_defconfig
+++ b/configs/pine64-lts_defconfig
@@ -10,6 +10,7 @@
 CONFIG_MMC_SUNXI_SLOT_EXTRA=2
 CONFIG_SPL_SPI_SUNXI=y
 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_SUPPORT_EMMC_BOOT=y
 CONFIG_SPI_FLASH_WINBOND=y
 CONFIG_SUN8I_EMAC=y
 CONFIG_SPI=y
diff --git a/configs/pine_h64_defconfig b/configs/pine_h64_defconfig
index 1e730dd..09a4275 100644
--- a/configs/pine_h64_defconfig
+++ b/configs/pine_h64_defconfig
@@ -11,6 +11,7 @@
 CONFIG_SPL_SPI_SUNXI=y
 # CONFIG_PSCI_RESET is not set
 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_SUPPORT_EMMC_BOOT=y
 CONFIG_SPI_FLASH_WINBOND=y
 CONFIG_SUN8I_EMAC=y
 CONFIG_PHY_SUN50I_USB3=y
diff --git a/configs/sopine_baseboard_defconfig b/configs/sopine_baseboard_defconfig
index 982f7b0..fbbef7a 100644
--- a/configs/sopine_baseboard_defconfig
+++ b/configs/sopine_baseboard_defconfig
@@ -11,6 +11,7 @@
 CONFIG_MMC_SUNXI_SLOT_EXTRA=2
 CONFIG_SPL_SPI_SUNXI=y
 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_SUPPORT_EMMC_BOOT=y
 CONFIG_SPI_FLASH_WINBOND=y
 CONFIG_SUN8I_EMAC=y
 CONFIG_SPI=y
diff --git a/configs/xilinx_zynqmp_virt_defconfig b/configs/xilinx_zynqmp_virt_defconfig
index 3e6b1ec..990f518 100644
--- a/configs/xilinx_zynqmp_virt_defconfig
+++ b/configs/xilinx_zynqmp_virt_defconfig
@@ -53,6 +53,7 @@
 CONFIG_CMD_FPGA_LOADP=y
 CONFIG_CMD_FPGA_LOAD_SECURE=y
 CONFIG_CMD_GPIO=y
+CONFIG_CMD_PWM=y
 CONFIG_CMD_GPT=y
 CONFIG_CMD_I2C=y
 CONFIG_CMD_MMC=y
@@ -164,6 +165,8 @@
 CONFIG_ZYNQ_GEM=y
 CONFIG_DM_REGULATOR=y
 CONFIG_DM_REGULATOR_FIXED=y
+CONFIG_DM_PWM=y
+CONFIG_PWM_CADENCE_TTC=y
 CONFIG_DM_RTC=y
 CONFIG_RTC_EMULATION=y
 CONFIG_RTC_ZYNQMP=y
diff --git a/drivers/clk/clk_zynqmp.c b/drivers/clk/clk_zynqmp.c
index 9038fb8..45c679a 100644
--- a/drivers/clk/clk_zynqmp.c
+++ b/drivers/clk/clk_zynqmp.c
@@ -238,6 +238,12 @@
 		return CRF_APB_DBG_TRACE_CTRL;
 	case dbg_tstmp:
 		return CRF_APB_DBG_TSTMP_CTRL;
+	case dp_video_ref:
+		return CRF_APB_DP_VIDEO_REF_CTRL;
+	case dp_audio_ref:
+		return CRF_APB_DP_AUDIO_REF_CTRL;
+	case dp_stc_ref:
+		return CRF_APB_DP_STC_REF_CTRL;
 	case gpu_ref ...  gpu_pp1_ref:
 		return CRF_APB_GPU_REF_CTRL;
 	case ddr_ref:
@@ -673,6 +679,7 @@
 	case dll_ref:
 		return zynqmp_clk_get_dll_rate(priv);
 	case gem_tsu_ref:
+	case dp_video_ref ... dp_stc_ref:
 	case pl0 ... pl3:
 	case gem0_ref ... gem3_ref:
 	case gem0_tx ... gem3_tx:
diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c
index 8916c55..0f0d2b0 100644
--- a/drivers/firmware/firmware-zynqmp.c
+++ b/drivers/firmware/firmware-zynqmp.c
@@ -140,6 +140,19 @@
 	return pm_api_version;
 };
 
+int zynqmp_pm_set_gem_config(u32 node, enum pm_gem_config_type config, u32 value)
+{
+	int ret;
+
+	ret = xilinx_pm_request(PM_IOCTL, node, IOCTL_SET_GEM_CONFIG,
+				config, value, NULL);
+	if (ret)
+		printf("%s: node %d: set_gem_config %d failed\n",
+		       __func__, node, config);
+
+	return ret;
+}
+
 int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value)
 {
 	int ret;
@@ -334,7 +347,11 @@
 	int ret;
 	struct udevice *child;
 
-	if (IS_ENABLED(CONFIG_ZYNQMP_POWER_DOMAIN)) {
+	if ((IS_ENABLED(CONFIG_SPL_BUILD) &&
+	     IS_ENABLED(CONFIG_SPL_POWER_DOMAIN) &&
+	     IS_ENABLED(CONFIG_ZYNQMP_POWER_DOMAIN)) ||
+	     (!IS_ENABLED(CONFIG_SPL_BUILD) &&
+	      IS_ENABLED(CONFIG_ZYNQMP_POWER_DOMAIN))) {
 		ret = device_bind_driver_to_node(dev, "zynqmp_power_domain",
 						 "zynqmp_power_domain",
 						 dev_ofnode(dev), &child);
diff --git a/drivers/gpio/gpio_slg7xl45106.c b/drivers/gpio/gpio_slg7xl45106.c
index 2cbf748..4ad06c1 100644
--- a/drivers/gpio/gpio_slg7xl45106.c
+++ b/drivers/gpio/gpio_slg7xl45106.c
@@ -11,6 +11,7 @@
 #include <asm/gpio.h>
 #include <dm.h>
 #include <i2c.h>
+#include <dt-bindings/gpio/gpio.h>
 #include <asm/arch/hardware.h>
 
 #define SLG7XL45106_REG		0xdb
@@ -26,6 +27,7 @@
 				     struct ofnode_phandle_args *args)
 {
 	desc->offset = (unsigned int)args->args[0];
+	desc->flags = (args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0);
 
 	return 0;
 }
diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c
index 6c3c108..1e85db1 100644
--- a/drivers/gpio/sunxi_gpio.c
+++ b/drivers/gpio/sunxi_gpio.c
@@ -16,15 +16,8 @@
 #include <malloc.h>
 #include <asm/io.h>
 #include <asm/gpio.h>
-#include <dm/device-internal.h>
 #include <dt-bindings/gpio/gpio.h>
 
-struct sunxi_gpio_plat {
-	struct sunxi_gpio *regs;
-	const char *bank_name;	/* Name of bank, e.g. "B" */
-	int gpio_count;
-};
-
 #if !CONFIG_IS_ENABLED(DM_GPIO)
 static int sunxi_gpio_output(u32 pin, u32 val)
 {
@@ -211,28 +204,6 @@
 	.set_flags		= sunxi_gpio_set_flags,
 };
 
-/**
- * Returns the name of a GPIO bank
- *
- * GPIO banks are named A, B, C, ...
- *
- * @bank:	Bank number (0, 1..n-1)
- * Return: allocated string containing the name
- */
-static char *gpio_bank_name(int bank)
-{
-	char *name;
-
-	name = malloc(3);
-	if (name) {
-		name[0] = 'P';
-		name[1] = 'A' + bank;
-		name[2] = '\0';
-	}
-
-	return name;
-}
-
 static int gpio_sunxi_probe(struct udevice *dev)
 {
 	struct sunxi_gpio_plat *plat = dev_get_plat(dev);
@@ -240,114 +211,17 @@
 
 	/* Tell the uclass how many GPIOs we have */
 	if (plat) {
-		uc_priv->gpio_count = plat->gpio_count;
+		uc_priv->gpio_count = SUNXI_GPIOS_PER_BANK;
 		uc_priv->bank_name = plat->bank_name;
 	}
 
 	return 0;
 }
 
-struct sunxi_gpio_soc_data {
-	int start;
-	int no_banks;
-};
-
-/**
- * We have a top-level GPIO device with no actual GPIOs. It has a child
- * device for each Sunxi bank.
- */
-static int gpio_sunxi_bind(struct udevice *parent)
-{
-	struct sunxi_gpio_soc_data *soc_data =
-		(struct sunxi_gpio_soc_data *)dev_get_driver_data(parent);
-	struct sunxi_gpio_plat *plat = dev_get_plat(parent);
-	struct sunxi_gpio_reg *ctlr;
-	int bank, ret;
-
-	/* If this is a child device, there is nothing to do here */
-	if (plat)
-		return 0;
-
-	ctlr = dev_read_addr_ptr(parent);
-	for (bank = 0; bank < soc_data->no_banks; bank++) {
-		struct sunxi_gpio_plat *plat;
-		struct udevice *dev;
-
-		plat = calloc(1, sizeof(*plat));
-		if (!plat)
-			return -ENOMEM;
-		plat->regs = &ctlr->gpio_bank[bank];
-		plat->bank_name = gpio_bank_name(soc_data->start + bank);
-		plat->gpio_count = SUNXI_GPIOS_PER_BANK;
-
-		ret = device_bind(parent, parent->driver, plat->bank_name, plat,
-				  dev_ofnode(parent), &dev);
-		if (ret)
-			return ret;
-	}
-
-	return 0;
-}
-
-static const struct sunxi_gpio_soc_data soc_data_a_all = {
-	.start = 0,
-	.no_banks = SUNXI_GPIO_BANKS,
-};
-
-static const struct sunxi_gpio_soc_data soc_data_l_1 = {
-	.start = 'L' - 'A',
-	.no_banks = 1,
-};
-
-static const struct sunxi_gpio_soc_data soc_data_l_2 = {
-	.start = 'L' - 'A',
-	.no_banks = 2,
-};
-
-static const struct sunxi_gpio_soc_data soc_data_l_3 = {
-	.start = 'L' - 'A',
-	.no_banks = 3,
-};
-
-#define ID(_compat_, _soc_data_) \
-	{ .compatible = _compat_, .data = (ulong)&soc_data_##_soc_data_ }
-
-static const struct udevice_id sunxi_gpio_ids[] = {
-	ID("allwinner,sun4i-a10-pinctrl",	a_all),
-	ID("allwinner,sun5i-a10s-pinctrl",	a_all),
-	ID("allwinner,sun5i-a13-pinctrl",	a_all),
-	ID("allwinner,sun50i-h5-pinctrl",	a_all),
-	ID("allwinner,sun6i-a31-pinctrl",	a_all),
-	ID("allwinner,sun6i-a31s-pinctrl",	a_all),
-	ID("allwinner,sun7i-a20-pinctrl",	a_all),
-	ID("allwinner,sun8i-a23-pinctrl",	a_all),
-	ID("allwinner,sun8i-a33-pinctrl",	a_all),
-	ID("allwinner,sun8i-a83t-pinctrl",	a_all),
-	ID("allwinner,sun8i-h3-pinctrl",	a_all),
-	ID("allwinner,sun8i-r40-pinctrl",	a_all),
-	ID("allwinner,sun8i-v3-pinctrl",	a_all),
-	ID("allwinner,sun8i-v3s-pinctrl",	a_all),
-	ID("allwinner,sun9i-a80-pinctrl",	a_all),
-	ID("allwinner,sun50i-a64-pinctrl",	a_all),
-	ID("allwinner,sun50i-h6-pinctrl",	a_all),
-	ID("allwinner,sun50i-h616-pinctrl",	a_all),
-	ID("allwinner,sun6i-a31-r-pinctrl",	l_2),
-	ID("allwinner,sun8i-a23-r-pinctrl",	l_1),
-	ID("allwinner,sun8i-a83t-r-pinctrl",	l_1),
-	ID("allwinner,sun8i-h3-r-pinctrl",	l_1),
-	ID("allwinner,sun9i-a80-r-pinctrl",	l_3),
-	ID("allwinner,sun50i-a64-r-pinctrl",	l_1),
-	ID("allwinner,sun50i-h6-r-pinctrl",	l_2),
-	ID("allwinner,sun50i-h616-r-pinctrl",	l_1),
-	{ }
-};
-
 U_BOOT_DRIVER(gpio_sunxi) = {
 	.name	= "gpio_sunxi",
 	.id	= UCLASS_GPIO,
-	.ops	= &gpio_sunxi_ops,
-	.of_match = sunxi_gpio_ids,
-	.bind	= gpio_sunxi_bind,
 	.probe	= gpio_sunxi_probe,
+	.ops	= &gpio_sunxi_ops,
 };
 #endif /* DM_GPIO */
diff --git a/drivers/i2c/sun6i_p2wi.c b/drivers/i2c/sun6i_p2wi.c
index c9e1b3f..d221323 100644
--- a/drivers/i2c/sun6i_p2wi.c
+++ b/drivers/i2c/sun6i_p2wi.c
@@ -14,10 +14,12 @@
  */
 
 #include <axp_pmic.h>
+#include <clk.h>
 #include <common.h>
 #include <dm.h>
 #include <errno.h>
 #include <i2c.h>
+#include <reset.h>
 #include <time.h>
 #include <asm/io.h>
 #include <asm/arch/cpu.h>
@@ -102,12 +104,6 @@
 
 static void sun6i_p2wi_init(struct sunxi_p2wi_reg *base)
 {
-	/* Enable p2wi and PIO clk, and de-assert their resets */
-	prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_P2WI);
-
-	sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN6I_GPL0_R_P2WI_SCK);
-	sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN6I_GPL1_R_P2WI_SDA);
-
 	/* Reset p2wi controller and set clock to CLKIN(12)/8 = 1.5 MHz */
 	writel(P2WI_CTRL_RESET, &base->ctrl);
 	sdelay(0x100);
@@ -142,6 +138,12 @@
 {
 	struct sunxi_p2wi_reg *base = (struct sunxi_p2wi_reg *)SUN6I_P2WI_BASE;
 
+	/* Enable p2wi and PIO clk, and de-assert their resets */
+	prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_P2WI);
+
+	sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN6I_GPL0_R_P2WI_SCK);
+	sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN6I_GPL1_R_P2WI_SDA);
+
 	sun6i_p2wi_init(base);
 }
 #endif
@@ -180,9 +182,19 @@
 static int sun6i_p2wi_probe(struct udevice *bus)
 {
 	struct sun6i_p2wi_priv *priv = dev_get_priv(bus);
+	struct reset_ctl *reset;
+	struct clk *clk;
 
 	priv->base = dev_read_addr_ptr(bus);
 
+	reset = devm_reset_control_get(bus, NULL);
+	if (!IS_ERR(reset))
+		reset_deassert(reset);
+
+	clk = devm_clk_get(bus, NULL);
+	if (!IS_ERR(clk))
+		clk_enable(clk);
+
 	sun6i_p2wi_init(priv->base);
 
 	return 0;
@@ -191,11 +203,12 @@
 static int sun6i_p2wi_child_pre_probe(struct udevice *child)
 {
 	struct dm_i2c_chip *chip = dev_get_parent_plat(child);
+	struct udevice *bus = child->parent;
 
 	/* Ensure each transfer is for a single register. */
 	chip->flags |= DM_I2C_CHIP_RD_ADDRESS | DM_I2C_CHIP_WR_ADDRESS;
 
-	return 0;
+	return sun6i_p2wi_probe_chip(bus, chip->chip_addr, 0);
 }
 
 static const struct dm_i2c_ops sun6i_p2wi_ops = {
diff --git a/drivers/i2c/sun8i_rsb.c b/drivers/i2c/sun8i_rsb.c
index 716b245..47fa05b 100644
--- a/drivers/i2c/sun8i_rsb.c
+++ b/drivers/i2c/sun8i_rsb.c
@@ -9,10 +9,12 @@
  */
 
 #include <axp_pmic.h>
+#include <clk.h>
 #include <common.h>
 #include <dm.h>
 #include <errno.h>
 #include <i2c.h>
+#include <reset.h>
 #include <time.h>
 #include <asm/arch/cpu.h>
 #include <asm/arch/gpio.h>
@@ -95,27 +97,6 @@
 	return sun8i_rsb_do_trans(base);
 }
 
-static void sun8i_rsb_cfg_io(void)
-{
-#ifdef CONFIG_MACH_SUN8I
-	sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_GPL_R_RSB);
-	sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN8I_GPL_R_RSB);
-	sunxi_gpio_set_pull(SUNXI_GPL(0), 1);
-	sunxi_gpio_set_pull(SUNXI_GPL(1), 1);
-	sunxi_gpio_set_drv(SUNXI_GPL(0), 2);
-	sunxi_gpio_set_drv(SUNXI_GPL(1), 2);
-#elif defined CONFIG_MACH_SUN9I
-	sunxi_gpio_set_cfgpin(SUNXI_GPN(0), SUN9I_GPN_R_RSB);
-	sunxi_gpio_set_cfgpin(SUNXI_GPN(1), SUN9I_GPN_R_RSB);
-	sunxi_gpio_set_pull(SUNXI_GPN(0), 1);
-	sunxi_gpio_set_pull(SUNXI_GPN(1), 1);
-	sunxi_gpio_set_drv(SUNXI_GPN(0), 2);
-	sunxi_gpio_set_drv(SUNXI_GPN(1), 2);
-#else
-#error unsupported MACH_SUNXI
-#endif
-}
-
 static void sun8i_rsb_set_clk(struct sunxi_rsb_reg *base)
 {
 	u32 div = 0;
@@ -147,12 +128,6 @@
 
 static int sun8i_rsb_init(struct sunxi_rsb_reg *base)
 {
-	/* Enable RSB and PIO clk, and de-assert their resets */
-	prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_RSB);
-
-	/* Setup external pins */
-	sun8i_rsb_cfg_io();
-
 	writel(RSB_CTRL_SOFT_RST, &base->ctrl);
 	sun8i_rsb_set_clk(base);
 
@@ -185,6 +160,25 @@
 {
 	struct sunxi_rsb_reg *base = (struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
 
+	/* Enable RSB and PIO clk, and de-assert their resets */
+	prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_RSB);
+
+	if (IS_ENABLED(CONFIG_MACH_SUN9I)) {
+		sunxi_gpio_set_cfgpin(SUNXI_GPN(0), SUN9I_GPN_R_RSB);
+		sunxi_gpio_set_cfgpin(SUNXI_GPN(1), SUN9I_GPN_R_RSB);
+		sunxi_gpio_set_pull(SUNXI_GPN(0), 1);
+		sunxi_gpio_set_pull(SUNXI_GPN(1), 1);
+		sunxi_gpio_set_drv(SUNXI_GPN(0), 2);
+		sunxi_gpio_set_drv(SUNXI_GPN(1), 2);
+	} else {
+		sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_GPL_R_RSB);
+		sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN8I_GPL_R_RSB);
+		sunxi_gpio_set_pull(SUNXI_GPL(0), 1);
+		sunxi_gpio_set_pull(SUNXI_GPL(1), 1);
+		sunxi_gpio_set_drv(SUNXI_GPL(0), 2);
+		sunxi_gpio_set_drv(SUNXI_GPL(1), 2);
+	}
+
 	return sun8i_rsb_init(base);
 }
 #endif
@@ -243,20 +237,31 @@
 static int sun8i_rsb_probe(struct udevice *bus)
 {
 	struct sun8i_rsb_priv *priv = dev_get_priv(bus);
+	struct reset_ctl *reset;
+	struct clk *clk;
 
 	priv->base = dev_read_addr_ptr(bus);
 
+	reset = devm_reset_control_get(bus, NULL);
+	if (!IS_ERR(reset))
+		reset_deassert(reset);
+
+	clk = devm_clk_get(bus, NULL);
+	if (!IS_ERR(clk))
+		clk_enable(clk);
+
 	return sun8i_rsb_init(priv->base);
 }
 
 static int sun8i_rsb_child_pre_probe(struct udevice *child)
 {
 	struct dm_i2c_chip *chip = dev_get_parent_plat(child);
+	struct udevice *bus = child->parent;
 
 	/* Ensure each transfer is for a single register. */
 	chip->flags |= DM_I2C_CHIP_RD_ADDRESS | DM_I2C_CHIP_WR_ADDRESS;
 
-	return 0;
+	return sun8i_rsb_probe_chip(bus, chip->chip_addr, 0);
 }
 
 static const struct dm_i2c_ops sun8i_rsb_ops = {
diff --git a/drivers/mmc/zynq_sdhci.c b/drivers/mmc/zynq_sdhci.c
index d96f5d5..a59d96c 100644
--- a/drivers/mmc/zynq_sdhci.c
+++ b/drivers/mmc/zynq_sdhci.c
@@ -765,6 +765,15 @@
 
 	mhz = DIV64_U64_ROUND_UP(clock, 1000000);
 
+	if (mhz > 100 && mhz <= 200)
+		mhz = 200;
+	else if (mhz > 50 && mhz <= 100)
+		mhz = 100;
+	else if (mhz > 25 && mhz <= 50)
+		mhz = 50;
+	else
+		mhz = 25;
+
 	ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_BASECLK, mhz);
 	if (ret) {
 		dev_err(dev, "SD_CONFIG_BASECLK failed\n");
diff --git a/drivers/net/phy/ethernet_id.c b/drivers/net/phy/ethernet_id.c
index 5617ac3..1a78a75 100644
--- a/drivers/net/phy/ethernet_id.c
+++ b/drivers/net/phy/ethernet_id.c
@@ -12,7 +12,7 @@
 #include <asm/gpio.h>
 
 struct phy_device *phy_connect_phy_id(struct mii_dev *bus, struct udevice *dev,
-				      phy_interface_t interface)
+				      int phyaddr, phy_interface_t interface)
 {
 	struct phy_device *phydev;
 	struct ofnode_phandle_args phandle_args;
@@ -33,35 +33,42 @@
 
 	ret = ofnode_read_eth_phy_id(node, &vendor, &device);
 	if (ret) {
-		dev_err(dev, "Failed to read eth PHY id, err: %d\n", ret);
+		debug("Failed to read eth PHY id, err: %d\n", ret);
 		return NULL;
 	}
 
-	ret = gpio_request_by_name_nodev(node, "reset-gpios", 0, &gpio,
-					 GPIOD_ACTIVE_LOW);
-	if (!ret) {
-		assert = ofnode_read_u32_default(node, "reset-assert-us", 0);
-		deassert = ofnode_read_u32_default(node,
-						   "reset-deassert-us", 0);
-		ret = dm_gpio_set_value(&gpio, 1);
-		if (ret) {
-			dev_err(dev, "Failed assert gpio, err: %d\n", ret);
-			return NULL;
-		}
+	if (!IS_ENABLED(CONFIG_DM_ETH_PHY)) {
+		ret = gpio_request_by_name_nodev(node, "reset-gpios", 0, &gpio,
+						 GPIOD_ACTIVE_LOW);
+		if (!ret) {
+			assert = ofnode_read_u32_default(node,
+							 "reset-assert-us", 0);
+			deassert = ofnode_read_u32_default(node,
+							   "reset-deassert-us",
+							   0);
+			ret = dm_gpio_set_value(&gpio, 1);
+			if (ret) {
+				dev_err(dev,
+					"Failed assert gpio, err: %d\n", ret);
+				return NULL;
+			}
 
-		udelay(assert);
+			udelay(assert);
 
-		ret = dm_gpio_set_value(&gpio, 0);
-		if (ret) {
-			dev_err(dev, "Failed deassert gpio, err: %d\n", ret);
-			return NULL;
-		}
+			ret = dm_gpio_set_value(&gpio, 0);
+			if (ret) {
+				dev_err(dev,
+					"Failed deassert gpio, err: %d\n",
+					ret);
+				return NULL;
+			}
 
-		udelay(deassert);
+			udelay(deassert);
+		}
 	}
 
 	id =  vendor << 16 | device;
-	phydev = phy_device_create(bus, 0, id, false, interface);
+	phydev = phy_device_create(bus, phyaddr, id, false, interface);
 	if (phydev)
 		phydev->node = node;
 
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 92fff5b..d473186 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -1049,7 +1049,7 @@
 
 #ifdef CONFIG_PHY_ETHERNET_ID
 	if (!phydev)
-		phydev = phy_connect_phy_id(bus, dev, interface);
+		phydev = phy_connect_phy_id(bus, dev, addr, interface);
 #endif
 
 #ifdef CONFIG_PHY_XILINX_GMII2RGMII
diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c
index 2e24d12..b23faa2 100644
--- a/drivers/net/sun8i_emac.c
+++ b/drivers/net/sun8i_emac.c
@@ -29,7 +29,6 @@
 #include <miiphy.h>
 #include <net.h>
 #include <reset.h>
-#include <dt-bindings/pinctrl/sun4i-a10.h>
 #include <wait_bit.h>
 
 #define MDIO_CMD_MII_BUSY		BIT(0)
@@ -81,13 +80,6 @@
 
 #define AHB_GATE_OFFSET_EPHY	0
 
-/* IO mux settings */
-#define SUN8I_IOMUX_H3		2
-#define SUN8I_IOMUX_R40		5
-#define SUN8I_IOMUX_H6		5
-#define SUN8I_IOMUX_H616	2
-#define SUN8I_IOMUX		4
-
 /* H3/A64 EMAC Register's offset */
 #define EMAC_CTL0		0x00
 #define EMAC_CTL0_FULL_DUPLEX		BIT(0)
@@ -519,85 +511,6 @@
 	return 0;
 }
 
-static int parse_phy_pins(struct udevice *dev)
-{
-	int offset;
-	const char *pin_name;
-	int drive, pull = SUN4I_PINCTRL_NO_PULL, i;
-	u32 iomux;
-
-	offset = fdtdec_lookup_phandle(gd->fdt_blob, dev_of_offset(dev),
-				       "pinctrl-0");
-	if (offset < 0) {
-		printf("WARNING: emac: cannot find pinctrl-0 node\n");
-		return offset;
-	}
-
-	drive = fdt_getprop_u32_default_node(gd->fdt_blob, offset, 0,
-					     "drive-strength", ~0);
-	if (drive != ~0) {
-		if (drive <= 10)
-			drive = SUN4I_PINCTRL_10_MA;
-		else if (drive <= 20)
-			drive = SUN4I_PINCTRL_20_MA;
-		else if (drive <= 30)
-			drive = SUN4I_PINCTRL_30_MA;
-		else
-			drive = SUN4I_PINCTRL_40_MA;
-	}
-
-	if (fdt_get_property(gd->fdt_blob, offset, "bias-pull-up", NULL))
-		pull = SUN4I_PINCTRL_PULL_UP;
-	else if (fdt_get_property(gd->fdt_blob, offset, "bias-pull-down", NULL))
-		pull = SUN4I_PINCTRL_PULL_DOWN;
-
-	/*
-	 * The GPIO pinmux value is an integration choice, so depends on the
-	 * SoC, not the EMAC variant.
-	 */
-	if (IS_ENABLED(CONFIG_MACH_SUNXI_H3_H5))
-		iomux = SUN8I_IOMUX_H3;
-	else if (IS_ENABLED(CONFIG_MACH_SUN8I_R40))
-		iomux = SUN8I_IOMUX_R40;
-	else if (IS_ENABLED(CONFIG_MACH_SUN50I_H6))
-		iomux = SUN8I_IOMUX_H6;
-	else if (IS_ENABLED(CONFIG_MACH_SUN50I_H616))
-		iomux = SUN8I_IOMUX_H616;
-	else if (IS_ENABLED(CONFIG_MACH_SUN8I_A83T))
-		iomux = SUN8I_IOMUX;
-	else if (IS_ENABLED(CONFIG_MACH_SUN50I))
-		iomux = SUN8I_IOMUX;
-	else
-		BUILD_BUG_ON_MSG(1, "missing pinmux value for Ethernet pins");
-
-	for (i = 0; ; i++) {
-		int pin;
-
-		pin_name = fdt_stringlist_get(gd->fdt_blob, offset,
-					      "pins", i, NULL);
-		if (!pin_name)
-			break;
-
-		pin = sunxi_name_to_gpio(pin_name);
-		if (pin < 0)
-			continue;
-
-		sunxi_gpio_set_cfgpin(pin, iomux);
-
-		if (drive != ~0)
-			sunxi_gpio_set_drv(pin, drive);
-		if (pull != ~0)
-			sunxi_gpio_set_pull(pin, pull);
-	}
-
-	if (!i) {
-		printf("WARNING: emac: cannot find pins property\n");
-		return -2;
-	}
-
-	return 0;
-}
-
 static int sun8i_emac_eth_recv(struct udevice *dev, int flags, uchar **packetp)
 {
 	struct emac_eth_dev *priv = dev_get_priv(dev);
@@ -965,9 +878,6 @@
 
 	priv->interface = pdata->phy_interface;
 
-	if (!priv->use_internal_phy)
-		parse_phy_pins(dev);
-
 	sun8i_pdata->tx_delay_ps = fdtdec_get_int(gd->fdt_blob, node,
 						  "allwinner,tx-delay-ps", 0);
 	if (sun8i_pdata->tx_delay_ps < 0 || sun8i_pdata->tx_delay_ps > 700)
diff --git a/drivers/net/sunxi_emac.c b/drivers/net/sunxi_emac.c
index 17ad88e..d15b0ad 100644
--- a/drivers/net/sunxi_emac.c
+++ b/drivers/net/sunxi_emac.c
@@ -17,7 +17,6 @@
 #include <net.h>
 #include <asm/io.h>
 #include <asm/arch/clock.h>
-#include <asm/arch/gpio.h>
 
 /* EMAC register  */
 struct emac_regs {
@@ -511,15 +510,11 @@
 	struct sunxi_sramc_regs *sram =
 		(struct sunxi_sramc_regs *)SUNXI_SRAMC_BASE;
 	struct emac_regs *regs = priv->regs;
-	int pin, ret;
+	int ret;
 
 	/* Map SRAM to EMAC */
 	setbits_le32(&sram->ctrl1, 0x5 << 2);
 
-	/* Configure pin mux settings for MII Ethernet */
-	for (pin = SUNXI_GPA(0); pin <= SUNXI_GPA(17); pin++)
-		sunxi_gpio_set_cfgpin(pin, SUNXI_GPA_EMAC);
-
 	/* Set up clock gating */
 	ret = clk_enable(&priv->clk);
 	if (ret) {
diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c
index 3118d14..4c83ccc 100644
--- a/drivers/net/zynq_gem.c
+++ b/drivers/net/zynq_gem.c
@@ -33,6 +33,8 @@
 #include <linux/bitops.h>
 #include <linux/err.h>
 #include <linux/errno.h>
+#include <eth_phy.h>
+#include <zynqmp_firmware.h>
 
 /* Bit/mask specification */
 #define ZYNQ_GEM_PHYMNTNC_OP_MASK	0x40020000 /* operation mask bits */
@@ -321,6 +323,9 @@
 	/* Enable only MDIO bus */
 	writel(ZYNQ_GEM_NWCTRL_MDEN_MASK, &regs_mdio->nwctrl);
 
+	if (IS_ENABLED(CONFIG_DM_ETH_PHY))
+		priv->phyaddr = eth_phy_get_addr(dev);
+
 	priv->phydev = phy_connect(priv->bus, priv->phyaddr, dev,
 				   priv->interface);
 	if (!priv->phydev)
@@ -710,6 +715,40 @@
 	return 0;
 }
 
+static int gem_zynqmp_set_dynamic_config(struct udevice *dev)
+{
+	u32 pm_info[2];
+	int ret;
+
+	if (IS_ENABLED(CONFIG_ARCH_ZYNQMP)) {
+		if (!zynqmp_pm_is_function_supported(PM_IOCTL,
+						     IOCTL_SET_GEM_CONFIG)) {
+			ret = ofnode_read_u32_array(dev_ofnode(dev),
+						    "power-domains",
+						    pm_info,
+						    ARRAY_SIZE(pm_info));
+			if (ret) {
+				dev_err(dev,
+					"Failed to read power-domains info\n");
+				return ret;
+			}
+
+			ret = zynqmp_pm_set_gem_config(pm_info[1],
+						       GEM_CONFIG_FIXED, 0);
+			if (ret)
+				return ret;
+
+			ret = zynqmp_pm_set_gem_config(pm_info[1],
+						       GEM_CONFIG_SGMII_MODE,
+						       1);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
 static int zynq_gem_probe(struct udevice *dev)
 {
 	void *bd_space;
@@ -771,25 +810,48 @@
 		}
 	}
 
-	priv->bus = mdio_alloc();
-	priv->bus->read = zynq_gem_miiphy_read;
-	priv->bus->write = zynq_gem_miiphy_write;
-	priv->bus->priv = priv;
+	if (IS_ENABLED(CONFIG_DM_ETH_PHY))
+		priv->bus = eth_phy_get_mdio_bus(dev);
 
-	ret = mdio_register_seq(priv->bus, dev_seq(dev));
-	if (ret)
-		goto err2;
+	if (!priv->bus) {
+		priv->bus = mdio_alloc();
+		priv->bus->read = zynq_gem_miiphy_read;
+		priv->bus->write = zynq_gem_miiphy_write;
+		priv->bus->priv = priv;
+
+		ret = mdio_register_seq(priv->bus, dev_seq(dev));
+		if (ret)
+			goto err2;
+	}
+
+	if (IS_ENABLED(CONFIG_DM_ETH_PHY))
+		eth_phy_set_mdio_bus(dev, priv->bus);
 
 	ret = zynq_phy_init(dev);
 	if (ret)
 		goto err3;
 
 	if (priv->interface == PHY_INTERFACE_MODE_SGMII && phy.dev) {
+		if (IS_ENABLED(CONFIG_DM_ETH_PHY)) {
+			if (device_is_compatible(dev, "cdns,zynqmp-gem")) {
+				ret = gem_zynqmp_set_dynamic_config(dev);
+				if (ret) {
+					dev_err
+					(dev,
+					 "Failed to set gem dynamic config\n");
+					return ret;
+				}
+			}
+		}
 		ret = generic_phy_power_on(&phy);
 		if (ret)
 			return ret;
 	}
 
+	printf("\nZYNQ GEM: %lx, mdio bus %lx, phyaddr %d, interface %s\n",
+	       (ulong)priv->iobase, (ulong)priv->mdiobase, priv->phydev->addr,
+	       phy_string_for_interface(priv->interface));
+
 	return ret;
 
 err3:
@@ -841,8 +903,10 @@
 		ofnode parent;
 
 		debug("phy-handle does exist %s\n", dev->name);
-		priv->phyaddr = ofnode_read_u32_default(phandle_args.node,
-							"reg", -1);
+		if (!(IS_ENABLED(CONFIG_DM_ETH_PHY)))
+			priv->phyaddr = ofnode_read_u32_default
+					(phandle_args.node, "reg", -1);
+
 		priv->phy_of_node = phandle_args.node;
 		priv->max_speed = ofnode_read_u32_default(phandle_args.node,
 							  "max-speed",
@@ -870,10 +934,6 @@
 
 	priv->int_pcs = dev_read_bool(dev, "is-internal-pcspma");
 
-	printf("\nZYNQ GEM: %lx, mdio bus %lx, phyaddr %d, interface %s\n",
-	       (ulong)priv->iobase, (ulong)priv->mdiobase, priv->phyaddr,
-	       phy_string_for_interface(priv->interface));
-
 	priv->clk_en_info = dev_get_driver_data(dev);
 
 	return 0;
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index d7477d7..1303319 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -342,6 +342,7 @@
 source "drivers/pinctrl/nxp/Kconfig"
 source "drivers/pinctrl/renesas/Kconfig"
 source "drivers/pinctrl/rockchip/Kconfig"
+source "drivers/pinctrl/sunxi/Kconfig"
 source "drivers/pinctrl/uniphier/Kconfig"
 
 endmenu
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 030c38f..9b49782 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -14,7 +14,7 @@
 obj-$(CONFIG_ARCH_MTMIPS) += mtmips/
 obj-$(CONFIG_ARCH_RMOBILE) += renesas/
 obj-$(CONFIG_PINCTRL_SANDBOX)	+= pinctrl-sandbox.o
-
+obj-$(CONFIG_PINCTRL_SUNXI)	+= sunxi/
 obj-$(CONFIG_PINCTRL_UNIPHIER)	+= uniphier/
 obj-$(CONFIG_PINCTRL_PIC32)	+= pinctrl_pic32.o
 obj-$(CONFIG_PINCTRL_EXYNOS)	+= exynos/
diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig
new file mode 100644
index 0000000..77da908
--- /dev/null
+++ b/drivers/pinctrl/sunxi/Kconfig
@@ -0,0 +1,127 @@
+# SPDX-License-Identifier: GPL-2.0
+
+if ARCH_SUNXI
+
+config PINCTRL_SUNXI
+	select PINCTRL_FULL
+	select PINCTRL_GENERIC
+	select PINCONF
+	select PINMUX
+	bool
+
+config PINCTRL_SUNIV_F1C100S
+	bool "Support for the Allwinner F1C100s PIO"
+	default MACH_SUNIV
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN4I_A10
+	bool "Support for the Allwinner A10 PIO"
+	default MACH_SUN4I
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN5I_A13
+	bool "Support for the Allwinner A10s/A13 PIO"
+	default MACH_SUN5I
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN6I_A31
+	bool "Support for the Allwinner A31 PIO"
+	default MACH_SUN6I
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN6I_A31_R
+	bool "Support for the Allwinner A31 R-PIO"
+	default MACH_SUN6I
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN7I_A20
+	bool "Support for the Allwinner A20/R40 PIO"
+	default MACH_SUN7I || MACH_SUN8I_R40
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN8I_A23
+	bool "Support for the Allwinner A23 PIO"
+	default MACH_SUN8I_A23
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN8I_A23_R
+	bool "Support for the Allwinner A23/A33 R-PIO"
+	default MACH_SUN8I_A23 || MACH_SUN8I_A33
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN8I_A33
+	bool "Support for the Allwinner A33 PIO"
+	default MACH_SUN8I_A33
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN8I_A83T
+	bool "Support for the Allwinner A83T PIO"
+	default MACH_SUN8I_A83T
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN8I_A83T_R
+	bool "Support for the Allwinner A83T R-PIO"
+	default MACH_SUN8I_A83T
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN8I_H3
+	bool "Support for the Allwinner H3 PIO"
+	default MACH_SUN8I_H3
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN8I_H3_R
+	bool "Support for the Allwinner H3/H5 R-PIO"
+	default MACH_SUN8I_H3 || MACH_SUN50I_H5
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN8I_V3S
+	bool "Support for the Allwinner V3s PIO"
+	default MACH_SUN8I_V3S
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN9I_A80
+	bool "Support for the Allwinner A80 PIO"
+	default MACH_SUN9I
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN9I_A80_R
+	bool "Support for the Allwinner A80 R-PIO"
+	default MACH_SUN9I
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN50I_A64
+	bool "Support for the Allwinner A64 PIO"
+	default MACH_SUN50I
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN50I_A64_R
+	bool "Support for the Allwinner A64 R-PIO"
+	default MACH_SUN50I
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN50I_H5
+	bool "Support for the Allwinner H5 PIO"
+	default MACH_SUN50I_H5
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN50I_H6
+	bool "Support for the Allwinner H6 PIO"
+	default MACH_SUN50I_H6
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN50I_H6_R
+	bool "Support for the Allwinner H6 R-PIO"
+	default MACH_SUN50I_H6
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN50I_H616
+	bool "Support for the Allwinner H616 PIO"
+	default MACH_SUN50I_H616
+	select PINCTRL_SUNXI
+
+config PINCTRL_SUN50I_H616_R
+	bool "Support for the Allwinner H616 R-PIO"
+	default MACH_SUN50I_H616
+	select PINCTRL_SUNXI
+
+endif
diff --git a/drivers/pinctrl/sunxi/Makefile b/drivers/pinctrl/sunxi/Makefile
new file mode 100644
index 0000000..6a8c01f
--- /dev/null
+++ b/drivers/pinctrl/sunxi/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y					+= pinctrl-sunxi.o
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
new file mode 100644
index 0000000..9ce2bc1
--- /dev/null
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -0,0 +1,897 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <clk.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/pinctrl.h>
+#include <errno.h>
+#include <malloc.h>
+
+#include <asm/gpio.h>
+
+extern U_BOOT_DRIVER(gpio_sunxi);
+
+/*
+ * This structure implements a simplified view of the possible pinmux settings:
+ * Each mux value is assumed to be the same for a given function, across the
+ * pins in each group (almost universally true, with same rare exceptions not
+ * relevant to U-Boot), but also across different ports (not true in many
+ * cases). We ignore the first problem, and work around the latter by just
+ * supporting one particular port for a each function. This works fine for all
+ * board configurations so far. If this would need to be revisited, we could
+ * add a "u8 port;" below and match that, with 0 encoding the "don't care" case.
+ */
+struct sunxi_pinctrl_function {
+	const char	name[sizeof("gpio_out")];
+	u8		mux;
+};
+
+struct sunxi_pinctrl_desc {
+	const struct sunxi_pinctrl_function	*functions;
+	u8					num_functions;
+	u8					first_bank;
+	u8					num_banks;
+};
+
+struct sunxi_pinctrl_plat {
+	struct sunxi_gpio __iomem *base;
+};
+
+static int sunxi_pinctrl_get_pins_count(struct udevice *dev)
+{
+	const struct sunxi_pinctrl_desc *desc = dev_get_priv(dev);
+
+	return desc->num_banks * SUNXI_GPIOS_PER_BANK;
+}
+
+static const char *sunxi_pinctrl_get_pin_name(struct udevice *dev,
+					      uint pin_selector)
+{
+	const struct sunxi_pinctrl_desc *desc = dev_get_priv(dev);
+	static char pin_name[sizeof("PN31")];
+
+	snprintf(pin_name, sizeof(pin_name), "P%c%d",
+		 pin_selector / SUNXI_GPIOS_PER_BANK + desc->first_bank + 'A',
+		 pin_selector % SUNXI_GPIOS_PER_BANK);
+
+	return pin_name;
+}
+
+static int sunxi_pinctrl_get_functions_count(struct udevice *dev)
+{
+	const struct sunxi_pinctrl_desc *desc = dev_get_priv(dev);
+
+	return desc->num_functions;
+}
+
+static const char *sunxi_pinctrl_get_function_name(struct udevice *dev,
+						   uint func_selector)
+{
+	const struct sunxi_pinctrl_desc *desc = dev_get_priv(dev);
+
+	return desc->functions[func_selector].name;
+}
+
+static int sunxi_pinctrl_pinmux_set(struct udevice *dev, uint pin_selector,
+				    uint func_selector)
+{
+	const struct sunxi_pinctrl_desc *desc = dev_get_priv(dev);
+	struct sunxi_pinctrl_plat *plat = dev_get_plat(dev);
+	int bank = pin_selector / SUNXI_GPIOS_PER_BANK;
+	int pin	 = pin_selector % SUNXI_GPIOS_PER_BANK;
+
+	debug("set mux: %-4s => %s (%d)\n",
+	      sunxi_pinctrl_get_pin_name(dev, pin_selector),
+	      sunxi_pinctrl_get_function_name(dev, func_selector),
+	      desc->functions[func_selector].mux);
+
+	sunxi_gpio_set_cfgbank(plat->base + bank, pin,
+			       desc->functions[func_selector].mux);
+
+	return 0;
+}
+
+static const struct pinconf_param sunxi_pinctrl_pinconf_params[] = {
+	{ "bias-disable",	PIN_CONFIG_BIAS_DISABLE,	 0 },
+	{ "bias-pull-down",	PIN_CONFIG_BIAS_PULL_DOWN,	 2 },
+	{ "bias-pull-up",	PIN_CONFIG_BIAS_PULL_UP,	 1 },
+	{ "drive-strength",	PIN_CONFIG_DRIVE_STRENGTH,	10 },
+};
+
+static int sunxi_pinctrl_pinconf_set_pull(struct sunxi_pinctrl_plat *plat,
+					  uint bank, uint pin, uint bias)
+{
+	struct sunxi_gpio *regs = &plat->base[bank];
+
+	sunxi_gpio_set_pull_bank(regs, pin, bias);
+
+	return 0;
+}
+
+static int sunxi_pinctrl_pinconf_set_drive(struct sunxi_pinctrl_plat *plat,
+					   uint bank, uint pin, uint drive)
+{
+	struct sunxi_gpio *regs = &plat->base[bank];
+
+	if (drive < 10 || drive > 40)
+		return -EINVAL;
+
+	/* Convert mA to the register value, rounding down. */
+	sunxi_gpio_set_drv_bank(regs, pin, drive / 10 - 1);
+
+	return 0;
+}
+
+static int sunxi_pinctrl_pinconf_set(struct udevice *dev, uint pin_selector,
+				     uint param, uint val)
+{
+	struct sunxi_pinctrl_plat *plat = dev_get_plat(dev);
+	int bank = pin_selector / SUNXI_GPIOS_PER_BANK;
+	int pin  = pin_selector % SUNXI_GPIOS_PER_BANK;
+
+	switch (param) {
+	case PIN_CONFIG_BIAS_DISABLE:
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+	case PIN_CONFIG_BIAS_PULL_UP:
+		return sunxi_pinctrl_pinconf_set_pull(plat, bank, pin, val);
+	case PIN_CONFIG_DRIVE_STRENGTH:
+		return sunxi_pinctrl_pinconf_set_drive(plat, bank, pin, val);
+	}
+
+	return -EINVAL;
+}
+
+static int sunxi_pinctrl_get_pin_muxing(struct udevice *dev, uint pin_selector,
+					char *buf, int size)
+{
+	struct sunxi_pinctrl_plat *plat = dev_get_plat(dev);
+	int bank = pin_selector / SUNXI_GPIOS_PER_BANK;
+	int pin	 = pin_selector % SUNXI_GPIOS_PER_BANK;
+	int mux  = sunxi_gpio_get_cfgbank(plat->base + bank, pin);
+
+	switch (mux) {
+	case SUNXI_GPIO_INPUT:
+		strlcpy(buf, "gpio input", size);
+		break;
+	case SUNXI_GPIO_OUTPUT:
+		strlcpy(buf, "gpio output", size);
+		break;
+	case SUNXI_GPIO_DISABLE:
+		strlcpy(buf, "disabled", size);
+		break;
+	default:
+		snprintf(buf, size, "function %d", mux);
+		break;
+	}
+
+	return 0;
+}
+
+static const struct pinctrl_ops sunxi_pinctrl_ops = {
+	.get_pins_count		= sunxi_pinctrl_get_pins_count,
+	.get_pin_name		= sunxi_pinctrl_get_pin_name,
+	.get_functions_count	= sunxi_pinctrl_get_functions_count,
+	.get_function_name	= sunxi_pinctrl_get_function_name,
+	.pinmux_set		= sunxi_pinctrl_pinmux_set,
+	.pinconf_num_params	= ARRAY_SIZE(sunxi_pinctrl_pinconf_params),
+	.pinconf_params		= sunxi_pinctrl_pinconf_params,
+	.pinconf_set		= sunxi_pinctrl_pinconf_set,
+	.set_state		= pinctrl_generic_set_state,
+	.get_pin_muxing		= sunxi_pinctrl_get_pin_muxing,
+};
+
+static int sunxi_pinctrl_bind(struct udevice *dev)
+{
+	struct sunxi_pinctrl_plat *plat = dev_get_plat(dev);
+	struct sunxi_pinctrl_desc *desc;
+	struct sunxi_gpio_plat *gpio_plat;
+	struct udevice *gpio_dev;
+	int i, ret;
+
+	desc = (void *)dev_get_driver_data(dev);
+	if (!desc)
+		return -EINVAL;
+	dev_set_priv(dev, desc);
+
+	plat->base = dev_read_addr_ptr(dev);
+
+	ret = device_bind_driver_to_node(dev, "gpio_sunxi", dev->name,
+					 dev_ofnode(dev), &gpio_dev);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < desc->num_banks; ++i) {
+		gpio_plat = malloc(sizeof(*gpio_plat));
+		if (!gpio_plat)
+			return -ENOMEM;
+
+		gpio_plat->regs = plat->base + i;
+		gpio_plat->bank_name[0] = 'P';
+		gpio_plat->bank_name[1] = 'A' + desc->first_bank + i;
+		gpio_plat->bank_name[2] = '\0';
+
+		ret = device_bind(gpio_dev, DM_DRIVER_REF(gpio_sunxi),
+				  gpio_plat->bank_name, gpio_plat,
+				  ofnode_null(), NULL);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int sunxi_pinctrl_probe(struct udevice *dev)
+{
+	struct clk *apb_clk;
+
+	apb_clk = devm_clk_get(dev, "apb");
+	if (!IS_ERR(apb_clk))
+		clk_enable(apb_clk);
+
+	return 0;
+}
+
+static const struct sunxi_pinctrl_function suniv_f1c100s_pinctrl_functions[] = {
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "i2c0",	3 },	/* PE11-PE12 */
+	{ "i2c1",	3 },	/* PD5-PD6 */
+	{ "mmc0",	2 },	/* PF0-PF5 */
+	{ "mmc1",	3 },	/* PC0-PC2 */
+	{ "spi0",	2 },	/* PC0-PC3 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+	{ "uart0",	3 },	/* PF2-PF4 */
+#else
+	{ "uart0",	5 },	/* PE0-PE1 */
+#endif
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused suniv_f1c100s_pinctrl_desc = {
+	.functions	= suniv_f1c100s_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(suniv_f1c100s_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_A,
+	.num_banks	= 6,
+};
+
+static const struct sunxi_pinctrl_function sun4i_a10_pinctrl_functions[] = {
+	{ "emac",	2 },	/* PA0-PA17 */
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "i2c0",	2 },	/* PB0-PB1 */
+	{ "i2c1",	2 },	/* PB18-PB19 */
+	{ "mmc0",	2 },	/* PF0-PF5 */
+#if IS_ENABLED(CONFIG_MMC1_PINS_PH)
+	{ "mmc1",	5 },	/* PH22-PH27 */
+#else
+	{ "mmc1",	4 },	/* PG0-PG5 */
+#endif
+	{ "mmc2",	3 },	/* PC6-PC15 */
+	{ "mmc3",	2 },	/* PI4-PI9 */
+	{ "spi0",	3 },	/* PC0-PC2, PC23 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+	{ "uart0",	4 },	/* PF2-PF4 */
+#else
+	{ "uart0",	2 },	/* PB22-PB23 */
+#endif
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun4i_a10_pinctrl_desc = {
+	.functions	= sun4i_a10_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun4i_a10_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_A,
+	.num_banks	= 9,
+};
+
+static const struct sunxi_pinctrl_function sun5i_a13_pinctrl_functions[] = {
+	{ "emac",	2 },	/* PA0-PA17 */
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "i2c0",	2 },	/* PB0-PB1 */
+	{ "i2c1",	2 },	/* PB15-PB16 */
+	{ "mmc0",	2 },	/* PF0-PF5 */
+	{ "mmc1",	2 },	/* PG3-PG8 */
+	{ "mmc2",	3 },	/* PC6-PC15 */
+	{ "spi0",	3 },	/* PC0-PC3 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+	{ "uart0",	4 },	/* PF2-PF4 */
+#else
+	{ "uart0",	2 },	/* PB19-PB20 */
+#endif
+	{ "uart1",	4 },	/* PG3-PG4 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun5i_a13_pinctrl_desc = {
+	.functions	= sun5i_a13_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun5i_a13_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_A,
+	.num_banks	= 7,
+};
+
+static const struct sunxi_pinctrl_function sun6i_a31_pinctrl_functions[] = {
+	{ "gmac",	2 },	/* PA0-PA27 */
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "i2c0",	2 },	/* PH14-PH15 */
+	{ "i2c1",	2 },	/* PH16-PH17 */
+	{ "mmc0",	2 },	/* PF0-PF5 */
+	{ "mmc1",	2 },	/* PG0-PG5 */
+	{ "mmc2",	3 },	/* PC6-PC15, PC24 */
+	{ "mmc3",	4 },	/* PC6-PC15, PC24 */
+	{ "spi0",	3 },	/* PC0-PC2, PC27 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+	{ "uart0",	3 },	/* PF2-PF4 */
+#else
+	{ "uart0",	2 },	/* PH20-PH21 */
+#endif
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun6i_a31_pinctrl_desc = {
+	.functions	= sun6i_a31_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun6i_a31_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_A,
+	.num_banks	= 8,
+};
+
+static const struct sunxi_pinctrl_function sun6i_a31_r_pinctrl_functions[] = {
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "s_i2c",	2 },	/* PL0-PL1 */
+	{ "s_uart",	2 },	/* PL2-PL3 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun6i_a31_r_pinctrl_desc = {
+	.functions	= sun6i_a31_r_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun6i_a31_r_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_L,
+	.num_banks	= 2,
+};
+
+static const struct sunxi_pinctrl_function sun7i_a20_pinctrl_functions[] = {
+	{ "emac",	2 },	/* PA0-PA17 */
+	{ "gmac",	5 },	/* PA0-PA17 */
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "i2c0",	2 },	/* PB0-PB1 */
+	{ "i2c1",	2 },	/* PB18-PB19 */
+	{ "mmc0",	2 },	/* PF0-PF5 */
+#if IS_ENABLED(CONFIG_MMC1_PINS_PH)
+	{ "mmc1",	5 },	/* PH22-PH27 */
+#else
+	{ "mmc1",	4 },	/* PG0-PG5 */
+#endif
+	{ "mmc2",	3 },	/* PC5-PC15, PC24 */
+	{ "spi0",	3 },	/* PC0-PC2, PC23 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+	{ "uart0",	4 },	/* PF2-PF4 */
+#else
+	{ "uart0",	2 },	/* PB22-PB23 */
+#endif
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun7i_a20_pinctrl_desc = {
+	.functions	= sun7i_a20_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun7i_a20_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_A,
+	.num_banks	= 9,
+};
+
+static const struct sunxi_pinctrl_function sun8i_a23_pinctrl_functions[] = {
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "i2c0",	2 },	/* PH2-PH3 */
+	{ "i2c1",	2 },	/* PH4-PH5 */
+	{ "mmc0",	2 },	/* PF0-PF5 */
+	{ "mmc1",	2 },	/* PG0-PG5 */
+	{ "mmc2",	3 },	/* PC5-PC16 */
+	{ "spi0",	3 },	/* PC0-PC3 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+	{ "uart0",	3 },	/* PF2-PF4 */
+#endif
+	{ "uart1",	2 },	/* PG6-PG7 */
+	{ "uart2",	2 },	/* PB0-PB1 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun8i_a23_pinctrl_desc = {
+	.functions	= sun8i_a23_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun8i_a23_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_A,
+	.num_banks	= 8,
+};
+
+static const struct sunxi_pinctrl_function sun8i_a23_r_pinctrl_functions[] = {
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "s_i2c",	3 },	/* PL0-PL1 */
+	{ "s_uart",	2 },	/* PL2-PL3 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun8i_a23_r_pinctrl_desc = {
+	.functions	= sun8i_a23_r_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun8i_a23_r_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_L,
+	.num_banks	= 1,
+};
+
+static const struct sunxi_pinctrl_function sun8i_a33_pinctrl_functions[] = {
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "i2c0",	2 },	/* PH2-PH3 */
+	{ "i2c1",	2 },	/* PH4-PH5 */
+	{ "mmc0",	2 },	/* PF0-PF5 */
+	{ "mmc1",	2 },	/* PG0-PG5 */
+	{ "mmc2",	3 },	/* PC5-PC16 */
+	{ "spi0",	3 },	/* PC0-PC3 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+	{ "uart0",	3 },	/* PF2-PF4 */
+#else
+	{ "uart0",	3 },	/* PB0-PB1 */
+#endif
+	{ "uart1",	2 },	/* PG6-PG7 */
+	{ "uart2",	2 },	/* PB0-PB1 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun8i_a33_pinctrl_desc = {
+	.functions	= sun8i_a33_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun8i_a33_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_A,
+	.num_banks	= 8,
+};
+
+static const struct sunxi_pinctrl_function sun8i_a83t_pinctrl_functions[] = {
+	{ "gmac",	4 },	/* PD2-PD23 */
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "i2c0",	2 },	/* PH0-PH1 */
+	{ "i2c1",	2 },	/* PH2-PH3 */
+	{ "mmc0",	2 },	/* PF0-PF5 */
+	{ "mmc1",	2 },	/* PG0-PG5 */
+	{ "mmc2",	3 },	/* PC5-PC16 */
+	{ "spi0",	3 },	/* PC0-PC3 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+	{ "uart0",	3 },	/* PF2-PF4 */
+#else
+	{ "uart0",	2 },	/* PB9-PB10 */
+#endif
+	{ "uart1",	2 },	/* PG6-PG7 */
+	{ "uart2",	2 },	/* PB0-PB1 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun8i_a83t_pinctrl_desc = {
+	.functions	= sun8i_a83t_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun8i_a83t_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_A,
+	.num_banks	= 8,
+};
+
+static const struct sunxi_pinctrl_function sun8i_a83t_r_pinctrl_functions[] = {
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "s_i2c",	2 },	/* PL8-PL9 */
+	{ "s_uart",	2 },	/* PL2-PL3 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun8i_a83t_r_pinctrl_desc = {
+	.functions	= sun8i_a83t_r_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun8i_a83t_r_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_L,
+	.num_banks	= 1,
+};
+
+static const struct sunxi_pinctrl_function sun8i_h3_pinctrl_functions[] = {
+	{ "emac",	2 },	/* PD0-PD17 */
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "i2c0",	2 },	/* PA11-PA12 */
+	{ "i2c1",	3 },	/* PA18-PA19 */
+	{ "mmc0",	2 },	/* PF0-PF5 */
+	{ "mmc1",	2 },	/* PG0-PG5 */
+	{ "mmc2",	3 },	/* PC5-PC16 */
+	{ "spi0",	3 },	/* PC0-PC3 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+	{ "uart0",	3 },	/* PF2-PF4 */
+#else
+	{ "uart0",	2 },	/* PA4-PA5 */
+#endif
+	{ "uart1",	2 },	/* PG6-PG7 */
+	{ "uart2",	2 },	/* PA0-PA1 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun8i_h3_pinctrl_desc = {
+	.functions	= sun8i_h3_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun8i_h3_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_A,
+	.num_banks	= 7,
+};
+
+static const struct sunxi_pinctrl_function sun8i_h3_r_pinctrl_functions[] = {
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "s_i2c",	2 },	/* PL0-PL1 */
+	{ "s_uart",	2 },	/* PL2-PL3 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun8i_h3_r_pinctrl_desc = {
+	.functions	= sun8i_h3_r_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun8i_h3_r_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_L,
+	.num_banks	= 1,
+};
+
+static const struct sunxi_pinctrl_function sun8i_v3s_pinctrl_functions[] = {
+	{ "emac",	4 },	/* PD0-PD17 */
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "i2c0",	2 },	/* PB6-PB7 */
+	{ "i2c1",	2 },	/* PB8-PB9 */
+	{ "mmc0",	2 },	/* PF0-PF5 */
+	{ "mmc1",	2 },	/* PG0-PG5 */
+	{ "mmc2",	2 },	/* PC0-PC10 */
+	{ "spi0",	3 },	/* PC0-PC3 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+	{ "uart0",	3 },	/* PF2-PF4 */
+#else
+	{ "uart0",	3 },	/* PB8-PB9 */
+#endif
+	{ "uart1",	2 },	/* PG6-PG7 */
+	{ "uart2",	2 },	/* PB0-PB1 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun8i_v3s_pinctrl_desc = {
+	.functions	= sun8i_v3s_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun8i_v3s_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_A,
+	.num_banks	= 7,
+};
+
+static const struct sunxi_pinctrl_function sun9i_a80_pinctrl_functions[] = {
+	{ "gmac",	2 },	/* PA0-PA17 */
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "i2c0",	2 },	/* PH0-PH1 */
+	{ "i2c1",	2 },	/* PH2-PH3 */
+	{ "mmc0",	2 },	/* PF0-PF5 */
+	{ "mmc1",	2 },	/* PG0-PG5 */
+	{ "mmc2",	3 },	/* PC6-PC16 */
+	{ "spi0",	3 },	/* PC0-PC2, PC19 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+	{ "uart0",	4 },	/* PF2-PF4 */
+#else
+	{ "uart0",	2 },	/* PH12-PH13 */
+#endif
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun9i_a80_pinctrl_desc = {
+	.functions	= sun9i_a80_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun9i_a80_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_A,
+	.num_banks	= 8,
+};
+
+static const struct sunxi_pinctrl_function sun9i_a80_r_pinctrl_functions[] = {
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "s_i2c0",	2 },	/* PN0-PN1 */
+	{ "s_i2c1",	3 },	/* PM8-PM9 */
+	{ "s_uart",	3 },	/* PL0-PL1 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun9i_a80_r_pinctrl_desc = {
+	.functions	= sun9i_a80_r_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun9i_a80_r_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_L,
+	.num_banks	= 3,
+};
+
+static const struct sunxi_pinctrl_function sun50i_a64_pinctrl_functions[] = {
+	{ "emac",	4 },	/* PD8-PD23 */
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "i2c0",	2 },	/* PH0-PH1 */
+	{ "i2c1",	2 },	/* PH2-PH3 */
+	{ "mmc0",	2 },	/* PF0-PF5 */
+	{ "mmc1",	2 },	/* PG0-PG5 */
+	{ "mmc2",	3 },	/* PC1-PC16 */
+	{ "pwm",	2 },	/* PD22 */
+	{ "spi0",	4 },	/* PC0-PC3 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+	{ "uart0",	3 },	/* PF2-PF4 */
+#else
+	{ "uart0",	4 },	/* PB8-PB9 */
+#endif
+	{ "uart1",	2 },	/* PG6-PG7 */
+	{ "uart2",	2 },	/* PB0-PB1 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun50i_a64_pinctrl_desc = {
+	.functions	= sun50i_a64_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun50i_a64_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_A,
+	.num_banks	= 8,
+};
+
+static const struct sunxi_pinctrl_function sun50i_a64_r_pinctrl_functions[] = {
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "s_i2c",	2 },	/* PL8-PL9 */
+	{ "s_uart",	2 },	/* PL2-PL3 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun50i_a64_r_pinctrl_desc = {
+	.functions	= sun50i_a64_r_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun50i_a64_r_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_L,
+	.num_banks	= 1,
+};
+
+static const struct sunxi_pinctrl_function sun50i_h5_pinctrl_functions[] = {
+	{ "emac",	2 },	/* PD0-PD17 */
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "i2c0",	2 },	/* PA11-PA12 */
+	{ "i2c1",	3 },	/* PA18-PA19 */
+	{ "mmc0",	2 },	/* PF0-PF5 */
+	{ "mmc1",	2 },	/* PG0-PG5 */
+	{ "mmc2",	3 },	/* PC1-PC16 */
+	{ "spi0",	3 },	/* PC0-PC3 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+	{ "uart0",	3 },	/* PF2-PF4 */
+#else
+	{ "uart0",	2 },	/* PA4-PA5 */
+#endif
+	{ "uart1",	2 },	/* PG6-PG7 */
+	{ "uart2",	2 },	/* PA0-PA1 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun50i_h5_pinctrl_desc = {
+	.functions	= sun50i_h5_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun50i_h5_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_A,
+	.num_banks	= 7,
+};
+
+static const struct sunxi_pinctrl_function sun50i_h6_pinctrl_functions[] = {
+	{ "emac",	5 },	/* PD0-PD20 */
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "i2c0",	2 },	/* PD25-PD26 */
+	{ "i2c1",	4 },	/* PH5-PH6 */
+	{ "mmc0",	2 },	/* PF0-PF5 */
+	{ "mmc1",	2 },	/* PG0-PG5 */
+	{ "mmc2",	3 },	/* PC1-PC14 */
+	{ "spi0",	4 },	/* PC0-PC7 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+	{ "uart0",	3 },	/* PF2-PF4 */
+#else
+	{ "uart0",	2 },	/* PH0-PH1 */
+#endif
+	{ "uart1",	2 },	/* PG6-PG7 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun50i_h6_pinctrl_desc = {
+	.functions	= sun50i_h6_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun50i_h6_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_A,
+	.num_banks	= 8,
+};
+
+static const struct sunxi_pinctrl_function sun50i_h6_r_pinctrl_functions[] = {
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "s_i2c",	3 },	/* PL0-PL1 */
+	{ "s_uart",	2 },	/* PL2-PL3 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun50i_h6_r_pinctrl_desc = {
+	.functions	= sun50i_h6_r_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun50i_h6_r_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_L,
+	.num_banks	= 2,
+};
+
+static const struct sunxi_pinctrl_function sun50i_h616_pinctrl_functions[] = {
+	{ "emac0",	2 },	/* PI0-PI16 */
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "mmc0",	2 },	/* PF0-PF5 */
+	{ "mmc1",	2 },	/* PG0-PG5 */
+	{ "mmc2",	3 },	/* PC0-PC16 */
+	{ "spi0",	4 },	/* PC0-PC7, PC15-PC16 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+	{ "uart0",	3 },	/* PF2-PF4 */
+#else
+	{ "uart0",	2 },	/* PH0-PH1 */
+#endif
+	{ "uart1",	2 },	/* PG6-PG7 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun50i_h616_pinctrl_desc = {
+	.functions	= sun50i_h616_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun50i_h616_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_A,
+	.num_banks	= 9,
+};
+
+static const struct sunxi_pinctrl_function sun50i_h616_r_pinctrl_functions[] = {
+	{ "gpio_in",	0 },
+	{ "gpio_out",	1 },
+	{ "s_i2c",	3 },	/* PL0-PL1 */
+	{ "s_uart",	2 },	/* PL2-PL3 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun50i_h616_r_pinctrl_desc = {
+	.functions	= sun50i_h616_r_pinctrl_functions,
+	.num_functions	= ARRAY_SIZE(sun50i_h616_r_pinctrl_functions),
+	.first_bank	= SUNXI_GPIO_L,
+	.num_banks	= 1,
+};
+
+static const struct udevice_id sunxi_pinctrl_ids[] = {
+#ifdef CONFIG_PINCTRL_SUNIV_F1C100S
+	{
+		.compatible = "allwinner,suniv-f1c100s-pinctrl",
+		.data = (ulong)&suniv_f1c100s_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN4I_A10
+	{
+		.compatible = "allwinner,sun4i-a10-pinctrl",
+		.data = (ulong)&sun4i_a10_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN5I_A13
+	{
+		.compatible = "allwinner,sun5i-a10s-pinctrl",
+		.data = (ulong)&sun5i_a13_pinctrl_desc,
+	},
+	{
+		.compatible = "allwinner,sun5i-a13-pinctrl",
+		.data = (ulong)&sun5i_a13_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN6I_A31
+	{
+		.compatible = "allwinner,sun6i-a31-pinctrl",
+		.data = (ulong)&sun6i_a31_pinctrl_desc,
+	},
+	{
+		.compatible = "allwinner,sun6i-a31s-pinctrl",
+		.data = (ulong)&sun6i_a31_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN6I_A31_R
+	{
+		.compatible = "allwinner,sun6i-a31-r-pinctrl",
+		.data = (ulong)&sun6i_a31_r_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN7I_A20
+	{
+		.compatible = "allwinner,sun7i-a20-pinctrl",
+		.data = (ulong)&sun7i_a20_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN8I_A23
+	{
+		.compatible = "allwinner,sun8i-a23-pinctrl",
+		.data = (ulong)&sun8i_a23_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN8I_A23_R
+	{
+		.compatible = "allwinner,sun8i-a23-r-pinctrl",
+		.data = (ulong)&sun8i_a23_r_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN8I_A33
+	{
+		.compatible = "allwinner,sun8i-a33-pinctrl",
+		.data = (ulong)&sun8i_a33_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN8I_A83T
+	{
+		.compatible = "allwinner,sun8i-a83t-pinctrl",
+		.data = (ulong)&sun8i_a83t_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN8I_A83T_R
+	{
+		.compatible = "allwinner,sun8i-a83t-r-pinctrl",
+		.data = (ulong)&sun8i_a83t_r_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN8I_H3
+	{
+		.compatible = "allwinner,sun8i-h3-pinctrl",
+		.data = (ulong)&sun8i_h3_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN8I_H3_R
+	{
+		.compatible = "allwinner,sun8i-h3-r-pinctrl",
+		.data = (ulong)&sun8i_h3_r_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN7I_A20
+	{
+		.compatible = "allwinner,sun8i-r40-pinctrl",
+		.data = (ulong)&sun7i_a20_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN8I_V3S
+	{
+		.compatible = "allwinner,sun8i-v3-pinctrl",
+		.data = (ulong)&sun8i_v3s_pinctrl_desc,
+	},
+	{
+		.compatible = "allwinner,sun8i-v3s-pinctrl",
+		.data = (ulong)&sun8i_v3s_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN9I_A80
+	{
+		.compatible = "allwinner,sun9i-a80-pinctrl",
+		.data = (ulong)&sun9i_a80_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN9I_A80_R
+	{
+		.compatible = "allwinner,sun9i-a80-r-pinctrl",
+		.data = (ulong)&sun9i_a80_r_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN50I_A64
+	{
+		.compatible = "allwinner,sun50i-a64-pinctrl",
+		.data = (ulong)&sun50i_a64_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN50I_A64_R
+	{
+		.compatible = "allwinner,sun50i-a64-r-pinctrl",
+		.data = (ulong)&sun50i_a64_r_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN50I_H5
+	{
+		.compatible = "allwinner,sun50i-h5-pinctrl",
+		.data = (ulong)&sun50i_h5_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN50I_H6
+	{
+		.compatible = "allwinner,sun50i-h6-pinctrl",
+		.data = (ulong)&sun50i_h6_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN50I_H6_R
+	{
+		.compatible = "allwinner,sun50i-h6-r-pinctrl",
+		.data = (ulong)&sun50i_h6_r_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN50I_H616
+	{
+		.compatible = "allwinner,sun50i-h616-pinctrl",
+		.data = (ulong)&sun50i_h616_pinctrl_desc,
+	},
+#endif
+#ifdef CONFIG_PINCTRL_SUN50I_H616_R
+	{
+		.compatible = "allwinner,sun50i-h616-r-pinctrl",
+		.data = (ulong)&sun50i_h616_r_pinctrl_desc,
+	},
+#endif
+	{}
+};
+
+U_BOOT_DRIVER(sunxi_pinctrl) = {
+	.name		= "sunxi-pinctrl",
+	.id		= UCLASS_PINCTRL,
+	.of_match	= sunxi_pinctrl_ids,
+	.bind		= sunxi_pinctrl_bind,
+	.probe		= sunxi_pinctrl_probe,
+	.plat_auto	= sizeof(struct sunxi_pinctrl_plat),
+	.ops		= &sunxi_pinctrl_ops,
+};
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 6be612d..cb54e67 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -23,6 +23,13 @@
 	help
 	  Support for PWM hardware on AT91 based SoC.
 
+config PWM_CADENCE_TTC
+	bool "Enable support for the Cadence TTC PWM"
+	depends on DM_PWM && !CADENCE_TTC_TIMER
+	help
+	  Cadence TTC can be configured as timer which is done via
+	  CONFIG_CADENCE_TTC_TIMER or as PWM. This is covering only PWM now.
+
 config PWM_CROS_EC
 	bool "Enable support for the Chrome OS EC PWM"
 	depends on DM_PWM
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 5d31812..bd119a6 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -12,6 +12,7 @@
 
 obj-$(CONFIG_PWM_ASPEED)	+= pwm-aspeed.o
 obj-$(CONFIG_PWM_AT91)		+= pwm-at91.o
+obj-$(CONFIG_PWM_CADENCE_TTC)	+= pwm-cadence-ttc.o
 obj-$(CONFIG_PWM_CROS_EC)	+= cros_ec_pwm.o
 obj-$(CONFIG_PWM_EXYNOS)	+= exynos_pwm.o
 obj-$(CONFIG_PWM_IMX)		+= pwm-imx.o pwm-imx-util.o
diff --git a/drivers/pwm/pwm-cadence-ttc.c b/drivers/pwm/pwm-cadence-ttc.c
new file mode 100644
index 0000000..dc3b314
--- /dev/null
+++ b/drivers/pwm/pwm-cadence-ttc.c
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021 Xilinx, Inc. Michal Simek
+ */
+
+#define LOG_CATEGORY UCLASS_PWM
+
+#include <clk.h>
+#include <common.h>
+#include <div64.h>
+#include <dm.h>
+#include <log.h>
+#include <pwm.h>
+#include <asm/io.h>
+#include <log.h>
+#include <div64.h>
+#include <linux/bitfield.h>
+#include <linux/math64.h>
+#include <linux/log2.h>
+#include <dm/device_compat.h>
+
+#define CLOCK_CONTROL		0
+#define COUNTER_CONTROL		0xc
+#define INTERVAL_COUNTER	0x24
+#define MATCH_1_COUNTER		0x30
+
+#define CLK_FALLING_EDGE	BIT(6)
+#define CLK_SRC_EXTERNAL	BIT(5)
+#define CLK_PRESCALE_MASK	GENMASK(4, 1)
+#define CLK_PRESCALE_ENABLE	BIT(0)
+
+#define COUNTER_WAVE_POL		BIT(6)
+#define COUNTER_WAVE_DISABLE		BIT(5)
+#define COUNTER_RESET			BIT(4)
+#define COUNTER_MATCH_ENABLE		BIT(3)
+#define COUNTER_DECREMENT_ENABLE	BIT(2)
+#define COUNTER_INTERVAL_ENABLE		BIT(1)
+#define COUNTER_COUNTING_DISABLE	BIT(0)
+
+#define NSEC_PER_SEC	1000000000L
+
+#define TTC_REG(reg, channel) ((reg) + (channel) * sizeof(u32))
+#define TTC_CLOCK_CONTROL(reg, channel) \
+	TTC_REG((reg) + CLOCK_CONTROL, (channel))
+#define TTC_COUNTER_CONTROL(reg, channel) \
+	TTC_REG((reg) + COUNTER_CONTROL, (channel))
+#define TTC_INTERVAL_COUNTER(reg, channel) \
+	TTC_REG((reg) + INTERVAL_COUNTER, (channel))
+#define TTC_MATCH_1_COUNTER(reg, channel) \
+	TTC_REG((reg) + MATCH_1_COUNTER, (channel))
+
+struct cadence_ttc_pwm_plat {
+	u8 *regs;
+	u32 timer_width;
+};
+
+struct cadence_ttc_pwm_priv {
+	u8 *regs;
+	u32 timer_width;
+	u32 timer_mask;
+	unsigned long frequency;
+	bool invert[2];
+};
+
+static int cadence_ttc_pwm_set_invert(struct udevice *dev, uint channel,
+				      bool polarity)
+{
+	struct cadence_ttc_pwm_priv *priv = dev_get_priv(dev);
+
+	if (channel > 2) {
+		dev_err(dev, "Unsupported channel number %d(max 2)\n", channel);
+		return -EINVAL;
+	}
+
+	priv->invert[channel] = polarity;
+
+	dev_dbg(dev, "polarity=%u. Please config PWM again\n", polarity);
+
+	return 0;
+}
+
+static int cadence_ttc_pwm_set_config(struct udevice *dev, uint channel,
+				      uint period_ns, uint duty_ns)
+{
+	struct cadence_ttc_pwm_priv *priv = dev_get_priv(dev);
+	u32 counter_ctrl, clock_ctrl;
+	int period_clocks, duty_clocks, prescaler;
+
+	dev_dbg(dev, "channel %d, duty %d/period %d ns\n", channel,
+		duty_ns, period_ns);
+
+	if (channel > 2) {
+		dev_err(dev, "Unsupported channel number %d(max 2)\n", channel);
+		return -EINVAL;
+	}
+
+	/* Make sure counter is stopped */
+	counter_ctrl = readl(TTC_COUNTER_CONTROL(priv->regs, channel));
+	setbits_le32(TTC_COUNTER_CONTROL(priv->regs, channel),
+		     COUNTER_COUNTING_DISABLE | COUNTER_WAVE_DISABLE);
+
+	/* Calculate period, prescaler and set clock control register */
+	period_clocks = div64_u64(((int64_t)period_ns * priv->frequency),
+				  NSEC_PER_SEC);
+
+	prescaler = ilog2(period_clocks) + 1 - priv->timer_width;
+	if (prescaler < 0)
+		prescaler = 0;
+
+	clock_ctrl = readl(TTC_CLOCK_CONTROL(priv->regs, channel));
+
+	if (!prescaler) {
+		clock_ctrl &= ~(CLK_PRESCALE_ENABLE | CLK_PRESCALE_MASK);
+	} else {
+		clock_ctrl &= ~CLK_PRESCALE_MASK;
+		clock_ctrl |= CLK_PRESCALE_ENABLE;
+		clock_ctrl |= FIELD_PREP(CLK_PRESCALE_MASK, prescaler - 1);
+	};
+
+	/* External source is not handled by this driver now */
+	clock_ctrl &= ~CLK_SRC_EXTERNAL;
+
+	writel(clock_ctrl, TTC_CLOCK_CONTROL(priv->regs, channel));
+
+	/* Calculate interval and set counter control value */
+	duty_clocks = div64_u64(((int64_t)duty_ns * priv->frequency),
+				NSEC_PER_SEC);
+
+	writel((period_clocks >> prescaler) & priv->timer_mask,
+	       TTC_INTERVAL_COUNTER(priv->regs, channel));
+	writel((duty_clocks >> prescaler) & priv->timer_mask,
+	       TTC_MATCH_1_COUNTER(priv->regs, channel));
+
+	/* Restore/reset counter */
+	counter_ctrl &= ~COUNTER_DECREMENT_ENABLE;
+	counter_ctrl |= COUNTER_INTERVAL_ENABLE |
+			COUNTER_RESET |
+			COUNTER_MATCH_ENABLE;
+
+	if (priv->invert[channel])
+		counter_ctrl |= COUNTER_WAVE_POL;
+	else
+		counter_ctrl &= ~COUNTER_WAVE_POL;
+
+	writel(counter_ctrl, TTC_COUNTER_CONTROL(priv->regs, channel));
+
+	dev_dbg(dev, "%d/%d clocks, prescaler 2^%d\n", duty_clocks,
+		period_clocks, prescaler);
+
+	return 0;
+};
+
+static int cadence_ttc_pwm_set_enable(struct udevice *dev, uint channel,
+				      bool enable)
+{
+	struct cadence_ttc_pwm_priv *priv = dev_get_priv(dev);
+
+	if (channel > 2) {
+		dev_err(dev, "Unsupported channel number %d(max 2)\n", channel);
+		return -EINVAL;
+	}
+
+	dev_dbg(dev, "Enable: %d, channel %d\n", enable, channel);
+
+	if (enable) {
+		clrbits_le32(TTC_COUNTER_CONTROL(priv->regs, channel),
+			     COUNTER_COUNTING_DISABLE |
+			     COUNTER_WAVE_DISABLE);
+		setbits_le32(TTC_COUNTER_CONTROL(priv->regs, channel),
+			     COUNTER_RESET);
+	} else {
+		setbits_le32(TTC_COUNTER_CONTROL(priv->regs, channel),
+			     COUNTER_COUNTING_DISABLE |
+			     COUNTER_WAVE_DISABLE);
+	}
+
+	return 0;
+};
+
+static int cadence_ttc_pwm_probe(struct udevice *dev)
+{
+	struct cadence_ttc_pwm_priv *priv = dev_get_priv(dev);
+	struct cadence_ttc_pwm_plat *plat = dev_get_plat(dev);
+	struct clk clk;
+	int ret;
+
+	priv->regs = plat->regs;
+	priv->timer_width = plat->timer_width;
+	priv->timer_mask = GENMASK(priv->timer_width - 1, 0);
+
+	ret = clk_get_by_index(dev, 0, &clk);
+	if (ret < 0) {
+		dev_err(dev, "failed to get clock\n");
+		return ret;
+	}
+
+	priv->frequency = clk_get_rate(&clk);
+	if (IS_ERR_VALUE(priv->frequency)) {
+		dev_err(dev, "failed to get rate\n");
+		return priv->frequency;
+	}
+	dev_dbg(dev, "Clk frequency: %ld\n", priv->frequency);
+
+	ret = clk_enable(&clk);
+	if (ret) {
+		dev_err(dev, "failed to enable clock\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int cadence_ttc_pwm_of_to_plat(struct udevice *dev)
+{
+	struct cadence_ttc_pwm_plat *plat = dev_get_plat(dev);
+	const char *cells;
+
+	cells = dev_read_prop(dev, "#pwm-cells", NULL);
+	if (!cells)
+		return -EINVAL;
+
+	plat->regs = dev_read_addr_ptr(dev);
+
+	plat->timer_width = dev_read_u32_default(dev, "timer-width", 16);
+
+	return 0;
+}
+
+static int cadence_ttc_pwm_bind(struct udevice *dev)
+{
+	const char *cells;
+
+	cells = dev_read_prop(dev, "#pwm-cells", NULL);
+	if (!cells)
+		return -ENODEV;
+
+	return 0;
+}
+
+static const struct pwm_ops cadence_ttc_pwm_ops = {
+	.set_invert = cadence_ttc_pwm_set_invert,
+	.set_config = cadence_ttc_pwm_set_config,
+	.set_enable = cadence_ttc_pwm_set_enable,
+};
+
+static const struct udevice_id cadence_ttc_pwm_ids[] = {
+	{ .compatible = "cdns,ttc" },
+	{ }
+};
+
+U_BOOT_DRIVER(cadence_ttc_pwm) = {
+	.name = "cadence_ttc_pwm",
+	.id = UCLASS_PWM,
+	.of_match = cadence_ttc_pwm_ids,
+	.ops = &cadence_ttc_pwm_ops,
+	.bind = cadence_ttc_pwm_bind,
+	.of_to_plat = cadence_ttc_pwm_of_to_plat,
+	.probe = cadence_ttc_pwm_probe,
+	.priv_auto = sizeof(struct cadence_ttc_pwm_priv),
+	.plat_auto = sizeof(struct cadence_ttc_pwm_plat),
+};
diff --git a/drivers/pwm/sunxi_pwm.c b/drivers/pwm/sunxi_pwm.c
index e3d5ee4..bb1bec0 100644
--- a/drivers/pwm/sunxi_pwm.c
+++ b/drivers/pwm/sunxi_pwm.c
@@ -13,7 +13,6 @@
 #include <asm/global_data.h>
 #include <asm/io.h>
 #include <asm/arch/pwm.h>
-#include <asm/arch/gpio.h>
 #include <power/regulator.h>
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -45,14 +44,6 @@
 	1,	/* 1111 */
 };
 
-static int sunxi_pwm_config_pinmux(void)
-{
-#ifdef CONFIG_MACH_SUN50I
-	sunxi_gpio_set_cfgpin(SUNXI_GPD(22), SUNXI_GPD_PWM);
-#endif
-	return 0;
-}
-
 static int sunxi_pwm_set_invert(struct udevice *dev, uint channel,
 				bool polarity)
 {
@@ -137,8 +128,6 @@
 		return 0;
 	}
 
-	sunxi_pwm_config_pinmux();
-
 	if (priv->invert)
 		v &= ~SUNXI_PWM_CTRL_CH0_ACT_STA;
 	else
diff --git a/drivers/serial/serial_zynq.c b/drivers/serial/serial_zynq.c
index fd99936..6bb003d 100644
--- a/drivers/serial/serial_zynq.c
+++ b/drivers/serial/serial_zynq.c
@@ -21,6 +21,7 @@
 
 #define ZYNQ_UART_SR_TXACTIVE	BIT(11) /* TX active */
 #define ZYNQ_UART_SR_TXFULL	BIT(4) /* TX FIFO full */
+#define ZYNQ_UART_SR_TXEMPTY	BIT(3) /* TX FIFO empty */
 #define ZYNQ_UART_SR_RXEMPTY	BIT(1) /* RX FIFO empty */
 
 #define ZYNQ_UART_CR_TX_EN	BIT(4) /* TX enabled */
@@ -107,8 +108,13 @@
 
 static int _uart_zynq_serial_putc(struct uart_zynq *regs, const char c)
 {
-	if (readl(&regs->channel_sts) & ZYNQ_UART_SR_TXFULL)
-		return -EAGAIN;
+	if (CONFIG_IS_ENABLED(DEBUG_UART_ZYNQ)) {
+		if (!(readl(&regs->channel_sts) & ZYNQ_UART_SR_TXEMPTY))
+			return -EAGAIN;
+	} else {
+		if (readl(&regs->channel_sts) & ZYNQ_UART_SR_TXFULL)
+			return -EAGAIN;
+	}
 
 	writel(c, &regs->tx_rx_fifo);
 
diff --git a/drivers/spi/spi-sunxi.c b/drivers/spi/spi-sunxi.c
index d62355e..b6cd7dd 100644
--- a/drivers/spi/spi-sunxi.c
+++ b/drivers/spi/spi-sunxi.c
@@ -32,7 +32,6 @@
 #include <linux/bitops.h>
 
 #include <asm/bitops.h>
-#include <asm/gpio.h>
 #include <asm/io.h>
 
 #include <linux/iopoll.h>
@@ -180,87 +179,6 @@
 	writel(reg, SPI_REG(priv, SPI_TCR));
 }
 
-static int sun4i_spi_parse_pins(struct udevice *dev)
-{
-	const void *fdt = gd->fdt_blob;
-	const char *pin_name;
-	const fdt32_t *list;
-	u32 phandle;
-	int drive, pull = 0, pin, i;
-	int offset;
-	int size;
-
-	list = fdt_getprop(fdt, dev_of_offset(dev), "pinctrl-0", &size);
-	if (!list) {
-		printf("WARNING: sun4i_spi: cannot find pinctrl-0 node\n");
-		return -EINVAL;
-	}
-
-	while (size) {
-		phandle = fdt32_to_cpu(*list++);
-		size -= sizeof(*list);
-
-		offset = fdt_node_offset_by_phandle(fdt, phandle);
-		if (offset < 0)
-			return offset;
-
-		drive = fdt_getprop_u32_default_node(fdt, offset, 0,
-						     "drive-strength", 0);
-		if (drive) {
-			if (drive <= 10)
-				drive = 0;
-			else if (drive <= 20)
-				drive = 1;
-			else if (drive <= 30)
-				drive = 2;
-			else
-				drive = 3;
-		} else {
-			drive = fdt_getprop_u32_default_node(fdt, offset, 0,
-							     "allwinner,drive",
-							      0);
-			drive = min(drive, 3);
-		}
-
-		if (fdt_get_property(fdt, offset, "bias-disable", NULL))
-			pull = 0;
-		else if (fdt_get_property(fdt, offset, "bias-pull-up", NULL))
-			pull = 1;
-		else if (fdt_get_property(fdt, offset, "bias-pull-down", NULL))
-			pull = 2;
-		else
-			pull = fdt_getprop_u32_default_node(fdt, offset, 0,
-							    "allwinner,pull",
-							     0);
-		pull = min(pull, 2);
-
-		for (i = 0; ; i++) {
-			pin_name = fdt_stringlist_get(fdt, offset,
-						      "pins", i, NULL);
-			if (!pin_name) {
-				pin_name = fdt_stringlist_get(fdt, offset,
-							      "allwinner,pins",
-							       i, NULL);
-				if (!pin_name)
-					break;
-			}
-
-			pin = sunxi_name_to_gpio(pin_name);
-			if (pin < 0)
-				break;
-
-			if (IS_ENABLED(CONFIG_MACH_SUN50I) ||
-			    IS_ENABLED(CONFIG_SUN50I_GEN_H6))
-				sunxi_gpio_set_cfgpin(pin, SUN50I_GPC_SPI0);
-			else
-				sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_SPI0);
-			sunxi_gpio_set_drv(pin, drive);
-			sunxi_gpio_set_pull(pin, pull);
-		}
-	}
-	return 0;
-}
-
 static inline int sun4i_spi_set_clock(struct udevice *dev, bool enable)
 {
 	struct sun4i_spi_priv *priv = dev_get_priv(dev);
@@ -507,8 +425,6 @@
 		return ret;
 	}
 
-	sun4i_spi_parse_pins(bus);
-
 	priv->variant = plat->variant;
 	priv->base = plat->base;
 	priv->freq = plat->max_hz;
diff --git a/drivers/timer/cadence-ttc.c b/drivers/timer/cadence-ttc.c
index 2f95d45..2eff450 100644
--- a/drivers/timer/cadence-ttc.c
+++ b/drivers/timer/cadence-ttc.c
@@ -97,6 +97,17 @@
 	return 0;
 }
 
+static int cadence_ttc_bind(struct udevice *dev)
+{
+	const char *cells;
+
+	cells = dev_read_prop(dev, "#pwm-cells", NULL);
+	if (cells)
+		return -ENODEV;
+
+	return 0;
+}
+
 static const struct timer_ops cadence_ttc_ops = {
 	.get_count = cadence_ttc_get_count,
 };
@@ -114,4 +125,5 @@
 	.priv_auto	= sizeof(struct cadence_ttc_priv),
 	.probe = cadence_ttc_probe,
 	.ops = &cadence_ttc_ops,
+	.bind = cadence_ttc_bind,
 };
diff --git a/include/configs/xilinx_versal.h b/include/configs/xilinx_versal.h
index 60df795..80e9411 100644
--- a/include/configs/xilinx_versal.h
+++ b/include/configs/xilinx_versal.h
@@ -43,7 +43,7 @@
 # define PHY_ANEG_TIMEOUT       20000
 #endif
 
-#define CONFIG_SYS_BOOTM_LEN	(60 * 1024 * 1024)
+#define CONFIG_SYS_BOOTM_LEN	(100 * 1024 * 1024)
 
 #define ENV_MEM_LAYOUT_SETTINGS \
 	"fdt_addr_r=0x40000000\0" \
diff --git a/include/configs/xilinx_zynqmp.h b/include/configs/xilinx_zynqmp.h
index a063c01..1985a09 100644
--- a/include/configs/xilinx_zynqmp.h
+++ b/include/configs/xilinx_zynqmp.h
@@ -58,7 +58,7 @@
 # define PHY_ANEG_TIMEOUT       20000
 #endif
 
-#define CONFIG_SYS_BOOTM_LEN	(60 * 1024 * 1024)
+#define CONFIG_SYS_BOOTM_LEN	(100 * 1024 * 1024)
 
 #define ENV_MEM_LAYOUT_SETTINGS \
 	"fdt_addr_r=0x40000000\0" \
diff --git a/include/dt-bindings/phy/phy.h b/include/dt-bindings/phy/phy.h
index d3714ed..f48c9ac 100644
--- a/include/dt-bindings/phy/phy.h
+++ b/include/dt-bindings/phy/phy.h
@@ -1,10 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
 /*
  *
  * This header provides constants for the phy framework
  *
  * Copyright (C) 2014 STMicroelectronics
  * Author: Gabriel Fernandez <gabriel.fernandez@st.com>
- * License terms:  GNU General Public License (GPL), version 2
  */
 
 #ifndef _DT_BINDINGS_PHY
@@ -20,5 +20,7 @@
 #define PHY_TYPE_XPCS		7
 #define PHY_TYPE_SGMII		8
 #define PHY_TYPE_QSGMII		9
+#define PHY_TYPE_DPHY		10
+#define PHY_TYPE_CPHY		11
 
 #endif /* _DT_BINDINGS_PHY */
diff --git a/include/dt-bindings/power/xlnx-versal-power.h b/include/dt-bindings/power/xlnx-versal-power.h
index 4a72775..51d1def 100644
--- a/include/dt-bindings/power/xlnx-versal-power.h
+++ b/include/dt-bindings/power/xlnx-versal-power.h
@@ -6,6 +6,16 @@
 #ifndef _DT_BINDINGS_VERSAL_POWER_H
 #define _DT_BINDINGS_VERSAL_POWER_H
 
+#define PM_DEV_RPU0_0				(0x18110005U)
+#define PM_DEV_RPU0_1				(0x18110006U)
+#define PM_DEV_OCM_0				(0x18314007U)
+#define PM_DEV_OCM_1				(0x18314008U)
+#define PM_DEV_OCM_2				(0x18314009U)
+#define PM_DEV_OCM_3				(0x1831400aU)
+#define PM_DEV_TCM_0_A				(0x1831800bU)
+#define PM_DEV_TCM_0_B				(0x1831800cU)
+#define PM_DEV_TCM_1_A				(0x1831800dU)
+#define PM_DEV_TCM_1_B				(0x1831800eU)
 #define PM_DEV_USB_0				(0x18224018U)
 #define PM_DEV_GEM_0				(0x18224019U)
 #define PM_DEV_GEM_1				(0x1822401aU)
@@ -38,6 +48,7 @@
 #define PM_DEV_ADMA_5				(0x1822403aU)
 #define PM_DEV_ADMA_6				(0x1822403bU)
 #define PM_DEV_ADMA_7				(0x1822403cU)
+#define PM_DEV_AMS_ROOT				(0x18224055U)
 #define PM_DEV_AI				(0x18224072U)
 
 #endif
diff --git a/include/dt-bindings/power/xlnx-zynqmp-power.h b/include/dt-bindings/power/xlnx-zynqmp-power.h
index 0d9a412..e7eb096 100644
--- a/include/dt-bindings/power/xlnx-zynqmp-power.h
+++ b/include/dt-bindings/power/xlnx-zynqmp-power.h
@@ -6,6 +6,16 @@
 #ifndef _DT_BINDINGS_ZYNQMP_POWER_H
 #define _DT_BINDINGS_ZYNQMP_POWER_H
 
+#define		PD_RPU_0	6
+#define		PD_RPU_1	7
+#define		PD_OCM_BANK_0	11
+#define		PD_OCM_BANK_1	12
+#define		PD_OCM_BANK_2	13
+#define		PD_OCM_BANK_3	14
+#define		PD_TCM_BANK_0	15
+#define		PD_TCM_BANK_1	16
+#define		PD_TCM_BANK_2	17
+#define		PD_TCM_BANK_3	18
 #define		PD_USB_0	22
 #define		PD_USB_1	23
 #define		PD_TTC_0	24
@@ -35,5 +45,6 @@
 #define		PD_CAN_1	48
 #define		PD_GPU		58
 #define		PD_PCIE		59
+#define		PD_PL		69
 
 #endif
diff --git a/include/image.h b/include/image.h
index 498eb7f..673b5f5 100644
--- a/include/image.h
+++ b/include/image.h
@@ -228,6 +228,7 @@
 	IH_TYPE_IMX8IMAGE,		/* Freescale IMX8Boot Image	*/
 	IH_TYPE_COPRO,			/* Coprocessor Image for remoteproc*/
 	IH_TYPE_SUNXI_EGON,		/* Allwinner eGON Boot Image */
+	IH_TYPE_SUNXI_TOC0,		/* Allwinner TOC0 Boot Image */
 
 	IH_TYPE_COUNT,			/* Number of image types */
 };
diff --git a/include/phy.h b/include/phy.h
index 9ea4bd4..5e3da4b 100644
--- a/include/phy.h
+++ b/include/phy.h
@@ -479,7 +479,7 @@
  *			or NULL otherwise
  */
 struct phy_device *phy_connect_phy_id(struct mii_dev *bus, struct udevice *dev,
-				      phy_interface_t interface);
+				      int phyaddr, phy_interface_t interface);
 
 static inline ofnode phy_get_ofnode(struct phy_device *phydev)
 {
diff --git a/include/spl.h b/include/spl.h
index 8ceb3c0..6134aba 100644
--- a/include/spl.h
+++ b/include/spl.h
@@ -14,6 +14,7 @@
 #include <asm/global_data.h>
 #include <asm/spl.h>
 #include <handoff.h>
+#include <mmc.h>
 
 struct blk_desc;
 struct image_header;
@@ -375,7 +376,7 @@
  * Note:  It is important to use the boot_device parameter instead of e.g.
  * spl_boot_device() as U-Boot is not always loaded from the same device as SPL.
  */
-u32 spl_mmc_boot_mode(const u32 boot_device);
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device);
 
 /**
  * spl_mmc_boot_partition() - MMC partition to load U-Boot from.
diff --git a/include/sunxi_image.h b/include/sunxi_image.h
index 5b2055c..379ca91 100644
--- a/include/sunxi_image.h
+++ b/include/sunxi_image.h
@@ -9,9 +9,13 @@
  *
  * Shared between mkimage and the SPL.
  */
+
 #ifndef	SUNXI_IMAGE_H
 #define	SUNXI_IMAGE_H
 
+#include <linux/compiler_attributes.h>
+#include <linux/types.h>
+
 #define BOOT0_MAGIC		"eGON.BT0"
 #define BROM_STAMP_VALUE	0x5f0a6c39
 #define SPL_SIGNATURE		"SPL" /* marks "sunxi" SPL header */
@@ -79,4 +83,37 @@
 /* Compile time check to assure proper alignment of structure */
 typedef char boot_file_head_not_multiple_of_32[1 - 2*(sizeof(struct boot_file_head) % 32)];
 
+struct __packed toc0_main_info {
+	uint8_t	name[8];
+	__le32	magic;
+	__le32	checksum;
+	__le32	serial;
+	__le32	status;
+	__le32	num_items;
+	__le32	length;
+	uint8_t	platform[4];
+	uint8_t	reserved[8];
+	uint8_t	end[4];
+};
+
+#define TOC0_MAIN_INFO_NAME		"TOC0.GLH"
+#define TOC0_MAIN_INFO_MAGIC		0x89119800
+#define TOC0_MAIN_INFO_END		"MIE;"
+
+struct __packed toc0_item_info {
+	__le32	name;
+	__le32	offset;
+	__le32	length;
+	__le32	status;
+	__le32	type;
+	__le32	load_addr;
+	uint8_t	reserved[4];
+	uint8_t	end[4];
+};
+
+#define TOC0_ITEM_INFO_NAME_CERT	0x00010101
+#define TOC0_ITEM_INFO_NAME_FIRMWARE	0x00010202
+#define TOC0_ITEM_INFO_NAME_KEY		0x00010303
+#define TOC0_ITEM_INFO_END		"IIE;"
+
 #endif
diff --git a/include/zynqmp_firmware.h b/include/zynqmp_firmware.h
index f577008..76ec214 100644
--- a/include/zynqmp_firmware.h
+++ b/include/zynqmp_firmware.h
@@ -408,6 +408,11 @@
 	SD_CONFIG_FIXED = 4,	/* To set fixed config registers */
 };
 
+enum pm_gem_config_type {
+	GEM_CONFIG_SGMII_MODE = 1, /* To set GEM_SGMII_MODE in GEM_CLK_CTRL */
+	GEM_CONFIG_FIXED = 2,   /* To set fixed config registers */
+};
+
 #define PM_SIP_SVC	0xc2000000
 
 #define ZYNQMP_PM_VERSION_MAJOR		1
@@ -439,6 +444,8 @@
 int xilinx_pm_request(u32 api_id, u32 arg0, u32 arg1, u32 arg2,
 		      u32 arg3, u32 *ret_payload);
 int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value);
+int zynqmp_pm_set_gem_config(u32 node, enum pm_gem_config_type config,
+			     u32 value);
 int zynqmp_pm_is_function_supported(const u32 api_id, const u32 id);
 
 /* Type of Config Object */
diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl
index 83a95ee..6ad82ce 100644
--- a/scripts/Makefile.spl
+++ b/scripts/Makefile.spl
@@ -411,7 +411,10 @@
 $(obj)/$(SPL_BIN).sfp: $(obj)/$(SPL_BIN).bin FORCE
 	$(call if_changed,mkimage)
 
-MKIMAGEFLAGS_sunxi-spl.bin = -T sunxi_egon \
+MKIMAGEFLAGS_sunxi-spl.bin = \
+	-A $(ARCH) \
+	-T $(CONFIG_SPL_IMAGE_TYPE) \
+	-a $(CONFIG_SPL_TEXT_BASE) \
 	-n $(CONFIG_DEFAULT_DEVICE_TREE)
 
 OBJCOPYFLAGS_u-boot-spl-dtb.hex := -I binary -O ihex --change-address=$(CONFIG_SPL_TEXT_BASE)
diff --git a/tools/Makefile b/tools/Makefile
index 60231c7..e17271b 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -94,9 +94,10 @@
 AES_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/aes/, \
 					aes-encrypt.o aes-decrypt.o)
 
-# Cryptographic helpers that depend on openssl/libcrypto
-LIBCRYPTO_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/, \
-					fdt-libcrypto.o)
+# Cryptographic helpers and image types that depend on openssl/libcrypto
+LIBCRYPTO_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := \
+			lib/fdt-libcrypto.o \
+			sunxi_toc0.o
 
 ROCKCHIP_OBS = lib/rc4.o rkcommon.o rkimage.o rksd.o rkspi.o
 
diff --git a/tools/imagetool.h b/tools/imagetool.h
index 5169b02..05dd94d 100644
--- a/tools/imagetool.h
+++ b/tools/imagetool.h
@@ -53,6 +53,7 @@
 	int pflag;
 	int vflag;
 	int xflag;
+	int Aflag;
 	int skipcpy;
 	int os;
 	int arch;
diff --git a/tools/mkimage.c b/tools/mkimage.c
index 74bd072..b46216f 100644
--- a/tools/mkimage.c
+++ b/tools/mkimage.c
@@ -172,6 +172,7 @@
 				show_valid_options(IH_ARCH);
 				usage("Invalid architecture");
 			}
+			params.Aflag = 1;
 			break;
 		case 'b':
 			if (add_content(IH_TYPE_FLATDT, optarg)) {
diff --git a/tools/sunxi_egon.c b/tools/sunxi_egon.c
index d1398c0..d45b6f5 100644
--- a/tools/sunxi_egon.c
+++ b/tools/sunxi_egon.c
@@ -15,9 +15,29 @@
 #define PAD_SIZE			8192
 #define PAD_SIZE_MIN			512
 
+static int egon_get_arch(struct image_tool_params *params)
+{
+	if (params->Aflag)
+		return params->arch;
+
+	/* For compatibility, assume ARM when no architecture specified */
+	return IH_ARCH_ARM;
+}
+
 static int egon_check_params(struct image_tool_params *params)
 {
-	/* We just need a binary image file. */
+	/*
+	 * Check whether the architecture is supported.
+	 */
+	switch (egon_get_arch(params)) {
+	case IH_ARCH_ARM:
+	case IH_ARCH_RISCV:
+		break;
+	default:
+		return EXIT_FAILURE;
+	}
+
+	/* We need a binary image file. */
 	return !params->dflag;
 }
 
@@ -27,9 +47,22 @@
 	const struct boot_file_head *header = (void *)ptr;
 	uint32_t length;
 
-	/* First 4 bytes must be an ARM branch instruction. */
-	if ((le32_to_cpu(header->b_instruction) & 0xff000000) != 0xea000000)
-		return EXIT_FAILURE;
+	/*
+	 * First 4 bytes must be a branch instruction of the corresponding
+	 * architecture.
+	 */
+	switch (egon_get_arch(params)) {
+	case IH_ARCH_ARM:
+		if ((le32_to_cpu(header->b_instruction) & 0xff000000) != 0xea000000)
+			return EXIT_FAILURE;
+		break;
+	case IH_ARCH_RISCV:
+		if ((le32_to_cpu(header->b_instruction) & 0x00000fff) != 0x0000006f)
+			return EXIT_FAILURE;
+		break;
+	default:
+		return EXIT_FAILURE; /* Unknown architecture */
+	}
 
 	if (memcmp(header->magic, BOOT0_MAGIC, sizeof(header->magic)))
 		return EXIT_FAILURE;
@@ -78,9 +111,35 @@
 	uint32_t checksum = 0, value;
 	int i;
 
-	/* Generate an ARM branch instruction to jump over the header. */
-	value = 0xea000000 | (sizeof(struct boot_file_head) / 4 - 2);
-	header->b_instruction = cpu_to_le32(value);
+	/*
+	 * Different architectures need different first instruction to
+	 * branch to the body.
+	 */
+	switch (egon_get_arch(params)) {
+	case IH_ARCH_ARM:
+		/* Generate an ARM branch instruction to jump over the header. */
+		value = 0xea000000 | (sizeof(struct boot_file_head) / 4 - 2);
+		header->b_instruction = cpu_to_le32(value);
+		break;
+	case IH_ARCH_RISCV:
+		/*
+		 * Generate a RISC-V JAL instruction with rd=x0
+		 * (pseudo instruction J, jump without side effects).
+		 *
+		 * The following weird bit operation maps imm[20]
+		 * to inst[31], imm[10:1] to inst[30:21],
+		 * imm[11] to inst[20], imm[19:12] to inst[19:12],
+		 * and imm[0] is dropped (because 1-byte RISC-V instruction
+		 * is not allowed).
+		 */
+		value = 0x0000006f |
+			((sizeof(struct boot_file_head) & 0x00100000) << 11) |
+			((sizeof(struct boot_file_head) & 0x000007fe) << 20) |
+			((sizeof(struct boot_file_head) & 0x00000800) << 9) |
+			((sizeof(struct boot_file_head) & 0x000ff000) << 0);
+		header->b_instruction = cpu_to_le32(value);
+		break;
+	}
 
 	memcpy(header->magic, BOOT0_MAGIC, sizeof(header->magic));
 	header->check_sum = cpu_to_le32(BROM_STAMP_VALUE);
diff --git a/tools/sunxi_toc0.c b/tools/sunxi_toc0.c
new file mode 100644
index 0000000..58a6e7a
--- /dev/null
+++ b/tools/sunxi_toc0.c
@@ -0,0 +1,907 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018 Arm Ltd.
+ * (C) Copyright 2020-2021 Samuel Holland <samuel@sholland.org>
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+#include <image.h>
+#include <sunxi_image.h>
+
+#include "imagetool.h"
+#include "mkimage.h"
+
+/*
+ * NAND requires 8K padding. For other devices, BROM requires only
+ * 512B padding, but let's use the larger padding to cover everything.
+ */
+#define PAD_SIZE		8192
+
+#define pr_fmt(fmt)		"mkimage (TOC0): %s: " fmt
+#define pr_err(fmt, args...)	fprintf(stderr, pr_fmt(fmt), "error", ##args)
+#define pr_warn(fmt, args...)	fprintf(stderr, pr_fmt(fmt), "warning", ##args)
+#define pr_info(fmt, args...)	fprintf(stderr, pr_fmt(fmt), "info", ##args)
+
+struct __packed toc0_key_item {
+	__le32  vendor_id;
+	__le32  key0_n_len;
+	__le32  key0_e_len;
+	__le32  key1_n_len;
+	__le32  key1_e_len;
+	__le32  sig_len;
+	uint8_t key0[512];
+	uint8_t key1[512];
+	uint8_t reserved[32];
+	uint8_t sig[256];
+};
+
+/*
+ * This looks somewhat like an X.509 certificate, but it is not valid BER.
+ *
+ * Some differences:
+ *  - Some X.509 certificate fields are missing or rearranged.
+ *  - Some sequences have the wrong tag.
+ *  - Zero-length sequences are accepted.
+ *  - Large strings and integers must be an even number of bytes long.
+ *  - Positive integers are not zero-extended to maintain their sign.
+ *
+ * See https://linux-sunxi.org/TOC0 for more information.
+ */
+struct __packed toc0_small_tag {
+	uint8_t tag;
+	uint8_t length;
+};
+
+typedef struct toc0_small_tag toc0_small_int;
+typedef struct toc0_small_tag toc0_small_oct;
+typedef struct toc0_small_tag toc0_small_seq;
+typedef struct toc0_small_tag toc0_small_exp;
+
+#define TOC0_SMALL_INT(len) { 0x02, (len) }
+#define TOC0_SMALL_SEQ(len) { 0x30, (len) }
+#define TOC0_SMALL_EXP(tag, len) { 0xa0 | (tag), len }
+
+struct __packed toc0_large_tag {
+	uint8_t tag;
+	uint8_t prefix;
+	uint8_t length_hi;
+	uint8_t length_lo;
+};
+
+typedef struct toc0_large_tag toc0_large_int;
+typedef struct toc0_large_tag toc0_large_bit;
+typedef struct toc0_large_tag toc0_large_seq;
+
+#define TOC0_LARGE_INT(len) { 0x02, 0x82, (len) >> 8, (len) & 0xff }
+#define TOC0_LARGE_BIT(len) { 0x03, 0x82, (len) >> 8, (len) & 0xff }
+#define TOC0_LARGE_SEQ(len) { 0x30, 0x82, (len) >> 8, (len) & 0xff }
+
+struct __packed toc0_cert_item {
+	toc0_large_seq tag_totalSequence;
+	struct __packed toc0_totalSequence {
+		toc0_large_seq tag_mainSequence;
+		struct __packed toc0_mainSequence {
+			toc0_small_exp tag_explicit0;
+			struct __packed toc0_explicit0 {
+				toc0_small_int tag_version;
+				uint8_t version;
+			} explicit0;
+			toc0_small_int tag_serialNumber;
+			uint8_t serialNumber;
+			toc0_small_seq tag_signature;
+			toc0_small_seq tag_issuer;
+			toc0_small_seq tag_validity;
+			toc0_small_seq tag_subject;
+			toc0_large_seq tag_subjectPublicKeyInfo;
+			struct __packed toc0_subjectPublicKeyInfo {
+				toc0_small_seq tag_algorithm;
+				toc0_large_seq tag_publicKey;
+				struct __packed toc0_publicKey {
+					toc0_large_int tag_n;
+					uint8_t n[256];
+					toc0_small_int tag_e;
+					uint8_t e[3];
+				} publicKey;
+			} subjectPublicKeyInfo;
+			toc0_small_exp tag_explicit3;
+			struct __packed toc0_explicit3 {
+				toc0_small_seq tag_extension;
+				struct __packed toc0_extension {
+					toc0_small_int tag_digest;
+					uint8_t digest[32];
+				} extension;
+			} explicit3;
+		} mainSequence;
+		toc0_large_bit tag_sigSequence;
+		struct __packed toc0_sigSequence {
+			toc0_small_seq tag_algorithm;
+			toc0_large_bit tag_signature;
+			uint8_t signature[256];
+		} sigSequence;
+	} totalSequence;
+};
+
+#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER))
+
+static const struct toc0_cert_item cert_item_template = {
+	TOC0_LARGE_SEQ(sizeof(struct toc0_totalSequence)),
+	{
+		TOC0_LARGE_SEQ(sizeof(struct toc0_mainSequence)),
+		{
+			TOC0_SMALL_EXP(0, sizeof(struct toc0_explicit0)),
+			{
+				TOC0_SMALL_INT(sizeof_field(struct toc0_explicit0, version)),
+				0,
+			},
+			TOC0_SMALL_INT(sizeof_field(struct toc0_mainSequence, serialNumber)),
+			0,
+			TOC0_SMALL_SEQ(0),
+			TOC0_SMALL_SEQ(0),
+			TOC0_SMALL_SEQ(0),
+			TOC0_SMALL_SEQ(0),
+			TOC0_LARGE_SEQ(sizeof(struct toc0_subjectPublicKeyInfo)),
+			{
+				TOC0_SMALL_SEQ(0),
+				TOC0_LARGE_SEQ(sizeof(struct toc0_publicKey)),
+				{
+					TOC0_LARGE_INT(sizeof_field(struct toc0_publicKey, n)),
+					{},
+					TOC0_SMALL_INT(sizeof_field(struct toc0_publicKey, e)),
+					{},
+				},
+			},
+			TOC0_SMALL_EXP(3, sizeof(struct toc0_explicit3)),
+			{
+				TOC0_SMALL_SEQ(sizeof(struct toc0_extension)),
+				{
+					TOC0_SMALL_INT(sizeof_field(struct toc0_extension, digest)),
+					{},
+				},
+			},
+		},
+		TOC0_LARGE_BIT(sizeof(struct toc0_sigSequence)),
+		{
+			TOC0_SMALL_SEQ(0),
+			TOC0_LARGE_BIT(sizeof_field(struct toc0_sigSequence, signature)),
+			{},
+		},
+	},
+};
+
+#define TOC0_DEFAULT_NUM_ITEMS		3
+#define TOC0_DEFAULT_HEADER_LEN						  \
+	ALIGN(								  \
+		sizeof(struct toc0_main_info)				+ \
+		sizeof(struct toc0_item_info) *	TOC0_DEFAULT_NUM_ITEMS	+ \
+		sizeof(struct toc0_cert_item)				+ \
+		sizeof(struct toc0_key_item),				  \
+	32)
+
+static char *fw_key_file   = "fw_key.pem";
+static char *key_item_file = "key_item.bin";
+static char *root_key_file = "root_key.pem";
+
+/*
+ * Create a key item in @buf, containing the public keys @root_key and @fw_key,
+ * and signed by the RSA key @root_key.
+ */
+static int toc0_create_key_item(uint8_t *buf, uint32_t *len,
+				RSA *root_key, RSA *fw_key)
+{
+	struct toc0_key_item *key_item = (void *)buf;
+	uint8_t digest[SHA256_DIGEST_LENGTH];
+	int ret = EXIT_FAILURE;
+	unsigned int sig_len;
+	int n_len, e_len;
+
+	/* Store key 0. */
+	n_len = BN_bn2bin(RSA_get0_n(root_key), key_item->key0);
+	e_len = BN_bn2bin(RSA_get0_e(root_key), key_item->key0 + n_len);
+	if (n_len + e_len > sizeof(key_item->key0)) {
+		pr_err("Root key is too big for key item\n");
+		goto err;
+	}
+	key_item->key0_n_len = cpu_to_le32(n_len);
+	key_item->key0_e_len = cpu_to_le32(e_len);
+
+	/* Store key 1. */
+	n_len = BN_bn2bin(RSA_get0_n(fw_key), key_item->key1);
+	e_len = BN_bn2bin(RSA_get0_e(fw_key), key_item->key1 + n_len);
+	if (n_len + e_len > sizeof(key_item->key1)) {
+		pr_err("Firmware key is too big for key item\n");
+		goto err;
+	}
+	key_item->key1_n_len = cpu_to_le32(n_len);
+	key_item->key1_e_len = cpu_to_le32(e_len);
+
+	/* Sign the key item. */
+	key_item->sig_len = cpu_to_le32(RSA_size(root_key));
+	SHA256(buf, key_item->sig - buf, digest);
+	if (!RSA_sign(NID_sha256, digest, sizeof(digest),
+		      key_item->sig, &sig_len, root_key)) {
+		pr_err("Failed to sign key item\n");
+		goto err;
+	}
+	if (sig_len != sizeof(key_item->sig)) {
+		pr_err("Bad key item signature length\n");
+		goto err;
+	}
+
+	*len = sizeof(*key_item);
+	ret = EXIT_SUCCESS;
+
+err:
+	return ret;
+}
+
+/*
+ * Verify the key item in @buf, containing two public keys @key0 and @key1,
+ * and signed by the RSA key @key0. If @root_key is provided, only signatures
+ * by that key will be accepted. @key1 is returned in @key.
+ */
+static int toc0_verify_key_item(const uint8_t *buf, uint32_t len,
+				RSA *root_key, RSA **fw_key)
+{
+	struct toc0_key_item *key_item = (void *)buf;
+	uint8_t digest[SHA256_DIGEST_LENGTH];
+	int ret = EXIT_FAILURE;
+	int n_len, e_len;
+	RSA *key0 = NULL;
+	RSA *key1 = NULL;
+	BIGNUM *n, *e;
+
+	if (len < sizeof(*key_item))
+		goto err;
+
+	/* Load key 0. */
+	n_len = le32_to_cpu(key_item->key0_n_len);
+	e_len = le32_to_cpu(key_item->key0_e_len);
+	if (n_len + e_len > sizeof(key_item->key0)) {
+		pr_err("Bad root key size in key item\n");
+		goto err;
+	}
+	n = BN_bin2bn(key_item->key0, n_len, NULL);
+	e = BN_bin2bn(key_item->key0 + n_len, e_len, NULL);
+	key0 = RSA_new();
+	if (!key0)
+		goto err;
+	if (!RSA_set0_key(key0, n, e, NULL))
+		goto err;
+
+	/* If a root key was provided, compare it to key 0. */
+	if (root_key && (BN_cmp(n, RSA_get0_n(root_key)) ||
+			 BN_cmp(e, RSA_get0_e(root_key)))) {
+		pr_err("Wrong root key in key item\n");
+		goto err;
+	}
+
+	/* Verify the key item signature. */
+	SHA256(buf, key_item->sig - buf, digest);
+	if (!RSA_verify(NID_sha256, digest, sizeof(digest),
+			key_item->sig, le32_to_cpu(key_item->sig_len), key0)) {
+		pr_err("Bad key item signature\n");
+		goto err;
+	}
+
+	if (fw_key) {
+		/* Load key 1. */
+		n_len = le32_to_cpu(key_item->key1_n_len);
+		e_len = le32_to_cpu(key_item->key1_e_len);
+		if (n_len + e_len > sizeof(key_item->key1)) {
+			pr_err("Bad firmware key size in key item\n");
+			goto err;
+		}
+		n = BN_bin2bn(key_item->key1, n_len, NULL);
+		e = BN_bin2bn(key_item->key1 + n_len, e_len, NULL);
+		key1 = RSA_new();
+		if (!key1)
+			goto err;
+		if (!RSA_set0_key(key1, n, e, NULL))
+			goto err;
+
+		if (*fw_key) {
+			/* If a FW key was provided, compare it to key 1. */
+			if (BN_cmp(n, RSA_get0_n(*fw_key)) ||
+			    BN_cmp(e, RSA_get0_e(*fw_key))) {
+				pr_err("Wrong firmware key in key item\n");
+				goto err;
+			}
+		} else {
+			/* Otherwise, send key1 back to the caller. */
+			*fw_key = key1;
+			key1 = NULL;
+		}
+	}
+
+	ret = EXIT_SUCCESS;
+
+err:
+	RSA_free(key0);
+	RSA_free(key1);
+
+	return ret;
+}
+
+/*
+ * Create a certificate in @buf, describing the firmware with SHA256 digest
+ * @digest, and signed by the RSA key @fw_key.
+ */
+static int toc0_create_cert_item(uint8_t *buf, uint32_t *len, RSA *fw_key,
+				 uint8_t digest[static SHA256_DIGEST_LENGTH])
+{
+	struct toc0_cert_item *cert_item = (void *)buf;
+	uint8_t cert_digest[SHA256_DIGEST_LENGTH];
+	struct toc0_totalSequence *totalSequence;
+	struct toc0_sigSequence *sigSequence;
+	struct toc0_extension *extension;
+	struct toc0_publicKey *publicKey;
+	int ret = EXIT_FAILURE;
+	unsigned int sig_len;
+
+	memcpy(cert_item, &cert_item_template, sizeof(*cert_item));
+	*len = sizeof(*cert_item);
+
+	/*
+	 * Fill in the public key.
+	 *
+	 * Only 2048-bit RSA keys are supported. Since this uses a fixed-size
+	 * structure, it may fail for non-standard exponents.
+	 */
+	totalSequence = &cert_item->totalSequence;
+	publicKey = &totalSequence->mainSequence.subjectPublicKeyInfo.publicKey;
+	if (BN_bn2binpad(RSA_get0_n(fw_key), publicKey->n, sizeof(publicKey->n)) < 0 ||
+	    BN_bn2binpad(RSA_get0_e(fw_key), publicKey->e, sizeof(publicKey->e)) < 0) {
+		pr_err("Firmware key is too big for certificate\n");
+		goto err;
+	}
+
+	/* Fill in the firmware digest. */
+	extension = &totalSequence->mainSequence.explicit3.extension;
+	memcpy(&extension->digest, digest, SHA256_DIGEST_LENGTH);
+
+	/*
+	 * Sign the certificate.
+	 *
+	 * In older SBROM versions (and by default in newer versions),
+	 * the last 4 bytes of the certificate are not signed.
+	 *
+	 * (The buffer passed to SHA256 starts at tag_mainSequence, but
+	 *  the buffer size does not include the length of that tag.)
+	 */
+	SHA256((uint8_t *)totalSequence, sizeof(struct toc0_mainSequence), cert_digest);
+	sigSequence = &totalSequence->sigSequence;
+	if (!RSA_sign(NID_sha256, cert_digest, SHA256_DIGEST_LENGTH,
+		      sigSequence->signature, &sig_len, fw_key)) {
+		pr_err("Failed to sign certificate\n");
+		goto err;
+	}
+	if (sig_len != sizeof(sigSequence->signature)) {
+		pr_err("Bad certificate signature length\n");
+		goto err;
+	}
+
+	ret = EXIT_SUCCESS;
+
+err:
+	return ret;
+}
+
+/*
+ * Verify the certificate in @buf, describing the firmware with SHA256 digest
+ * @digest, and signed by the RSA key contained within. If @fw_key is provided,
+ * only that key will be accepted.
+ *
+ * This function is only expected to work with images created by mkimage.
+ */
+static int toc0_verify_cert_item(const uint8_t *buf, uint32_t len, RSA *fw_key,
+				 uint8_t digest[static SHA256_DIGEST_LENGTH])
+{
+	const struct toc0_cert_item *cert_item = (const void *)buf;
+	uint8_t cert_digest[SHA256_DIGEST_LENGTH];
+	const struct toc0_totalSequence *totalSequence;
+	const struct toc0_sigSequence *sigSequence;
+	const struct toc0_extension *extension;
+	const struct toc0_publicKey *publicKey;
+	int ret = EXIT_FAILURE;
+	RSA *key = NULL;
+	BIGNUM *n, *e;
+
+	/* Extract the public key from the certificate. */
+	totalSequence = &cert_item->totalSequence;
+	publicKey = &totalSequence->mainSequence.subjectPublicKeyInfo.publicKey;
+	n = BN_bin2bn(publicKey->n, sizeof(publicKey->n), NULL);
+	e = BN_bin2bn(publicKey->e, sizeof(publicKey->e), NULL);
+	key = RSA_new();
+	if (!key)
+		goto err;
+	if (!RSA_set0_key(key, n, e, NULL))
+		goto err;
+
+	/* If a key was provided, compare it to the embedded key. */
+	if (fw_key && (BN_cmp(RSA_get0_n(key), RSA_get0_n(fw_key)) ||
+		       BN_cmp(RSA_get0_e(key), RSA_get0_e(fw_key)))) {
+		pr_err("Wrong firmware key in certificate\n");
+		goto err;
+	}
+
+	/* If a digest was provided, compare it to the embedded digest. */
+	extension = &totalSequence->mainSequence.explicit3.extension;
+	if (digest && memcmp(&extension->digest, digest, SHA256_DIGEST_LENGTH)) {
+		pr_err("Wrong firmware digest in certificate\n");
+		goto err;
+	}
+
+	/* Verify the certificate's signature. See the comment above. */
+	SHA256((uint8_t *)totalSequence, sizeof(struct toc0_mainSequence), cert_digest);
+	sigSequence = &totalSequence->sigSequence;
+	if (!RSA_verify(NID_sha256, cert_digest, SHA256_DIGEST_LENGTH,
+			sigSequence->signature,
+			sizeof(sigSequence->signature), key)) {
+		pr_err("Bad certificate signature\n");
+		goto err;
+	}
+
+	ret = EXIT_SUCCESS;
+
+err:
+	RSA_free(key);
+
+	return ret;
+}
+
+/*
+ * Always create a TOC0 containing 3 items. The extra item will be ignored on
+ * SoCs which do not support it.
+ */
+static int toc0_create(uint8_t *buf, uint32_t len, RSA *root_key, RSA *fw_key,
+		       uint8_t *key_item, uint32_t key_item_len,
+		       uint8_t *fw_item, uint32_t fw_item_len, uint32_t fw_addr)
+{
+	struct toc0_main_info *main_info = (void *)buf;
+	struct toc0_item_info *item_info = (void *)(main_info + 1);
+	uint8_t digest[SHA256_DIGEST_LENGTH];
+	uint32_t *buf32 = (void *)buf;
+	RSA *orig_fw_key = fw_key;
+	int ret = EXIT_FAILURE;
+	uint32_t checksum = 0;
+	uint32_t item_offset;
+	uint32_t item_length;
+	int i;
+
+	/* Hash the firmware for inclusion in the certificate. */
+	SHA256(fw_item, fw_item_len, digest);
+
+	/* Create the main TOC0 header, containing three items. */
+	memcpy(main_info->name, TOC0_MAIN_INFO_NAME, sizeof(main_info->name));
+	main_info->magic	= cpu_to_le32(TOC0_MAIN_INFO_MAGIC);
+	main_info->checksum	= cpu_to_le32(BROM_STAMP_VALUE);
+	main_info->num_items	= cpu_to_le32(TOC0_DEFAULT_NUM_ITEMS);
+	memcpy(main_info->end, TOC0_MAIN_INFO_END, sizeof(main_info->end));
+
+	/* The first item links the ROTPK to the signing key. */
+	item_offset = sizeof(*main_info) +
+		      sizeof(*item_info) * TOC0_DEFAULT_NUM_ITEMS;
+	/* Using an existing key item avoids needing the root private key. */
+	if (key_item) {
+		item_length = sizeof(*key_item);
+		if (toc0_verify_key_item(key_item, item_length,
+					 root_key, &fw_key))
+			goto err;
+		memcpy(buf + item_offset, key_item, item_length);
+	} else if (toc0_create_key_item(buf + item_offset, &item_length,
+					root_key, fw_key)) {
+		goto err;
+	}
+
+	item_info->name		= cpu_to_le32(TOC0_ITEM_INFO_NAME_KEY);
+	item_info->offset	= cpu_to_le32(item_offset);
+	item_info->length	= cpu_to_le32(item_length);
+	memcpy(item_info->end, TOC0_ITEM_INFO_END, sizeof(item_info->end));
+
+	/* The second item contains a certificate signed by the firmware key. */
+	item_offset = item_offset + item_length;
+	if (toc0_create_cert_item(buf + item_offset, &item_length,
+				  fw_key, digest))
+		goto err;
+
+	item_info++;
+	item_info->name		= cpu_to_le32(TOC0_ITEM_INFO_NAME_CERT);
+	item_info->offset	= cpu_to_le32(item_offset);
+	item_info->length	= cpu_to_le32(item_length);
+	memcpy(item_info->end, TOC0_ITEM_INFO_END, sizeof(item_info->end));
+
+	/* The third item contains the actual boot code. */
+	item_offset = ALIGN(item_offset + item_length, 32);
+	item_length = fw_item_len;
+	if (buf + item_offset != fw_item)
+		memmove(buf + item_offset, fw_item, item_length);
+
+	item_info++;
+	item_info->name		= cpu_to_le32(TOC0_ITEM_INFO_NAME_FIRMWARE);
+	item_info->offset	= cpu_to_le32(item_offset);
+	item_info->length	= cpu_to_le32(item_length);
+	item_info->load_addr	= cpu_to_le32(fw_addr);
+	memcpy(item_info->end, TOC0_ITEM_INFO_END, sizeof(item_info->end));
+
+	/* Pad to the required block size with 0xff to be flash-friendly. */
+	item_offset = item_offset + item_length;
+	item_length = ALIGN(item_offset, PAD_SIZE) - item_offset;
+	memset(buf + item_offset, 0xff, item_length);
+
+	/* Fill in the total padded file length. */
+	item_offset = item_offset + item_length;
+	main_info->length = cpu_to_le32(item_offset);
+
+	/* Verify enough space was provided when creating the image. */
+	assert(len >= item_offset);
+
+	/* Calculate the checksum. Yes, it's that simple. */
+	for (i = 0; i < item_offset / 4; ++i)
+		checksum += le32_to_cpu(buf32[i]);
+	main_info->checksum = cpu_to_le32(checksum);
+
+	ret = EXIT_SUCCESS;
+
+err:
+	if (fw_key != orig_fw_key)
+		RSA_free(fw_key);
+
+	return ret;
+}
+
+static const struct toc0_item_info *
+toc0_find_item(const struct toc0_main_info *main_info, uint32_t name,
+	       uint32_t *offset, uint32_t *length)
+{
+	const struct toc0_item_info *item_info = (void *)(main_info + 1);
+	uint32_t item_offset, item_length;
+	uint32_t num_items, main_length;
+	int i;
+
+	num_items   = le32_to_cpu(main_info->num_items);
+	main_length = le32_to_cpu(main_info->length);
+
+	for (i = 0; i < num_items; ++i, ++item_info) {
+		if (le32_to_cpu(item_info->name) != name)
+			continue;
+
+		item_offset = le32_to_cpu(item_info->offset);
+		item_length = le32_to_cpu(item_info->length);
+
+		if (item_offset > main_length ||
+		    item_length > main_length - item_offset)
+			continue;
+
+		*offset = item_offset;
+		*length = item_length;
+
+		return item_info;
+	}
+
+	return NULL;
+}
+
+static int toc0_verify(const uint8_t *buf, uint32_t len, RSA *root_key)
+{
+	const struct toc0_main_info *main_info = (void *)buf;
+	const struct toc0_item_info *item_info;
+	uint8_t digest[SHA256_DIGEST_LENGTH];
+	uint32_t main_length = le32_to_cpu(main_info->length);
+	uint32_t checksum = BROM_STAMP_VALUE;
+	uint32_t *buf32 = (void *)buf;
+	uint32_t length, offset;
+	int ret = EXIT_FAILURE;
+	RSA *fw_key = NULL;
+	int i;
+
+	if (len < main_length)
+		goto err;
+
+	/* Verify the main header. */
+	if (memcmp(main_info->name, TOC0_MAIN_INFO_NAME, sizeof(main_info->name)))
+		goto err;
+	if (le32_to_cpu(main_info->magic) != TOC0_MAIN_INFO_MAGIC)
+		goto err;
+	/* Verify the checksum without modifying the buffer. */
+	for (i = 0; i < main_length / 4; ++i)
+		checksum += le32_to_cpu(buf32[i]);
+	if (checksum != 2 * le32_to_cpu(main_info->checksum))
+		goto err;
+	/* The length must be at least 512 byte aligned. */
+	if (main_length % 512)
+		goto err;
+	if (memcmp(main_info->end, TOC0_MAIN_INFO_END, sizeof(main_info->end)))
+		goto err;
+
+	/* Verify the key item if present (it is optional). */
+	item_info = toc0_find_item(main_info, TOC0_ITEM_INFO_NAME_KEY,
+				   &offset, &length);
+	if (!item_info)
+		fw_key = root_key;
+	else if (toc0_verify_key_item(buf + offset, length, root_key, &fw_key))
+		goto err;
+
+	/* Hash the firmware to compare with the certificate. */
+	item_info = toc0_find_item(main_info, TOC0_ITEM_INFO_NAME_FIRMWARE,
+				   &offset, &length);
+	if (!item_info) {
+		pr_err("Missing firmware item\n");
+		goto err;
+	}
+	SHA256(buf + offset, length, digest);
+
+	/* Verify the certificate item. */
+	item_info = toc0_find_item(main_info, TOC0_ITEM_INFO_NAME_CERT,
+				   &offset, &length);
+	if (!item_info) {
+		pr_err("Missing certificate item\n");
+		goto err;
+	}
+	if (toc0_verify_cert_item(buf + offset, length, fw_key, digest))
+		goto err;
+
+	ret = EXIT_SUCCESS;
+
+err:
+	if (fw_key != root_key)
+		RSA_free(fw_key);
+
+	return ret;
+}
+
+static int toc0_check_params(struct image_tool_params *params)
+{
+	if (!params->dflag)
+		return -EINVAL;
+
+	/*
+	 * If a key directory was provided, look for key files there.
+	 * Otherwise, look for them in the current directory. The key files are
+	 * the "quoted" terms in the description below.
+	 *
+	 * A summary of the chain of trust on most SoCs:
+	 *  1) eFuse contains a SHA256 digest of the public "root key".
+	 *  2) Private "root key" signs the certificate item (generated here).
+	 *  3) Certificate item contains a SHA256 digest of the firmware item.
+	 *
+	 * A summary of the chain of trust on the H6 (by default; a bit in the
+	 * BROM_CONFIG eFuse makes it work like above):
+	 *  1) eFuse contains a SHA256 digest of the public "root key".
+	 *  2) Private "root key" signs the "key item" (generated here).
+	 *  3) "Key item" contains the public "root key" and public "fw key".
+	 *  4) Private "fw key" signs the certificate item (generated here).
+	 *  5) Certificate item contains a SHA256 digest of the firmware item.
+	 *
+	 * This means there are three valid ways to generate a TOC0:
+	 *  1) Provide the private "root key" only. This works everywhere.
+	 *     For H6, the "root key" will also be used as the "fw key".
+	 *  2) FOR H6 ONLY: Provide the private "root key" and a separate
+	 *     private "fw key".
+	 *  3) FOR H6 ONLY: Provide the private "fw key" and a pre-existing
+	 *     "key item" containing the corresponding  public "fw key".
+	 *     In this case, the private "root key" can be kept offline. The
+	 *     "key item" can be extracted from a TOC0 image generated using
+	 *     method #2 above.
+	 *
+	 *  Note that until the ROTPK_HASH eFuse is programmed, any "root key"
+	 *  will be accepted by the BROM.
+	 */
+	if (params->keydir) {
+		if (asprintf(&fw_key_file, "%s/%s", params->keydir, fw_key_file) < 0)
+			return -ENOMEM;
+		if (asprintf(&key_item_file, "%s/%s", params->keydir, key_item_file) < 0)
+			return -ENOMEM;
+		if (asprintf(&root_key_file, "%s/%s", params->keydir, root_key_file) < 0)
+			return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int toc0_verify_header(unsigned char *buf, int image_size,
+			      struct image_tool_params *params)
+{
+	int ret = EXIT_FAILURE;
+	RSA *root_key = NULL;
+	FILE *fp;
+
+	/* A root public key is optional. */
+	fp = fopen(root_key_file, "rb");
+	if (fp) {
+		pr_info("Verifying image with existing root key\n");
+		root_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
+		if (!root_key)
+			root_key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL);
+		fclose(fp);
+		if (!root_key) {
+			pr_err("Failed to read public key from '%s'\n",
+			       root_key_file);
+			goto err;
+		}
+	}
+
+	ret = toc0_verify(buf, image_size, root_key);
+
+err:
+	RSA_free(root_key);
+
+	return ret;
+}
+
+static const char *toc0_item_name(uint32_t name)
+{
+	if (name == TOC0_ITEM_INFO_NAME_CERT)
+		return "Certificate";
+	if (name == TOC0_ITEM_INFO_NAME_FIRMWARE)
+		return "Firmware";
+	if (name == TOC0_ITEM_INFO_NAME_KEY)
+		return "Key";
+	return "(unknown)";
+}
+
+static void toc0_print_header(const void *buf)
+{
+	const struct toc0_main_info *main_info = buf;
+	const struct toc0_item_info *item_info = (void *)(main_info + 1);
+	uint32_t head_length, main_length, num_items;
+	uint32_t item_offset, item_length, item_name;
+	int load_addr = -1;
+	int i;
+
+	num_items   = le32_to_cpu(main_info->num_items);
+	head_length = sizeof(*main_info) + num_items * sizeof(*item_info);
+	main_length = le32_to_cpu(main_info->length);
+
+	printf("Allwinner TOC0 Image\n"
+	       "Size: %d bytes\n"
+	       "Contents: %d items\n"
+	       " 00000000:%08x Headers\n",
+	       main_length, num_items, head_length);
+
+	for (i = 0; i < num_items; ++i, ++item_info) {
+		item_offset = le32_to_cpu(item_info->offset);
+		item_length = le32_to_cpu(item_info->length);
+		item_name   = le32_to_cpu(item_info->name);
+
+		if (item_name == TOC0_ITEM_INFO_NAME_FIRMWARE)
+			load_addr = le32_to_cpu(item_info->load_addr);
+
+		printf(" %08x:%08x %s\n",
+		       item_offset, item_length,
+		       toc0_item_name(item_name));
+	}
+
+	if (num_items && item_offset + item_length < main_length) {
+		item_offset = item_offset + item_length;
+		item_length = main_length - item_offset;
+
+		printf(" %08x:%08x Padding\n",
+		       item_offset, item_length);
+	}
+
+	if (load_addr != -1)
+		printf("Load address: 0x%08x\n", load_addr);
+}
+
+static void toc0_set_header(void *buf, struct stat *sbuf, int ifd,
+			    struct image_tool_params *params)
+{
+	uint32_t key_item_len = 0;
+	uint8_t *key_item = NULL;
+	int ret = EXIT_FAILURE;
+	RSA *root_key = NULL;
+	RSA *fw_key = NULL;
+	FILE *fp;
+
+	/* Either a key item or the root private key is required. */
+	fp = fopen(key_item_file, "rb");
+	if (fp) {
+		pr_info("Creating image using existing key item\n");
+		key_item_len = sizeof(struct toc0_key_item);
+		key_item = OPENSSL_malloc(key_item_len);
+		if (!key_item || fread(key_item, key_item_len, 1, fp) != 1) {
+			pr_err("Failed to read key item from '%s'\n",
+			       root_key_file);
+			goto err;
+		}
+		fclose(fp);
+		fp = NULL;
+	}
+
+	fp = fopen(root_key_file, "rb");
+	if (fp) {
+		root_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
+		if (!root_key)
+			root_key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL);
+		fclose(fp);
+		fp = NULL;
+	}
+
+	/* When using an existing key item, the root key is optional. */
+	if (!key_item && (!root_key || !RSA_get0_d(root_key))) {
+		pr_err("Failed to read private key from '%s'\n",
+		       root_key_file);
+		pr_info("Try 'openssl genrsa -out root_key.pem'\n");
+		goto err;
+	}
+
+	/* The certificate/firmware private key is always required. */
+	fp = fopen(fw_key_file, "rb");
+	if (fp) {
+		fw_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
+		fclose(fp);
+		fp = NULL;
+	}
+	if (!fw_key) {
+		/* If the root key is a private key, it can be used instead. */
+		if (root_key && RSA_get0_d(root_key)) {
+			pr_info("Using root key as firmware key\n");
+			fw_key = root_key;
+		} else {
+			pr_err("Failed to read private key from '%s'\n",
+			       fw_key_file);
+			goto err;
+		}
+	}
+
+	/* Warn about potential compatibility issues. */
+	if (key_item || fw_key != root_key)
+		pr_warn("Only H6 supports separate root and firmware keys\n");
+
+	ret = toc0_create(buf, params->file_size, root_key, fw_key,
+			  key_item, key_item_len,
+			  buf + TOC0_DEFAULT_HEADER_LEN,
+			  params->orig_file_size, params->addr);
+
+err:
+	OPENSSL_free(key_item);
+	OPENSSL_free(root_key);
+	if (fw_key != root_key)
+		OPENSSL_free(fw_key);
+	if (fp)
+		fclose(fp);
+
+	if (ret != EXIT_SUCCESS)
+		exit(ret);
+}
+
+static int toc0_check_image_type(uint8_t type)
+{
+	return type == IH_TYPE_SUNXI_TOC0 ? 0 : 1;
+}
+
+static int toc0_vrec_header(struct image_tool_params *params,
+			    struct image_type_params *tparams)
+{
+	tparams->hdr = calloc(tparams->header_size, 1);
+
+	/* Save off the unpadded data size for SHA256 calculation. */
+	params->orig_file_size = params->file_size - TOC0_DEFAULT_HEADER_LEN;
+
+	/* Return padding to 8K blocks. */
+	return ALIGN(params->file_size, PAD_SIZE) - params->file_size;
+}
+
+U_BOOT_IMAGE_TYPE(
+	sunxi_toc0,
+	"Allwinner TOC0 Boot Image support",
+	TOC0_DEFAULT_HEADER_LEN,
+	NULL,
+	toc0_check_params,
+	toc0_verify_header,
+	toc0_print_header,
+	toc0_set_header,
+	NULL,
+	toc0_check_image_type,
+	NULL,
+	toc0_vrec_header
+);