Add support for the T113 family, along with 4 boards.
Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
endef
+define U-Boot/mangopi_mqdual_t113
+ BUILD_SUBTARGET:=cortexa7
+ NAME:=MangoPi MQDual (T113)
+ BUILD_DEVICES:=widora_mangopi-mqdual-t113
+endef
+
+define U-Boot/myir_myd_t113x
+ BUILD_SUBTARGET:=cortexa7
+ NAME:=MYIR MYD-T113X
+ BUILD_DEVICES:=myir_myd-yt113x
+ UENV:=t113.ttyS5
+endef
+
+define U-Boot/myir_myd_t113x-spi
+ BUILD_SUBTARGET:=cortexa7
+ NAME:=MYIR MYD-T113X SPI
+ BUILD_DEVICES:=myir_myd-yt113x-spi
+ UENV:=t113.ttyS5
+endef
+
+define U-Boot/rongpin_rp_t113
+ BUILD_SUBTARGET:=cortexa7
+ NAME:=Rongpin RP-T113
+ BUILD_DEVICES:=rongpin_rp-t113
+ UENV:=t113.ttyS3
+endef
+
UBOOT_TARGETS := \
+ mangopi_mqdual_t113 \
+ myir_myd_t113x \
+ myir_myd_t113x-spi \
+ rongpin_rp_t113 \
a64-olinuxino \
a64-olinuxino-emmc \
A10-OLinuXino-Lime \
orangepi_pc2 \
orangepi_zero2 \
pangolin \
- pine64_plus \
Sinovoip_BPI_M3 \
+ pine64_plus \
sopine_baseboard \
orangepi_zero_plus \
libretech_all_h3_cc_h5
--- /dev/null
+From 705db6ba7f566810a339985ac34170d0a84ea868 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 15 Jul 2022 16:52:14 +0100
+Subject: [PATCH 4000/4052] sunxi: remove CONFIG_SATAPWR
+
+The CONFIG_SATAPWR Kconfig symbol was used to point to a GPIO that
+enables the power for a SATA harddisk.
+In the DT this is described with the target-supply property in the AHCI
+DT node, pointing to a (GPIO controlled) regulator. Since we need SATA
+only in U-Boot proper, and use a DM driver for AHCI there, we should use
+the DT instead of hardcoding this.
+
+Add code to the sunxi AHCI driver to check the DT for that regulator and
+enable it, at probe time. Then drop the current code from board.c, which
+was doing that job before.
+This allows us to remove the SATAPWR Kconfig definition and the
+respective values from the defconfigs.
+We also select the generic fixed regulator driver, which handles those
+GPIO controlled regulators.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+---
+ arch/arm/Kconfig | 2 ++
+ arch/arm/mach-sunxi/Kconfig | 8 --------
+ board/sunxi/board.c | 16 +---------------
+ configs/A10-OLinuXino-Lime_defconfig | 1 -
+ configs/A20-OLinuXino-Lime2-eMMC_defconfig | 1 -
+ configs/A20-OLinuXino-Lime2_defconfig | 1 -
+ configs/A20-OLinuXino-Lime_defconfig | 1 -
+ configs/A20-OLinuXino_MICRO-eMMC_defconfig | 1 -
+ configs/A20-OLinuXino_MICRO_defconfig | 1 -
+ configs/A20-Olimex-SOM-EVB_defconfig | 1 -
+ configs/A20-Olimex-SOM204-EVB-eMMC_defconfig | 1 -
+ configs/A20-Olimex-SOM204-EVB_defconfig | 1 -
+ configs/Cubieboard2_defconfig | 1 -
+ configs/Cubieboard_defconfig | 1 -
+ configs/Cubietruck_defconfig | 1 -
+ configs/Itead_Ibox_A20_defconfig | 1 -
+ configs/Lamobo_R1_defconfig | 1 -
+ configs/Linksprite_pcDuino3_Nano_defconfig | 1 -
+ configs/Linksprite_pcDuino3_defconfig | 1 -
+ configs/Sinovoip_BPI_M3_defconfig | 1 -
+ configs/orangepi_plus_defconfig | 2 +-
+ drivers/ata/ahci_sunxi.c | 9 +++++++++
+ 22 files changed, 13 insertions(+), 41 deletions(-)
+
+diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
+index 8a1e223422..d5a6d293ce 100644
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -1130,6 +1130,8 @@ config ARCH_SUNXI
+ imply CMD_GPT
+ imply CMD_UBI if MTD_RAW_NAND
+ imply DISTRO_DEFAULTS
++ imply DM_REGULATOR
++ imply DM_REGULATOR_FIXED
+ imply FAT_WRITE
+ imply FIT
+ imply OF_LIBFDT_OVERLAY
+diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
+index 6417aee944..c78a553493 100644
+--- a/arch/arm/mach-sunxi/Kconfig
++++ b/arch/arm/mach-sunxi/Kconfig
+@@ -958,14 +958,6 @@ config VIDEO_LCD_TL059WV5C0
+
+ endchoice
+
+-config SATAPWR
+- string "SATA power pin"
+- default ""
+- help
+- Set the pins used to power the SATA. This takes a string in the
+- format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of
+- port H.
+-
+ config GMAC_TX_DELAY
+ int "GMAC Transmit Clock Delay Chain"
+ default 0
+diff --git a/board/sunxi/board.c b/board/sunxi/board.c
+index 827e545032..fe0e7bc2d9 100644
+--- a/board/sunxi/board.c
++++ b/board/sunxi/board.c
+@@ -187,7 +187,7 @@ enum env_location env_get_location(enum env_operation op, int prio)
+ /* add board specific code here */
+ int board_init(void)
+ {
+- __maybe_unused int id_pfr1, ret, satapwr_pin, macpwr_pin;
++ __maybe_unused int id_pfr1, ret, macpwr_pin;
+
+ gd->bd->bi_boot_params = (PHYS_SDRAM_0 + 0x100);
+
+@@ -225,20 +225,6 @@ int board_init(void)
+ return ret;
+
+ /* strcmp() would look better, but doesn't get optimised away. */
+- if (CONFIG_SATAPWR[0]) {
+- satapwr_pin = sunxi_name_to_gpio(CONFIG_SATAPWR);
+- if (satapwr_pin >= 0) {
+- gpio_request(satapwr_pin, "satapwr");
+- gpio_direction_output(satapwr_pin, 1);
+-
+- /*
+- * Give the attached SATA device time to power-up
+- * to avoid link timeouts
+- */
+- mdelay(500);
+- }
+- }
+-
+ if (CONFIG_MACPWR[0]) {
+ macpwr_pin = sunxi_name_to_gpio(CONFIG_MACPWR);
+ if (macpwr_pin >= 0) {
+diff --git a/configs/A10-OLinuXino-Lime_defconfig b/configs/A10-OLinuXino-Lime_defconfig
+index df4fdfaba4..57e91d0f01 100644
+--- a/configs/A10-OLinuXino-Lime_defconfig
++++ b/configs/A10-OLinuXino-Lime_defconfig
+@@ -7,7 +7,6 @@ CONFIG_DRAM_CLK=480
+ CONFIG_DRAM_EMR1=4
+ CONFIG_SYS_CLK_FREQ=912000000
+ CONFIG_I2C1_ENABLE=y
+-CONFIG_SATAPWR="PC3"
+ CONFIG_AHCI=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SPL_I2C=y
+diff --git a/configs/A20-OLinuXino-Lime2-eMMC_defconfig b/configs/A20-OLinuXino-Lime2-eMMC_defconfig
+index be49e9323a..44770ffb04 100644
+--- a/configs/A20-OLinuXino-Lime2-eMMC_defconfig
++++ b/configs/A20-OLinuXino-Lime2-eMMC_defconfig
+@@ -8,7 +8,6 @@ CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+ CONFIG_USB0_VBUS_PIN="PC17"
+ CONFIG_USB0_VBUS_DET="PH5"
+ CONFIG_I2C1_ENABLE=y
+-CONFIG_SATAPWR="PC3"
+ CONFIG_SPL_SPI_SUNXI=y
+ CONFIG_AHCI=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+diff --git a/configs/A20-OLinuXino-Lime2_defconfig b/configs/A20-OLinuXino-Lime2_defconfig
+index 43cd28c3dd..e10660c933 100644
+--- a/configs/A20-OLinuXino-Lime2_defconfig
++++ b/configs/A20-OLinuXino-Lime2_defconfig
+@@ -7,7 +7,6 @@ CONFIG_DRAM_CLK=384
+ CONFIG_USB0_VBUS_PIN="PC17"
+ CONFIG_USB0_VBUS_DET="PH5"
+ CONFIG_I2C1_ENABLE=y
+-CONFIG_SATAPWR="PC3"
+ CONFIG_AHCI=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SPL_I2C=y
+diff --git a/configs/A20-OLinuXino-Lime_defconfig b/configs/A20-OLinuXino-Lime_defconfig
+index 7c77f38fba..4ed666a034 100644
+--- a/configs/A20-OLinuXino-Lime_defconfig
++++ b/configs/A20-OLinuXino-Lime_defconfig
+@@ -5,7 +5,6 @@ CONFIG_SPL=y
+ CONFIG_MACH_SUN7I=y
+ CONFIG_DRAM_CLK=384
+ CONFIG_I2C1_ENABLE=y
+-CONFIG_SATAPWR="PC3"
+ CONFIG_AHCI=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SPL_I2C=y
+diff --git a/configs/A20-OLinuXino_MICRO-eMMC_defconfig b/configs/A20-OLinuXino_MICRO-eMMC_defconfig
+index 02116995a3..ca5869f43d 100644
+--- a/configs/A20-OLinuXino_MICRO-eMMC_defconfig
++++ b/configs/A20-OLinuXino_MICRO-eMMC_defconfig
+@@ -7,7 +7,6 @@ CONFIG_DRAM_CLK=384
+ CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+ CONFIG_I2C1_ENABLE=y
+ CONFIG_VIDEO_VGA=y
+-CONFIG_SATAPWR="PB8"
+ CONFIG_AHCI=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SPL_I2C=y
+diff --git a/configs/A20-OLinuXino_MICRO_defconfig b/configs/A20-OLinuXino_MICRO_defconfig
+index 895e8dbcbd..db4270f9b2 100644
+--- a/configs/A20-OLinuXino_MICRO_defconfig
++++ b/configs/A20-OLinuXino_MICRO_defconfig
+@@ -7,7 +7,6 @@ CONFIG_DRAM_CLK=384
+ CONFIG_MMC_SUNXI_SLOT_EXTRA=3
+ CONFIG_I2C1_ENABLE=y
+ CONFIG_VIDEO_VGA=y
+-CONFIG_SATAPWR="PB8"
+ CONFIG_AHCI=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SPL_I2C=y
+diff --git a/configs/A20-Olimex-SOM-EVB_defconfig b/configs/A20-Olimex-SOM-EVB_defconfig
+index 5bcc9f9f3c..ac900477d1 100644
+--- a/configs/A20-Olimex-SOM-EVB_defconfig
++++ b/configs/A20-Olimex-SOM-EVB_defconfig
+@@ -7,7 +7,6 @@ CONFIG_DRAM_CLK=384
+ CONFIG_MMC_SUNXI_SLOT_EXTRA=3
+ CONFIG_USB0_VBUS_PIN="PB9"
+ CONFIG_USB0_VBUS_DET="PH5"
+-CONFIG_SATAPWR="PC3"
+ CONFIG_AHCI=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SPL_I2C=y
+diff --git a/configs/A20-Olimex-SOM204-EVB-eMMC_defconfig b/configs/A20-Olimex-SOM204-EVB-eMMC_defconfig
+index e5881090dd..00a98140b3 100644
+--- a/configs/A20-Olimex-SOM204-EVB-eMMC_defconfig
++++ b/configs/A20-Olimex-SOM204-EVB-eMMC_defconfig
+@@ -8,7 +8,6 @@ CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+ CONFIG_USB0_VBUS_PIN="PC17"
+ CONFIG_USB0_VBUS_DET="PH5"
+ CONFIG_I2C1_ENABLE=y
+-CONFIG_SATAPWR="PC3"
+ CONFIG_GMAC_TX_DELAY=4
+ CONFIG_AHCI=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+diff --git a/configs/A20-Olimex-SOM204-EVB_defconfig b/configs/A20-Olimex-SOM204-EVB_defconfig
+index 592a79a6c7..f4ae3ae6d8 100644
+--- a/configs/A20-Olimex-SOM204-EVB_defconfig
++++ b/configs/A20-Olimex-SOM204-EVB_defconfig
+@@ -7,7 +7,6 @@ CONFIG_DRAM_CLK=384
+ CONFIG_USB0_VBUS_PIN="PC17"
+ CONFIG_USB0_VBUS_DET="PH5"
+ CONFIG_I2C1_ENABLE=y
+-CONFIG_SATAPWR="PC3"
+ CONFIG_GMAC_TX_DELAY=4
+ CONFIG_AHCI=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+diff --git a/configs/Cubieboard2_defconfig b/configs/Cubieboard2_defconfig
+index 0c23368741..ef4f11b7c6 100644
+--- a/configs/Cubieboard2_defconfig
++++ b/configs/Cubieboard2_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun7i-a20-cubieboard2"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN7I=y
+ CONFIG_DRAM_CLK=480
+-CONFIG_SATAPWR="PB8"
+ CONFIG_AHCI=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SPL_I2C=y
+diff --git a/configs/Cubieboard_defconfig b/configs/Cubieboard_defconfig
+index 71743f7b8a..ab3f65ad66 100644
+--- a/configs/Cubieboard_defconfig
++++ b/configs/Cubieboard_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun4i-a10-cubieboard"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN4I=y
+ CONFIG_DRAM_CLK=480
+-CONFIG_SATAPWR="PB8"
+ CONFIG_AHCI=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SPL_I2C=y
+diff --git a/configs/Cubietruck_defconfig b/configs/Cubietruck_defconfig
+index 184f305b19..184143d708 100644
+--- a/configs/Cubietruck_defconfig
++++ b/configs/Cubietruck_defconfig
+@@ -8,7 +8,6 @@ CONFIG_USB0_VBUS_PIN="PH17"
+ CONFIG_USB0_VBUS_DET="PH22"
+ CONFIG_USB0_ID_DET="PH19"
+ CONFIG_VIDEO_VGA=y
+-CONFIG_SATAPWR="PH12"
+ CONFIG_GMAC_TX_DELAY=1
+ CONFIG_AHCI=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+diff --git a/configs/Itead_Ibox_A20_defconfig b/configs/Itead_Ibox_A20_defconfig
+index 5d05f33798..d03fa62196 100644
+--- a/configs/Itead_Ibox_A20_defconfig
++++ b/configs/Itead_Ibox_A20_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun7i-a20-itead-ibox"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN7I=y
+ CONFIG_DRAM_CLK=480
+-CONFIG_SATAPWR="PB8"
+ CONFIG_AHCI=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SPL_I2C=y
+diff --git a/configs/Lamobo_R1_defconfig b/configs/Lamobo_R1_defconfig
+index 5294608459..9639cb6aad 100644
+--- a/configs/Lamobo_R1_defconfig
++++ b/configs/Lamobo_R1_defconfig
+@@ -5,7 +5,6 @@ CONFIG_SPL=y
+ CONFIG_MACH_SUN7I=y
+ CONFIG_DRAM_CLK=432
+ CONFIG_MACPWR="PH23"
+-CONFIG_SATAPWR="PB3"
+ CONFIG_GMAC_TX_DELAY=4
+ CONFIG_AHCI=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+diff --git a/configs/Linksprite_pcDuino3_Nano_defconfig b/configs/Linksprite_pcDuino3_Nano_defconfig
+index e3e30a4949..9eb9a918ae 100644
+--- a/configs/Linksprite_pcDuino3_Nano_defconfig
++++ b/configs/Linksprite_pcDuino3_Nano_defconfig
+@@ -6,7 +6,6 @@ CONFIG_MACH_SUN7I=y
+ CONFIG_DRAM_CLK=408
+ CONFIG_DRAM_ZQ=122
+ CONFIG_USB1_VBUS_PIN="PH11"
+-CONFIG_SATAPWR="PH2"
+ CONFIG_GMAC_TX_DELAY=3
+ CONFIG_AHCI=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+diff --git a/configs/Linksprite_pcDuino3_defconfig b/configs/Linksprite_pcDuino3_defconfig
+index 1fda0db4c9..7db10e685b 100644
+--- a/configs/Linksprite_pcDuino3_defconfig
++++ b/configs/Linksprite_pcDuino3_defconfig
+@@ -5,7 +5,6 @@ CONFIG_SPL=y
+ CONFIG_MACH_SUN7I=y
+ CONFIG_DRAM_CLK=480
+ CONFIG_DRAM_ZQ=122
+-CONFIG_SATAPWR="PH2"
+ CONFIG_AHCI=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SPL_I2C=y
+diff --git a/configs/Sinovoip_BPI_M3_defconfig b/configs/Sinovoip_BPI_M3_defconfig
+index 5116fab52d..5545b3d464 100644
+--- a/configs/Sinovoip_BPI_M3_defconfig
++++ b/configs/Sinovoip_BPI_M3_defconfig
+@@ -13,7 +13,6 @@ CONFIG_USB0_VBUS_DET="AXP0-VBUS-DETECT"
+ CONFIG_USB0_ID_DET="PH11"
+ CONFIG_USB1_VBUS_PIN="PD24"
+ CONFIG_AXP_GPIO=y
+-CONFIG_SATAPWR="PD25"
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_CONSOLE_MUX=y
+ CONFIG_PHY_REALTEK=y
+diff --git a/configs/orangepi_plus_defconfig b/configs/orangepi_plus_defconfig
+index 76de72aa22..ed585881d4 100644
+--- a/configs/orangepi_plus_defconfig
++++ b/configs/orangepi_plus_defconfig
+@@ -7,7 +7,6 @@ CONFIG_DRAM_CLK=672
+ CONFIG_MACPWR="PD6"
+ CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+ CONFIG_USB1_VBUS_PIN="PG13"
+-CONFIG_SATAPWR="PG11"
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SPL_I2C=y
+ CONFIG_SPL_SYS_I2C_LEGACY=y
+@@ -16,3 +15,4 @@ CONFIG_SUN8I_EMAC=y
+ CONFIG_SY8106A_POWER=y
+ CONFIG_USB_EHCI_HCD=y
+ CONFIG_USB_OHCI_HCD=y
++CONFIG_USB3_VBUS_PIN="PG11"
+diff --git a/drivers/ata/ahci_sunxi.c b/drivers/ata/ahci_sunxi.c
+index 94a3379c53..9064774e66 100644
+--- a/drivers/ata/ahci_sunxi.c
++++ b/drivers/ata/ahci_sunxi.c
+@@ -7,6 +7,7 @@
+ #include <asm/io.h>
+ #include <asm/gpio.h>
+ #include <linux/delay.h>
++#include <power/regulator.h>
+
+ #define AHCI_PHYCS0R 0x00c0
+ #define AHCI_PHYCS1R 0x00c4
+@@ -74,6 +75,7 @@ static int sunxi_ahci_phy_init(u8 *reg_base)
+
+ static int sunxi_sata_probe(struct udevice *dev)
+ {
++ struct udevice *reg_dev;
+ ulong base;
+ u8 *reg;
+ int ret;
+@@ -89,6 +91,13 @@ static int sunxi_sata_probe(struct udevice *dev)
+ debug("%s: Failed to init phy (err=%d)\n", __func__, ret);
+ return ret;
+ }
++
++ ret = device_get_supply_regulator(dev, "target-supply", ®_dev);
++ if (ret == 0) {
++ regulator_set_enable(reg_dev, true);
++ mdelay(500);
++ }
++
+ ret = ahci_probe_scsi(dev, base);
+ if (ret) {
+ debug("%s: Failed to probe (err=%d)\n", __func__, ret);
+--
+2.20.1
+
--- /dev/null
+From 02749b4f3d18cb5245b7eed9f60076c41cc308b7 Mon Sep 17 00:00:00 2001
+From: Marek Vasut <marek.vasut+renesas@mailbox.org>
+Date: Wed, 31 May 2023 00:51:24 +0200
+Subject: [PATCH 4001/4052] net: sunxi_emac: Switch to new U-Boot PHY API
+
+Use new U-Boot phy_connect() API which also supports fixed PHYs.
+
+Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
+Reviewed-by: Ramon Fried <rfried.dev@gmail.com>
+---
+ drivers/net/sunxi_emac.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/net/sunxi_emac.c b/drivers/net/sunxi_emac.c
+index ad9e1abd16..4c90d4b498 100644
+--- a/drivers/net/sunxi_emac.c
++++ b/drivers/net/sunxi_emac.c
+@@ -248,10 +248,10 @@ static int emac_mdio_write(struct mii_dev *bus, int addr, int devad, int reg,
+
+ static int sunxi_emac_init_phy(struct emac_eth_dev *priv, void *dev)
+ {
+- int ret, mask = 0xffffffff;
++ int ret, mask = -1;
+
+ #ifdef CONFIG_PHY_ADDR
+- mask = 1 << CONFIG_PHY_ADDR;
++ mask = CONFIG_PHY_ADDR;
+ #endif
+
+ priv->bus = mdio_alloc();
+@@ -269,11 +269,10 @@ static int sunxi_emac_init_phy(struct emac_eth_dev *priv, void *dev)
+ if (ret)
+ return ret;
+
+- priv->phydev = phy_find_by_mask(priv->bus, mask);
++ priv->phydev = phy_connect(priv->bus, mask, dev, PHY_INTERFACE_MODE_MII);
+ if (!priv->phydev)
+ return -ENODEV;
+
+- phy_connect_dev(priv->phydev, dev, PHY_INTERFACE_MODE_MII);
+ phy_config(priv->phydev);
+
+ return 0;
+--
+2.20.1
+
--- /dev/null
+From fbdfae3efbd295bd4ea4ad02c2af1a8dca649238 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 21 Jul 2023 14:45:47 +0100
+Subject: [PATCH 4002/4052] net: sunxi_emac: chase DT nodes to find PHY
+ regulator
+
+At the moment the sun4i EMAC driver relies on hardcoded CONFIG_MACPWR
+Kconfig symbols to enable potential PHY regulators. As we want to get rid
+of those, we need to find the regulator by chasing up the DT.
+
+The sun4i-emac binding puts the PHY regulator into the MDIO node, which
+is the parent of the PHY device. U-Boot does not have (and does not
+need) an MDIO driver, so we need to chase down the regulator through the
+EMAC node: we follow the "phy-handle" property to find the PHY node,
+then go up to its parent, where we find the "phy-supply" link to the
+regulator. Let U-Boot find the associated regulator device, and put that
+into the private device struct, so we can find and enable the regulator
+at probe time, later.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+Reviewed-by: Sam Edwards <CFSworks@gmail.com>
+---
+ drivers/net/sunxi_emac.c | 39 +++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 39 insertions(+)
+
+diff --git a/drivers/net/sunxi_emac.c b/drivers/net/sunxi_emac.c
+index 4c90d4b498..f1f0e5bbbb 100644
+--- a/drivers/net/sunxi_emac.c
++++ b/drivers/net/sunxi_emac.c
+@@ -17,6 +17,7 @@
+ #include <net.h>
+ #include <asm/io.h>
+ #include <asm/arch/clock.h>
++#include <power/regulator.h>
+
+ /* EMAC register */
+ struct emac_regs {
+@@ -165,6 +166,7 @@ struct emac_eth_dev {
+ struct phy_device *phydev;
+ int link_printed;
+ uchar rx_buf[EMAC_RX_BUFSIZE];
++ struct udevice *phy_reg;
+ };
+
+ struct emac_rxhdr {
+@@ -572,6 +574,9 @@ static int sunxi_emac_eth_probe(struct udevice *dev)
+ if (ret)
+ return ret;
+
++ if (priv->phy_reg)
++ regulator_set_enable(priv->phy_reg, true);
++
+ return sunxi_emac_init_phy(priv, dev);
+ }
+
+@@ -585,9 +590,43 @@ static const struct eth_ops sunxi_emac_eth_ops = {
+ static int sunxi_emac_eth_of_to_plat(struct udevice *dev)
+ {
+ struct eth_pdata *pdata = dev_get_plat(dev);
++ struct emac_eth_dev *priv = dev_get_priv(dev);
++ struct ofnode_phandle_args args;
++ ofnode mdio_node;
++ int ret;
+
+ pdata->iobase = dev_read_addr(dev);
+
++ /* The PHY regulator is in the MDIO node, not the EMAC or PHY node. */
++ ret = dev_read_phandle_with_args(dev, "phy-handle", NULL, 0, 0, &args);
++ if (ret) {
++ dev_err(dev, "failed to get PHY node\n");
++ return ret;
++ }
++
++ /*
++ * U-Boot does not have (and does not need) a device driver for the
++ * MDIO device, so just "pass through" that DT node to get to the
++ * regulator phandle.
++ * The PHY regulator is optional, though: ignore if we cannot find
++ * a phy-supply property.
++ */
++ mdio_node = ofnode_get_parent(args.node);
++ ret= ofnode_parse_phandle_with_args(mdio_node, "phy-supply", NULL, 0, 0,
++ &args);
++ if (ret && ret != -ENOENT) {
++ dev_err(dev, "failed to get PHY supply node\n");
++ return ret;
++ }
++ if (!ret) {
++ ret = uclass_get_device_by_ofnode(UCLASS_REGULATOR, args.node,
++ &priv->phy_reg);
++ if (ret) {
++ dev_err(dev, "failed to get PHY regulator node\n");
++ return ret;
++ }
++ }
++
+ return 0;
+ }
+
+--
+2.20.1
+
--- /dev/null
+From 2c3f0b528257cb37445a63c7f25813c399f8b742 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 22 Jan 2023 16:51:02 -0600
+Subject: [PATCH 4003/4052] net: sun8i-emac: Add a structure for variant data
+
+Currently, EMAC variants are distinguished by their identity, but this
+gets unwieldy as more overlapping variants are added. Add a structure so
+we can describe the individual feature differences between the variants.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+Reviewed-by: Andre Przywara <andre.przywara@arm.com>
+Reviewed-by: Ramon Fried <rfried.dev@gmail.com>
+---
+ drivers/net/sun8i_emac.c | 65 +++++++++++++++++++++++++++-------------
+ 1 file changed, 45 insertions(+), 20 deletions(-)
+
+diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c
+index e800a326b8..986e565cd8 100644
+--- a/drivers/net/sun8i_emac.c
++++ b/drivers/net/sun8i_emac.c
+@@ -127,7 +127,7 @@
+
+ DECLARE_GLOBAL_DATA_PTR;
+
+-enum emac_variant {
++enum emac_variant_id {
+ A83T_EMAC = 1,
+ H3_EMAC,
+ A64_EMAC,
+@@ -135,6 +135,10 @@ enum emac_variant {
+ H6_EMAC,
+ };
+
++struct emac_variant {
++ enum emac_variant_id variant;
++};
++
+ struct emac_dma_desc {
+ u32 status;
+ u32 ctl_size;
+@@ -160,7 +164,7 @@ struct emac_eth_dev {
+ u32 tx_slot;
+ bool use_internal_phy;
+
+- enum emac_variant variant;
++ const struct emac_variant *variant;
+ void *mac_reg;
+ phys_addr_t sysctl_reg;
+ struct phy_device *phydev;
+@@ -317,7 +321,7 @@ static int sun8i_emac_set_syscon(struct sun8i_eth_pdata *pdata,
+ {
+ u32 reg;
+
+- if (priv->variant == R40_GMAC) {
++ if (priv->variant->variant == R40_GMAC) {
+ /* Select RGMII for R40 */
+ reg = readl(priv->sysctl_reg + 0x164);
+ reg |= SC_ETCS_INT_GMII |
+@@ -333,9 +337,9 @@ static int sun8i_emac_set_syscon(struct sun8i_eth_pdata *pdata,
+ reg = sun8i_emac_set_syscon_ephy(priv, reg);
+
+ reg &= ~(SC_ETCS_MASK | SC_EPIT);
+- if (priv->variant == H3_EMAC ||
+- priv->variant == A64_EMAC ||
+- priv->variant == H6_EMAC)
++ if (priv->variant->variant == H3_EMAC ||
++ priv->variant->variant == A64_EMAC ||
++ priv->variant->variant == H6_EMAC)
+ reg &= ~SC_RMII_EN;
+
+ switch (priv->interface) {
+@@ -349,9 +353,9 @@ static int sun8i_emac_set_syscon(struct sun8i_eth_pdata *pdata,
+ reg |= SC_EPIT | SC_ETCS_INT_GMII;
+ break;
+ case PHY_INTERFACE_MODE_RMII:
+- if (priv->variant == H3_EMAC ||
+- priv->variant == A64_EMAC ||
+- priv->variant == H6_EMAC) {
++ if (priv->variant->variant == H3_EMAC ||
++ priv->variant->variant == A64_EMAC ||
++ priv->variant->variant == H6_EMAC) {
+ reg |= SC_RMII_EN | SC_ETCS_EXT_GMII;
+ break;
+ }
+@@ -806,7 +810,7 @@ static int sun8i_emac_eth_of_to_plat(struct udevice *dev)
+ return -EINVAL;
+ }
+
+- priv->variant = dev_get_driver_data(dev);
++ priv->variant = (const void *)dev_get_driver_data(dev);
+
+ if (!priv->variant) {
+ printf("%s: Missing variant\n", __func__);
+@@ -860,7 +864,7 @@ static int sun8i_emac_eth_of_to_plat(struct udevice *dev)
+ if (pdata->phy_interface == PHY_INTERFACE_MODE_NA)
+ return -EINVAL;
+
+- if (priv->variant == H3_EMAC) {
++ if (priv->variant->variant == H3_EMAC) {
+ ret = sun8i_handle_internal_phy(dev, priv);
+ if (ret)
+ return ret;
+@@ -900,16 +904,37 @@ static int sun8i_emac_eth_of_to_plat(struct udevice *dev)
+ return 0;
+ }
+
++static const struct emac_variant emac_variant_a83t = {
++ .variant = A83T_EMAC,
++};
++
++static const struct emac_variant emac_variant_h3 = {
++ .variant = H3_EMAC,
++};
++
++static const struct emac_variant emac_variant_r40 = {
++ .variant = R40_GMAC,
++};
++
++static const struct emac_variant emac_variant_a64 = {
++ .variant = A64_EMAC,
++};
++
++static const struct emac_variant emac_variant_h6 = {
++ .variant = H6_EMAC,
++};
++
+ static const struct udevice_id sun8i_emac_eth_ids[] = {
+- {.compatible = "allwinner,sun8i-h3-emac", .data = (uintptr_t)H3_EMAC },
+- {.compatible = "allwinner,sun50i-a64-emac",
+- .data = (uintptr_t)A64_EMAC },
+- {.compatible = "allwinner,sun8i-a83t-emac",
+- .data = (uintptr_t)A83T_EMAC },
+- {.compatible = "allwinner,sun8i-r40-gmac",
+- .data = (uintptr_t)R40_GMAC },
+- {.compatible = "allwinner,sun50i-h6-emac",
+- .data = (uintptr_t)H6_EMAC },
++ { .compatible = "allwinner,sun8i-a83t-emac",
++ .data = (ulong)&emac_variant_a83t },
++ { .compatible = "allwinner,sun8i-h3-emac",
++ .data = (ulong)&emac_variant_h3 },
++ { .compatible = "allwinner,sun8i-r40-gmac",
++ .data = (ulong)&emac_variant_r40 },
++ { .compatible = "allwinner,sun50i-a64-emac",
++ .data = (ulong)&emac_variant_a64 },
++ { .compatible = "allwinner,sun50i-h6-emac",
++ .data = (ulong)&emac_variant_h6 },
+ { }
+ };
+
+--
+2.20.1
+
--- /dev/null
+From 0050a54e83e4e7c094a1ff1b3f67a4d9aebc22d8 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 22 Jan 2023 16:51:03 -0600
+Subject: [PATCH 4004/4052] net: sun8i-emac: Add a flag for RMII support
+
+Describe this feature instead of using the SoC ID.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+Reviewed-by: Andre Przywara <andre.przywara@arm.com>
+Reviewed-by: Ramon Fried <rfried.dev@gmail.com>
+---
+ drivers/net/sun8i_emac.c | 15 +++++++--------
+ 1 file changed, 7 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c
+index 986e565cd8..f232b8f087 100644
+--- a/drivers/net/sun8i_emac.c
++++ b/drivers/net/sun8i_emac.c
+@@ -137,6 +137,7 @@ enum emac_variant_id {
+
+ struct emac_variant {
+ enum emac_variant_id variant;
++ bool support_rmii;
+ };
+
+ struct emac_dma_desc {
+@@ -337,9 +338,7 @@ static int sun8i_emac_set_syscon(struct sun8i_eth_pdata *pdata,
+ reg = sun8i_emac_set_syscon_ephy(priv, reg);
+
+ reg &= ~(SC_ETCS_MASK | SC_EPIT);
+- if (priv->variant->variant == H3_EMAC ||
+- priv->variant->variant == A64_EMAC ||
+- priv->variant->variant == H6_EMAC)
++ if (priv->variant->support_rmii)
+ reg &= ~SC_RMII_EN;
+
+ switch (priv->interface) {
+@@ -353,13 +352,10 @@ static int sun8i_emac_set_syscon(struct sun8i_eth_pdata *pdata,
+ reg |= SC_EPIT | SC_ETCS_INT_GMII;
+ break;
+ case PHY_INTERFACE_MODE_RMII:
+- if (priv->variant->variant == H3_EMAC ||
+- priv->variant->variant == A64_EMAC ||
+- priv->variant->variant == H6_EMAC) {
++ if (priv->variant->support_rmii) {
+ reg |= SC_RMII_EN | SC_ETCS_EXT_GMII;
+- break;
++ break;
+ }
+- /* RMII not supported on A83T */
+ default:
+ debug("%s: Invalid PHY interface\n", __func__);
+ return -EINVAL;
+@@ -910,6 +906,7 @@ static const struct emac_variant emac_variant_a83t = {
+
+ static const struct emac_variant emac_variant_h3 = {
+ .variant = H3_EMAC,
++ .support_rmii = true,
+ };
+
+ static const struct emac_variant emac_variant_r40 = {
+@@ -918,10 +915,12 @@ static const struct emac_variant emac_variant_r40 = {
+
+ static const struct emac_variant emac_variant_a64 = {
+ .variant = A64_EMAC,
++ .support_rmii = true,
+ };
+
+ static const struct emac_variant emac_variant_h6 = {
+ .variant = H6_EMAC,
++ .support_rmii = true,
+ };
+
+ static const struct udevice_id sun8i_emac_eth_ids[] = {
+--
+2.20.1
+
--- /dev/null
+From 1c627c8d633cb2bc98db5897a8ff2b5e56c232fe Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 22 Jan 2023 16:51:04 -0600
+Subject: [PATCH 4005/4052] net: sun8i-emac: Add a flag for the internal PHY
+ switch
+
+Describe this feature instead of using the SoC ID.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+Reviewed-by: Andre Przywara <andre.przywara@arm.com>
+Reviewed-by: Ramon Fried <rfried.dev@gmail.com>
+---
+ drivers/net/sun8i_emac.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c
+index f232b8f087..36cc2498b5 100644
+--- a/drivers/net/sun8i_emac.c
++++ b/drivers/net/sun8i_emac.c
+@@ -137,6 +137,7 @@ enum emac_variant_id {
+
+ struct emac_variant {
+ enum emac_variant_id variant;
++ bool soc_has_internal_phy;
+ bool support_rmii;
+ };
+
+@@ -860,7 +861,7 @@ static int sun8i_emac_eth_of_to_plat(struct udevice *dev)
+ if (pdata->phy_interface == PHY_INTERFACE_MODE_NA)
+ return -EINVAL;
+
+- if (priv->variant->variant == H3_EMAC) {
++ if (priv->variant->soc_has_internal_phy) {
+ ret = sun8i_handle_internal_phy(dev, priv);
+ if (ret)
+ return ret;
+@@ -906,6 +907,7 @@ static const struct emac_variant emac_variant_a83t = {
+
+ static const struct emac_variant emac_variant_h3 = {
+ .variant = H3_EMAC,
++ .soc_has_internal_phy = true,
+ .support_rmii = true,
+ };
+
+--
+2.20.1
+
--- /dev/null
+From a31d1f97e36d42f80f3d04b07df121b6da142dc8 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 22 Jan 2023 16:51:05 -0600
+Subject: [PATCH 4006/4052] net: sun8i-emac: Use common syscon setup for R40
+
+While R40 puts the EMAC syscon register at a different address from
+other variants, the relevant portion of the register's layout is the
+same. Factor out the register offset so the same code can be shared
+by all variants. This matches what the Linux driver does.
+
+This change provides two benefits beyond the simplification:
+ - R40 boards now respect the RX delays from the devicetree
+ - This resolves a warning on architectures where readl/writel
+ expect the address to have a pointer type, not phys_addr_t.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+Reviewed-by: Andre Przywara <andre.przywara@arm.com>
+Reviewed-by: Ramon Fried <rfried.dev@gmail.com>
+---
+ drivers/net/sun8i_emac.c | 32 +++++++++++++++-----------------
+ 1 file changed, 15 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c
+index 36cc2498b5..231aac19e3 100644
+--- a/drivers/net/sun8i_emac.c
++++ b/drivers/net/sun8i_emac.c
+@@ -137,6 +137,7 @@ enum emac_variant_id {
+
+ struct emac_variant {
+ enum emac_variant_id variant;
++ uint syscon_offset;
+ bool soc_has_internal_phy;
+ bool support_rmii;
+ };
+@@ -168,7 +169,7 @@ struct emac_eth_dev {
+
+ const struct emac_variant *variant;
+ void *mac_reg;
+- phys_addr_t sysctl_reg;
++ void *sysctl_reg;
+ struct phy_device *phydev;
+ struct mii_dev *bus;
+ struct clk tx_clk;
+@@ -323,18 +324,7 @@ static int sun8i_emac_set_syscon(struct sun8i_eth_pdata *pdata,
+ {
+ u32 reg;
+
+- if (priv->variant->variant == R40_GMAC) {
+- /* Select RGMII for R40 */
+- reg = readl(priv->sysctl_reg + 0x164);
+- reg |= SC_ETCS_INT_GMII |
+- SC_EPIT |
+- (CONFIG_GMAC_TX_DELAY << SC_ETXDC_OFFSET);
+-
+- writel(reg, priv->sysctl_reg + 0x164);
+- return 0;
+- }
+-
+- reg = readl(priv->sysctl_reg + 0x30);
++ reg = readl(priv->sysctl_reg);
+
+ reg = sun8i_emac_set_syscon_ephy(priv, reg);
+
+@@ -370,7 +360,7 @@ static int sun8i_emac_set_syscon(struct sun8i_eth_pdata *pdata,
+ reg |= ((pdata->rx_delay_ps / 100) << SC_ERXDC_OFFSET)
+ & SC_ERXDC_MASK;
+
+- writel(reg, priv->sysctl_reg + 0x30);
++ writel(reg, priv->sysctl_reg);
+
+ return 0;
+ }
+@@ -793,6 +783,7 @@ static int sun8i_emac_eth_of_to_plat(struct udevice *dev)
+ struct sun8i_eth_pdata *sun8i_pdata = dev_get_plat(dev);
+ struct eth_pdata *pdata = &sun8i_pdata->eth_pdata;
+ struct emac_eth_dev *priv = dev_get_priv(dev);
++ phys_addr_t syscon_base;
+ const fdt32_t *reg;
+ int node = dev_of_offset(dev);
+ int offset = 0;
+@@ -838,13 +829,15 @@ static int sun8i_emac_eth_of_to_plat(struct udevice *dev)
+ __func__);
+ return -EINVAL;
+ }
+- priv->sysctl_reg = fdt_translate_address((void *)gd->fdt_blob,
+- offset, reg);
+- if (priv->sysctl_reg == FDT_ADDR_T_NONE) {
++
++ syscon_base = fdt_translate_address((void *)gd->fdt_blob, offset, reg);
++ if (syscon_base == FDT_ADDR_T_NONE) {
+ debug("%s: Cannot find syscon base address\n", __func__);
+ return -EINVAL;
+ }
+
++ priv->sysctl_reg = (void *)syscon_base + priv->variant->syscon_offset;
++
+ pdata->phy_interface = -1;
+ priv->phyaddr = -1;
+ priv->use_internal_phy = false;
+@@ -903,25 +896,30 @@ static int sun8i_emac_eth_of_to_plat(struct udevice *dev)
+
+ static const struct emac_variant emac_variant_a83t = {
+ .variant = A83T_EMAC,
++ .syscon_offset = 0x30,
+ };
+
+ static const struct emac_variant emac_variant_h3 = {
+ .variant = H3_EMAC,
++ .syscon_offset = 0x30,
+ .soc_has_internal_phy = true,
+ .support_rmii = true,
+ };
+
+ static const struct emac_variant emac_variant_r40 = {
+ .variant = R40_GMAC,
++ .syscon_offset = 0x164,
+ };
+
+ static const struct emac_variant emac_variant_a64 = {
+ .variant = A64_EMAC,
++ .syscon_offset = 0x30,
+ .support_rmii = true,
+ };
+
+ static const struct emac_variant emac_variant_h6 = {
+ .variant = H6_EMAC,
++ .syscon_offset = 0x30,
+ .support_rmii = true,
+ };
+
+--
+2.20.1
+
--- /dev/null
+From 645050bccaab8ab2b27077f2801da4b723ab8268 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 22 Jan 2023 16:51:06 -0600
+Subject: [PATCH 4007/4052] net: sun8i-emac: Remove the SoC variant ID
+
+Now that all differences in functionality are covered by individual
+flags, remove the enumeration of SoC variants.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+Reviewed-by: Andre Przywara <andre.przywara@arm.com>
+Reviewed-by: Ramon Fried <rfried.dev@gmail.com>
+---
+ drivers/net/sun8i_emac.c | 14 --------------
+ 1 file changed, 14 deletions(-)
+
+diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c
+index 231aac19e3..04c3274fbe 100644
+--- a/drivers/net/sun8i_emac.c
++++ b/drivers/net/sun8i_emac.c
+@@ -127,16 +127,7 @@
+
+ DECLARE_GLOBAL_DATA_PTR;
+
+-enum emac_variant_id {
+- A83T_EMAC = 1,
+- H3_EMAC,
+- A64_EMAC,
+- R40_GMAC,
+- H6_EMAC,
+-};
+-
+ struct emac_variant {
+- enum emac_variant_id variant;
+ uint syscon_offset;
+ bool soc_has_internal_phy;
+ bool support_rmii;
+@@ -895,30 +886,25 @@ static int sun8i_emac_eth_of_to_plat(struct udevice *dev)
+ }
+
+ static const struct emac_variant emac_variant_a83t = {
+- .variant = A83T_EMAC,
+ .syscon_offset = 0x30,
+ };
+
+ static const struct emac_variant emac_variant_h3 = {
+- .variant = H3_EMAC,
+ .syscon_offset = 0x30,
+ .soc_has_internal_phy = true,
+ .support_rmii = true,
+ };
+
+ static const struct emac_variant emac_variant_r40 = {
+- .variant = R40_GMAC,
+ .syscon_offset = 0x164,
+ };
+
+ static const struct emac_variant emac_variant_a64 = {
+- .variant = A64_EMAC,
+ .syscon_offset = 0x30,
+ .support_rmii = true,
+ };
+
+ static const struct emac_variant emac_variant_h6 = {
+- .variant = H6_EMAC,
+ .syscon_offset = 0x30,
+ .support_rmii = true,
+ };
+--
+2.20.1
+
--- /dev/null
+From e7d4da422816303af8fcbcb2e6d9b09843d31b33 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 21 Jul 2023 14:45:48 +0100
+Subject: [PATCH 4008/4052] sunxi: remove CONFIG_MACPWR
+
+The CONFIG_MACPWR Kconfig symbol is used to point to a GPIO that enables
+the power for the Ethernet "MAC" (mostly PHY, really).
+In the DT this is described with the phy-supply property in the MAC DT
+node, pointing to a (GPIO controlled) regulator. Since we need Ethernet
+only in U-Boot proper, and use a DM driver there, we should use the DT
+instead of hardcoding this.
+
+Add code to the sun8i_emac and sunxi_emac drivers to check the DT for
+that regulator and enable it, at probe time. Then drop the current code
+from board.c, which was doing that job before.
+This allows us to remove the MACPWR Kconfig definition and the respective
+values from the defconfigs.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+Reviewed-by: Sam Edwards <CFSworks@gmail.com>
+---
+ arch/arm/mach-sunxi/Kconfig | 7 -------
+ board/sunxi/board.c | 12 +-----------
+ configs/Bananapi_M2_Ultra_defconfig | 1 -
+ configs/Bananapi_defconfig | 1 -
+ configs/Bananapro_defconfig | 1 -
+ configs/Lamobo_R1_defconfig | 1 -
+ configs/Mele_A1000_defconfig | 1 -
+ configs/Orangepi_defconfig | 1 -
+ configs/Orangepi_mini_defconfig | 1 -
+ configs/bananapi_m1_plus_defconfig | 1 -
+ configs/bananapi_m2_plus_h3_defconfig | 1 -
+ configs/bananapi_m2_plus_h5_defconfig | 1 -
+ configs/i12-tvbox_defconfig | 1 -
+ configs/jesurun_q5_defconfig | 1 -
+ configs/mixtile_loftq_defconfig | 1 -
+ configs/nanopi_m1_plus_defconfig | 1 -
+ configs/nanopi_neo_plus2_defconfig | 1 -
+ configs/nanopi_r1s_h5_defconfig | 1 -
+ configs/orangepi_pc2_defconfig | 1 -
+ configs/orangepi_plus2e_defconfig | 1 -
+ configs/orangepi_plus_defconfig | 1 -
+ configs/pine_h64_defconfig | 1 -
+ configs/zeropi_defconfig | 1 -
+ drivers/net/sun8i_emac.c | 9 +++++++--
+ 24 files changed, 8 insertions(+), 41 deletions(-)
+
+diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
+index c78a553493..d716054f72 100644
+--- a/arch/arm/mach-sunxi/Kconfig
++++ b/arch/arm/mach-sunxi/Kconfig
+@@ -645,13 +645,6 @@ config OLD_SUNXI_KERNEL_COMPAT
+ Set this to enable various workarounds for old kernels, this results in
+ sub-optimal settings for newer kernels, only enable if needed.
+
+-config MACPWR
+- string "MAC power pin"
+- default ""
+- help
+- Set the pin used to power the MAC. This takes a string in the format
+- understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H.
+-
+ config MMC1_PINS_PH
+ bool "Pins for mmc1 are on Port H"
+ depends on MACH_SUN4I || MACH_SUN7I || MACH_SUN8I_R40
+diff --git a/board/sunxi/board.c b/board/sunxi/board.c
+index fe0e7bc2d9..9900c66ed0 100644
+--- a/board/sunxi/board.c
++++ b/board/sunxi/board.c
+@@ -187,7 +187,7 @@ enum env_location env_get_location(enum env_operation op, int prio)
+ /* add board specific code here */
+ int board_init(void)
+ {
+- __maybe_unused int id_pfr1, ret, macpwr_pin;
++ __maybe_unused int id_pfr1, ret;
+
+ gd->bd->bi_boot_params = (PHYS_SDRAM_0 + 0x100);
+
+@@ -224,15 +224,6 @@ int board_init(void)
+ if (ret)
+ return ret;
+
+- /* strcmp() would look better, but doesn't get optimised away. */
+- if (CONFIG_MACPWR[0]) {
+- macpwr_pin = sunxi_name_to_gpio(CONFIG_MACPWR);
+- if (macpwr_pin >= 0) {
+- gpio_request(macpwr_pin, "macpwr");
+- gpio_direction_output(macpwr_pin, 1);
+- }
+- }
+-
+ #if CONFIG_IS_ENABLED(DM_I2C)
+ /*
+ * Temporary workaround for enabling I2C clocks until proper sunxi DM
+@@ -240,7 +231,6 @@ int board_init(void)
+ */
+ i2c_init_board();
+ #endif
+-
+ eth_init_board();
+
+ return 0;
+diff --git a/configs/Bananapi_M2_Ultra_defconfig b/configs/Bananapi_M2_Ultra_defconfig
+index a5fe76af56..2cc7bbbd8b 100644
+--- a/configs/Bananapi_M2_Ultra_defconfig
++++ b/configs/Bananapi_M2_Ultra_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun8i-r40-bananapi-m2-ultra"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN8I_R40=y
+ CONFIG_DRAM_CLK=576
+-CONFIG_MACPWR="PA17"
+ CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+ CONFIG_USB1_VBUS_PIN="PH23"
+ CONFIG_USB2_VBUS_PIN="PH23"
+diff --git a/configs/Bananapi_defconfig b/configs/Bananapi_defconfig
+index 6c2a1f630e..f4910ba13a 100644
+--- a/configs/Bananapi_defconfig
++++ b/configs/Bananapi_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun7i-a20-bananapi"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN7I=y
+ CONFIG_DRAM_CLK=432
+-CONFIG_MACPWR="PH23"
+ CONFIG_VIDEO_COMPOSITE=y
+ CONFIG_GMAC_TX_DELAY=3
+ CONFIG_AHCI=y
+diff --git a/configs/Bananapro_defconfig b/configs/Bananapro_defconfig
+index 94fd74754e..02be8971df 100644
+--- a/configs/Bananapro_defconfig
++++ b/configs/Bananapro_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun7i-a20-bananapro"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN7I=y
+ CONFIG_DRAM_CLK=432
+-CONFIG_MACPWR="PH23"
+ CONFIG_USB1_VBUS_PIN="PH0"
+ CONFIG_USB2_VBUS_PIN="PH1"
+ CONFIG_VIDEO_COMPOSITE=y
+diff --git a/configs/Lamobo_R1_defconfig b/configs/Lamobo_R1_defconfig
+index 9639cb6aad..66f57ab3c8 100644
+--- a/configs/Lamobo_R1_defconfig
++++ b/configs/Lamobo_R1_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun7i-a20-lamobo-r1"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN7I=y
+ CONFIG_DRAM_CLK=432
+-CONFIG_MACPWR="PH23"
+ CONFIG_GMAC_TX_DELAY=4
+ CONFIG_AHCI=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+diff --git a/configs/Mele_A1000_defconfig b/configs/Mele_A1000_defconfig
+index f5b6d908cd..9ac2e4839d 100644
+--- a/configs/Mele_A1000_defconfig
++++ b/configs/Mele_A1000_defconfig
+@@ -3,7 +3,6 @@ CONFIG_ARCH_SUNXI=y
+ CONFIG_DEFAULT_DEVICE_TREE="sun4i-a10-a1000"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN4I=y
+-CONFIG_MACPWR="PH15"
+ CONFIG_VIDEO_VGA=y
+ CONFIG_VIDEO_COMPOSITE=y
+ CONFIG_AHCI=y
+diff --git a/configs/Orangepi_defconfig b/configs/Orangepi_defconfig
+index c89a9a1f9d..53edf525ec 100644
+--- a/configs/Orangepi_defconfig
++++ b/configs/Orangepi_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun7i-a20-orangepi"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN7I=y
+ CONFIG_DRAM_CLK=432
+-CONFIG_MACPWR="PH23"
+ CONFIG_USB1_VBUS_PIN="PH26"
+ CONFIG_USB2_VBUS_PIN="PH22"
+ CONFIG_VIDEO_VGA=y
+diff --git a/configs/Orangepi_mini_defconfig b/configs/Orangepi_mini_defconfig
+index fe9ce808a1..ccf3267017 100644
+--- a/configs/Orangepi_mini_defconfig
++++ b/configs/Orangepi_mini_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun7i-a20-orangepi-mini"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN7I=y
+ CONFIG_DRAM_CLK=432
+-CONFIG_MACPWR="PH23"
+ CONFIG_MMC_SUNXI_SLOT_EXTRA=3
+ CONFIG_USB1_VBUS_PIN="PH26"
+ CONFIG_USB2_VBUS_PIN="PH22"
+diff --git a/configs/bananapi_m1_plus_defconfig b/configs/bananapi_m1_plus_defconfig
+index 0fbb619d62..a432a01f6b 100644
+--- a/configs/bananapi_m1_plus_defconfig
++++ b/configs/bananapi_m1_plus_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun7i-a20-bananapi-m1-plus"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN7I=y
+ CONFIG_DRAM_CLK=432
+-CONFIG_MACPWR="PH23"
+ CONFIG_VIDEO_COMPOSITE=y
+ CONFIG_GMAC_TX_DELAY=3
+ CONFIG_AHCI=y
+diff --git a/configs/bananapi_m2_plus_h3_defconfig b/configs/bananapi_m2_plus_h3_defconfig
+index 26ced59fb0..a8f9b5044b 100644
+--- a/configs/bananapi_m2_plus_h3_defconfig
++++ b/configs/bananapi_m2_plus_h3_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun8i-h3-bananapi-m2-plus"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN8I_H3=y
+ CONFIG_DRAM_CLK=672
+-CONFIG_MACPWR="PD6"
+ CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SUN8I_EMAC=y
+diff --git a/configs/bananapi_m2_plus_h5_defconfig b/configs/bananapi_m2_plus_h5_defconfig
+index fb6c945919..1634f62619 100644
+--- a/configs/bananapi_m2_plus_h5_defconfig
++++ b/configs/bananapi_m2_plus_h5_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun50i-h5-bananapi-m2-plus"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN50I_H5=y
+ CONFIG_DRAM_CLK=672
+-CONFIG_MACPWR="PD6"
+ CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SUN8I_EMAC=y
+diff --git a/configs/i12-tvbox_defconfig b/configs/i12-tvbox_defconfig
+index 257dd89af4..37f0f53ae7 100644
+--- a/configs/i12-tvbox_defconfig
++++ b/configs/i12-tvbox_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun7i-a20-i12-tvbox"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN7I=y
+ CONFIG_DRAM_CLK=384
+-CONFIG_MACPWR="PH21"
+ CONFIG_VIDEO_COMPOSITE=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SPL_I2C=y
+diff --git a/configs/jesurun_q5_defconfig b/configs/jesurun_q5_defconfig
+index 0ff666b2ee..c99be7cea4 100644
+--- a/configs/jesurun_q5_defconfig
++++ b/configs/jesurun_q5_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun4i-a10-jesurun-q5"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN4I=y
+ CONFIG_DRAM_CLK=312
+-CONFIG_MACPWR="PH19"
+ CONFIG_USB0_VBUS_PIN="PB9"
+ CONFIG_VIDEO_COMPOSITE=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+diff --git a/configs/mixtile_loftq_defconfig b/configs/mixtile_loftq_defconfig
+index 0e4cdc4467..2f92228eb7 100644
+--- a/configs/mixtile_loftq_defconfig
++++ b/configs/mixtile_loftq_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun6i-a31-mixtile-loftq"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN6I=y
+ CONFIG_DRAM_ZQ=251
+-CONFIG_MACPWR="PA21"
+ CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+ CONFIG_USB1_VBUS_PIN="PH24"
+ CONFIG_USB2_VBUS_PIN=""
+diff --git a/configs/nanopi_m1_plus_defconfig b/configs/nanopi_m1_plus_defconfig
+index 76655d79ae..078e98b644 100644
+--- a/configs/nanopi_m1_plus_defconfig
++++ b/configs/nanopi_m1_plus_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun8i-h3-nanopi-m1-plus"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN8I_H3=y
+ CONFIG_DRAM_CLK=408
+-CONFIG_MACPWR="PD6"
+ CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SUN8I_EMAC=y
+diff --git a/configs/nanopi_neo_plus2_defconfig b/configs/nanopi_neo_plus2_defconfig
+index 924ff38f17..85ff31c6fe 100644
+--- a/configs/nanopi_neo_plus2_defconfig
++++ b/configs/nanopi_neo_plus2_defconfig
+@@ -6,7 +6,6 @@ CONFIG_MACH_SUN50I_H5=y
+ CONFIG_DRAM_CLK=408
+ CONFIG_DRAM_ZQ=3881977
+ # CONFIG_DRAM_ODT_EN is not set
+-CONFIG_MACPWR="PD6"
+ CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SUN8I_EMAC=y
+diff --git a/configs/nanopi_r1s_h5_defconfig b/configs/nanopi_r1s_h5_defconfig
+index 27cf172d72..2a6f94afe4 100644
+--- a/configs/nanopi_r1s_h5_defconfig
++++ b/configs/nanopi_r1s_h5_defconfig
+@@ -6,7 +6,6 @@ CONFIG_MACH_SUN50I_H5=y
+ CONFIG_DRAM_CLK=672
+ CONFIG_DRAM_ZQ=3881977
+ # CONFIG_DRAM_ODT_EN is not set
+-CONFIG_MACPWR="PD6"
+ CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SUN8I_EMAC=y
+diff --git a/configs/orangepi_pc2_defconfig b/configs/orangepi_pc2_defconfig
+index 777af8c60e..fb6fbaf787 100644
+--- a/configs/orangepi_pc2_defconfig
++++ b/configs/orangepi_pc2_defconfig
+@@ -5,7 +5,6 @@ CONFIG_SPL=y
+ CONFIG_MACH_SUN50I_H5=y
+ CONFIG_DRAM_CLK=672
+ CONFIG_DRAM_ZQ=3881977
+-CONFIG_MACPWR="PD6"
+ CONFIG_SPL_SPI_SUNXI=y
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SPL_I2C=y
+diff --git a/configs/orangepi_plus2e_defconfig b/configs/orangepi_plus2e_defconfig
+index 138a6a72b8..5e2cbc48ea 100644
+--- a/configs/orangepi_plus2e_defconfig
++++ b/configs/orangepi_plus2e_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun8i-h3-orangepi-plus2e"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN8I_H3=y
+ CONFIG_DRAM_CLK=672
+-CONFIG_MACPWR="PD6"
+ CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_SPL_I2C=y
+diff --git a/configs/orangepi_plus_defconfig b/configs/orangepi_plus_defconfig
+index ed585881d4..092ce77a6c 100644
+--- a/configs/orangepi_plus_defconfig
++++ b/configs/orangepi_plus_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun8i-h3-orangepi-plus"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN8I_H3=y
+ CONFIG_DRAM_CLK=672
+-CONFIG_MACPWR="PD6"
+ CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+ CONFIG_USB1_VBUS_PIN="PG13"
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+diff --git a/configs/pine_h64_defconfig b/configs/pine_h64_defconfig
+index 6dac6098d0..4712b8e469 100644
+--- a/configs/pine_h64_defconfig
++++ b/configs/pine_h64_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun50i-h6-pine-h64"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN50I_H6=y
+ CONFIG_SUNXI_DRAM_H6_LPDDR3=y
+-CONFIG_MACPWR="PC16"
+ CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+ CONFIG_USB3_VBUS_PIN="PL5"
+ CONFIG_SPL_SPI_SUNXI=y
+diff --git a/configs/zeropi_defconfig b/configs/zeropi_defconfig
+index 11f3715e6d..7901bffd15 100644
+--- a/configs/zeropi_defconfig
++++ b/configs/zeropi_defconfig
+@@ -4,7 +4,6 @@ CONFIG_DEFAULT_DEVICE_TREE="sun8i-h3-zeropi"
+ CONFIG_SPL=y
+ CONFIG_MACH_SUN8I_H3=y
+ CONFIG_DRAM_CLK=408
+-CONFIG_MACPWR="PD6"
+ # CONFIG_VIDEO_DE2 is not set
+ # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+ CONFIG_CONSOLE_MUX=y
+diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c
+index 04c3274fbe..7b60a60ad5 100644
+--- a/drivers/net/sun8i_emac.c
++++ b/drivers/net/sun8i_emac.c
+@@ -29,6 +29,7 @@
+ #include <net.h>
+ #include <reset.h>
+ #include <wait_bit.h>
++#include <power/regulator.h>
+
+ #define MDIO_CMD_MII_BUSY BIT(0)
+ #define MDIO_CMD_MII_WRITE BIT(1)
+@@ -167,9 +168,8 @@ struct emac_eth_dev {
+ struct clk ephy_clk;
+ struct reset_ctl tx_rst;
+ struct reset_ctl ephy_rst;
+-#if CONFIG_IS_ENABLED(DM_GPIO)
+ struct gpio_desc reset_gpio;
+-#endif
++ struct udevice *phy_reg;
+ };
+
+
+@@ -720,6 +720,9 @@ static int sun8i_emac_eth_probe(struct udevice *dev)
+
+ sun8i_emac_set_syscon(sun8i_pdata, priv);
+
++ if (priv->phy_reg)
++ regulator_set_enable(priv->phy_reg, true);
++
+ sun8i_mdio_init(dev->name, dev);
+ priv->bus = miiphy_get_dev_by_name(dev->name);
+
+@@ -829,6 +832,8 @@ static int sun8i_emac_eth_of_to_plat(struct udevice *dev)
+
+ priv->sysctl_reg = (void *)syscon_base + priv->variant->syscon_offset;
+
++ device_get_supply_regulator(dev, "phy-supply", &priv->phy_reg);
++
+ pdata->phy_interface = -1;
+ priv->phyaddr = -1;
+ priv->use_internal_phy = false;
+--
+2.20.1
+
--- /dev/null
+From 23bc60ec0f0a66e5afca0dbc71b42fc4d0216153 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 21 Jul 2023 14:45:49 +0100
+Subject: [PATCH 4009/4052] pinctrl: sunxi: add GPIO in/out wrappers
+
+So far we were open-coding the pincontroller's GPIO output/input access
+in each function using that.
+
+Provide functions that wrap that nicely, and follow the existing pattern
+(set/get_{bank,}), so users don't need to know about the internals, and
+we can abstract the new D1 pinctrl more easily.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+Reviewed-by: Sam Edwards <CFSworks@gmail.com>
+Tested-by: Sam Edwards <CFSworks@gmail.com>
+---
+ arch/arm/include/asm/arch-sunxi/gpio.h | 4 +++
+ arch/arm/mach-sunxi/pinmux.c | 28 +++++++++++++++
+ drivers/gpio/sunxi_gpio.c | 49 +++++---------------------
+ 3 files changed, 40 insertions(+), 41 deletions(-)
+
+diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h b/arch/arm/include/asm/arch-sunxi/gpio.h
+index 437e86479c..2e7c84e410 100644
+--- a/arch/arm/include/asm/arch-sunxi/gpio.h
++++ b/arch/arm/include/asm/arch-sunxi/gpio.h
+@@ -222,6 +222,10 @@ 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);
+ int sunxi_gpio_get_cfgpin(u32 pin);
++void sunxi_gpio_set_output_bank(struct sunxi_gpio *pio, int pin, bool set);
++void sunxi_gpio_set_output(u32 pin, bool set);
++int sunxi_gpio_get_output_bank(struct sunxi_gpio *pio, int pin);
++int sunxi_gpio_get_output(u32 pin);
+ void sunxi_gpio_set_drv(u32 pin, u32 val);
+ void sunxi_gpio_set_drv_bank(struct sunxi_gpio *pio, u32 bank_offset, u32 val);
+ void sunxi_gpio_set_pull(u32 pin, u32 val);
+diff --git a/arch/arm/mach-sunxi/pinmux.c b/arch/arm/mach-sunxi/pinmux.c
+index c95fcee9f6..751cac8e09 100644
+--- a/arch/arm/mach-sunxi/pinmux.c
++++ b/arch/arm/mach-sunxi/pinmux.c
+@@ -45,6 +45,34 @@ int sunxi_gpio_get_cfgpin(u32 pin)
+ return sunxi_gpio_get_cfgbank(pio, pin);
+ }
+
++void sunxi_gpio_set_output_bank(struct sunxi_gpio *pio, int pin, bool set)
++{
++ u32 mask = 1U << GPIO_NUM(pin);
++
++ clrsetbits_le32(&pio->dat, set ? 0 : mask, set ? mask : 0);
++}
++
++void sunxi_gpio_set_output(u32 pin, bool set)
++{
++ u32 bank = GPIO_BANK(pin);
++ struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
++
++ sunxi_gpio_set_output_bank(pio, pin, set);
++}
++
++int sunxi_gpio_get_output_bank(struct sunxi_gpio *pio, int pin)
++{
++ return !!(readl(&pio->dat) & (1U << GPIO_NUM(pin)));
++}
++
++int sunxi_gpio_get_output(u32 pin)
++{
++ u32 bank = GPIO_BANK(pin);
++ struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
++
++ return sunxi_gpio_get_output_bank(pio, pin);
++}
++
+ void sunxi_gpio_set_drv(u32 pin, u32 val)
+ {
+ u32 bank = GPIO_BANK(pin);
+diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c
+index 1e85db179a..6796375d35 100644
+--- a/drivers/gpio/sunxi_gpio.c
++++ b/drivers/gpio/sunxi_gpio.c
+@@ -19,37 +19,6 @@
+ #include <dt-bindings/gpio/gpio.h>
+
+ #if !CONFIG_IS_ENABLED(DM_GPIO)
+-static int sunxi_gpio_output(u32 pin, u32 val)
+-{
+- u32 dat;
+- u32 bank = GPIO_BANK(pin);
+- u32 num = GPIO_NUM(pin);
+- struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
+-
+- dat = readl(&pio->dat);
+- if (val)
+- dat |= 0x1 << num;
+- else
+- dat &= ~(0x1 << num);
+-
+- writel(dat, &pio->dat);
+-
+- return 0;
+-}
+-
+-static int sunxi_gpio_input(u32 pin)
+-{
+- u32 dat;
+- u32 bank = GPIO_BANK(pin);
+- u32 num = GPIO_NUM(pin);
+- struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
+-
+- dat = readl(&pio->dat);
+- dat >>= num;
+-
+- return dat & 0x1;
+-}
+-
+ int gpio_request(unsigned gpio, const char *label)
+ {
+ return 0;
+@@ -70,18 +39,21 @@ int gpio_direction_input(unsigned gpio)
+ int gpio_direction_output(unsigned gpio, int value)
+ {
+ sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT);
++ sunxi_gpio_set_output(gpio, value);
+
+- return sunxi_gpio_output(gpio, value);
++ return 0;
+ }
+
+ int gpio_get_value(unsigned gpio)
+ {
+- return sunxi_gpio_input(gpio);
++ return sunxi_gpio_get_output(gpio);
+ }
+
+ int gpio_set_value(unsigned gpio, int value)
+ {
+- return sunxi_gpio_output(gpio, value);
++ sunxi_gpio_set_output(gpio, value);
++
++ return 0;
+ }
+
+ int sunxi_name_to_gpio(const char *name)
+@@ -135,13 +107,8 @@ int sunxi_name_to_gpio(const char *name)
+ static int sunxi_gpio_get_value(struct udevice *dev, unsigned offset)
+ {
+ struct sunxi_gpio_plat *plat = dev_get_plat(dev);
+- u32 num = GPIO_NUM(offset);
+- unsigned dat;
+-
+- dat = readl(&plat->regs->dat);
+- dat >>= num;
+
+- return dat & 0x1;
++ return sunxi_gpio_get_output_bank(plat->regs, offset) & 0x1;
+ }
+
+ static int sunxi_gpio_get_function(struct udevice *dev, unsigned offset)
+@@ -181,7 +148,7 @@ static int sunxi_gpio_set_flags(struct udevice *dev, unsigned int offset,
+ u32 value = !!(flags & GPIOD_IS_OUT_ACTIVE);
+ u32 num = GPIO_NUM(offset);
+
+- clrsetbits_le32(&plat->regs->dat, 1 << num, value << num);
++ sunxi_gpio_set_output_bank(plat->regs, num, value);
+ sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_OUTPUT);
+ } else if (flags & GPIOD_IS_IN) {
+ u32 pull = 0;
+--
+2.20.1
+
--- /dev/null
+From 8e8e3f442213d583fed0d9c7666de7a6c05110a2 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 21 Jul 2023 14:45:50 +0100
+Subject: [PATCH 4010/4052] pinctrl: sunxi: remove struct sunxi_gpio
+
+So far every Allwinner SoC used the same basic pincontroller/GPIO
+register frame, and just differed by the number of implemented banks and
+pins, plus some special functionality from time to time. However the D1
+and successors use a slightly different pinctrl register layout.
+Use that opportunity to drop "struct sunxi_gpio", that described that
+MMIO frame in a C struct. That approach is somewhat frowned upon in the
+Linux world and rarely used there, though still popular with U-Boot.
+
+Switching from a C struct to a "base address plus offset" approach allows
+to switch between the two models more dynamically, without reverting to
+preprocessor macros and #ifdef's.
+
+Model the pinctrl MMIO register frame in the usual "base address +
+offset" way, and replace a hard-to-parse CPP macro with a more readable
+static function.
+All the users get converted over. There are no functional changes at
+this point, it just prepares the stages for the D1 and friends.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+---
+ arch/arm/include/asm/arch-sunxi/gpio.h | 67 +++++++++++---------------
+ arch/arm/mach-sunxi/pinmux.c | 66 +++++++++++++------------
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 14 +++---
+ 3 files changed, 71 insertions(+), 76 deletions(-)
+
+diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h b/arch/arm/include/asm/arch-sunxi/gpio.h
+index 2e7c84e410..96a29aa023 100644
+--- a/arch/arm/include/asm/arch-sunxi/gpio.h
++++ b/arch/arm/include/asm/arch-sunxi/gpio.h
+@@ -28,13 +28,6 @@
+ #define SUNXI_GPIO_H 7
+ #define SUNXI_GPIO_I 8
+
+-/*
+- * This defines the number of GPIO banks for the _main_ GPIO controller.
+- * You should fix up the padding in struct sunxi_gpio_reg below if you
+- * change this.
+- */
+-#define SUNXI_GPIO_BANKS 9
+-
+ /*
+ * sun6i/sun8i and later SoCs have an additional GPIO controller (R_PIO)
+ * at a different register offset.
+@@ -52,46 +45,42 @@
+ #define SUNXI_GPIO_M 12
+ #define SUNXI_GPIO_N 13
+
+-struct sunxi_gpio {
+- u32 cfg[4];
+- u32 dat;
+- u32 drv[2];
+- u32 pull[2];
+-};
+-
+-/* gpio interrupt control */
+-struct sunxi_gpio_int {
+- u32 cfg[3];
+- u32 ctl;
+- u32 sta;
+- u32 deb; /* interrupt debounce */
+-};
+-
+-struct sunxi_gpio_reg {
+- struct sunxi_gpio gpio_bank[SUNXI_GPIO_BANKS];
+- u8 res[0xbc];
+- struct sunxi_gpio_int gpio_int;
+-};
+-
+ #define SUN50I_H6_GPIO_POW_MOD_SEL 0x340
+ #define SUN50I_H6_GPIO_POW_MOD_VAL 0x348
+
+-#define BANK_TO_GPIO(bank) (((bank) < SUNXI_GPIO_L) ? \
+- &((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank] : \
+- &((struct sunxi_gpio_reg *)SUNXI_R_PIO_BASE)->gpio_bank[(bank) - SUNXI_GPIO_L])
+-
+ #define GPIO_BANK(pin) ((pin) >> 5)
+ #define GPIO_NUM(pin) ((pin) & 0x1f)
+
++#define GPIO_CFG_REG_OFFSET 0x00
+ #define GPIO_CFG_INDEX(pin) (((pin) & 0x1f) >> 3)
+ #define GPIO_CFG_OFFSET(pin) ((((pin) & 0x1f) & 0x7) << 2)
+
++#define GPIO_DAT_REG_OFFSET 0x10
++
++#define GPIO_DRV_REG_OFFSET 0x14
+ #define GPIO_DRV_INDEX(pin) (((pin) & 0x1f) >> 4)
+ #define GPIO_DRV_OFFSET(pin) ((((pin) & 0x1f) & 0xf) << 1)
+
++#define GPIO_PULL_REG_OFFSET 0x1c
+ #define GPIO_PULL_INDEX(pin) (((pin) & 0x1f) >> 4)
+ #define GPIO_PULL_OFFSET(pin) ((((pin) & 0x1f) & 0xf) << 1)
+
++#define SUNXI_PINCTRL_BANK_SIZE 0x24
++
++static inline void* BANK_TO_GPIO(int bank)
++{
++ void *pio_base;
++
++ if (bank < SUNXI_GPIO_L) {
++ pio_base = (void *)(uintptr_t)SUNXI_PIO_BASE;
++ } else {
++ pio_base = (void *)(uintptr_t)SUNXI_R_PIO_BASE;
++ bank -= SUNXI_GPIO_L;
++ }
++
++ return pio_base + bank * SUNXI_PINCTRL_BANK_SIZE;
++}
++
+ /* GPIO bank sizes */
+ #define SUNXI_GPIOS_PER_BANK 32
+
+@@ -214,22 +203,22 @@ enum sunxi_gpio_number {
+ #define SUNXI_GPIO_AXP0_GPIO_COUNT 6
+
+ struct sunxi_gpio_plat {
+- struct sunxi_gpio *regs;
++ void *regs;
+ char bank_name[3];
+ };
+
+-void sunxi_gpio_set_cfgbank(struct sunxi_gpio *pio, int bank_offset, u32 val);
++void sunxi_gpio_set_cfgbank(void *bank_base, int pin_offset, u32 val);
+ void sunxi_gpio_set_cfgpin(u32 pin, u32 val);
+-int sunxi_gpio_get_cfgbank(struct sunxi_gpio *pio, int bank_offset);
++int sunxi_gpio_get_cfgbank(void *bank_base, int pin_offset);
+ int sunxi_gpio_get_cfgpin(u32 pin);
+-void sunxi_gpio_set_output_bank(struct sunxi_gpio *pio, int pin, bool set);
++void sunxi_gpio_set_output_bank(void *bank_base, int pin, bool set);
+ void sunxi_gpio_set_output(u32 pin, bool set);
+-int sunxi_gpio_get_output_bank(struct sunxi_gpio *pio, int pin);
++int sunxi_gpio_get_output_bank(void *bank_base, int pin);
+ int sunxi_gpio_get_output(u32 pin);
+ void sunxi_gpio_set_drv(u32 pin, u32 val);
+-void sunxi_gpio_set_drv_bank(struct sunxi_gpio *pio, u32 bank_offset, u32 val);
++void sunxi_gpio_set_drv_bank(void *bank_base, u32 pin_offset, u32 val);
+ void sunxi_gpio_set_pull(u32 pin, u32 val);
+-void sunxi_gpio_set_pull_bank(struct sunxi_gpio *pio, int bank_offset, u32 val);
++void sunxi_gpio_set_pull_bank(void *bank_base, int pin_offset, u32 val);
+ int sunxi_name_to_gpio(const char *name);
+
+ #if !defined CONFIG_SPL_BUILD && defined CONFIG_AXP_GPIO
+diff --git a/arch/arm/mach-sunxi/pinmux.c b/arch/arm/mach-sunxi/pinmux.c
+index 751cac8e09..17d1a7bdb9 100644
+--- a/arch/arm/mach-sunxi/pinmux.c
++++ b/arch/arm/mach-sunxi/pinmux.c
+@@ -9,29 +9,30 @@
+ #include <asm/io.h>
+ #include <asm/arch/gpio.h>
+
+-void sunxi_gpio_set_cfgbank(struct sunxi_gpio *pio, int bank_offset, u32 val)
++void sunxi_gpio_set_cfgbank(void *bank_base, int pin_offset, u32 val)
+ {
+- u32 index = GPIO_CFG_INDEX(bank_offset);
+- u32 offset = GPIO_CFG_OFFSET(bank_offset);
++ u32 index = GPIO_CFG_INDEX(pin_offset);
++ u32 offset = GPIO_CFG_OFFSET(pin_offset);
+
+- clrsetbits_le32(&pio->cfg[index], 0xf << offset, val << offset);
++ clrsetbits_le32(bank_base + GPIO_CFG_REG_OFFSET + index * 4,
++ 0xfU << offset, val << offset);
+ }
+
+ void sunxi_gpio_set_cfgpin(u32 pin, u32 val)
+ {
+ u32 bank = GPIO_BANK(pin);
+- struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
++ void *pio = BANK_TO_GPIO(bank);
+
+- sunxi_gpio_set_cfgbank(pio, pin, val);
++ sunxi_gpio_set_cfgbank(pio, pin % 32, val);
+ }
+
+-int sunxi_gpio_get_cfgbank(struct sunxi_gpio *pio, int bank_offset)
++int sunxi_gpio_get_cfgbank(void *bank_base, int pin_offset)
+ {
+- u32 index = GPIO_CFG_INDEX(bank_offset);
+- u32 offset = GPIO_CFG_OFFSET(bank_offset);
++ u32 index = GPIO_CFG_INDEX(pin_offset);
++ u32 offset = GPIO_CFG_OFFSET(pin_offset);
+ u32 cfg;
+
+- cfg = readl(&pio->cfg[index]);
++ cfg = readl(bank_base + GPIO_CFG_REG_OFFSET + index * 4);
+ cfg >>= offset;
+
+ return cfg & 0xf;
+@@ -40,35 +41,38 @@ int sunxi_gpio_get_cfgbank(struct sunxi_gpio *pio, int bank_offset)
+ int sunxi_gpio_get_cfgpin(u32 pin)
+ {
+ u32 bank = GPIO_BANK(pin);
+- struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
++ void *bank_base = BANK_TO_GPIO(bank);
+
+- return sunxi_gpio_get_cfgbank(pio, pin);
++ return sunxi_gpio_get_cfgbank(bank_base, pin % 32);
+ }
+
+-void sunxi_gpio_set_output_bank(struct sunxi_gpio *pio, int pin, bool set)
++void sunxi_gpio_set_output_bank(void *bank_base, int pin, bool set)
+ {
+ u32 mask = 1U << GPIO_NUM(pin);
+
+- clrsetbits_le32(&pio->dat, set ? 0 : mask, set ? mask : 0);
++ clrsetbits_le32(bank_base + GPIO_DAT_REG_OFFSET,
++ set ? 0 : mask, set ? mask : 0);
+ }
+
+ void sunxi_gpio_set_output(u32 pin, bool set)
+ {
+ u32 bank = GPIO_BANK(pin);
+- struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
++ void *pio = BANK_TO_GPIO(bank);
+
+ sunxi_gpio_set_output_bank(pio, pin, set);
+ }
+
+-int sunxi_gpio_get_output_bank(struct sunxi_gpio *pio, int pin)
++int sunxi_gpio_get_output_bank(void *bank_base, int pin)
+ {
+- return !!(readl(&pio->dat) & (1U << GPIO_NUM(pin)));
++ u32 mask = 1U << GPIO_NUM(pin);
++
++ return !!(readl(bank_base + GPIO_DAT_REG_OFFSET) & mask);
+ }
+
+ int sunxi_gpio_get_output(u32 pin)
+ {
+ u32 bank = GPIO_BANK(pin);
+- struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
++ void *pio = BANK_TO_GPIO(bank);
+
+ return sunxi_gpio_get_output_bank(pio, pin);
+ }
+@@ -76,31 +80,33 @@ int sunxi_gpio_get_output(u32 pin)
+ void sunxi_gpio_set_drv(u32 pin, u32 val)
+ {
+ u32 bank = GPIO_BANK(pin);
+- struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
++ void *bank_base = BANK_TO_GPIO(bank);
+
+- sunxi_gpio_set_drv_bank(pio, pin, val);
++ sunxi_gpio_set_drv_bank(bank_base, pin % 32, val);
+ }
+
+-void sunxi_gpio_set_drv_bank(struct sunxi_gpio *pio, u32 bank_offset, u32 val)
++void sunxi_gpio_set_drv_bank(void *bank_base, u32 pin_offset, u32 val)
+ {
+- u32 index = GPIO_DRV_INDEX(bank_offset);
+- u32 offset = GPIO_DRV_OFFSET(bank_offset);
++ u32 index = GPIO_DRV_INDEX(pin_offset);
++ u32 offset = GPIO_DRV_OFFSET(pin_offset);
+
+- clrsetbits_le32(&pio->drv[index], 0x3 << offset, val << offset);
++ clrsetbits_le32(bank_base + GPIO_DRV_REG_OFFSET + index * 4,
++ 0x3U << offset, val << offset);
+ }
+
+ void sunxi_gpio_set_pull(u32 pin, u32 val)
+ {
+ u32 bank = GPIO_BANK(pin);
+- struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
++ void *bank_base = BANK_TO_GPIO(bank);
+
+- sunxi_gpio_set_pull_bank(pio, pin, val);
++ sunxi_gpio_set_pull_bank(bank_base, pin % 32, val);
+ }
+
+-void sunxi_gpio_set_pull_bank(struct sunxi_gpio *pio, int bank_offset, u32 val)
++void sunxi_gpio_set_pull_bank(void *bank_base, int pin_offset, u32 val)
+ {
+- u32 index = GPIO_PULL_INDEX(bank_offset);
+- u32 offset = GPIO_PULL_OFFSET(bank_offset);
++ u32 index = GPIO_PULL_INDEX(pin_offset);
++ u32 offset = GPIO_PULL_OFFSET(pin_offset);
+
+- clrsetbits_le32(&pio->pull[index], 0x3 << offset, val << offset);
++ clrsetbits_le32(bank_base + GPIO_PULL_REG_OFFSET + index * 4,
++ 0x3U << offset, val << offset);
+ }
+diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+index c4fbda7a92..b0144edcf4 100644
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -35,7 +35,7 @@ struct sunxi_pinctrl_desc {
+ };
+
+ struct sunxi_pinctrl_plat {
+- struct sunxi_gpio __iomem *base;
++ void __iomem *base;
+ };
+
+ static int sunxi_pinctrl_get_pins_count(struct udevice *dev)
+@@ -86,8 +86,8 @@ static int sunxi_pinctrl_pinmux_set(struct udevice *dev, uint 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);
++ sunxi_gpio_set_cfgbank(plat->base + bank * SUNXI_PINCTRL_BANK_SIZE,
++ pin, desc->functions[func_selector].mux);
+
+ return 0;
+ }
+@@ -102,7 +102,7 @@ static const struct pinconf_param sunxi_pinctrl_pinconf_params[] = {
+ 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];
++ void *regs = plat->base + bank * SUNXI_PINCTRL_BANK_SIZE;
+
+ sunxi_gpio_set_pull_bank(regs, pin, bias);
+
+@@ -112,7 +112,7 @@ static int sunxi_pinctrl_pinconf_set_pull(struct sunxi_pinctrl_plat *plat,
+ 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];
++ void *regs = plat->base + bank * SUNXI_PINCTRL_BANK_SIZE;
+
+ if (drive < 10 || drive > 40)
+ return -EINVAL;
+@@ -148,7 +148,7 @@ static int sunxi_pinctrl_get_pin_muxing(struct udevice *dev, uint pin_selector,
+ 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);
++ int mux = sunxi_gpio_get_cfgbank(plat->base + bank * SUNXI_PINCTRL_BANK_SIZE, pin);
+
+ switch (mux) {
+ case SUNXI_GPIO_INPUT:
+@@ -206,7 +206,7 @@ static int sunxi_pinctrl_bind(struct udevice *dev)
+ if (!gpio_plat)
+ return -ENOMEM;
+
+- gpio_plat->regs = plat->base + i;
++ gpio_plat->regs = plat->base + i * SUNXI_PINCTRL_BANK_SIZE;
+ gpio_plat->bank_name[0] = 'P';
+ gpio_plat->bank_name[1] = 'A' + desc->first_bank + i;
+ gpio_plat->bank_name[2] = '\0';
+--
+2.20.1
+
--- /dev/null
+From 18045603ba0d381200f86e2acefe28ce1b5ed071 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 21 Jul 2023 14:45:51 +0100
+Subject: [PATCH 4011/4052] pinctrl: sunxi: move pinctrl code and remove
+ GPIO_EXTRA_HEADER
+
+U-Boot's generic GPIO_EXTRA_HEADER is a convenience symbol to allow code
+to more easily include platform specific GPIO headers. This should not
+be needed in a DM world anymore, since the generic GPIO framework
+handles that nicely.
+For Allwinner boards we still need to deal with non-DM GPIO in the SPL,
+but this should become the exception, not the rule.
+
+Make this more obvious by removing the definition of GPIO_EXTRA_HEADER,
+and just force every legacy user of platform specific GPIO to include
+the new sunxi_gpio.h header explicitly. Everyone doing so should feel
+ashamed and should find a way to avoid it from now on.
+This also moves and renames the existing sunxi-specific low level
+pinctrl routines from arch/arm/mach-sunxi into board/sunxi, and the
+gpio.h header to the generic include/ directory, so the common code can
+be shared outside of arch/arm.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+---
+ arch/arm/Kconfig | 1 -
+ arch/arm/mach-sunxi/Makefile | 1 -
+ arch/arm/mach-sunxi/board.c | 1 +
+ arch/arm/mach-sunxi/dram_suniv.c | 2 +-
+ arch/arm/mach-sunxi/spl_spi_sunxi.c | 1 +
+ board/sunxi/Makefile | 1 +
+ board/sunxi/board.c | 1 +
+ board/sunxi/chip.c | 2 +-
+ arch/arm/mach-sunxi/pinmux.c => board/sunxi/pinctrl.c | 5 ++++-
+ drivers/gpio/axp_gpio.c | 1 +
+ drivers/gpio/sunxi_gpio.c | 1 +
+ drivers/i2c/sun6i_p2wi.c | 2 +-
+ drivers/i2c/sun8i_rsb.c | 2 +-
+ drivers/mmc/sunxi_mmc.c | 1 +
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 1 +
+ drivers/video/hitachi_tx18d42vm_lcd.c | 1 +
+ drivers/video/ssd2828.c | 1 -
+ drivers/video/sunxi/sunxi_display.c | 1 +
+ drivers/video/sunxi/sunxi_lcd.c | 1 +
+ .../include/asm/arch-sunxi/gpio.h => include/sunxi_gpio.h | 0
+ 20 files changed, 19 insertions(+), 8 deletions(-)
+ rename arch/arm/mach-sunxi/pinmux.c => board/sunxi/pinctrl.c (94%)
+ rename arch/arm/include/asm/arch-sunxi/gpio.h => include/sunxi_gpio.h (100%)
+
+diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
+index d5a6d293ce..2904e91ad3 100644
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -1106,7 +1106,6 @@ config ARCH_SUNXI
+ select DM_MMC if MMC
+ select DM_SCSI if SCSI
+ select DM_SERIAL
+- select GPIO_EXTRA_HEADER
+ select OF_BOARD_SETUP
+ select OF_CONTROL
+ select OF_SEPARATE
+diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
+index 58f807cb82..671211e932 100644
+--- a/arch/arm/mach-sunxi/Makefile
++++ b/arch/arm/mach-sunxi/Makefile
+@@ -10,7 +10,6 @@ obj-y += board.o
+ obj-y += clock.o
+ obj-y += cpu_info.o
+ obj-y += dram_helpers.o
+-obj-y += pinmux.o
+ obj-$(CONFIG_SUN6I_PRCM) += prcm.o
+ obj-$(CONFIG_AXP_PMIC_BUS) += pmic_bus.o
+ obj-$(CONFIG_MACH_SUNIV) += clock_sun6i.o
+diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
+index 391a65a549..ec46ab9279 100644
+--- a/arch/arm/mach-sunxi/board.c
++++ b/arch/arm/mach-sunxi/board.c
+@@ -17,6 +17,7 @@
+ #include <i2c.h>
+ #include <serial.h>
+ #include <spl.h>
++#include <sunxi_gpio.h>
+ #include <asm/cache.h>
+ #include <asm/gpio.h>
+ #include <asm/io.h>
+diff --git a/arch/arm/mach-sunxi/dram_suniv.c b/arch/arm/mach-sunxi/dram_suniv.c
+index 3aa3ce7627..9e583e1855 100644
+--- a/arch/arm/mach-sunxi/dram_suniv.c
++++ b/arch/arm/mach-sunxi/dram_suniv.c
+@@ -13,10 +13,10 @@
+ #include <asm/io.h>
+ #include <asm/arch/clock.h>
+ #include <asm/arch/dram.h>
+-#include <asm/arch/gpio.h>
+ #include <linux/bitops.h>
+ #include <linux/delay.h>
+ #include <hang.h>
++#include <sunxi_gpio.h>
+
+ #define SDR_T_CAS (0x2)
+ #define SDR_T_RAS (0x8)
+diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c b/arch/arm/mach-sunxi/spl_spi_sunxi.c
+index 81159cfee6..c2410dd7bb 100644
+--- a/arch/arm/mach-sunxi/spl_spi_sunxi.c
++++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c
+@@ -13,6 +13,7 @@
+ #include <linux/bitops.h>
+ #include <linux/delay.h>
+ #include <linux/libfdt.h>
++#include <sunxi_gpio.h>
+
+ #ifdef CONFIG_SPL_OS_BOOT
+ #error CONFIG_SPL_OS_BOOT is not supported yet
+diff --git a/board/sunxi/Makefile b/board/sunxi/Makefile
+index d96b7897b6..7763b032c8 100644
+--- a/board/sunxi/Makefile
++++ b/board/sunxi/Makefile
+@@ -7,6 +7,7 @@
+ # (C) Copyright 2000-2003
+ # Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ obj-y += board.o
++obj-y += pinctrl.o
+ obj-$(CONFIG_SUN7I_GMAC) += gmac.o
+ obj-$(CONFIG_MACH_SUN4I) += dram_sun4i_auto.o
+ obj-$(CONFIG_MACH_SUN5I) += dram_sun5i_auto.o
+diff --git a/board/sunxi/board.c b/board/sunxi/board.c
+index 9900c66ed0..661137f43c 100644
+--- a/board/sunxi/board.c
++++ b/board/sunxi/board.c
+@@ -38,6 +38,7 @@
+ #include <asm/armv7.h>
+ #endif
+ #include <asm/gpio.h>
++#include <sunxi_gpio.h>
+ #include <asm/io.h>
+ #include <u-boot/crc.h>
+ #include <env_internal.h>
+diff --git a/board/sunxi/chip.c b/board/sunxi/chip.c
+index cde04bebe9..eeee6319e7 100644
+--- a/board/sunxi/chip.c
++++ b/board/sunxi/chip.c
+@@ -12,7 +12,7 @@
+ #include <w1-eeprom.h>
+ #include <dm/device-internal.h>
+
+-#include <asm/arch/gpio.h>
++#include <sunxi_gpio.h>
+
+ #include <extension_board.h>
+
+diff --git a/arch/arm/mach-sunxi/pinmux.c b/board/sunxi/pinctrl.c
+similarity index 94%
+rename from arch/arm/mach-sunxi/pinmux.c
+rename to board/sunxi/pinctrl.c
+index 17d1a7bdb9..494d92c73b 100644
+--- a/arch/arm/mach-sunxi/pinmux.c
++++ b/board/sunxi/pinctrl.c
+@@ -3,11 +3,14 @@
+ * (C) Copyright 2007-2011
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Tom Cubie <tangliang@allwinnertech.com>
++ *
++ * Low level GPIO/pin controller access functions, to be shared by non-DM
++ * SPL code and the DM pinctrl/GPIO drivers.
+ */
+
+ #include <common.h>
+ #include <asm/io.h>
+-#include <asm/arch/gpio.h>
++#include <sunxi_gpio.h>
+
+ void sunxi_gpio_set_cfgbank(void *bank_base, int pin_offset, u32 val)
+ {
+diff --git a/drivers/gpio/axp_gpio.c b/drivers/gpio/axp_gpio.c
+index 35585dc8ac..14a99ce4c9 100644
+--- a/drivers/gpio/axp_gpio.c
++++ b/drivers/gpio/axp_gpio.c
+@@ -14,6 +14,7 @@
+ #include <dm/lists.h>
+ #include <dm/root.h>
+ #include <errno.h>
++#include <sunxi_gpio.h>
+
+ static int axp_gpio_set_value(struct udevice *dev, unsigned pin, int val);
+
+diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c
+index 6796375d35..921cf43b25 100644
+--- a/drivers/gpio/sunxi_gpio.c
++++ b/drivers/gpio/sunxi_gpio.c
+@@ -17,6 +17,7 @@
+ #include <asm/io.h>
+ #include <asm/gpio.h>
+ #include <dt-bindings/gpio/gpio.h>
++#include <sunxi_gpio.h>
+
+ #if !CONFIG_IS_ENABLED(DM_GPIO)
+ int gpio_request(unsigned gpio, const char *label)
+diff --git a/drivers/i2c/sun6i_p2wi.c b/drivers/i2c/sun6i_p2wi.c
+index d221323295..b8e07a533c 100644
+--- a/drivers/i2c/sun6i_p2wi.c
++++ b/drivers/i2c/sun6i_p2wi.c
+@@ -20,10 +20,10 @@
+ #include <errno.h>
+ #include <i2c.h>
+ #include <reset.h>
++#include <sunxi_gpio.h>
+ #include <time.h>
+ #include <asm/io.h>
+ #include <asm/arch/cpu.h>
+-#include <asm/arch/gpio.h>
+ #include <asm/arch/p2wi.h>
+ #include <asm/arch/prcm.h>
+ #include <asm/arch/sys_proto.h>
+diff --git a/drivers/i2c/sun8i_rsb.c b/drivers/i2c/sun8i_rsb.c
+index 47fa05b6d1..f36f2c7afa 100644
+--- a/drivers/i2c/sun8i_rsb.c
++++ b/drivers/i2c/sun8i_rsb.c
+@@ -14,10 +14,10 @@
+ #include <dm.h>
+ #include <errno.h>
+ #include <i2c.h>
++#include <sunxi_gpio.h>
+ #include <reset.h>
+ #include <time.h>
+ #include <asm/arch/cpu.h>
+-#include <asm/arch/gpio.h>
+ #include <asm/arch/prcm.h>
+ #include <asm/arch/rsb.h>
+
+diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c
+index 23bc7da917..03e33753fc 100644
+--- a/drivers/mmc/sunxi_mmc.c
++++ b/drivers/mmc/sunxi_mmc.c
+@@ -27,6 +27,7 @@
+ #include <asm/arch/cpu.h>
+ #include <asm/arch/mmc.h>
+ #include <linux/delay.h>
++#include <sunxi_gpio.h>
+
+ #ifndef CCM_MMC_CTRL_MODE_SEL_NEW
+ #define CCM_MMC_CTRL_MODE_SEL_NEW 0
+diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+index b0144edcf4..4c52e3fa74 100644
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -7,6 +7,7 @@
+ #include <dm/pinctrl.h>
+ #include <errno.h>
+ #include <malloc.h>
++#include <sunxi_gpio.h>
+
+ #include <asm/gpio.h>
+
+diff --git a/drivers/video/hitachi_tx18d42vm_lcd.c b/drivers/video/hitachi_tx18d42vm_lcd.c
+index 87c4d27438..95984fe3d3 100644
+--- a/drivers/video/hitachi_tx18d42vm_lcd.c
++++ b/drivers/video/hitachi_tx18d42vm_lcd.c
+@@ -10,6 +10,7 @@
+ #include <linux/delay.h>
+
+ #include <asm/gpio.h>
++#include <sunxi_gpio.h>
+ #include <errno.h>
+
+ /*
+diff --git a/drivers/video/ssd2828.c b/drivers/video/ssd2828.c
+index 4cdcbe7755..948f5e74d0 100644
+--- a/drivers/video/ssd2828.c
++++ b/drivers/video/ssd2828.c
+@@ -12,7 +12,6 @@
+ #include <common.h>
+ #include <malloc.h>
+ #include <mipi_display.h>
+-#include <asm/arch/gpio.h>
+ #include <asm/gpio.h>
+ #include <linux/delay.h>
+
+diff --git a/drivers/video/sunxi/sunxi_display.c b/drivers/video/sunxi/sunxi_display.c
+index 9110a48482..8da44a1bb6 100644
+--- a/drivers/video/sunxi/sunxi_display.c
++++ b/drivers/video/sunxi/sunxi_display.c
+@@ -31,6 +31,7 @@
+ #include <malloc.h>
+ #include <video.h>
+ #include <dm/uclass-internal.h>
++#include <sunxi_gpio.h>
+ #include "../videomodes.h"
+ #include "../anx9804.h"
+ #include "../hitachi_tx18d42vm_lcd.h"
+diff --git a/drivers/video/sunxi/sunxi_lcd.c b/drivers/video/sunxi/sunxi_lcd.c
+index 8b9c3b2bfa..7a01cc343c 100644
+--- a/drivers/video/sunxi/sunxi_lcd.c
++++ b/drivers/video/sunxi/sunxi_lcd.c
+@@ -17,6 +17,7 @@
+ #include <asm/arch/lcdc.h>
+ #include <asm/global_data.h>
+ #include <asm/gpio.h>
++#include <sunxi_gpio.h>
+
+ struct sunxi_lcd_priv {
+ struct display_timing timing;
+diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h b/include/sunxi_gpio.h
+similarity index 100%
+rename from arch/arm/include/asm/arch-sunxi/gpio.h
+rename to include/sunxi_gpio.h
+--
+2.20.1
+
--- /dev/null
+From c983afccce5c1290dff892ec09b8d0cafef936d4 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 21 Jul 2023 14:45:52 +0100
+Subject: [PATCH 4012/4052] pinctrl: sunxi: move PIO_BASE into sunxi_gpio.h
+
+On the Allwinner platform we were describing a quite comprehensive
+memory map in a per-SoC header unser arch/arm.
+In the old days that was used by every driver, but nowadays it should
+only be needed by SPL drivers (not using the DT). Many addresses in
+there were never used, and some are not needed anymore.
+
+To avoid a dependency on CPU specific headers in an arch specific
+directory, move the definition of the pinctroller MMIO base address into
+the sunxi_gpio.h header, because the SPL routines for GPIO should be the
+only one needing this address.
+This is a first step towards getting rid of cpu_sun[x]i.h completely,
+and allows to remove the inclusion of that file from the sunxi_gpio.h
+header.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+---
+ arch/arm/include/asm/arch-sunxi/cpu_sun4i.h | 2 --
+ arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h | 2 --
+ arch/arm/include/asm/arch-sunxi/cpu_sun9i.h | 2 --
+ include/sunxi_gpio.h | 12 +++++++++++-
+ 4 files changed, 11 insertions(+), 7 deletions(-)
+
+diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
+index f7ecc790db..d6fe51f24b 100644
+--- a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
++++ b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
+@@ -91,7 +91,6 @@
+
+ #define SUNXI_CCM_BASE 0x01c20000
+ #define SUNXI_INTC_BASE 0x01c20400
+-#define SUNXI_PIO_BASE 0x01c20800
+ #define SUNXI_TIMER_BASE 0x01c20c00
+ #ifndef CONFIG_SUNXI_GEN_SUN6I
+ #define SUNXI_PWM_BASE 0x01c20e00
+@@ -210,7 +209,6 @@ defined(CONFIG_MACH_SUN50I)
+
+ #define SUNXI_R_TWI_BASE 0x01f02400
+ #define SUNXI_R_UART_BASE 0x01f02800
+-#define SUNXI_R_PIO_BASE 0x01f02c00
+ #define SUN6I_P2WI_BASE 0x01f03400
+ #define SUNXI_RSB_BASE 0x01f03400
+
+diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h
+index d9cf8ae042..9b6bf84360 100644
+--- a/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h
++++ b/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h
+@@ -22,7 +22,6 @@
+ #define SUNXI_SIDC_BASE 0x03006000
+ #define SUNXI_SID_BASE 0x03006200
+ #define SUNXI_TIMER_BASE 0x03009000
+-#define SUNXI_PIO_BASE 0x0300B000
+ #define SUNXI_PSI_BASE 0x0300C000
+
+ #define SUNXI_GIC400_BASE 0x03020000
+@@ -68,7 +67,6 @@
+ #define SUNXI_R_CPUCFG_BASE 0x07000400
+ #define SUNXI_PRCM_BASE 0x07010000
+ #define SUNXI_R_WDOG_BASE 0x07020400
+-#define SUNXI_R_PIO_BASE 0x07022000
+ #define SUNXI_R_UART_BASE 0x07080000
+ #define SUNXI_R_TWI_BASE 0x07081400
+
+diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun9i.h b/arch/arm/include/asm/arch-sunxi/cpu_sun9i.h
+index 9c2d11b590..20025be231 100644
+--- a/arch/arm/include/asm/arch-sunxi/cpu_sun9i.h
++++ b/arch/arm/include/asm/arch-sunxi/cpu_sun9i.h
+@@ -81,7 +81,6 @@
+ /* APB0 Module */
+ #define SUNXI_CCM_BASE (REGS_APB0_BASE + 0x0000)
+ #define SUNXI_CCMMODULE_BASE (REGS_APB0_BASE + 0x0400)
+-#define SUNXI_PIO_BASE (REGS_APB0_BASE + 0x0800)
+ #define SUNXI_TIMER_BASE (REGS_APB0_BASE + 0x0C00)
+ #define SUNXI_PWM_BASE (REGS_APB0_BASE + 0x1400)
+ #define SUNXI_LRADC_BASE (REGS_APB0_BASE + 0x1800)
+@@ -102,7 +101,6 @@
+ /* RCPUS Module */
+ #define SUNXI_PRCM_BASE (REGS_RCPUS_BASE + 0x1400)
+ #define SUNXI_R_UART_BASE (REGS_RCPUS_BASE + 0x2800)
+-#define SUNXI_R_PIO_BASE (REGS_RCPUS_BASE + 0x2c00)
+ #define SUNXI_RSB_BASE (REGS_RCPUS_BASE + 0x3400)
+
+ /* Misc. */
+diff --git a/include/sunxi_gpio.h b/include/sunxi_gpio.h
+index 96a29aa023..2e06745a1c 100644
+--- a/include/sunxi_gpio.h
++++ b/include/sunxi_gpio.h
+@@ -9,7 +9,17 @@
+ #define _SUNXI_GPIO_H
+
+ #include <linux/types.h>
+-#include <asm/arch/cpu.h>
++
++#if defined(CONFIG_MACH_SUN9I)
++#define SUNXI_PIO_BASE 0x06000800
++#define SUNXI_R_PIO_BASE 0x08002c00
++#elif defined(CONFIG_SUN50I_GEN_H6)
++#define SUNXI_PIO_BASE 0x0300b000
++#define SUNXI_R_PIO_BASE 0x07022000
++#else
++#define SUNXI_PIO_BASE 0x01c20800
++#define SUNXI_R_PIO_BASE 0x01f02c00
++#endif
+
+ /*
+ * sunxi has 9 banks of gpio, they are:
+--
+2.20.1
+
--- /dev/null
+From 536e6204d5e32f4ce30ba99fcfee087aa725f4bd Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 21 Jul 2023 14:45:53 +0100
+Subject: [PATCH 4013/4052] pinctrl: sunxi: add new D1 pinctrl support
+
+For the first time since at least the Allwinner A10 SoCs, the D1 (and
+related cores) use a new pincontroller MMIO register layout, so we
+cannot use our hardcoded, fixed offsets anymore.
+Ideally this would all be handled by devicetree and DM drivers, but for
+the DT-less SPL we still need the legacy interfaces.
+
+Add a new Kconfig symbol to differenciate between the two generations of
+pincontrollers, and just use that to just switch some basic symbols.
+The rest is already abstracted enough, so works out of the box.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+Reviewed-by: Sam Edwards <CFSworks@gmail.com>
+Tested-by: Sam Edwards <CFSworks@gmail.com>
+---
+ arch/arm/mach-sunxi/Kconfig | 6 ++++++
+ include/sunxi_gpio.h | 26 +++++++++++++++++++++-----
+ 2 files changed, 27 insertions(+), 5 deletions(-)
+
+diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
+index d716054f72..b328ce8960 100644
+--- a/arch/arm/mach-sunxi/Kconfig
++++ b/arch/arm/mach-sunxi/Kconfig
+@@ -113,6 +113,12 @@ config SUNXI_SRAM_ADDRESS
+ config SUNXI_A64_TIMER_ERRATUM
+ bool
+
++config SUNXI_NEW_PINCTRL
++ bool
++ ---help---
++ The Allwinner D1 and other new SoCs use a different register map
++ for the GPIO block, which we need to know about in the SPL.
++
+ # Note only one of these may be selected at a time! But hidden choices are
+ # not supported by Kconfig
+ config SUNXI_GEN_SUN4I
+diff --git a/include/sunxi_gpio.h b/include/sunxi_gpio.h
+index 2e06745a1c..61978db6db 100644
+--- a/include/sunxi_gpio.h
++++ b/include/sunxi_gpio.h
+@@ -68,15 +68,32 @@
+ #define GPIO_DAT_REG_OFFSET 0x10
+
+ #define GPIO_DRV_REG_OFFSET 0x14
+-#define GPIO_DRV_INDEX(pin) (((pin) & 0x1f) >> 4)
+-#define GPIO_DRV_OFFSET(pin) ((((pin) & 0x1f) & 0xf) << 1)
++
++/* Newer SoCs use a slightly different register layout */
++#ifdef CONFIG_SUNXI_NEW_PINCTRL
++/* pin drive strength: 4 bits per pin */
++#define GPIO_DRV_INDEX(pin) ((pin) / 8)
++#define GPIO_DRV_OFFSET(pin) (((pin) % 8) * 4)
++
++#define GPIO_PULL_REG_OFFSET 0x24
++
++#define SUNXI_PINCTRL_BANK_SIZE 0x30
++#define SUNXI_GPIO_DISABLE 0xf
++
++#else /* older generation pin controllers */
++/* pin drive strength: 2 bits per pin */
++#define GPIO_DRV_INDEX(pin) ((pin) / 16)
++#define GPIO_DRV_OFFSET(pin) (((pin) % 16) * 2)
+
+ #define GPIO_PULL_REG_OFFSET 0x1c
++
++#define SUNXI_PINCTRL_BANK_SIZE 0x24
++#define SUNXI_GPIO_DISABLE 0x7
++#endif
++
+ #define GPIO_PULL_INDEX(pin) (((pin) & 0x1f) >> 4)
+ #define GPIO_PULL_OFFSET(pin) ((((pin) & 0x1f) & 0xf) << 1)
+
+-#define SUNXI_PINCTRL_BANK_SIZE 0x24
+-
+ static inline void* BANK_TO_GPIO(int bank)
+ {
+ void *pio_base;
+@@ -132,7 +149,6 @@ enum sunxi_gpio_number {
+ /* GPIO pin function config */
+ #define SUNXI_GPIO_INPUT 0
+ #define SUNXI_GPIO_OUTPUT 1
+-#define SUNXI_GPIO_DISABLE 7
+
+ #define SUN8I_H3_GPA_UART0 2
+ #define SUN8I_H3_GPA_UART2 2
+--
+2.20.1
+
--- /dev/null
+From 8f46a2cd8d00bf19cc6b18383a250d1669c88fc9 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 21 Jul 2023 14:45:54 +0100
+Subject: [PATCH 4014/4052] sunxi: introduce NCAT2 generation model
+
+Allwinner seems to typically stick to a common MMIO memory map for
+several SoCs, but from time to time does some breaking changes, which
+also introduce new generations of some peripherals. The last time this
+happened with the H6, which apart from re-organising the base addresses
+also changed the clock controller significantly. We added a
+CONFIG_SUN50I_GEN_H6 symbol back then to mark SoCs sharing those traits.
+
+Now the Allwinner D1 changes the memory map again, and also extends the
+pincontroller, among other peripherals.
+To mark this generation of SoCs, add a CONFIG_SUNXI_GEN_NCAT2 symbol,
+this name is reportedly used in the Allwinner BSP code, and prevents us
+from inventing our own name.
+
+Add this new symbol to some guards that were already checking for the H6
+generation, since many features are shared between the two (like the
+renovated clock controller).
+
+This paves the way to introduce a first user of this generation.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+Reviewed-by: Sam Edwards <CFSworks@gmail.com>
+Tested-by: Sam Edwards <CFSworks@gmail.com>
+---
+ arch/arm/include/asm/arch-sunxi/clock.h | 2 +-
+ arch/arm/include/asm/arch-sunxi/cpu.h | 2 +
+ .../include/asm/arch-sunxi/cpu_sunxi_ncat2.h | 43 +++++++++++++++++++
+ arch/arm/include/asm/arch-sunxi/mmc.h | 2 +-
+ arch/arm/include/asm/arch-sunxi/prcm.h | 2 +-
+ arch/arm/include/asm/arch-sunxi/timer.h | 2 +-
+ arch/arm/mach-sunxi/Kconfig | 12 +++++-
+ arch/arm/mach-sunxi/Makefile | 1 +
+ arch/arm/mach-sunxi/board.c | 22 ++++++----
+ common/spl/Kconfig | 2 +-
+ drivers/i2c/mvtwsi.c | 3 +-
+ drivers/mmc/sunxi_mmc.c | 10 +++--
+ include/sunxi_gpio.h | 3 ++
+ 13 files changed, 87 insertions(+), 19 deletions(-)
+ create mode 100644 arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h
+
+diff --git a/arch/arm/include/asm/arch-sunxi/clock.h b/arch/arm/include/asm/arch-sunxi/clock.h
+index 2cfd540742..3d34261b0e 100644
+--- a/arch/arm/include/asm/arch-sunxi/clock.h
++++ b/arch/arm/include/asm/arch-sunxi/clock.h
+@@ -16,7 +16,7 @@
+ /* clock control module regs definition */
+ #if defined(CONFIG_MACH_SUN8I_A83T)
+ #include <asm/arch/clock_sun8i_a83t.h>
+-#elif defined(CONFIG_SUN50I_GEN_H6)
++#elif defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2)
+ #include <asm/arch/clock_sun50i_h6.h>
+ #elif defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN8I) || \
+ defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUNIV)
+diff --git a/arch/arm/include/asm/arch-sunxi/cpu.h b/arch/arm/include/asm/arch-sunxi/cpu.h
+index b08f202374..768c6572d6 100644
+--- a/arch/arm/include/asm/arch-sunxi/cpu.h
++++ b/arch/arm/include/asm/arch-sunxi/cpu.h
+@@ -10,6 +10,8 @@
+ #include <asm/arch/cpu_sun9i.h>
+ #elif defined(CONFIG_SUN50I_GEN_H6)
+ #include <asm/arch/cpu_sun50i_h6.h>
++#elif defined(CONFIG_SUNXI_GEN_NCAT2)
++#include <asm/arch/cpu_sunxi_ncat2.h>
+ #else
+ #include <asm/arch/cpu_sun4i.h>
+ #endif
+diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h b/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h
+new file mode 100644
+index 0000000000..b13be2c4e8
+--- /dev/null
++++ b/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h
+@@ -0,0 +1,43 @@
++/*
++ * (C) Copyright 2022 Arm Limited
++ *
++ * SPDX-License-Identifier: GPL-2.0+
++ */
++
++#ifndef _SUNXI_CPU_SUNXI_NCAT2_H
++#define _SUNXI_CPU_SUNXI_NCAT2_H
++
++#define SUNXI_CCM_BASE 0x02001000
++#define SUNXI_TIMER_BASE 0x02050000
++
++#define SUNXI_UART0_BASE 0x02500000
++#define SUNXI_UART1_BASE 0x02500400
++#define SUNXI_UART2_BASE 0x02500800
++#define SUNXI_UART3_BASE 0x02500C00
++#define SUNXI_TWI0_BASE 0x02502000
++#define SUNXI_TWI1_BASE 0x02502400
++#define SUNXI_TWI2_BASE 0x02502800
++#define SUNXI_TWI3_BASE 0x02502C00
++
++#define SUNXI_SRAMC_BASE 0x03000000
++/* SID address space starts at 0x03006000, but e-fuse is at offset 0x200 */
++#define SUNXI_SIDC_BASE 0x03006000
++#define SUNXI_SID_BASE 0x03006200
++#define SUNXI_GIC400_BASE 0x03020000
++
++#define SUNXI_MMC0_BASE 0x04020000
++#define SUNXI_MMC1_BASE 0x04021000
++#define SUNXI_MMC2_BASE 0x04022000
++
++#define SUNXI_R_CPUCFG_BASE 0x07000400
++
++#define SUNXI_CPUX_BASE 0x09010000
++#define SUNXI_CPUCFG_BASE 0
++
++#ifndef __ASSEMBLY__
++void sunxi_board_init(void);
++void sunxi_reset(void);
++int sunxi_get_sid(unsigned int *sid);
++#endif
++
++#endif /* _SUNXI_CPU_SUNXI_NCAT2_H */
+diff --git a/arch/arm/include/asm/arch-sunxi/mmc.h b/arch/arm/include/asm/arch-sunxi/mmc.h
+index 5daacf10eb..8ed3e0459c 100644
+--- a/arch/arm/include/asm/arch-sunxi/mmc.h
++++ b/arch/arm/include/asm/arch-sunxi/mmc.h
+@@ -45,7 +45,7 @@ struct sunxi_mmc {
+ u32 chda; /* 0x90 */
+ u32 cbda; /* 0x94 */
+ u32 res2[26];
+-#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6)
++#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2)
+ u32 res3[17];
+ u32 samp_dl;
+ u32 res4[46];
+diff --git a/arch/arm/include/asm/arch-sunxi/prcm.h b/arch/arm/include/asm/arch-sunxi/prcm.h
+index 5106076f5e..c5418cfd28 100644
+--- a/arch/arm/include/asm/arch-sunxi/prcm.h
++++ b/arch/arm/include/asm/arch-sunxi/prcm.h
+@@ -9,7 +9,7 @@
+ #define _SUNXI_PRCM_H
+
+ /* prcm regs definition */
+-#if defined(CONFIG_SUN50I_GEN_H6)
++#if defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2)
+ #include <asm/arch/prcm_sun50i.h>
+ #else
+ #include <asm/arch/prcm_sun6i.h>
+diff --git a/arch/arm/include/asm/arch-sunxi/timer.h b/arch/arm/include/asm/arch-sunxi/timer.h
+index bb5626d893..e17db8588e 100644
+--- a/arch/arm/include/asm/arch-sunxi/timer.h
++++ b/arch/arm/include/asm/arch-sunxi/timer.h
+@@ -76,7 +76,7 @@ struct sunxi_timer_reg {
+ struct sunxi_tgp tgp[4];
+ u8 res5[8];
+ u32 cpu_cfg;
+-#elif defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6)
++#elif defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2)
+ u8 res3[16];
+ struct sunxi_wdog wdog[5]; /* We have 5 watchdogs */
+ #endif
+diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
+index b328ce8960..60ca1239dd 100644
+--- a/arch/arm/mach-sunxi/Kconfig
++++ b/arch/arm/mach-sunxi/Kconfig
+@@ -102,7 +102,7 @@ config AXP_PMIC_BUS
+ config SUNXI_SRAM_ADDRESS
+ hex
+ default 0x10000 if MACH_SUN9I || MACH_SUN50I || MACH_SUN50I_H5
+- default 0x20000 if SUN50I_GEN_H6
++ default 0x20000 if SUN50I_GEN_H6 || SUNXI_GEN_NCAT2
+ default 0x0
+ ---help---
+ Older Allwinner SoCs have their mask boot ROM mapped just below 4GB,
+@@ -144,6 +144,14 @@ config SUN50I_GEN_H6
+ Select this for sunxi SoCs which have H6 like peripherals, clocks
+ and memory map.
+
++config SUNXI_GEN_NCAT2
++ bool
++ select MMC_SUNXI_HAS_NEW_MODE
++ select SUPPORT_SPL
++ ---help---
++ Select this for sunxi SoCs which have D1 like peripherals, clocks
++ and memory map.
++
+ config SUNXI_DRAM_DW
+ bool
+ ---help---
+@@ -760,6 +768,7 @@ config VIDEO_SUNXI
+ depends on !MACH_SUN9I
+ depends on !MACH_SUN50I
+ depends on !SUN50I_GEN_H6
++ depends on !SUNXI_GEN_NCAT2
+ select VIDEO
+ select DISPLAY
+ imply VIDEO_DT_SIMPLEFB
+@@ -973,6 +982,7 @@ config SPL_STACK_R_ADDR
+ default 0x2fe00000 if MACH_SUN9I
+ default 0x4fe00000 if MACH_SUN50I
+ default 0x4fe00000 if SUN50I_GEN_H6
++ default 0x4fe00000 if SUNXI_GEN_NCAT2
+
+ config SPL_SPI_SUNXI
+ bool "Support for SPI Flash on Allwinner SoCs in SPL"
+diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
+index 671211e932..1d4c70ec35 100644
+--- a/arch/arm/mach-sunxi/Makefile
++++ b/arch/arm/mach-sunxi/Makefile
+@@ -25,6 +25,7 @@ obj-$(CONFIG_MACH_SUN8I) += clock_sun6i.o
+ endif
+ obj-$(CONFIG_MACH_SUN9I) += clock_sun9i.o gtbus_sun9i.o
+ obj-$(CONFIG_SUN50I_GEN_H6) += clock_sun50i_h6.o
++obj-$(CONFIG_SUNXI_GEN_NCAT2) += clock_sun50i_h6.o
+ ifndef CONFIG_ARM64
+ obj-y += timer.o
+ endif
+diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
+index ec46ab9279..51b8e708b0 100644
+--- a/arch/arm/mach-sunxi/board.c
++++ b/arch/arm/mach-sunxi/board.c
+@@ -176,13 +176,19 @@ static int gpio_init(void)
+ #error Unsupported console port number. Please fix pin mux settings in board.c
+ #endif
+
+-#ifdef CONFIG_SUN50I_GEN_H6
+- /* Update PIO power bias configuration by copy hardware detected value */
+- val = readl(SUNXI_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_VAL);
+- writel(val, SUNXI_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_SEL);
+- val = readl(SUNXI_R_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_VAL);
+- writel(val, SUNXI_R_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_SEL);
+-#endif
++ /*
++ * Update PIO power bias configuration by copying the hardware
++ * detected value.
++ */
++ if (IS_ENABLED(CONFIG_SUN50I_GEN_H6) ||
++ IS_ENABLED(CONFIG_SUN50I_GEN_NCAT2)) {
++ val = readl(SUNXI_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_VAL);
++ writel(val, SUNXI_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_SEL);
++ }
++ if (IS_ENABLED(CONFIG_SUN50I_GEN_H6)) {
++ val = readl(SUNXI_R_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_VAL);
++ writel(val, SUNXI_R_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_SEL);
++ }
+
+ return 0;
+ }
+@@ -481,7 +487,7 @@ void reset_cpu(void)
+ /* sun5i sometimes gets stuck without this */
+ writel(WDT_MODE_RESET_EN | WDT_MODE_EN, &wdog->mode);
+ }
+-#elif defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6)
++#elif defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2)
+ #if defined(CONFIG_MACH_SUN50I_H6)
+ /* WDOG is broken for some H6 rev. use the R_WDOG instead */
+ static const struct sunxi_wdog *wdog =
+diff --git a/common/spl/Kconfig b/common/spl/Kconfig
+index 3c2af453ab..06bcedca7d 100644
+--- a/common/spl/Kconfig
++++ b/common/spl/Kconfig
+@@ -265,7 +265,7 @@ config SPL_TEXT_BASE
+ default 0x402F0400 if AM33XX
+ default 0x40301350 if OMAP54XX
+ default 0x10060 if MACH_SUN50I || MACH_SUN50I_H5 || MACH_SUN9I
+- default 0x20060 if SUN50I_GEN_H6
++ default 0x20060 if SUN50I_GEN_H6 || SUNXI_GEN_NCAT2
+ default 0x00060 if ARCH_SUNXI
+ default 0xfffc0000 if ARCH_ZYNQMP
+ default 0x0
+diff --git a/drivers/i2c/mvtwsi.c b/drivers/i2c/mvtwsi.c
+index 93bbc6916e..5c9b92ccf9 100644
+--- a/drivers/i2c/mvtwsi.c
++++ b/drivers/i2c/mvtwsi.c
+@@ -124,7 +124,8 @@ enum mvtwsi_ctrl_register_fields {
+ * on other platforms, it is a normal r/w bit, which is cleared by writing 0.
+ */
+
+-#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6)
++#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) || \
++ defined(CONFIG_SUNXI_GEN_NCAT2)
+ #define MVTWSI_CONTROL_CLEAR_IFLG 0x00000008
+ #else
+ #define MVTWSI_CONTROL_CLEAR_IFLG 0x00000000
+diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c
+index 03e33753fc..a8e590561c 100644
+--- a/drivers/mmc/sunxi_mmc.c
++++ b/drivers/mmc/sunxi_mmc.c
+@@ -57,6 +57,7 @@ static bool sunxi_mmc_can_calibrate(void)
+ return IS_ENABLED(CONFIG_MACH_SUN50I) ||
+ IS_ENABLED(CONFIG_MACH_SUN50I_H5) ||
+ IS_ENABLED(CONFIG_SUN50I_GEN_H6) ||
++ IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2) ||
+ IS_ENABLED(CONFIG_MACH_SUN8I_R40);
+ }
+
+@@ -191,7 +192,7 @@ static int mmc_config_clock(struct sunxi_mmc_priv *priv, struct mmc *mmc)
+ rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK;
+ writel(rval, &priv->reg->clkcr);
+
+-#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6)
++#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2)
+ /* A64 supports calibration of delays on MMC controller and we
+ * have to set delay of zero before starting calibration.
+ * Allwinner BSP driver sets a delay only in the case of
+@@ -530,7 +531,8 @@ struct mmc *sunxi_mmc_init(int sdc_no)
+ cfg->host_caps = MMC_MODE_4BIT;
+
+ if ((IS_ENABLED(CONFIG_MACH_SUN50I) || IS_ENABLED(CONFIG_MACH_SUN8I) ||
+- IS_ENABLED(CONFIG_SUN50I_GEN_H6)) && (sdc_no == 2))
++ IS_ENABLED(CONFIG_SUN50I_GEN_H6) ||
++ IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)) && (sdc_no == 2))
+ cfg->host_caps = MMC_MODE_8BIT;
+
+ cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
+@@ -544,7 +546,7 @@ struct mmc *sunxi_mmc_init(int sdc_no)
+
+ /* config ahb clock */
+ debug("init mmc %d clock and io\n", sdc_no);
+-#if !defined(CONFIG_SUN50I_GEN_H6)
++#if !defined(CONFIG_SUN50I_GEN_H6) && !defined(CONFIG_SUNXI_GEN_NCAT2)
+ setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MMC(sdc_no));
+
+ #ifdef CONFIG_SUNXI_GEN_SUN6I
+@@ -619,7 +621,7 @@ static unsigned get_mclk_offset(void)
+ if (IS_ENABLED(CONFIG_MACH_SUN9I_A80))
+ return 0x410;
+
+- if (IS_ENABLED(CONFIG_SUN50I_GEN_H6))
++ if (IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2))
+ return 0x830;
+
+ return 0x88;
+diff --git a/include/sunxi_gpio.h b/include/sunxi_gpio.h
+index 61978db6db..8cf9047ff9 100644
+--- a/include/sunxi_gpio.h
++++ b/include/sunxi_gpio.h
+@@ -16,6 +16,9 @@
+ #elif defined(CONFIG_SUN50I_GEN_H6)
+ #define SUNXI_PIO_BASE 0x0300b000
+ #define SUNXI_R_PIO_BASE 0x07022000
++#elif defined(CONFIG_SUNXI_GEN_NCAT2)
++#define SUNXI_PIO_BASE 0x02000000
++#define SUNXI_R_PIO_BASE 0
+ #else
+ #define SUNXI_PIO_BASE 0x01c20800
+ #define SUNXI_R_PIO_BASE 0x01f02c00
+--
+2.20.1
+
--- /dev/null
+From 8d93569214bc09410ea6eaa98d0636b94d030bb8 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 21 Jul 2023 14:45:55 +0100
+Subject: [PATCH 4015/4052] pinctrl: sunxi: add Allwinner D1 pinctrl
+ description
+
+Apart from using the new pinctrl MMIO register layout, the Allwinner D1
+and related SoCs still need to usual set of mux values hardcoded in
+U-Boot's pinctrl driver.
+Add the values we need so far to this list, so that DM based drivers
+will just work without further ado.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+---
+ drivers/pinctrl/sunxi/Kconfig | 4 ++++
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 28 +++++++++++++++++++++++++++
+ 2 files changed, 32 insertions(+)
+
+diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig
+index 77da90836b..c8f937d91e 100644
+--- a/drivers/pinctrl/sunxi/Kconfig
++++ b/drivers/pinctrl/sunxi/Kconfig
+@@ -124,4 +124,8 @@ config PINCTRL_SUN50I_H616_R
+ default MACH_SUN50I_H616
+ select PINCTRL_SUNXI
+
++config PINCTRL_SUN20I_D1
++ bool "Support for the Allwinner D1/R528 PIO"
++ select PINCTRL_SUNXI
++
+ endif
+diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+index 4c52e3fa74..614cfe6b73 100644
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -735,6 +735,28 @@ static const struct sunxi_pinctrl_desc __maybe_unused sun50i_h616_r_pinctrl_desc
+ .num_banks = 1,
+ };
+
++static const struct sunxi_pinctrl_function sun20i_d1_pinctrl_functions[] = {
++ { "emac", 8 }, /* PE0-PE15 */
++ { "gpio_in", 0 },
++ { "gpio_out", 1 },
++ { "mmc0", 2 }, /* PF0-PF5 */
++ { "mmc2", 3 }, /* PC1-PC7 */
++ { "spi0", 2 }, /* PC2-PC7 */
++#if IS_ENABLED(CONFIG_UART0_PORT_F)
++ { "uart0", 3 }, /* PF2-PF4 */
++#else
++ { "uart0", 6 }, /* PB2-PB3 */
++#endif
++ { "uart3", 7 }, /* PB6-PB9 */
++};
++
++static const struct sunxi_pinctrl_desc __maybe_unused sun20i_d1_pinctrl_desc = {
++ .functions = sun20i_d1_pinctrl_functions,
++ .num_functions = ARRAY_SIZE(sun20i_d1_pinctrl_functions),
++ .first_bank = SUNXI_GPIO_A,
++ .num_banks = 7,
++};
++
+ static const struct udevice_id sunxi_pinctrl_ids[] = {
+ #ifdef CONFIG_PINCTRL_SUNIV_F1C100S
+ {
+@@ -891,6 +913,12 @@ static const struct udevice_id sunxi_pinctrl_ids[] = {
+ .compatible = "allwinner,sun50i-h616-r-pinctrl",
+ .data = (ulong)&sun50i_h616_r_pinctrl_desc,
+ },
++#endif
++#ifdef CONFIG_PINCTRL_SUN20I_D1
++ {
++ .compatible = "allwinner,sun20i-d1-pinctrl",
++ .data = (ulong)&sun20i_d1_pinctrl_desc,
++ },
+ #endif
+ {}
+ };
+--
+2.20.1
+
--- /dev/null
+From 43f55d26fbe4c6cf7d4dced762167bb4c4a52993 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Fri, 21 Jul 2023 14:45:56 +0100
+Subject: [PATCH 4016/4052] clk: sunxi: Add support for the D1 CCU
+
+Since the D1 CCU binding is defined, we can add support for its
+gates/resets, following the pattern of the existing drivers.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+Reviewed-by: Andre Przywara <andre.przywara@arm.com>
+Acked-by: Sean Anderson <seanga2@gmail.com>
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+---
+ drivers/clk/sunxi/Kconfig | 6 +
+ drivers/clk/sunxi/Makefile | 1 +
+ drivers/clk/sunxi/clk_d1.c | 84 ++++++++++++
+ drivers/clk/sunxi/clk_sunxi.c | 5 +
+ include/dt-bindings/clock/sun20i-d1-ccu.h | 158 ++++++++++++++++++++++
+ include/dt-bindings/reset/sun20i-d1-ccu.h | 79 +++++++++++
+ 6 files changed, 333 insertions(+)
+ create mode 100644 drivers/clk/sunxi/clk_d1.c
+ create mode 100644 include/dt-bindings/clock/sun20i-d1-ccu.h
+ create mode 100644 include/dt-bindings/reset/sun20i-d1-ccu.h
+
+diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig
+index bf11fad6ee..f65e482ba4 100644
+--- a/drivers/clk/sunxi/Kconfig
++++ b/drivers/clk/sunxi/Kconfig
+@@ -87,6 +87,12 @@ config CLK_SUN8I_H3
+ This enables common clock driver support for platforms based
+ on Allwinner H3/H5 SoC.
+
++config CLK_SUN20I_D1
++ bool "Clock driver for Allwinner D1"
++ help
++ This enables common clock driver support for platforms based
++ on Allwinner D1 SoC.
++
+ config CLK_SUN50I_H6
+ bool "Clock driver for Allwinner H6"
+ default MACH_SUN50I_H6
+diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
+index 895da02ebe..90a277489d 100644
+--- a/drivers/clk/sunxi/Makefile
++++ b/drivers/clk/sunxi/Makefile
+@@ -19,6 +19,7 @@ obj-$(CONFIG_CLK_SUN8I_R40) += clk_r40.o
+ obj-$(CONFIG_CLK_SUN8I_V3S) += clk_v3s.o
+ obj-$(CONFIG_CLK_SUN9I_A80) += clk_a80.o
+ obj-$(CONFIG_CLK_SUN8I_H3) += clk_h3.o
++obj-$(CONFIG_CLK_SUN20I_D1) += clk_d1.o
+ obj-$(CONFIG_CLK_SUN50I_H6) += clk_h6.o
+ obj-$(CONFIG_CLK_SUN50I_H6_R) += clk_h6_r.o
+ obj-$(CONFIG_CLK_SUN50I_H616) += clk_h616.o
+diff --git a/drivers/clk/sunxi/clk_d1.c b/drivers/clk/sunxi/clk_d1.c
+new file mode 100644
+index 0000000000..9dae761de8
+--- /dev/null
++++ b/drivers/clk/sunxi/clk_d1.c
+@@ -0,0 +1,84 @@
++// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
++/*
++ * Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
++ */
++
++#include <common.h>
++#include <clk-uclass.h>
++#include <dm.h>
++#include <errno.h>
++#include <clk/sunxi.h>
++#include <dt-bindings/clock/sun20i-d1-ccu.h>
++#include <dt-bindings/reset/sun20i-d1-ccu.h>
++#include <linux/bitops.h>
++
++static struct ccu_clk_gate d1_gates[] = {
++ [CLK_APB0] = GATE_DUMMY,
++
++ [CLK_BUS_MMC0] = GATE(0x84c, BIT(0)),
++ [CLK_BUS_MMC1] = GATE(0x84c, BIT(1)),
++ [CLK_BUS_MMC2] = GATE(0x84c, BIT(2)),
++ [CLK_BUS_UART0] = GATE(0x90c, BIT(0)),
++ [CLK_BUS_UART1] = GATE(0x90c, BIT(1)),
++ [CLK_BUS_UART2] = GATE(0x90c, BIT(2)),
++ [CLK_BUS_UART3] = GATE(0x90c, BIT(3)),
++ [CLK_BUS_UART4] = GATE(0x90c, BIT(4)),
++ [CLK_BUS_UART5] = GATE(0x90c, BIT(5)),
++ [CLK_BUS_I2C0] = GATE(0x91c, BIT(0)),
++ [CLK_BUS_I2C1] = GATE(0x91c, BIT(1)),
++ [CLK_BUS_I2C2] = GATE(0x91c, BIT(2)),
++ [CLK_BUS_I2C3] = GATE(0x91c, BIT(3)),
++ [CLK_SPI0] = GATE(0x940, BIT(31)),
++ [CLK_SPI1] = GATE(0x944, BIT(31)),
++ [CLK_BUS_SPI0] = GATE(0x96c, BIT(0)),
++ [CLK_BUS_SPI1] = GATE(0x96c, BIT(1)),
++
++ [CLK_BUS_EMAC] = GATE(0x97c, BIT(0)),
++
++ [CLK_USB_OHCI0] = GATE(0xa70, BIT(31)),
++ [CLK_USB_OHCI1] = GATE(0xa74, BIT(31)),
++ [CLK_BUS_OHCI0] = GATE(0xa8c, BIT(0)),
++ [CLK_BUS_OHCI1] = GATE(0xa8c, BIT(1)),
++ [CLK_BUS_EHCI0] = GATE(0xa8c, BIT(4)),
++ [CLK_BUS_EHCI1] = GATE(0xa8c, BIT(5)),
++ [CLK_BUS_OTG] = GATE(0xa8c, BIT(8)),
++ [CLK_BUS_LRADC] = GATE(0xa9c, BIT(0)),
++
++ [CLK_RISCV] = GATE(0xd04, BIT(31)),
++};
++
++static struct ccu_reset d1_resets[] = {
++ [RST_BUS_MMC0] = RESET(0x84c, BIT(16)),
++ [RST_BUS_MMC1] = RESET(0x84c, BIT(17)),
++ [RST_BUS_MMC2] = RESET(0x84c, BIT(18)),
++ [RST_BUS_UART0] = RESET(0x90c, BIT(16)),
++ [RST_BUS_UART1] = RESET(0x90c, BIT(17)),
++ [RST_BUS_UART2] = RESET(0x90c, BIT(18)),
++ [RST_BUS_UART3] = RESET(0x90c, BIT(19)),
++ [RST_BUS_UART4] = RESET(0x90c, BIT(20)),
++ [RST_BUS_UART5] = RESET(0x90c, BIT(21)),
++ [RST_BUS_I2C0] = RESET(0x91c, BIT(16)),
++ [RST_BUS_I2C1] = RESET(0x91c, BIT(17)),
++ [RST_BUS_I2C2] = RESET(0x91c, BIT(18)),
++ [RST_BUS_I2C3] = RESET(0x91c, BIT(19)),
++ [RST_BUS_SPI0] = RESET(0x96c, BIT(16)),
++ [RST_BUS_SPI1] = RESET(0x96c, BIT(17)),
++
++ [RST_BUS_EMAC] = RESET(0x97c, BIT(16)),
++
++ [RST_USB_PHY0] = RESET(0xa70, BIT(30)),
++ [RST_USB_PHY1] = RESET(0xa74, BIT(30)),
++ [RST_BUS_OHCI0] = RESET(0xa8c, BIT(16)),
++ [RST_BUS_OHCI1] = RESET(0xa8c, BIT(17)),
++ [RST_BUS_EHCI0] = RESET(0xa8c, BIT(20)),
++ [RST_BUS_EHCI1] = RESET(0xa8c, BIT(21)),
++ [RST_BUS_OTG] = RESET(0xa8c, BIT(24)),
++ [RST_BUS_LRADC] = RESET(0xa9c, BIT(16)),
++};
++
++const struct ccu_desc d1_ccu_desc = {
++ .gates = d1_gates,
++ .resets = d1_resets,
++ .num_gates = ARRAY_SIZE(d1_gates),
++ .num_resets = ARRAY_SIZE(d1_resets),
++};
+diff --git a/drivers/clk/sunxi/clk_sunxi.c b/drivers/clk/sunxi/clk_sunxi.c
+index ec02a2d037..a0011a35d9 100644
+--- a/drivers/clk/sunxi/clk_sunxi.c
++++ b/drivers/clk/sunxi/clk_sunxi.c
+@@ -118,6 +118,7 @@ extern const struct ccu_desc a64_ccu_desc;
+ extern const struct ccu_desc a80_ccu_desc;
+ extern const struct ccu_desc a80_mmc_clk_desc;
+ extern const struct ccu_desc a83t_ccu_desc;
++extern const struct ccu_desc d1_ccu_desc;
+ extern const struct ccu_desc f1c100s_ccu_desc;
+ extern const struct ccu_desc h3_ccu_desc;
+ extern const struct ccu_desc h6_ccu_desc;
+@@ -214,6 +215,10 @@ static const struct udevice_id sunxi_clk_ids[] = {
+ #ifdef CONFIG_CLK_SUNIV_F1C100S
+ { .compatible = "allwinner,suniv-f1c100s-ccu",
+ .data = (ulong)&f1c100s_ccu_desc },
++#endif
++#ifdef CONFIG_CLK_SUN20I_D1
++ { .compatible = "allwinner,sun20i-d1-ccu",
++ .data = (ulong)&d1_ccu_desc },
+ #endif
+ { }
+ };
+diff --git a/include/dt-bindings/clock/sun20i-d1-ccu.h b/include/dt-bindings/clock/sun20i-d1-ccu.h
+new file mode 100644
+index 0000000000..e143b99297
+--- /dev/null
++++ b/include/dt-bindings/clock/sun20i-d1-ccu.h
+@@ -0,0 +1,158 @@
++/* SPDX-License-Identifier: (GPL-2.0+ or MIT) */
++/*
++ * Copyright (C) 2020 huangzhenwei@allwinnertech.com
++ * Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
++ */
++
++#ifndef _DT_BINDINGS_CLK_SUN20I_D1_CCU_H_
++#define _DT_BINDINGS_CLK_SUN20I_D1_CCU_H_
++
++#define CLK_PLL_CPUX 0
++#define CLK_PLL_DDR0 1
++#define CLK_PLL_PERIPH0_4X 2
++#define CLK_PLL_PERIPH0_2X 3
++#define CLK_PLL_PERIPH0_800M 4
++#define CLK_PLL_PERIPH0 5
++#define CLK_PLL_PERIPH0_DIV3 6
++#define CLK_PLL_VIDEO0_4X 7
++#define CLK_PLL_VIDEO0_2X 8
++#define CLK_PLL_VIDEO0 9
++#define CLK_PLL_VIDEO1_4X 10
++#define CLK_PLL_VIDEO1_2X 11
++#define CLK_PLL_VIDEO1 12
++#define CLK_PLL_VE 13
++#define CLK_PLL_AUDIO0_4X 14
++#define CLK_PLL_AUDIO0_2X 15
++#define CLK_PLL_AUDIO0 16
++#define CLK_PLL_AUDIO1 17
++#define CLK_PLL_AUDIO1_DIV2 18
++#define CLK_PLL_AUDIO1_DIV5 19
++#define CLK_CPUX 20
++#define CLK_CPUX_AXI 21
++#define CLK_CPUX_APB 22
++#define CLK_PSI_AHB 23
++#define CLK_APB0 24
++#define CLK_APB1 25
++#define CLK_MBUS 26
++#define CLK_DE 27
++#define CLK_BUS_DE 28
++#define CLK_DI 29
++#define CLK_BUS_DI 30
++#define CLK_G2D 31
++#define CLK_BUS_G2D 32
++#define CLK_CE 33
++#define CLK_BUS_CE 34
++#define CLK_VE 35
++#define CLK_BUS_VE 36
++#define CLK_BUS_DMA 37
++#define CLK_BUS_MSGBOX0 38
++#define CLK_BUS_MSGBOX1 39
++#define CLK_BUS_MSGBOX2 40
++#define CLK_BUS_SPINLOCK 41
++#define CLK_BUS_HSTIMER 42
++#define CLK_AVS 43
++#define CLK_BUS_DBG 44
++#define CLK_BUS_PWM 45
++#define CLK_BUS_IOMMU 46
++#define CLK_DRAM 47
++#define CLK_MBUS_DMA 48
++#define CLK_MBUS_VE 49
++#define CLK_MBUS_CE 50
++#define CLK_MBUS_TVIN 51
++#define CLK_MBUS_CSI 52
++#define CLK_MBUS_G2D 53
++#define CLK_MBUS_RISCV 54
++#define CLK_BUS_DRAM 55
++#define CLK_MMC0 56
++#define CLK_MMC1 57
++#define CLK_MMC2 58
++#define CLK_BUS_MMC0 59
++#define CLK_BUS_MMC1 60
++#define CLK_BUS_MMC2 61
++#define CLK_BUS_UART0 62
++#define CLK_BUS_UART1 63
++#define CLK_BUS_UART2 64
++#define CLK_BUS_UART3 65
++#define CLK_BUS_UART4 66
++#define CLK_BUS_UART5 67
++#define CLK_BUS_I2C0 68
++#define CLK_BUS_I2C1 69
++#define CLK_BUS_I2C2 70
++#define CLK_BUS_I2C3 71
++#define CLK_SPI0 72
++#define CLK_SPI1 73
++#define CLK_BUS_SPI0 74
++#define CLK_BUS_SPI1 75
++#define CLK_EMAC_25M 76
++#define CLK_BUS_EMAC 77
++#define CLK_IR_TX 78
++#define CLK_BUS_IR_TX 79
++#define CLK_BUS_GPADC 80
++#define CLK_BUS_THS 81
++#define CLK_I2S0 82
++#define CLK_I2S1 83
++#define CLK_I2S2 84
++#define CLK_I2S2_ASRC 85
++#define CLK_BUS_I2S0 86
++#define CLK_BUS_I2S1 87
++#define CLK_BUS_I2S2 88
++#define CLK_SPDIF_TX 89
++#define CLK_SPDIF_RX 90
++#define CLK_BUS_SPDIF 91
++#define CLK_DMIC 92
++#define CLK_BUS_DMIC 93
++#define CLK_AUDIO_DAC 94
++#define CLK_AUDIO_ADC 95
++#define CLK_BUS_AUDIO 96
++#define CLK_USB_OHCI0 97
++#define CLK_USB_OHCI1 98
++#define CLK_BUS_OHCI0 99
++#define CLK_BUS_OHCI1 100
++#define CLK_BUS_EHCI0 101
++#define CLK_BUS_EHCI1 102
++#define CLK_BUS_OTG 103
++#define CLK_BUS_LRADC 104
++#define CLK_BUS_DPSS_TOP 105
++#define CLK_HDMI_24M 106
++#define CLK_HDMI_CEC_32K 107
++#define CLK_HDMI_CEC 108
++#define CLK_BUS_HDMI 109
++#define CLK_MIPI_DSI 110
++#define CLK_BUS_MIPI_DSI 111
++#define CLK_TCON_LCD0 112
++#define CLK_BUS_TCON_LCD0 113
++#define CLK_TCON_TV 114
++#define CLK_BUS_TCON_TV 115
++#define CLK_TVE 116
++#define CLK_BUS_TVE_TOP 117
++#define CLK_BUS_TVE 118
++#define CLK_TVD 119
++#define CLK_BUS_TVD_TOP 120
++#define CLK_BUS_TVD 121
++#define CLK_LEDC 122
++#define CLK_BUS_LEDC 123
++#define CLK_CSI_TOP 124
++#define CLK_CSI_MCLK 125
++#define CLK_BUS_CSI 126
++#define CLK_TPADC 127
++#define CLK_BUS_TPADC 128
++#define CLK_BUS_TZMA 129
++#define CLK_DSP 130
++#define CLK_BUS_DSP_CFG 131
++#define CLK_RISCV 132
++#define CLK_RISCV_AXI 133
++#define CLK_BUS_RISCV_CFG 134
++#define CLK_FANOUT_24M 135
++#define CLK_FANOUT_12M 136
++#define CLK_FANOUT_16M 137
++#define CLK_FANOUT_25M 138
++#define CLK_FANOUT_32K 139
++#define CLK_FANOUT_27M 140
++#define CLK_FANOUT_PCLK 141
++#define CLK_FANOUT0 142
++#define CLK_FANOUT1 143
++#define CLK_FANOUT2 144
++#define CLK_BUS_CAN0 145
++#define CLK_BUS_CAN1 146
++
++#endif /* _DT_BINDINGS_CLK_SUN20I_D1_CCU_H_ */
+diff --git a/include/dt-bindings/reset/sun20i-d1-ccu.h b/include/dt-bindings/reset/sun20i-d1-ccu.h
+new file mode 100644
+index 0000000000..f8001cf50b
+--- /dev/null
++++ b/include/dt-bindings/reset/sun20i-d1-ccu.h
+@@ -0,0 +1,79 @@
++/* SPDX-License-Identifier: (GPL-2.0+ or MIT) */
++/*
++ * Copyright (c) 2020 huangzhenwei@allwinnertech.com
++ * Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
++ */
++
++#ifndef _DT_BINDINGS_RST_SUN20I_D1_CCU_H_
++#define _DT_BINDINGS_RST_SUN20I_D1_CCU_H_
++
++#define RST_MBUS 0
++#define RST_BUS_DE 1
++#define RST_BUS_DI 2
++#define RST_BUS_G2D 3
++#define RST_BUS_CE 4
++#define RST_BUS_VE 5
++#define RST_BUS_DMA 6
++#define RST_BUS_MSGBOX0 7
++#define RST_BUS_MSGBOX1 8
++#define RST_BUS_MSGBOX2 9
++#define RST_BUS_SPINLOCK 10
++#define RST_BUS_HSTIMER 11
++#define RST_BUS_DBG 12
++#define RST_BUS_PWM 13
++#define RST_BUS_DRAM 14
++#define RST_BUS_MMC0 15
++#define RST_BUS_MMC1 16
++#define RST_BUS_MMC2 17
++#define RST_BUS_UART0 18
++#define RST_BUS_UART1 19
++#define RST_BUS_UART2 20
++#define RST_BUS_UART3 21
++#define RST_BUS_UART4 22
++#define RST_BUS_UART5 23
++#define RST_BUS_I2C0 24
++#define RST_BUS_I2C1 25
++#define RST_BUS_I2C2 26
++#define RST_BUS_I2C3 27
++#define RST_BUS_SPI0 28
++#define RST_BUS_SPI1 29
++#define RST_BUS_EMAC 30
++#define RST_BUS_IR_TX 31
++#define RST_BUS_GPADC 32
++#define RST_BUS_THS 33
++#define RST_BUS_I2S0 34
++#define RST_BUS_I2S1 35
++#define RST_BUS_I2S2 36
++#define RST_BUS_SPDIF 37
++#define RST_BUS_DMIC 38
++#define RST_BUS_AUDIO 39
++#define RST_USB_PHY0 40
++#define RST_USB_PHY1 41
++#define RST_BUS_OHCI0 42
++#define RST_BUS_OHCI1 43
++#define RST_BUS_EHCI0 44
++#define RST_BUS_EHCI1 45
++#define RST_BUS_OTG 46
++#define RST_BUS_LRADC 47
++#define RST_BUS_DPSS_TOP 48
++#define RST_BUS_HDMI_SUB 49
++#define RST_BUS_HDMI_MAIN 50
++#define RST_BUS_MIPI_DSI 51
++#define RST_BUS_TCON_LCD0 52
++#define RST_BUS_TCON_TV 53
++#define RST_BUS_LVDS0 54
++#define RST_BUS_TVE 55
++#define RST_BUS_TVE_TOP 56
++#define RST_BUS_TVD 57
++#define RST_BUS_TVD_TOP 58
++#define RST_BUS_LEDC 59
++#define RST_BUS_CSI 60
++#define RST_BUS_TPADC 61
++#define RST_DSP 62
++#define RST_BUS_DSP_CFG 63
++#define RST_BUS_DSP_DBG 64
++#define RST_BUS_RISCV_CFG 65
++#define RST_BUS_CAN0 66
++#define RST_BUS_CAN1 67
++
++#endif /* _DT_BINDINGS_RST_SUN20I_D1_CCU_H_ */
+--
+2.20.1
+
--- /dev/null
+From 1b5b04c85989c73be3da502ce788e847b83f8e10 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 21 Jul 2023 14:45:57 +0100
+Subject: [PATCH 4017/4052] sunxi: clock: D1/R528: Enable PLL LDO during PLL1
+ setup
+
+The D1/R528/T113s SoCs introduce a new "LDO enable" bit in the CPUX_PLL.
+Just enable that when we program that PLL.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+---
+ arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h | 1 +
+ arch/arm/mach-sunxi/clock_sun50i_h6.c | 12 +++++++-----
+ 2 files changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
+index 37df4410ea..9895c2c220 100644
+--- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
++++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
+@@ -228,6 +228,7 @@ struct sunxi_ccm_reg {
+
+ /* pll1 bit field */
+ #define CCM_PLL1_CTRL_EN BIT(31)
++#define CCM_PLL1_LDO_EN BIT(30)
+ #define CCM_PLL1_LOCK_EN BIT(29)
+ #define CCM_PLL1_LOCK BIT(28)
+ #define CCM_PLL1_OUT_EN BIT(27)
+diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
+index 7926394cf7..90110eab10 100644
+--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
++++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
+@@ -86,11 +86,13 @@ void clock_set_pll1(unsigned int clk)
+ writel(val, &ccm->cpu_axi_cfg);
+
+ /* clk = 24*n/p, p is ignored if clock is >288MHz */
+- writel(CCM_PLL1_CTRL_EN | CCM_PLL1_LOCK_EN | CCM_PLL1_CLOCK_TIME_2 |
+-#ifdef CONFIG_MACH_SUN50I_H616
+- CCM_PLL1_OUT_EN |
+-#endif
+- CCM_PLL1_CTRL_N(clk / 24000000), &ccm->pll1_cfg);
++ val = CCM_PLL1_CTRL_EN | CCM_PLL1_LOCK_EN | CCM_PLL1_CLOCK_TIME_2;
++ val |= CCM_PLL1_CTRL_N(clk / 24000000);
++ if (IS_ENABLED(CONFIG_MACH_SUN50I_H616))
++ val |= CCM_PLL1_OUT_EN;
++ if (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2))
++ val |= CCM_PLL1_OUT_EN | CCM_PLL1_LDO_EN;
++ writel(val, &ccm->pll1_cfg);
+ while (!(readl(&ccm->pll1_cfg) & CCM_PLL1_LOCK)) {}
+
+ /* Switch CPU to PLL1 */
+--
+2.20.1
+
--- /dev/null
+From a601a3257233308d99c5f34c44e26043ac196491 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 21 Jul 2023 14:45:58 +0100
+Subject: [PATCH 4018/4052] sunxi: clock: support D1/R528 PLL6 clock
+
+The PLL_PERIPH0 clock changed a bit in the D1/R528/T113s SoCs: there is
+new P0 divider at bits [18:16], and the M divider is 1.
+
+Add code to support this version of "PLL6".
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+---
+ .../include/asm/arch-sunxi/clock_sun50i_h6.h | 2 ++
+ arch/arm/mach-sunxi/clock_sun50i_h6.c | 24 +++++++++++++------
+ 2 files changed, 19 insertions(+), 7 deletions(-)
+
+diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
+index 9895c2c220..8471e11aa0 100644
+--- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
++++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
+@@ -249,6 +249,8 @@ struct sunxi_ccm_reg {
+ #define CCM_PLL6_CTRL_EN BIT(31)
+ #define CCM_PLL6_LOCK_EN BIT(29)
+ #define CCM_PLL6_LOCK BIT(28)
++#define CCM_PLL6_CTRL_P0_SHIFT 16
++#define CCM_PLL6_CTRL_P0_MASK (0x7 << CCM_PLL6_CTRL_P0_SHIFT)
+ #define CCM_PLL6_CTRL_N_SHIFT 8
+ #define CCM_PLL6_CTRL_N_MASK (0xff << CCM_PLL6_CTRL_N_SHIFT)
+ #define CCM_PLL6_CTRL_DIV1_SHIFT 0
+diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
+index 90110eab10..607efe6a9c 100644
+--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
++++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
+@@ -107,16 +107,26 @@ unsigned int clock_get_pll6(void)
+ {
+ struct sunxi_ccm_reg *const ccm =
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+- int m = IS_ENABLED(CONFIG_MACH_SUN50I_H6) ? 4 : 2;
+-
+ uint32_t rval = readl(&ccm->pll6_cfg);
+ int n = ((rval & CCM_PLL6_CTRL_N_MASK) >> CCM_PLL6_CTRL_N_SHIFT) + 1;
+- int div1 = ((rval & CCM_PLL6_CTRL_DIV1_MASK) >>
+- CCM_PLL6_CTRL_DIV1_SHIFT) + 1;
+ int div2 = ((rval & CCM_PLL6_CTRL_DIV2_MASK) >>
+- CCM_PLL6_CTRL_DIV2_SHIFT) + 1;
+- /* The register defines PLL6-2X or PLL6-4X, not plain PLL6 */
+- return 24000000 / m * n / div1 / div2;
++ CCM_PLL6_CTRL_DIV2_SHIFT) + 1;
++ int div1, m;
++
++ if (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)) {
++ div1 = ((rval & CCM_PLL6_CTRL_P0_MASK) >>
++ CCM_PLL6_CTRL_P0_SHIFT) + 1;
++ m = 1;
++ } else {
++ div1 = ((rval & CCM_PLL6_CTRL_DIV1_MASK) >>
++ CCM_PLL6_CTRL_DIV1_SHIFT) + 1;
++ if (IS_ENABLED(CONFIG_MACH_SUN50I_H6))
++ m = 4;
++ else
++ m = 2;
++ }
++
++ return 24000000U * n / m / div1 / div2;
+ }
+
+ int clock_twi_onoff(int port, int state)
+--
+2.20.1
+
--- /dev/null
+From 5f144dac504c89850a5e37e475b1745137f81d1f Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 21 Jul 2023 14:45:59 +0100
+Subject: [PATCH 4019/4052] sunxi: clock: h6: prepare for PRCM less SoCs
+
+The Allwinner D1/R528/T113 SoCs have a very minimal separate
+"management" power plane, with almost no device attached to it (so
+no r_i2c or r_uart). This means we don't need to flip any clock gates in
+the PRCM block, which in fact those SoCs do not have.
+
+Prepare the code for those SoCs by making the PRCM block optional in the
+H6 SPL clock code, which we otherwise share to this new family of SoCs.
+If the memory map (cpu.h) does not define the PRCM address, we simply
+skip any attempt to program gates there.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+---
+ arch/arm/mach-sunxi/clock_sun50i_h6.c | 22 +++++++++++++++++++---
+ 1 file changed, 19 insertions(+), 3 deletions(-)
+
+diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
+index 607efe6a9c..c3a4623d34 100644
+--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
++++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
+@@ -4,14 +4,20 @@
+ #include <asm/arch/clock.h>
+ #include <asm/arch/prcm.h>
+
++#ifndef SUNXI_PRCM_BASE
++#define SUNXI_PRCM_BASE 0
++#endif
++
+ #ifdef CONFIG_SPL_BUILD
+-void clock_init_safe(void)
++
++static void clock_init_safe_prcm(void)
+ {
+- 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 (!prcm)
++ return;
++
+ if (IS_ENABLED(CONFIG_MACH_SUN50I_H616)) {
+ /* this seems to enable PLLs on H616 */
+ setbits_le32(&prcm->sys_pwroff_gating, 0x10);
+@@ -27,6 +33,14 @@ void clock_init_safe(void)
+ /* set PLL VDD LDO output to 1.14 V */
+ setbits_le32(&prcm->pll_ldo_cfg, 0x60000);
+ }
++}
++
++void clock_init_safe(void)
++{
++ struct sunxi_ccm_reg *const ccm =
++ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
++
++ clock_init_safe_prcm();
+
+ clock_set_pll1(408000000);
+
+@@ -141,6 +155,8 @@ int clock_twi_onoff(int port, int state)
+ value = BIT(GATE_SHIFT) | BIT (RESET_SHIFT);
+
+ if (port == 5) {
++ if (!prcm)
++ return -ENODEV;
+ shift = 0;
+ ptr = &prcm->twi_gate_reset;
+ } else {
+--
+2.20.1
+
--- /dev/null
+From 9d9fdd0d200b0af1250186186cd9061a72f4c341 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Sat, 26 Aug 2023 16:56:03 +0200
+Subject: [PATCH 4020/4052] Kconfig: sunxi: prepare for using drivers/ram/sunxi
+
+At the moment all Allwinner DRAM initialisation routines are stored in
+arch/arm/mach-sunxi, even though those "drivers" are just a giant
+collection of writel's, without any architectural dependency.
+
+The R528/T113-s SoC (with ARM cores) and the D1/D1s Soc (with RISC-V
+cores) share the same die, so should share the same DRAM init routines as
+well.
+
+To prepare for this, add a new sunxi directory inside drivers/ram, and
+add some stub entries to prepare for the addition of the share DRAM code
+for those SoCs.
+
+The RISC-V D1(s) SoCs will probably use SPL_DM, so make this entry
+depend on that already.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+---
+ drivers/ram/Kconfig | 1 +
+ drivers/ram/sunxi/Kconfig | 13 +++++++++++++
+ 2 files changed, 14 insertions(+)
+ create mode 100644 drivers/ram/sunxi/Kconfig
+
+diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig
+index e085119963..636374be59 100644
+--- a/drivers/ram/Kconfig
++++ b/drivers/ram/Kconfig
+@@ -112,3 +112,4 @@ source "drivers/ram/rockchip/Kconfig"
+ source "drivers/ram/sifive/Kconfig"
+ source "drivers/ram/stm32mp1/Kconfig"
+ source "drivers/ram/octeon/Kconfig"
++source "drivers/ram/sunxi/Kconfig"
+diff --git a/drivers/ram/sunxi/Kconfig b/drivers/ram/sunxi/Kconfig
+new file mode 100644
+index 0000000000..97e261de54
+--- /dev/null
++++ b/drivers/ram/sunxi/Kconfig
+@@ -0,0 +1,13 @@
++config DRAM_SUN20I_D1
++ bool "DM DRAM driver support for Allwinner D1"
++ depends on RAM && ARCH_SUNXI
++ default y
++ help
++ This enables support for DRAM drivers using the driver model
++ for Allwinner SoCs.
++
++config DRAM_SUN8I_R528
++ bool "DRAM driver support for Allwinner R528/T113s"
++ default y if MACH_SUN8I_R528
++ help
++ Select this DRAM controller driver for the R528/T113s SoCs.
+--
+2.20.1
+
--- /dev/null
+From 2c2c7ff45ae5a37909042dce3dbd66fa5bdb5fdb Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 21 Jul 2023 14:46:01 +0100
+Subject: [PATCH 4021/4052] sunxi: add R528/T113-s3/D1(s) DRAM initialisation
+ code
+
+The Allwinner R528/T113-s/D1/D1s SoCs all share the same die, so use the
+same DRAM initialisation code.
+Make use of prior art here and lift some code from awboot[1], which
+carried init code based on earlier decompilation efforts, but with a
+GPL2 license tag.
+This code has been heavily reworked and cleaned up, to match previous
+DRAM routines for other SoCs, and also to be closer to U-Boot's coding
+style and support routines.
+The actual DRAM chip timing parameters are included in the main file,
+since they cover all DRAM types, and are protected by a new Kconfig
+CONFIG_SUNXI_DRAM_TYPE symbol, which allows the compiler to pick only
+the relevant settings, at build time.
+
+The relevant DRAM chips/board specific configuration parameters are
+delivered via Kconfig, so this code here should work for all supported
+SoCs and DRAM chips combinations.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+Tested-by: Sam Edwards <CFSworks@gmail.com>
+---
+ drivers/Makefile | 1 +
+ drivers/ram/Makefile | 3 +
+ drivers/ram/sunxi/Kconfig | 59 ++
+ drivers/ram/sunxi/Makefile | 4 +
+ drivers/ram/sunxi/dram_sun20i_d1.c | 1432 ++++++++++++++++++++++++++++
+ drivers/ram/sunxi/dram_sun20i_d1.h | 73 ++
+ 6 files changed, 1572 insertions(+)
+ create mode 100644 drivers/ram/sunxi/Makefile
+ create mode 100644 drivers/ram/sunxi/dram_sun20i_d1.c
+ create mode 100644 drivers/ram/sunxi/dram_sun20i_d1.h
+
+diff --git a/drivers/Makefile b/drivers/Makefile
+index 15d19d0c8a..1542ce7caf 100644
+--- a/drivers/Makefile
++++ b/drivers/Makefile
+@@ -52,6 +52,7 @@ obj-$(CONFIG_$(SPL_)ALTERA_SDRAM) += ddr/altera/
+ obj-$(CONFIG_ARCH_IMX8M) += ddr/imx/imx8m/
+ obj-$(CONFIG_IMX8ULP_DRAM) += ddr/imx/imx8ulp/
+ obj-$(CONFIG_ARCH_IMX9) += ddr/imx/imx9/
++obj-$(CONFIG_DRAM_SUN8I_R528) += ram/
+ obj-$(CONFIG_SPL_DM_RESET) += reset/
+ obj-$(CONFIG_SPL_MUSB_NEW) += usb/musb-new/
+ obj-$(CONFIG_SPL_USB_GADGET) += usb/gadget/
+diff --git a/drivers/ram/Makefile b/drivers/ram/Makefile
+index 83948e2c43..ae3cf65fa4 100644
+--- a/drivers/ram/Makefile
++++ b/drivers/ram/Makefile
+@@ -10,6 +10,9 @@ obj-$(CONFIG_STM32MP1_DDR) += stm32mp1/
+ obj-$(CONFIG_STM32_SDRAM) += stm32_sdram.o
+ obj-$(CONFIG_ARCH_BMIPS) += bmips_ram.o
+
++obj-$(CONFIG_DRAM_SUN8I_R528) += sunxi/
++
++
+ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
+
+ obj-$(CONFIG_K3_AM654_DDRSS) += k3-am654-ddrss.o
+diff --git a/drivers/ram/sunxi/Kconfig b/drivers/ram/sunxi/Kconfig
+index 97e261de54..657f47a870 100644
+--- a/drivers/ram/sunxi/Kconfig
++++ b/drivers/ram/sunxi/Kconfig
+@@ -11,3 +11,62 @@ config DRAM_SUN8I_R528
+ default y if MACH_SUN8I_R528
+ help
+ Select this DRAM controller driver for the R528/T113s SoCs.
++
++if DRAM_SUN20I_D1 || DRAM_SUN8I_R528
++
++config DRAM_SUNXI_ODT_EN
++ hex "DRAM ODT EN parameter"
++ default 0x1
++ help
++ ODT EN value from vendor DRAM settings.
++
++config DRAM_SUNXI_TPR0
++ hex "DRAM TPR0 parameter"
++ default 0x0
++ help
++ TPR0 value from vendor DRAM settings.
++
++config DRAM_SUNXI_TPR11
++ hex "DRAM TPR11 parameter"
++ default 0x0
++ help
++ TPR11 value from vendor DRAM settings.
++
++config DRAM_SUNXI_TPR12
++ hex "DRAM TPR12 parameter"
++ default 0x0
++ help
++ TPR12 value from vendor DRAM settings.
++
++config DRAM_SUNXI_TPR13
++ hex "DRAM TPR13 parameter"
++ default 0x0
++ help
++ TPR13 value from vendor DRAM settings. It tells which features
++ should be configured.
++
++choice
++ prompt "DRAM chip type"
++ default SUNXI_DRAM_TYPE_DDR3 if DRAM_SUN8I_R528 || DRAM_SUN20I_D1
++
++config SUNXI_DRAM_TYPE_DDR2
++ bool "DDR2 chips"
++
++config SUNXI_DRAM_TYPE_DDR3
++ bool "DDR3 chips"
++
++config SUNXI_DRAM_TYPE_LPDDR2
++ bool "LPDDR2 chips"
++
++config SUNXI_DRAM_TYPE_LPDDR3
++ bool "LPDDR3 chips"
++endchoice
++
++config SUNXI_DRAM_TYPE
++ int
++ default 2 if SUNXI_DRAM_TYPE_DDR2
++ default 3 if SUNXI_DRAM_TYPE_DDR3
++ default 6 if SUNXI_DRAM_TYPE_LPDDR2
++ default 7 if SUNXI_DRAM_TYPE_LPDDR3
++
++endif
+diff --git a/drivers/ram/sunxi/Makefile b/drivers/ram/sunxi/Makefile
+new file mode 100644
+index 0000000000..d6fb2cf0b6
+--- /dev/null
++++ b/drivers/ram/sunxi/Makefile
+@@ -0,0 +1,4 @@
++# SPDX-License-Identifier: GPL-2.0+
++
++obj-$(CONFIG_DRAM_SUN20I_D1) += dram_sun20i_d1.o
++obj-$(CONFIG_DRAM_SUN8I_R528) += dram_sun20i_d1.o
+diff --git a/drivers/ram/sunxi/dram_sun20i_d1.c b/drivers/ram/sunxi/dram_sun20i_d1.c
+new file mode 100644
+index 0000000000..c766fc2406
+--- /dev/null
++++ b/drivers/ram/sunxi/dram_sun20i_d1.c
+@@ -0,0 +1,1432 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Allwinner D1/D1s/R528/T113-sx DRAM initialisation
++ *
++ * As usual there is no documentation for the memory controller or PHY IP
++ * used here. The baseline of this code was lifted from awboot[1], which
++ * seems to be based on some form of de-compilation of some original Allwinner
++ * code bits (with a GPL2 license tag from the very beginning).
++ * This version here is a reworked version, to match the U-Boot coding style
++ * and style of the other Allwinner DRAM drivers.
++ *
++ * [1] https://github.com/szemzoa/awboot.git
++ */
++
++#include <asm/io.h>
++#include <common.h>
++#ifdef CONFIG_RAM
++ #include <dm.h>
++ #include <ram.h>
++#endif
++#include <linux/delay.h>
++
++#include "dram_sun20i_d1.h"
++
++#ifndef SUNXI_SID_BASE
++#define SUNXI_SID_BASE 0x3006200
++#endif
++
++#ifndef SUNXI_CCM_BASE
++#define SUNXI_CCM_BASE 0x2001000
++#endif
++
++static void sid_read_ldoB_cal(const dram_para_t *para)
++{
++ uint32_t reg;
++
++ reg = (readl(SUNXI_SID_BASE + 0x1c) & 0xff00) >> 8;
++
++ if (reg == 0)
++ return;
++
++ switch (para->dram_type) {
++ case SUNXI_DRAM_TYPE_DDR2:
++ break;
++ case SUNXI_DRAM_TYPE_DDR3:
++ if (reg > 0x20)
++ reg -= 0x16;
++ break;
++ default:
++ reg = 0;
++ break;
++ }
++
++ clrsetbits_le32(0x3000150, 0xff00, reg << 8);
++}
++
++static void dram_voltage_set(const dram_para_t *para)
++{
++ int vol;
++
++ switch (para->dram_type) {
++ case SUNXI_DRAM_TYPE_DDR2:
++ vol = 47;
++ break;
++ case SUNXI_DRAM_TYPE_DDR3:
++ vol = 25;
++ break;
++ default:
++ vol = 0;
++ break;
++ }
++
++ clrsetbits_le32(0x3000150, 0x20ff00, vol << 8);
++
++ udelay(1);
++
++ sid_read_ldoB_cal(para);
++}
++
++static void dram_enable_all_master(void)
++{
++ writel(~0, 0x3102020);
++ writel(0xff, 0x3102024);
++ writel(0xffff, 0x3102028);
++ udelay(10);
++}
++
++static void dram_disable_all_master(void)
++{
++ writel(1, 0x3102020);
++ writel(0, 0x3102024);
++ writel(0, 0x3102028);
++ udelay(10);
++}
++
++static void eye_delay_compensation(const dram_para_t *para)
++{
++ uint32_t delay;
++ unsigned long ptr;
++
++ // DATn0IOCR, n = 0...7
++ delay = (para->dram_tpr11 & 0xf) << 9;
++ delay |= (para->dram_tpr12 & 0xf) << 1;
++ for (ptr = 0x3103310; ptr < 0x3103334; ptr += 4)
++ setbits_le32(ptr, delay);
++
++ // DATn1IOCR, n = 0...7
++ delay = (para->dram_tpr11 & 0xf0) << 5;
++ delay |= (para->dram_tpr12 & 0xf0) >> 3;
++ for (ptr = 0x3103390; ptr != 0x31033b4; ptr += 4)
++ setbits_le32(ptr, delay);
++
++ // PGCR0: assert AC loopback FIFO reset
++ clrbits_le32(0x3103100, 0x04000000);
++
++ // ??
++
++ delay = (para->dram_tpr11 & 0xf0000) >> 7;
++ delay |= (para->dram_tpr12 & 0xf0000) >> 15;
++ setbits_le32(0x3103334, delay);
++ setbits_le32(0x3103338, delay);
++
++ delay = (para->dram_tpr11 & 0xf00000) >> 11;
++ delay |= (para->dram_tpr12 & 0xf00000) >> 19;
++ setbits_le32(0x31033b4, delay);
++ setbits_le32(0x31033b8, delay);
++
++ setbits_le32(0x310333c, (para->dram_tpr11 & 0xf0000) << 9);
++ setbits_le32(0x31033bc, (para->dram_tpr11 & 0xf00000) << 5);
++
++ // PGCR0: release AC loopback FIFO reset
++ setbits_le32(0x3103100, BIT(26));
++
++ udelay(1);
++
++ delay = (para->dram_tpr10 & 0xf0) << 4;
++ for (ptr = 0x3103240; ptr != 0x310327c; ptr += 4)
++ setbits_le32(ptr, delay);
++ for (ptr = 0x3103228; ptr != 0x3103240; ptr += 4)
++ setbits_le32(ptr, delay);
++
++ setbits_le32(0x3103218, (para->dram_tpr10 & 0x0f) << 8);
++ setbits_le32(0x310321c, (para->dram_tpr10 & 0x0f) << 8);
++
++ setbits_le32(0x3103280, (para->dram_tpr10 & 0xf00) >> 4);
++}
++
++/*
++ * Main purpose of the auto_set_timing routine seems to be to calculate all
++ * timing settings for the specific type of sdram used. Read together with
++ * an sdram datasheet for context on the various variables.
++ */
++static void mctl_set_timing_params(const dram_para_t *para,
++ const dram_config_t *config)
++{
++ /* DRAM_TPR0 */
++ u8 tccd = 2;
++ u8 tfaw;
++ u8 trrd;
++ u8 trcd;
++ u8 trc;
++
++ /* DRAM_TPR1 */
++ u8 txp;
++ u8 twtr;
++ u8 trtp = 4;
++ u8 twr;
++ u8 trp;
++ u8 tras;
++
++ /* DRAM_TPR2 */
++ u16 trefi;
++ u16 trfc;
++
++ u8 tcksrx;
++ u8 tckesr;
++ u8 trd2wr;
++ u8 twr2rd;
++ u8 trasmax;
++ u8 twtp;
++ u8 tcke;
++ u8 tmod;
++ u8 tmrd;
++ u8 tmrw;
++
++ u8 tcl;
++ u8 tcwl;
++ u8 t_rdata_en;
++ u8 wr_latency;
++
++ u32 mr0;
++ u32 mr1;
++ u32 mr2;
++ u32 mr3;
++
++ u32 tdinit0;
++ u32 tdinit1;
++ u32 tdinit2;
++ u32 tdinit3;
++
++ switch (para->dram_type) {
++ case SUNXI_DRAM_TYPE_DDR2:
++ /* DRAM_TPR0 */
++ tfaw = ns_to_t(50);
++ trrd = ns_to_t(10);
++ trcd = ns_to_t(20);
++ trc = ns_to_t(65);
++
++ /* DRAM_TPR1 */
++ txp = 2;
++ twtr = ns_to_t(8);
++ twr = ns_to_t(15);
++ trp = ns_to_t(15);
++ tras = ns_to_t(45);
++
++ /* DRAM_TRP2 */
++ trfc = ns_to_t(328);
++ trefi = ns_to_t(7800) / 32;
++
++ trasmax = CONFIG_DRAM_CLK / 30;
++ if (CONFIG_DRAM_CLK < 409) {
++ t_rdata_en = 1;
++ tcl = 3;
++ mr0 = 0x06a3;
++ } else {
++ t_rdata_en = 2;
++ tcl = 4;
++ mr0 = 0x0e73;
++ }
++ tmrd = 2;
++ twtp = twr + 5;
++ tcksrx = 5;
++ tckesr = 4;
++ trd2wr = 4;
++ tcke = 3;
++ tmod = 12;
++ wr_latency = 1;
++ tmrw = 0;
++ twr2rd = twtr + 5;
++ tcwl = 0;
++
++ mr1 = para->dram_mr1;
++ mr2 = 0;
++ mr3 = 0;
++
++ tdinit0 = 200 * CONFIG_DRAM_CLK + 1;
++ tdinit1 = 100 * CONFIG_DRAM_CLK / 1000 + 1;
++ tdinit2 = 200 * CONFIG_DRAM_CLK + 1;
++ tdinit3 = 1 * CONFIG_DRAM_CLK + 1;
++
++ break;
++ case SUNXI_DRAM_TYPE_DDR3:
++ trfc = ns_to_t(350);
++ trefi = ns_to_t(7800) / 32 + 1; // XXX
++
++ twtr = ns_to_t(8) + 2; // + 2 ? XXX
++ /* Only used by trd2wr calculation, which gets discard below */
++// twr = max(ns_to_t(15), 2);
++ trrd = max(ns_to_t(10), 2);
++ txp = max(ns_to_t(10), 2);
++
++ if (CONFIG_DRAM_CLK <= 800) {
++ tfaw = ns_to_t(50);
++ trcd = ns_to_t(15);
++ trp = ns_to_t(15);
++ trc = ns_to_t(53);
++ tras = ns_to_t(38);
++
++ mr0 = 0x1c70;
++ mr2 = 0x18;
++ tcl = 6;
++ wr_latency = 2;
++ tcwl = 4;
++ t_rdata_en = 4;
++ } else {
++ tfaw = ns_to_t(35);
++ trcd = ns_to_t(14);
++ trp = ns_to_t(14);
++ trc = ns_to_t(48);
++ tras = ns_to_t(34);
++
++ mr0 = 0x1e14;
++ mr2 = 0x20;
++ tcl = 7;
++ wr_latency = 3;
++ tcwl = 5;
++ t_rdata_en = 5;
++ }
++
++ trasmax = CONFIG_DRAM_CLK / 30;
++ twtp = tcwl + 2 + twtr; // WL+BL/2+tWTR
++ /* Gets overwritten below */
++// trd2wr = tcwl + 2 + twr; // WL+BL/2+tWR
++ twr2rd = tcwl + twtr; // WL+tWTR
++
++ tdinit0 = 500 * CONFIG_DRAM_CLK + 1; // 500 us
++ tdinit1 = 360 * CONFIG_DRAM_CLK / 1000 + 1; // 360 ns
++ tdinit2 = 200 * CONFIG_DRAM_CLK + 1; // 200 us
++ tdinit3 = 1 * CONFIG_DRAM_CLK + 1; // 1 us
++
++ mr1 = para->dram_mr1;
++ mr3 = 0;
++ tcke = 3;
++ tcksrx = 5;
++ tckesr = 4;
++ if (((config->dram_tpr13 & 0xc) == 0x04) || CONFIG_DRAM_CLK < 912)
++ trd2wr = 5;
++ else
++ trd2wr = 6;
++
++ tmod = 12;
++ tmrd = 4;
++ tmrw = 0;
++
++ break;
++ case SUNXI_DRAM_TYPE_LPDDR2:
++ tfaw = max(ns_to_t(50), 4);
++ trrd = max(ns_to_t(10), 1);
++ trcd = max(ns_to_t(24), 2);
++ trc = ns_to_t(70);
++ txp = ns_to_t(8);
++ if (txp < 2) {
++ txp++;
++ twtr = 2;
++ } else {
++ twtr = txp;
++ }
++ twr = max(ns_to_t(15), 2);
++ trp = ns_to_t(17);
++ tras = ns_to_t(42);
++ trefi = ns_to_t(3900) / 32;
++ trfc = ns_to_t(210);
++
++ trasmax = CONFIG_DRAM_CLK / 60;
++ mr3 = para->dram_mr3;
++ twtp = twr + 5;
++ mr2 = 6;
++ mr1 = 5;
++ tcksrx = 5;
++ tckesr = 5;
++ trd2wr = 10;
++ tcke = 2;
++ tmod = 5;
++ tmrd = 5;
++ tmrw = 3;
++ tcl = 4;
++ wr_latency = 1;
++ t_rdata_en = 1;
++
++ tdinit0 = 200 * CONFIG_DRAM_CLK + 1;
++ tdinit1 = 100 * CONFIG_DRAM_CLK / 1000 + 1;
++ tdinit2 = 11 * CONFIG_DRAM_CLK + 1;
++ tdinit3 = 1 * CONFIG_DRAM_CLK + 1;
++ twr2rd = twtr + 5;
++ tcwl = 2;
++ mr1 = 195;
++ mr0 = 0;
++
++ break;
++ case SUNXI_DRAM_TYPE_LPDDR3:
++ tfaw = max(ns_to_t(50), 4);
++ trrd = max(ns_to_t(10), 1);
++ trcd = max(ns_to_t(24), 2);
++ trc = ns_to_t(70);
++ twtr = max(ns_to_t(8), 2);
++ twr = max(ns_to_t(15), 2);
++ trp = ns_to_t(17);
++ tras = ns_to_t(42);
++ trefi = ns_to_t(3900) / 32;
++ trfc = ns_to_t(210);
++ txp = twtr;
++
++ trasmax = CONFIG_DRAM_CLK / 60;
++ if (CONFIG_DRAM_CLK < 800) {
++ tcwl = 4;
++ wr_latency = 3;
++ t_rdata_en = 6;
++ mr2 = 12;
++ } else {
++ tcwl = 3;
++ tcke = 6;
++ wr_latency = 2;
++ t_rdata_en = 5;
++ mr2 = 10;
++ }
++ twtp = tcwl + 5;
++ tcl = 7;
++ mr3 = para->dram_mr3;
++ tcksrx = 5;
++ tckesr = 5;
++ trd2wr = 13;
++ tcke = 3;
++ tmod = 12;
++ tdinit0 = 400 * CONFIG_DRAM_CLK + 1;
++ tdinit1 = 500 * CONFIG_DRAM_CLK / 1000 + 1;
++ tdinit2 = 11 * CONFIG_DRAM_CLK + 1;
++ tdinit3 = 1 * CONFIG_DRAM_CLK + 1;
++ tmrd = 5;
++ tmrw = 5;
++ twr2rd = tcwl + twtr + 5;
++ mr1 = 195;
++ mr0 = 0;
++
++ break;
++ default:
++ trfc = 128;
++ trp = 6;
++ trefi = 98;
++ txp = 10;
++ twr = 8;
++ twtr = 3;
++ tras = 14;
++ tfaw = 16;
++ trc = 20;
++ trcd = 6;
++ trrd = 3;
++
++ twr2rd = 8;
++ tcksrx = 4;
++ tckesr = 3;
++ trd2wr = 4;
++ trasmax = 27;
++ twtp = 12;
++ tcke = 2;
++ tmod = 6;
++ tmrd = 2;
++ tmrw = 0;
++ tcwl = 3;
++ tcl = 3;
++ wr_latency = 1;
++ t_rdata_en = 1;
++ mr3 = 0;
++ mr2 = 0;
++ mr1 = 0;
++ mr0 = 0;
++ tdinit3 = 0;
++ tdinit2 = 0;
++ tdinit1 = 0;
++ tdinit0 = 0;
++
++ break;
++ }
++
++ /* Set mode registers */
++ writel(mr0, 0x3103030);
++ writel(mr1, 0x3103034);
++ writel(mr2, 0x3103038);
++ writel(mr3, 0x310303c);
++ /* TODO: dram_odt_en is either 0x0 or 0x1, so right shift looks weird */
++ writel((para->dram_odt_en >> 4) & 0x3, 0x310302c);
++
++ /* Set dram timing DRAMTMG0 - DRAMTMG5 */
++ writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | (tras << 0),
++ 0x3103058);
++ writel((txp << 16) | (trtp << 8) | (trc << 0),
++ 0x310305c);
++ writel((tcwl << 24) | (tcl << 16) | (trd2wr << 8) | (twr2rd << 0),
++ 0x3103060);
++ writel((tmrw << 16) | (tmrd << 12) | (tmod << 0),
++ 0x3103064);
++ writel((trcd << 24) | (tccd << 16) | (trrd << 8) | (trp << 0),
++ 0x3103068);
++ writel((tcksrx << 24) | (tcksrx << 16) | (tckesr << 8) | (tcke << 0),
++ 0x310306c);
++
++ /* Set dual rank timing */
++ clrsetbits_le32(0x3103078, 0xf000ffff,
++ (CONFIG_DRAM_CLK < 800) ? 0xf0006610 : 0xf0007610);
++
++ /* Set phy interface time PITMG0, PTR3, PTR4 */
++ writel((0x2 << 24) | (t_rdata_en << 16) | BIT(8) | (wr_latency << 0),
++ 0x3103080);
++ writel(((tdinit0 << 0) | (tdinit1 << 20)), 0x3103050);
++ writel(((tdinit2 << 0) | (tdinit3 << 20)), 0x3103054);
++
++ /* Set refresh timing and mode */
++ writel((trefi << 16) | (trfc << 0), 0x3103090);
++ writel((trefi << 15) & 0x0fff0000, 0x3103094);
++}
++
++// Purpose of this routine seems to be to initialize the PLL driving
++// the MBUS and sdram.
++//
++static int ccu_set_pll_ddr_clk(int index, const dram_para_t *para,
++ const dram_config_t *config)
++{
++ unsigned int val, clk, n;
++
++ if (config->dram_tpr13 & BIT(6))
++ clk = para->dram_tpr9;
++ else
++ clk = para->dram_clk;
++
++ // set VCO clock divider
++ n = (clk * 2) / 24;
++
++ val = readl(SUNXI_CCM_BASE + 0x10);
++ val &= ~0x0007ff03; // clear dividers
++ val |= (n - 1) << 8; // set PLL division
++ val |= BIT(31) | BIT(30); // enable PLL and LDO
++ writel(val | BIT(29), SUNXI_CCM_BASE + 0x10);
++
++ // wait for PLL to lock
++ while ((readl(SUNXI_CCM_BASE + 0x10) & BIT(28)) == 0)
++ ;
++
++ udelay(20);
++
++ // enable PLL output
++ setbits_le32(SUNXI_CCM_BASE + 0x0, BIT(27));
++
++ // turn clock gate on
++ val = readl(SUNXI_CCM_BASE + 0x800);
++ val &= ~0x03000303; // select DDR clk source, n=1, m=1
++ val |= BIT(31); // turn clock on
++ writel(val, SUNXI_CCM_BASE + 0x800);
++
++ return n * 24;
++}
++
++/* Set up the PLL and clock gates for the DRAM controller and MBUS clocks. */
++static void mctl_sys_init(const dram_para_t *para, const dram_config_t *config)
++{
++ // assert MBUS reset
++ clrbits_le32(SUNXI_CCM_BASE + 0x540, BIT(30));
++
++ // turn off sdram clock gate, assert sdram reset
++ clrbits_le32(SUNXI_CCM_BASE + 0x80c, 0x10001);
++ clrsetbits_le32(SUNXI_CCM_BASE + 0x800, BIT(31) | BIT(30), BIT(27));
++ udelay(10);
++
++ // set ddr pll clock
++ ccu_set_pll_ddr_clk(0, para, config);
++ udelay(100);
++ dram_disable_all_master();
++
++ // release sdram reset
++ setbits_le32(SUNXI_CCM_BASE + 0x80c, BIT(16));
++
++ // release MBUS reset
++ setbits_le32(SUNXI_CCM_BASE + 0x540, BIT(30));
++ setbits_le32(SUNXI_CCM_BASE + 0x800, BIT(30));
++
++ udelay(5);
++
++ // turn on sdram clock gate
++ setbits_le32(SUNXI_CCM_BASE + 0x80c, BIT(0));
++
++ // turn dram clock gate on, trigger sdr clock update
++ setbits_le32(SUNXI_CCM_BASE + 0x800, BIT(31) | BIT(27));
++ udelay(5);
++
++ // mCTL clock enable
++ writel(0x8000, 0x310300c);
++ udelay(10);
++}
++
++// The main purpose of this routine seems to be to copy an address configuration
++// from the dram_para1 and dram_para2 fields to the PHY configuration registers
++// (0x3102000, 0x3102004).
++//
++static void mctl_com_init(const dram_para_t *para, const dram_config_t *config)
++{
++ uint32_t val, width;
++ unsigned long ptr;
++ int i;
++
++ // purpose ??
++ clrsetbits_le32(0x3102008, 0x3f00, 0x2000);
++
++ // set SDRAM type and word width
++ val = readl(0x3102000) & ~0x00fff000;
++ val |= (para->dram_type & 0x7) << 16; // DRAM type
++ val |= (~config->dram_para2 & 0x1) << 12; // DQ width
++ val |= BIT(22); // ??
++ if (para->dram_type == SUNXI_DRAM_TYPE_LPDDR2 ||
++ para->dram_type == SUNXI_DRAM_TYPE_LPDDR3) {
++ val |= BIT(19); // type 6 and 7 must use 1T
++ } else {
++ if (config->dram_tpr13 & BIT(5))
++ val |= BIT(19);
++ }
++ writel(val, 0x3102000);
++
++ // init rank / bank / row for single/dual or two different ranks
++ if ((config->dram_para2 & BIT(8)) &&
++ ((config->dram_para2 & 0xf000) != 0x1000))
++ width = 32;
++ else
++ width = 16;
++
++ ptr = 0x3102000;
++ for (i = 0; i < width; i += 16) {
++ val = readl(ptr) & 0xfffff000;
++
++ val |= (config->dram_para2 >> 12) & 0x3; // rank
++ val |= ((config->dram_para1 >> (i + 12)) << 2) & 0x4; // bank - 2
++ val |= (((config->dram_para1 >> (i + 4)) - 1) << 4) & 0xff; // row - 1
++
++ // convert from page size to column addr width - 3
++ switch ((config->dram_para1 >> i) & 0xf) {
++ case 8: val |= 0xa00; break;
++ case 4: val |= 0x900; break;
++ case 2: val |= 0x800; break;
++ case 1: val |= 0x700; break;
++ default: val |= 0x600; break;
++ }
++ writel(val, ptr);
++ ptr += 4;
++ }
++
++ // set ODTMAP based on number of ranks in use
++ val = (readl(0x3102000) & 0x1) ? 0x303 : 0x201;
++ writel(val, 0x3103120);
++
++ // set mctl reg 3c4 to zero when using half DQ
++ if (config->dram_para2 & BIT(0))
++ writel(0, 0x31033c4);
++
++ // purpose ??
++ if (para->dram_tpr4) {
++ setbits_le32(0x3102000, (para->dram_tpr4 & 0x3) << 25);
++ setbits_le32(0x3102004, (para->dram_tpr4 & 0x7fc) << 10);
++ }
++}
++
++static const uint8_t ac_remapping_tables[][22] = {
++ [0] = { 0 },
++ [1] = { 1, 9, 3, 7, 8, 18, 4, 13, 5, 6, 10,
++ 2, 14, 12, 0, 0, 21, 17, 20, 19, 11, 22 },
++ [2] = { 4, 9, 3, 7, 8, 18, 1, 13, 2, 6, 10,
++ 5, 14, 12, 0, 0, 21, 17, 20, 19, 11, 22 },
++ [3] = { 1, 7, 8, 12, 10, 18, 4, 13, 5, 6, 3,
++ 2, 9, 0, 0, 0, 21, 17, 20, 19, 11, 22 },
++ [4] = { 4, 12, 10, 7, 8, 18, 1, 13, 2, 6, 3,
++ 5, 9, 0, 0, 0, 21, 17, 20, 19, 11, 22 },
++ [5] = { 13, 2, 7, 9, 12, 19, 5, 1, 6, 3, 4,
++ 8, 10, 0, 0, 0, 21, 22, 18, 17, 11, 20 },
++ [6] = { 3, 10, 7, 13, 9, 11, 1, 2, 4, 6, 8,
++ 5, 12, 0, 0, 0, 20, 1, 0, 21, 22, 17 },
++ [7] = { 3, 2, 4, 7, 9, 1, 17, 12, 18, 14, 13,
++ 8, 15, 6, 10, 5, 19, 22, 16, 21, 20, 11 },
++};
++
++/*
++ * This routine chooses one of several remapping tables for 22 lines.
++ * It is unclear which lines are being remapped. It seems to pick
++ * table cfg7 for the Nezha board.
++ */
++static void mctl_phy_ac_remapping(const dram_para_t *para,
++ const dram_config_t *config)
++{
++ const uint8_t *cfg;
++ uint32_t fuse, val;
++
++ /*
++ * It is unclear whether the LPDDRx types don't need any remapping,
++ * or whether the original code just didn't provide tables.
++ */
++ if (para->dram_type != SUNXI_DRAM_TYPE_DDR2 &&
++ para->dram_type != SUNXI_DRAM_TYPE_DDR3)
++ return;
++
++ fuse = (readl(SUNXI_SID_BASE + 0x28) & 0xf00) >> 8;
++ debug("DDR efuse: 0x%x\n", fuse);
++
++ if (para->dram_type == SUNXI_DRAM_TYPE_DDR2) {
++ if (fuse == 15)
++ return;
++ cfg = ac_remapping_tables[6];
++ } else {
++ if (config->dram_tpr13 & 0xc0000) {
++ cfg = ac_remapping_tables[7];
++ } else {
++ switch (fuse) {
++ case 8: cfg = ac_remapping_tables[2]; break;
++ case 9: cfg = ac_remapping_tables[3]; break;
++ case 10: cfg = ac_remapping_tables[5]; break;
++ case 11: cfg = ac_remapping_tables[4]; break;
++ default:
++ case 12: cfg = ac_remapping_tables[1]; break;
++ case 13:
++ case 14: cfg = ac_remapping_tables[0]; break;
++ }
++ }
++ }
++
++ val = (cfg[4] << 25) | (cfg[3] << 20) | (cfg[2] << 15) |
++ (cfg[1] << 10) | (cfg[0] << 5);
++ writel(val, 0x3102500);
++
++ val = (cfg[10] << 25) | (cfg[9] << 20) | (cfg[8] << 15) |
++ (cfg[ 7] << 10) | (cfg[6] << 5) | cfg[5];
++ writel(val, 0x3102504);
++
++ val = (cfg[15] << 20) | (cfg[14] << 15) | (cfg[13] << 10) |
++ (cfg[12] << 5) | cfg[11];
++ writel(val, 0x3102508);
++
++ val = (cfg[21] << 25) | (cfg[20] << 20) | (cfg[19] << 15) |
++ (cfg[18] << 10) | (cfg[17] << 5) | cfg[16];
++ writel(val, 0x310250c);
++
++ val = (cfg[4] << 25) | (cfg[3] << 20) | (cfg[2] << 15) |
++ (cfg[1] << 10) | (cfg[0] << 5) | 1;
++ writel(val, 0x3102500);
++}
++
++// Init the controller channel. The key part is placing commands in the main
++// command register (PIR, 0x3103000) and checking command status (PGSR0, 0x3103010).
++//
++static unsigned int mctl_channel_init(unsigned int ch_index,
++ const dram_para_t *para,
++ const dram_config_t *config)
++{
++ unsigned int val, dqs_gating_mode;
++
++ dqs_gating_mode = (config->dram_tpr13 & 0xc) >> 2;
++
++ // set DDR clock to half of CPU clock
++ clrsetbits_le32(0x310200c, 0xfff, (para->dram_clk / 2) - 1);
++
++ // MRCTRL0 nibble 3 undocumented
++ clrsetbits_le32(0x3103108, 0xf00, 0x300);
++
++ if (para->dram_odt_en)
++ val = 0;
++ else
++ val = BIT(5);
++
++ // DX0GCR0
++ if (para->dram_clk > 672)
++ clrsetbits_le32(0x3103344, 0xf63e, val);
++ else
++ clrsetbits_le32(0x3103344, 0xf03e, val);
++
++ // DX1GCR0
++ if (para->dram_clk > 672) {
++ setbits_le32(0x3103344, 0x400);
++ clrsetbits_le32(0x31033c4, 0xf63e, val);
++ } else {
++ clrsetbits_le32(0x31033c4, 0xf03e, val);
++ }
++
++ // 0x3103208 undocumented
++ setbits_le32(0x3103208, BIT(1));
++
++ eye_delay_compensation(para);
++
++ // set PLL SSCG ?
++ val = readl(0x3103108);
++ if (dqs_gating_mode == 1) {
++ clrsetbits_le32(0x3103108, 0xc0, 0);
++ clrbits_le32(0x31030bc, 0x107);
++ } else if (dqs_gating_mode == 2) {
++ clrsetbits_le32(0x3103108, 0xc0, 0x80);
++
++ clrsetbits_le32(0x31030bc, 0x107,
++ (((config->dram_tpr13 >> 16) & 0x1f) - 2) | 0x100);
++ clrsetbits_le32(0x310311c, BIT(31), BIT(27));
++ } else {
++ clrbits_le32(0x3103108, 0x40);
++ udelay(10);
++ setbits_le32(0x3103108, 0xc0);
++ }
++
++ if (para->dram_type == SUNXI_DRAM_TYPE_LPDDR2 ||
++ para->dram_type == SUNXI_DRAM_TYPE_LPDDR3) {
++ if (dqs_gating_mode == 1)
++ clrsetbits_le32(0x310311c, 0x080000c0, 0x80000000);
++ else
++ clrsetbits_le32(0x310311c, 0x77000000, 0x22000000);
++ }
++
++ clrsetbits_le32(0x31030c0, 0x0fffffff,
++ (config->dram_para2 & BIT(12)) ? 0x03000001 : 0x01000007);
++
++ if (readl(0x70005d4) & BIT(16)) {
++ clrbits_le32(0x7010250, 0x2);
++ udelay(10);
++ }
++
++ // Set ZQ config
++ clrsetbits_le32(0x3103140, 0x3ffffff,
++ (para->dram_zq & 0x00ffffff) | BIT(25));
++
++ // Initialise DRAM controller
++ if (dqs_gating_mode == 1) {
++ //writel(0x52, 0x3103000); // prep PHY reset + PLL init + z-cal
++ writel(0x53, 0x3103000); // Go
++
++ while ((readl(0x3103010) & 0x1) == 0) {
++ } // wait for IDONE
++ udelay(10);
++
++ // 0x520 = prep DQS gating + DRAM init + d-cal
++ if (para->dram_type == SUNXI_DRAM_TYPE_DDR3)
++ writel(0x5a0, 0x3103000); // + DRAM reset
++ else
++ writel(0x520, 0x3103000);
++ } else {
++ if ((readl(0x70005d4) & (1 << 16)) == 0) {
++ // prep DRAM init + PHY reset + d-cal + PLL init + z-cal
++ if (para->dram_type == SUNXI_DRAM_TYPE_DDR3)
++ writel(0x1f2, 0x3103000); // + DRAM reset
++ else
++ writel(0x172, 0x3103000);
++ } else {
++ // prep PHY reset + d-cal + z-cal
++ writel(0x62, 0x3103000);
++ }
++ }
++
++ setbits_le32(0x3103000, 0x1); // GO
++
++ udelay(10);
++ while ((readl(0x3103010) & 0x1) == 0) {
++ } // wait for IDONE
++
++ if (readl(0x70005d4) & BIT(16)) {
++ clrsetbits_le32(0x310310c, 0x06000000, 0x04000000);
++ udelay(10);
++
++ setbits_le32(0x3103004, 0x1);
++
++ while ((readl(0x3103018) & 0x7) != 0x3) {
++ }
++
++ clrbits_le32(0x7010250, 0x1);
++ udelay(10);
++
++ clrbits_le32(0x3103004, 0x1);
++
++ while ((readl(0x3103018) & 0x7) != 0x1) {
++ }
++
++ udelay(15);
++
++ if (dqs_gating_mode == 1) {
++ clrbits_le32(0x3103108, 0xc0);
++ clrsetbits_le32(0x310310c, 0x06000000, 0x02000000);
++ udelay(1);
++ writel(0x401, 0x3103000);
++
++ while ((readl(0x3103010) & 0x1) == 0) {
++ }
++ }
++ }
++
++ // Check for training error
++ if (readl(0x3103010) & BIT(20)) {
++ printf("ZQ calibration error, check external 240 ohm resistor\n");
++ return 0;
++ }
++
++ // STATR = Zynq STAT? Wait for status 'normal'?
++ while ((readl(0x3103018) & 0x1) == 0) {
++ }
++
++ setbits_le32(0x310308c, BIT(31));
++ udelay(10);
++ clrbits_le32(0x310308c, BIT(31));
++ udelay(10);
++ setbits_le32(0x3102014, BIT(31));
++ udelay(10);
++
++ clrbits_le32(0x310310c, 0x06000000);
++
++ if (dqs_gating_mode == 1)
++ clrsetbits_le32(0x310311c, 0xc0, 0x40);
++
++ return 1;
++}
++
++static unsigned int calculate_rank_size(uint32_t regval)
++{
++ unsigned int bits;
++
++ bits = (regval >> 8) & 0xf; /* page size - 3 */
++ bits += (regval >> 4) & 0xf; /* row width - 1 */
++ bits += (regval >> 2) & 0x3; /* bank count - 2 */
++ bits -= 14; /* 1MB = 20 bits, minus above 6 = 14 */
++
++ return 1U << bits;
++}
++
++/*
++ * The below routine reads the dram config registers and extracts
++ * the number of address bits in each rank available. It then calculates
++ * total memory size in MB.
++ */
++static unsigned int DRAMC_get_dram_size(void)
++{
++ uint32_t val;
++ unsigned int size;
++
++ val = readl(0x3102000); /* MC_WORK_MODE0 */
++ size = calculate_rank_size(val);
++ if ((val & 0x3) == 0) /* single rank? */
++ return size;
++
++ val = readl(0x3102004); /* MC_WORK_MODE1 */
++ if ((val & 0x3) == 0) /* two identical ranks? */
++ return size * 2;
++
++ /* add sizes of both ranks */
++ return size + calculate_rank_size(val);
++}
++
++/*
++ * The below routine reads the command status register to extract
++ * DQ width and rank count. This follows the DQS training command in
++ * channel_init. If error bit 22 is reset, we have two ranks and full DQ.
++ * If there was an error, figure out whether it was half DQ, single rank,
++ * or both. Set bit 12 and 0 in dram_para2 with the results.
++ */
++static int dqs_gate_detect(dram_config_t *config)
++{
++ uint32_t dx0, dx1;
++
++ if ((readl(0x3103010) & BIT(22)) == 0) {
++ config->dram_para2 = (config->dram_para2 & ~0xf) | BIT(12);
++ debug("dual rank and full DQ\n");
++
++ return 1;
++ }
++
++ dx0 = (readl(0x3103348) & 0x3000000) >> 24;
++ if (dx0 == 0) {
++ config->dram_para2 = (config->dram_para2 & ~0xf) | 0x1001;
++ debug("dual rank and half DQ\n");
++
++ return 1;
++ }
++
++ if (dx0 == 2) {
++ dx1 = (readl(0x31033c8) & 0x3000000) >> 24;
++ if (dx1 == 2) {
++ config->dram_para2 = config->dram_para2 & ~0xf00f;
++ debug("single rank and full DQ\n");
++ } else {
++ config->dram_para2 = (config->dram_para2 & ~0xf00f) | BIT(0);
++ debug("single rank and half DQ\n");
++ }
++
++ return 1;
++ }
++
++ if ((config->dram_tpr13 & BIT(29)) == 0)
++ return 0;
++
++ debug("DX0 state: %d\n", dx0);
++ debug("DX1 state: %d\n", dx1);
++
++ return 0;
++}
++
++static int dramc_simple_wr_test(unsigned int mem_mb, int len)
++{
++ unsigned int offs = (mem_mb / 2) << 18; // half of memory size
++ unsigned int patt1 = 0x01234567;
++ unsigned int patt2 = 0xfedcba98;
++ unsigned int *addr, v1, v2, i;
++
++ addr = (unsigned int *)CFG_SYS_SDRAM_BASE;
++ for (i = 0; i != len; i++, addr++) {
++ writel(patt1 + i, (unsigned long)addr);
++ writel(patt2 + i, (unsigned long)(addr + offs));
++ }
++
++ addr = (unsigned int *)CFG_SYS_SDRAM_BASE;
++ for (i = 0; i != len; i++) {
++ v1 = readl((unsigned long)(addr + i));
++ v2 = patt1 + i;
++ if (v1 != v2) {
++ printf("DRAM: simple test FAIL\n");
++ printf("%x != %x at address %p\n", v1, v2, addr + i);
++ return 1;
++ }
++ v1 = readl((unsigned long)(addr + offs + i));
++ v2 = patt2 + i;
++ if (v1 != v2) {
++ printf("DRAM: simple test FAIL\n");
++ printf("%x != %x at address %p\n", v1, v2, addr + offs + i);
++ return 1;
++ }
++ }
++
++ debug("DRAM: simple test OK\n");
++ return 0;
++}
++
++// Set the Vref mode for the controller
++//
++static void mctl_vrefzq_init(const dram_para_t *para, const dram_config_t *config)
++{
++ if (config->dram_tpr13 & BIT(17))
++ return;
++
++ clrsetbits_le32(0x3103110, 0x7f7f7f7f, para->dram_tpr5);
++
++ // IOCVR1
++ if ((config->dram_tpr13 & BIT(16)) == 0)
++ clrsetbits_le32(0x3103114, 0x7f, para->dram_tpr6 & 0x7f);
++}
++
++// Perform an init of the controller. This is actually done 3 times. The first
++// time to establish the number of ranks and DQ width. The second time to
++// establish the actual ram size. The third time is final one, with the final
++// settings.
++//
++static int mctl_core_init(const dram_para_t *para, const dram_config_t *config)
++{
++ mctl_sys_init(para, config);
++
++ mctl_vrefzq_init(para, config);
++
++ mctl_com_init(para, config);
++
++ mctl_phy_ac_remapping(para, config);
++
++ mctl_set_timing_params(para, config);
++
++ return mctl_channel_init(0, para, config);
++}
++
++/*
++ * This routine sizes a DRAM device by cycling through address lines and
++ * figuring out if they are connected to a real address line, or if the
++ * address is a mirror.
++ * First the column and bank bit allocations are set to low values (2 and 9
++ * address lines). Then a maximum allocation (16 lines) is set for rows and
++ * this is tested.
++ * Next the BA2 line is checked. This seems to be placed above the column,
++ * BA0-1 and row addresses. Finally, the column address is allocated 13 lines
++ * and these are tested. The results are placed in dram_para1 and dram_para2.
++ */
++static int auto_scan_dram_size(const dram_para_t *para, dram_config_t *config)
++{
++ unsigned int rval, i, j, rank, maxrank, offs;
++ unsigned int shft;
++ unsigned long ptr, mc_work_mode, chk;
++
++ if (mctl_core_init(para, config) == 0) {
++ printf("DRAM initialisation error : 0\n");
++ return 0;
++ }
++
++ maxrank = (config->dram_para2 & 0xf000) ? 2 : 1;
++ mc_work_mode = 0x3102000;
++ offs = 0;
++
++ /* write test pattern */
++ for (i = 0, ptr = CFG_SYS_SDRAM_BASE; i < 64; i++, ptr += 4)
++ writel((i & 0x1) ? ptr : ~ptr, ptr);
++
++ for (rank = 0; rank < maxrank;) {
++ /* set row mode */
++ clrsetbits_le32(mc_work_mode, 0xf0c, 0x6f0);
++ udelay(1);
++
++ // Scan per address line, until address wraps (i.e. see shadow)
++ for (i = 11; i < 17; i++) {
++ chk = CFG_SYS_SDRAM_BASE + (1U << (i + 11));
++ ptr = CFG_SYS_SDRAM_BASE;
++ for (j = 0; j < 64; j++) {
++ if (readl(chk) != ((j & 1) ? ptr : ~ptr))
++ break;
++ ptr += 4;
++ chk += 4;
++ }
++ if (j == 64)
++ break;
++ }
++ if (i > 16)
++ i = 16;
++ debug("rank %d row = %d\n", rank, i);
++
++ /* Store rows in para 1 */
++ shft = offs + 4;
++ rval = config->dram_para1;
++ rval &= ~(0xff << shft);
++ rval |= i << shft;
++ config->dram_para1 = rval;
++
++ if (rank == 1) /* Set bank mode for rank0 */
++ clrsetbits_le32(0x3102000, 0xffc, 0x6a4);
++
++ /* Set bank mode for current rank */
++ clrsetbits_le32(mc_work_mode, 0xffc, 0x6a4);
++ udelay(1);
++
++ // Test if bit A23 is BA2 or mirror XXX A22?
++ chk = CFG_SYS_SDRAM_BASE + (1U << 22);
++ ptr = CFG_SYS_SDRAM_BASE;
++ for (i = 0, j = 0; i < 64; i++) {
++ if (readl(chk) != ((i & 1) ? ptr : ~ptr)) {
++ j = 1;
++ break;
++ }
++ ptr += 4;
++ chk += 4;
++ }
++
++ debug("rank %d bank = %d\n", rank, (j + 1) << 2); /* 4 or 8 */
++
++ /* Store banks in para 1 */
++ shft = 12 + offs;
++ rval = config->dram_para1;
++ rval &= ~(0xf << shft);
++ rval |= j << shft;
++ config->dram_para1 = rval;
++
++ if (rank == 1) /* Set page mode for rank0 */
++ clrsetbits_le32(0x3102000, 0xffc, 0xaa0);
++
++ /* Set page mode for current rank */
++ clrsetbits_le32(mc_work_mode, 0xffc, 0xaa0);
++ udelay(1);
++
++ // Scan per address line, until address wraps (i.e. see shadow)
++ for (i = 9; i < 14; i++) {
++ chk = CFG_SYS_SDRAM_BASE + (1U << i);
++ ptr = CFG_SYS_SDRAM_BASE;
++ for (j = 0; j < 64; j++) {
++ if (readl(chk) != ((j & 1) ? ptr : ~ptr))
++ break;
++ ptr += 4;
++ chk += 4;
++ }
++ if (j == 64)
++ break;
++ }
++ if (i > 13)
++ i = 13;
++
++ unsigned int pgsize = (i == 9) ? 0 : (1 << (i - 10));
++ debug("rank %d page size = %d KB\n", rank, pgsize);
++
++ /* Store page size */
++ shft = offs;
++ rval = config->dram_para1;
++ rval &= ~(0xf << shft);
++ rval |= pgsize << shft;
++ config->dram_para1 = rval;
++
++ // Move to next rank
++ rank++;
++ if (rank != maxrank) {
++ if (rank == 1) {
++ /* MC_WORK_MODE */
++ clrsetbits_le32(0x3202000, 0xffc, 0x6f0);
++
++ /* MC_WORK_MODE2 */
++ clrsetbits_le32(0x3202004, 0xffc, 0x6f0);
++ }
++ /* store rank1 config in upper half of para1 */
++ offs += 16;
++ mc_work_mode += 4; /* move to MC_WORK_MODE2 */
++ }
++ }
++ if (maxrank == 2) {
++ config->dram_para2 &= 0xfffff0ff;
++ /* note: rval is equal to para->dram_para1 here */
++ if ((rval & 0xffff) == (rval >> 16)) {
++ debug("rank1 config same as rank0\n");
++ } else {
++ config->dram_para2 |= BIT(8);
++ debug("rank1 config different from rank0\n");
++ }
++ }
++
++ return 1;
++}
++
++/*
++ * This routine sets up parameters with dqs_gating_mode equal to 1 and two
++ * ranks enabled. It then configures the core and tests for 1 or 2 ranks and
++ * full or half DQ width. It then resets the parameters to the original values.
++ * dram_para2 is updated with the rank and width findings.
++ */
++static int auto_scan_dram_rank_width(const dram_para_t *para,
++ dram_config_t *config)
++{
++ unsigned int s1 = config->dram_tpr13;
++ unsigned int s2 = config->dram_para1;
++
++ config->dram_para1 = 0x00b000b0;
++ config->dram_para2 = (config->dram_para2 & ~0xf) | BIT(12);
++
++ /* set DQS probe mode */
++ config->dram_tpr13 = (config->dram_tpr13 & ~0x8) | BIT(2) | BIT(0);
++
++ mctl_core_init(para, config);
++
++ if (readl(0x3103010) & BIT(20))
++ return 0;
++
++ if (dqs_gate_detect(config) == 0)
++ return 0;
++
++ config->dram_tpr13 = s1;
++ config->dram_para1 = s2;
++
++ return 1;
++}
++
++/*
++ * This routine determines the SDRAM topology. It first establishes the number
++ * of ranks and the DQ width. Then it scans the SDRAM address lines to establish
++ * the size of each rank. It then updates dram_tpr13 to reflect that the sizes
++ * are now known: a re-init will not repeat the autoscan.
++ */
++static int auto_scan_dram_config(const dram_para_t *para,
++ dram_config_t *config)
++{
++ if (((config->dram_tpr13 & BIT(14)) == 0) &&
++ (auto_scan_dram_rank_width(para, config) == 0)) {
++ printf("ERROR: auto scan dram rank & width failed\n");
++ return 0;
++ }
++
++ if (((config->dram_tpr13 & BIT(0)) == 0) &&
++ (auto_scan_dram_size(para, config) == 0)) {
++ printf("ERROR: auto scan dram size failed\n");
++ return 0;
++ }
++
++ if ((config->dram_tpr13 & BIT(15)) == 0)
++ config->dram_tpr13 |= BIT(14) | BIT(13) | BIT(1) | BIT(0);
++
++ return 1;
++}
++
++static int init_DRAM(int type, const dram_para_t *para)
++{
++ dram_config_t config = {
++ .dram_para1 = 0x000010d2,
++ .dram_para2 = 0,
++ .dram_tpr13 = CONFIG_DRAM_SUNXI_TPR13,
++ };
++ u32 rc, mem_size_mb;
++
++ debug("DRAM BOOT DRIVE INFO: %s\n", "V0.24");
++ debug("DRAM CLK = %d MHz\n", para->dram_clk);
++ debug("DRAM Type = %d (2:DDR2,3:DDR3)\n", para->dram_type);
++ if ((para->dram_odt_en & 0x1) == 0)
++ debug("DRAMC read ODT off\n");
++ else
++ debug("DRAMC ZQ value: 0x%x\n", para->dram_zq);
++
++ /* Test ZQ status */
++ if (config.dram_tpr13 & BIT(16)) {
++ debug("DRAM only have internal ZQ\n");
++ setbits_le32(0x3000160, BIT(8));
++ writel(0, 0x3000168);
++ udelay(10);
++ } else {
++ clrbits_le32(0x3000160, 0x3);
++ writel(config.dram_tpr13 & BIT(16), 0x7010254);
++ udelay(10);
++ clrsetbits_le32(0x3000160, 0x108, BIT(1));
++ udelay(10);
++ setbits_le32(0x3000160, BIT(0));
++ udelay(20);
++ debug("ZQ value = 0x%x\n", readl(0x300016c));
++ }
++
++ dram_voltage_set(para);
++
++ /* Set SDRAM controller auto config */
++ if ((config.dram_tpr13 & BIT(0)) == 0) {
++ if (auto_scan_dram_config(para, &config) == 0) {
++ printf("auto_scan_dram_config() FAILED\n");
++ return 0;
++ }
++ }
++
++ /* report ODT */
++ rc = para->dram_mr1;
++ if ((rc & 0x44) == 0)
++ debug("DRAM ODT off\n");
++ else
++ debug("DRAM ODT value: 0x%x\n", rc);
++
++ /* Init core, final run */
++ if (mctl_core_init(para, &config) == 0) {
++ printf("DRAM initialisation error: 1\n");
++ return 0;
++ }
++
++ /* Get SDRAM size */
++ /* TODO: who ever puts a negative number in the top half? */
++ rc = config.dram_para2;
++ if (rc & BIT(31)) {
++ rc = (rc >> 16) & ~BIT(15);
++ } else {
++ rc = DRAMC_get_dram_size();
++ debug("DRAM: size = %dMB\n", rc);
++ config.dram_para2 = (config.dram_para2 & 0xffffU) | rc << 16;
++ }
++ mem_size_mb = rc;
++
++ /* Purpose ?? */
++ if (config.dram_tpr13 & BIT(30)) {
++ rc = para->dram_tpr8;
++ if (rc == 0)
++ rc = 0x10000200;
++ writel(rc, 0x31030a0);
++ writel(0x40a, 0x310309c);
++ setbits_le32(0x3103004, BIT(0));
++ debug("Enable Auto SR\n");
++ } else {
++ clrbits_le32(0x31030a0, 0xffff);
++ clrbits_le32(0x3103004, 0x1);
++ }
++
++ /* Purpose ?? */
++ if (config.dram_tpr13 & BIT(9)) {
++ clrsetbits_le32(0x3103100, 0xf000, 0x5000);
++ } else {
++ if (para->dram_type != SUNXI_DRAM_TYPE_LPDDR2)
++ clrbits_le32(0x3103100, 0xf000);
++ }
++
++ setbits_le32(0x3103140, BIT(31));
++
++ /* CHECK: is that really writing to a different register? */
++ if (config.dram_tpr13 & BIT(8))
++ writel(readl(0x3103140) | 0x300, 0x31030b8);
++
++ if (config.dram_tpr13 & BIT(16))
++ clrbits_le32(0x3103108, BIT(13));
++ else
++ setbits_le32(0x3103108, BIT(13));
++
++ /* Purpose ?? */
++ if (para->dram_type == SUNXI_DRAM_TYPE_LPDDR3)
++ clrsetbits_le32(0x310307c, 0xf0000, 0x1000);
++
++ dram_enable_all_master();
++ if (config.dram_tpr13 & BIT(28)) {
++ if ((readl(0x70005d4) & BIT(16)) ||
++ dramc_simple_wr_test(mem_size_mb, 4096))
++ return 0;
++ }
++
++ return mem_size_mb;
++}
++
++ static const dram_para_t para = {
++ .dram_clk = CONFIG_DRAM_CLK,
++ .dram_type = CONFIG_SUNXI_DRAM_TYPE,
++ .dram_zq = CONFIG_DRAM_ZQ,
++ .dram_odt_en = CONFIG_DRAM_SUNXI_ODT_EN,
++ .dram_mr0 = 0x1c70,
++ .dram_mr1 = 0x42,
++ .dram_mr2 = 0x18,
++ .dram_mr3 = 0,
++ .dram_tpr0 = 0x004a2195,
++ .dram_tpr1 = 0x02423190,
++ .dram_tpr2 = 0x0008b061,
++ .dram_tpr3 = 0xb4787896, // unused
++ .dram_tpr4 = 0,
++ .dram_tpr5 = 0x48484848,
++ .dram_tpr6 = 0x00000048,
++ .dram_tpr7 = 0x1620121e, // unused
++ .dram_tpr8 = 0,
++ .dram_tpr9 = 0, // clock?
++ .dram_tpr10 = 0,
++ .dram_tpr11 = CONFIG_DRAM_SUNXI_TPR11,
++ .dram_tpr12 = CONFIG_DRAM_SUNXI_TPR12,
++ };
++
++unsigned long sunxi_dram_init(void)
++{
++ return init_DRAM(0, ¶) * 1024UL * 1024;
++};
++
++#ifdef CONFIG_RAM /* using the driver model */
++struct sunxi_ram_priv {
++ size_t size;
++};
++
++static int sunxi_ram_probe(struct udevice *dev)
++{
++ struct sunxi_ram_priv *priv = dev_get_priv(dev);
++ unsigned long dram_size;
++
++ debug("%s: %s: probing\n", __func__, dev->name);
++
++ dram_size = sunxi_dram_init();
++ if (!dram_size) {
++ printf("DRAM init failed: %d\n", ret);
++ return -ENODEV;
++ }
++
++ priv->size = dram_size;
++
++ return 0;
++}
++
++static int sunxi_ram_get_info(struct udevice *dev, struct ram_info *info)
++{
++ struct sunxi_ram_priv *priv = dev_get_priv(dev);
++
++ debug("%s: %s: getting info\n", __func__, dev->name);
++
++ info->base = CFG_SYS_SDRAM_BASE;
++ info->size = priv->size;
++
++ return 0;
++}
++
++static struct ram_ops sunxi_ram_ops = {
++ .get_info = sunxi_ram_get_info,
++};
++
++static const struct udevice_id sunxi_ram_ids[] = {
++ { .compatible = "allwinner,sun20i-d1-mbus" },
++ { }
++};
++
++U_BOOT_DRIVER(sunxi_ram) = {
++ .name = "sunxi_ram",
++ .id = UCLASS_RAM,
++ .of_match = sunxi_ram_ids,
++ .ops = &sunxi_ram_ops,
++ .probe = sunxi_ram_probe,
++ .priv_auto = sizeof(struct sunxi_ram_priv),
++};
++#endif /* CONFIG_RAM (using driver model) */
+diff --git a/drivers/ram/sunxi/dram_sun20i_d1.h b/drivers/ram/sunxi/dram_sun20i_d1.h
+new file mode 100644
+index 0000000000..91383f6cf1
+--- /dev/null
++++ b/drivers/ram/sunxi/dram_sun20i_d1.h
+@@ -0,0 +1,73 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * D1/R528/T113 DRAM controller register and constant defines
++ *
++ * (C) Copyright 2022 Arm Ltd.
++ * Based on H6 and H616 header, which are:
++ * (C) Copyright 2017 Icenowy Zheng <icenowy@aosc.io>
++ * (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net>
++ *
++ */
++
++#ifndef _SUNXI_DRAM_SUN20I_D1_H
++#define _SUNXI_DRAM_SUN20I_D1_H
++
++enum sunxi_dram_type {
++ SUNXI_DRAM_TYPE_DDR2 = 2,
++ SUNXI_DRAM_TYPE_DDR3 = 3,
++ SUNXI_DRAM_TYPE_LPDDR2 = 6,
++ SUNXI_DRAM_TYPE_LPDDR3 = 7,
++};
++
++/*
++ * This structure contains a mixture of fixed configuration settings,
++ * variables that are used at runtime to communicate settings between
++ * different stages and functions, and unused values.
++ * This is copied from Allwinner's boot0 data structure, which can be
++ * found at offset 0x38 in any boot0 binary. To allow matching up some
++ * board specific settings, this struct is kept compatible, even though
++ * we don't need all members in our code.
++ */
++typedef struct dram_para {
++ /* normal configuration */
++ const u32 dram_clk;
++ const u32 dram_type;
++ const u32 dram_zq;
++ const u32 dram_odt_en;
++
++ /* timing configuration */
++ const u32 dram_mr0;
++ const u32 dram_mr1;
++ const u32 dram_mr2;
++ const u32 dram_mr3;
++ const u32 dram_tpr0; //DRAMTMG0
++ const u32 dram_tpr1; //DRAMTMG1
++ const u32 dram_tpr2; //DRAMTMG2
++ const u32 dram_tpr3; //DRAMTMG3
++ const u32 dram_tpr4; //DRAMTMG4
++ const u32 dram_tpr5; //DRAMTMG5
++ const u32 dram_tpr6; //DRAMTMG8
++ const u32 dram_tpr7;
++ const u32 dram_tpr8;
++ const u32 dram_tpr9;
++ const u32 dram_tpr10;
++ const u32 dram_tpr11;
++ const u32 dram_tpr12;
++} dram_para_t;
++
++typedef struct dram_config {
++ /* control configuration */
++ u32 dram_para1;
++ u32 dram_para2;
++ /* contains a bitfield of DRAM setup settings */
++ u32 dram_tpr13;
++} dram_config_t;
++
++static inline int ns_to_t(int nanoseconds)
++{
++ const unsigned int ctrl_freq = CONFIG_DRAM_CLK / 2;
++
++ return DIV_ROUND_UP(ctrl_freq * nanoseconds, 1000);
++}
++
++#endif /* _SUNXI_DRAM_SUN20I_D1_H */
+--
+2.20.1
+
--- /dev/null
+From 6dfc8d5a9ea84c7e7221f6a7d840feaa60479b1c Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 21 Jul 2023 14:46:02 +0100
+Subject: [PATCH 4022/4052] sunxi: add Allwinner R528/T113 SoC support
+
+This adds the remaining code bits to teach U-Boot about Allwinner's
+newest SoC generation. This was introduced with the RISC-V based
+Allwinner D1 SoC, which actually shares a die with the ARM cores versions
+called R528 (BGA, without DRAM) and T113s (QFP, with embedded DRAM).
+
+This adds the new Kconfig stanza, using the two newly introduced symbols
+for the new SoC generation and pincontroller. It also adds the new symbols
+to the relavent code places, to set all the hardcoded bits directly.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+Tested-by: Maksim Kiselev <bigunclemax@gmail.com>
+---
+ arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h | 9 +++++++--
+ arch/arm/mach-sunxi/Kconfig | 11 +++++++++++
+ arch/arm/mach-sunxi/board.c | 8 ++++++++
+ arch/arm/mach-sunxi/clock_sun50i_h6.c | 2 ++
+ arch/arm/mach-sunxi/cpu_info.c | 2 ++
+ common/spl/Kconfig | 1 +
+ drivers/clk/sunxi/Kconfig | 1 +
+ drivers/mmc/sunxi_mmc.c | 1 +
+ drivers/pinctrl/sunxi/Kconfig | 1 +
+ 9 files changed, 34 insertions(+), 2 deletions(-)
+
+diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
+index 8471e11aa0..a84a57e5b4 100644
+--- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
++++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
+@@ -266,7 +266,7 @@ struct sunxi_ccm_reg {
+ #define CCM_CPU_AXI_AXI_MASK 0x3
+ #define CCM_CPU_AXI_DEFAULT_FACTORS 0x301
+
+-#ifdef CONFIG_MACH_SUN50I_H6
++#ifdef CONFIG_MACH_SUN50I_H6 /* H6 */
+ #define CCM_PLL6_DEFAULT 0xa0006300
+
+ /* psi_ahb1_ahb2 bit field */
+@@ -277,7 +277,7 @@ struct sunxi_ccm_reg {
+
+ /* apb1 bit field */
+ #define CCM_APB1_DEFAULT 0x03000102
+-#elif CONFIG_MACH_SUN50I_H616
++#elif CONFIG_MACH_SUN50I_H616 /* H616 */
+ #define CCM_PLL6_DEFAULT 0xa8003100
+
+ /* psi_ahb1_ahb2 bit field */
+@@ -288,6 +288,11 @@ struct sunxi_ccm_reg {
+
+ /* apb1 bit field */
+ #define CCM_APB1_DEFAULT 0x03000102
++#elif CONFIG_MACH_SUN8I_R528 /* R528 */
++#define CCM_PLL6_DEFAULT 0xe8216300
++#define CCM_PSI_AHB1_AHB2_DEFAULT 0x03000002
++//#define CCM_AHB3_DEFAULT 0x03000002
++#define CCM_APB1_DEFAULT 0x03000102
+ #endif
+
+ /* apb2 bit field */
+diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
+index 60ca1239dd..77b510cdfe 100644
+--- a/arch/arm/mach-sunxi/Kconfig
++++ b/arch/arm/mach-sunxi/Kconfig
+@@ -316,6 +316,15 @@ config MACH_SUN8I_R40
+ select PHY_SUN4I_USB
+ imply SPL_SYS_I2C_LEGACY
+
++config MACH_SUN8I_R528
++ bool "sun8i (Allwinner R528)"
++ select CPU_V7A
++ select SUNXI_GEN_NCAT2
++ select SUNXI_NEW_PINCTRL
++ select MMC_SUNXI_HAS_NEW_MODE
++ select SUPPORT_SPL
++ select DRAM_SUN8I_R528
++
+ config MACH_SUN8I_V3S
+ bool "sun8i (Allwinner V3/V3s/S3/S3L)"
+ select CPU_V7A
+@@ -612,6 +621,7 @@ config SYS_CLK_FREQ
+ default 1008000000 if MACH_SUN9I
+ default 888000000 if MACH_SUN50I_H6
+ default 1008000000 if MACH_SUN50I_H616
++ default 1008000000 if MACH_SUN8I_R528
+
+ config SYS_CONFIG_NAME
+ default "suniv" if MACH_SUNIV
+@@ -620,6 +630,7 @@ config SYS_CONFIG_NAME
+ default "sun6i" if MACH_SUN6I
+ default "sun7i" if MACH_SUN7I
+ default "sun8i" if MACH_SUN8I
++ default "sun8i" if MACH_SUN8I_R528
+ default "sun9i" if MACH_SUN9I
+ default "sun50i" if MACH_SUN50I
+ default "sun50i" if MACH_SUN50I_H6
+diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
+index 51b8e708b0..8980ffb509 100644
+--- a/arch/arm/mach-sunxi/board.c
++++ b/arch/arm/mach-sunxi/board.c
+@@ -147,6 +147,10 @@ static int gpio_init(void)
+ sunxi_gpio_set_cfgpin(SUNXI_GPH(12), SUN9I_GPH_UART0);
+ sunxi_gpio_set_cfgpin(SUNXI_GPH(13), SUN9I_GPH_UART0);
+ sunxi_gpio_set_pull(SUNXI_GPH(13), SUNXI_GPIO_PULL_UP);
++#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN8I_R528)
++ sunxi_gpio_set_cfgpin(SUNXI_GPE(2), 6);
++ sunxi_gpio_set_cfgpin(SUNXI_GPE(3), 6);
++ sunxi_gpio_set_pull(SUNXI_GPE(3), SUNXI_GPIO_PULL_UP);
+ #elif CONFIG_CONS_INDEX == 2 && defined(CONFIG_MACH_SUNIV)
+ sunxi_gpio_set_cfgpin(SUNXI_GPA(2), SUNIV_GPE_UART0);
+ sunxi_gpio_set_cfgpin(SUNXI_GPA(3), SUNIV_GPE_UART0);
+@@ -163,6 +167,10 @@ static int gpio_init(void)
+ sunxi_gpio_set_cfgpin(SUNXI_GPB(0), SUN8I_GPB_UART2);
+ sunxi_gpio_set_cfgpin(SUNXI_GPB(1), SUN8I_GPB_UART2);
+ sunxi_gpio_set_pull(SUNXI_GPB(1), SUNXI_GPIO_PULL_UP);
++#elif CONFIG_CONS_INDEX == 4 && defined(CONFIG_MACH_SUN8I_R528)
++ sunxi_gpio_set_cfgpin(SUNXI_GPB(6), 7);
++ sunxi_gpio_set_cfgpin(SUNXI_GPB(7), 7);
++ sunxi_gpio_set_pull(SUNXI_GPB(7), SUNXI_GPIO_PULL_UP);
+ #elif CONFIG_CONS_INDEX == 5 && defined(CONFIG_MACH_SUN8I)
+ sunxi_gpio_set_cfgpin(SUNXI_GPL(2), SUN8I_GPL_R_UART);
+ sunxi_gpio_set_cfgpin(SUNXI_GPL(3), SUN8I_GPL_R_UART);
+diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
+index c3a4623d34..bf21a71542 100644
+--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
++++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
+@@ -52,7 +52,9 @@ void clock_init_safe(void)
+ CCM_CPU_AXI_DEFAULT_FACTORS);
+
+ writel(CCM_PSI_AHB1_AHB2_DEFAULT, &ccm->psi_ahb1_ahb2_cfg);
++#ifdef CCM_AHB3_DEFAULT
+ writel(CCM_AHB3_DEFAULT, &ccm->ahb3_cfg);
++#endif
+ writel(CCM_APB1_DEFAULT, &ccm->apb1_cfg);
+
+ /*
+diff --git a/arch/arm/mach-sunxi/cpu_info.c b/arch/arm/mach-sunxi/cpu_info.c
+index 7eef178859..7fecc3b88d 100644
+--- a/arch/arm/mach-sunxi/cpu_info.c
++++ b/arch/arm/mach-sunxi/cpu_info.c
+@@ -93,6 +93,8 @@ int print_cpuinfo(void)
+ printf("CPU: Allwinner R40 (SUN8I %04x)\n", sunxi_get_sram_id());
+ #elif defined CONFIG_MACH_SUN8I_V3S
+ printf("CPU: Allwinner V3s (SUN8I %04x)\n", sunxi_get_sram_id());
++#elif defined CONFIG_MACH_SUN8I_R528
++ puts("CPU: Allwinner R528 (SUN8I)\n");
+ #elif defined CONFIG_MACH_SUN9I
+ puts("CPU: Allwinner A80 (SUN9I)\n");
+ #elif defined CONFIG_MACH_SUN50I
+diff --git a/common/spl/Kconfig b/common/spl/Kconfig
+index 06bcedca7d..196a250ef9 100644
+--- a/common/spl/Kconfig
++++ b/common/spl/Kconfig
+@@ -357,6 +357,7 @@ config SPL_STACK
+ default 0x91ffb8 if ARCH_MX6 && !MX6_OCRAM_256KB
+ default 0x118000 if MACH_SUN50I_H6
+ default 0x58000 if MACH_SUN50I_H616
++ default 0x40000 if MACH_SUN8I_R528
+ default 0x54000 if MACH_SUN50I || MACH_SUN50I_H5
+ default 0x18000 if MACH_SUN9I
+ default 0x8000 if ARCH_SUNXI
+diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig
+index f65e482ba4..8bdc094489 100644
+--- a/drivers/clk/sunxi/Kconfig
++++ b/drivers/clk/sunxi/Kconfig
+@@ -89,6 +89,7 @@ config CLK_SUN8I_H3
+
+ config CLK_SUN20I_D1
+ bool "Clock driver for Allwinner D1"
++ default MACH_SUN8I_R528
+ help
+ This enables common clock driver support for platforms based
+ on Allwinner D1 SoC.
+diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c
+index a8e590561c..0837e8bb30 100644
+--- a/drivers/mmc/sunxi_mmc.c
++++ b/drivers/mmc/sunxi_mmc.c
+@@ -708,6 +708,7 @@ static const struct udevice_id sunxi_mmc_ids[] = {
+ { .compatible = "allwinner,sun50i-h6-emmc" },
+ { .compatible = "allwinner,sun50i-a100-mmc" },
+ { .compatible = "allwinner,sun50i-a100-emmc" },
++ { .compatible = "allwinner,sun20i-d1-mmc" },
+ { /* sentinel */ }
+ };
+
+diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig
+index c8f937d91e..cbd6179598 100644
+--- a/drivers/pinctrl/sunxi/Kconfig
++++ b/drivers/pinctrl/sunxi/Kconfig
+@@ -126,6 +126,7 @@ config PINCTRL_SUN50I_H616_R
+
+ config PINCTRL_SUN20I_D1
+ bool "Support for the Allwinner D1/R528 PIO"
++ default MACH_SUN8I_R528
+ select PINCTRL_SUNXI
+
+ endif
+--
+2.20.1
+
--- /dev/null
+From 81723d361cf210f94a49ba4084a5e1267a9bc390 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 21 Jul 2023 14:46:03 +0100
+Subject: [PATCH 4023/4052] sunxi: refactor serial base addresses to avoid
+ asm/arch/cpu.h
+
+At the moment we have each SoC's memory map defined in its own cpu.h,
+which is included in include/configs/sunxi_common.h. This will be a
+problem with the introduction of Allwinner RISC-V support.
+
+Remove the inclusion of that header file from the common config header,
+instead move the required serial base addresses (for the SPL) into a
+separate header file. Then include the original cpu.h file only where
+we really need it, which is only under arch/arm now.
+
+This disentangles the architecture specific header files from the
+generic code.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+---
+ arch/arm/cpu/armv7/sunxi/sram.c | 1 +
+ arch/arm/cpu/armv8/fel_utils.S | 1 +
+ arch/arm/include/asm/arch-sunxi/boot0.h | 2 ++
+ arch/arm/include/asm/arch-sunxi/clock.h | 1 +
+ arch/arm/include/asm/arch-sunxi/cpu_sun4i.h | 15 ---------
+ .../include/asm/arch-sunxi/cpu_sun50i_h6.h | 5 ---
+ arch/arm/include/asm/arch-sunxi/cpu_sun9i.h | 7 ----
+ .../include/asm/arch-sunxi/cpu_sunxi_ncat2.h | 4 ---
+ arch/arm/include/asm/arch-sunxi/serial.h | 32 +++++++++++++++++++
+ arch/arm/mach-sunxi/gtbus_sun9i.c | 1 +
+ arch/arm/mach-sunxi/timer.c | 1 +
+ include/configs/sunxi-common.h | 2 +-
+ 12 files changed, 40 insertions(+), 32 deletions(-)
+ create mode 100644 arch/arm/include/asm/arch-sunxi/serial.h
+
+diff --git a/arch/arm/cpu/armv7/sunxi/sram.c b/arch/arm/cpu/armv7/sunxi/sram.c
+index 28564c2846..28ff6a1b7c 100644
+--- a/arch/arm/cpu/armv7/sunxi/sram.c
++++ b/arch/arm/cpu/armv7/sunxi/sram.c
+@@ -12,6 +12,7 @@
+ #include <common.h>
+ #include <init.h>
+ #include <asm/io.h>
++#include <asm/arch/cpu.h>
+
+ void sunxi_sram_init(void)
+ {
+diff --git a/arch/arm/cpu/armv8/fel_utils.S b/arch/arm/cpu/armv8/fel_utils.S
+index 2fe38a1a04..939869b9ff 100644
+--- a/arch/arm/cpu/armv8/fel_utils.S
++++ b/arch/arm/cpu/armv8/fel_utils.S
+@@ -10,6 +10,7 @@
+ #include <config.h>
+ #include <asm/system.h>
+ #include <linux/linkage.h>
++#include <asm/arch/cpu.h>
+
+ /*
+ * We don't overwrite save_boot_params() here, to save the FEL state upon
+diff --git a/arch/arm/include/asm/arch-sunxi/boot0.h b/arch/arm/include/asm/arch-sunxi/boot0.h
+index 46b7e073b5..8ff7ca9b20 100644
+--- a/arch/arm/include/asm/arch-sunxi/boot0.h
++++ b/arch/arm/include/asm/arch-sunxi/boot0.h
+@@ -3,6 +3,8 @@
+ * Configuration settings for the Allwinner A64 (sun50i) CPU
+ */
+
++#include <asm/arch/cpu.h>
++
+ #if defined(CONFIG_RESERVE_ALLWINNER_BOOT0_HEADER) && !defined(CONFIG_SPL_BUILD)
+ /* reserve space for BOOT0 header information */
+ b reset
+diff --git a/arch/arm/include/asm/arch-sunxi/clock.h b/arch/arm/include/asm/arch-sunxi/clock.h
+index 3d34261b0e..fcc8966cb0 100644
+--- a/arch/arm/include/asm/arch-sunxi/clock.h
++++ b/arch/arm/include/asm/arch-sunxi/clock.h
+@@ -9,6 +9,7 @@
+ #define _SUNXI_CLOCK_H
+
+ #include <linux/types.h>
++#include <asm/arch/cpu.h>
+
+ #define CLK_GATE_OPEN 0x1
+ #define CLK_GATE_CLOSE 0x0
+diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
+index d6fe51f24b..3daee2f574 100644
+--- a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
++++ b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
+@@ -128,20 +128,6 @@ defined(CONFIG_MACH_SUN50I)
+ #define SUNXI_CPUCFG_BASE 0x01c25c00
+ #endif
+
+-#ifdef CONFIG_MACH_SUNIV
+-#define SUNXI_UART0_BASE 0x01c25000
+-#define SUNXI_UART1_BASE 0x01c25400
+-#define SUNXI_UART2_BASE 0x01c25800
+-#else
+-#define SUNXI_UART0_BASE 0x01c28000
+-#define SUNXI_UART1_BASE 0x01c28400
+-#define SUNXI_UART2_BASE 0x01c28800
+-#endif
+-#define SUNXI_UART3_BASE 0x01c28c00
+-#define SUNXI_UART4_BASE 0x01c29000
+-#define SUNXI_UART5_BASE 0x01c29400
+-#define SUNXI_UART6_BASE 0x01c29800
+-#define SUNXI_UART7_BASE 0x01c29c00
+ #define SUNXI_PS2_0_BASE 0x01c2a000
+ #define SUNXI_PS2_1_BASE 0x01c2a400
+
+@@ -208,7 +194,6 @@ defined(CONFIG_MACH_SUN50I)
+ #endif
+
+ #define SUNXI_R_TWI_BASE 0x01f02400
+-#define SUNXI_R_UART_BASE 0x01f02800
+ #define SUN6I_P2WI_BASE 0x01f03400
+ #define SUNXI_RSB_BASE 0x01f03400
+
+diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h
+index 9b6bf84360..15ee092d35 100644
+--- a/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h
++++ b/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h
+@@ -42,10 +42,6 @@
+ #define SUNXI_DRAM_PHY0_BASE 0x04800000
+ #endif
+
+-#define SUNXI_UART0_BASE 0x05000000
+-#define SUNXI_UART1_BASE 0x05000400
+-#define SUNXI_UART2_BASE 0x05000800
+-#define SUNXI_UART3_BASE 0x05000C00
+ #define SUNXI_TWI0_BASE 0x05002000
+ #define SUNXI_TWI1_BASE 0x05002400
+ #define SUNXI_TWI2_BASE 0x05002800
+@@ -67,7 +63,6 @@
+ #define SUNXI_R_CPUCFG_BASE 0x07000400
+ #define SUNXI_PRCM_BASE 0x07010000
+ #define SUNXI_R_WDOG_BASE 0x07020400
+-#define SUNXI_R_UART_BASE 0x07080000
+ #define SUNXI_R_TWI_BASE 0x07081400
+
+ #ifndef __ASSEMBLY__
+diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun9i.h b/arch/arm/include/asm/arch-sunxi/cpu_sun9i.h
+index 20025be231..2bf2675d5c 100644
+--- a/arch/arm/include/asm/arch-sunxi/cpu_sun9i.h
++++ b/arch/arm/include/asm/arch-sunxi/cpu_sun9i.h
+@@ -86,12 +86,6 @@
+ #define SUNXI_LRADC_BASE (REGS_APB0_BASE + 0x1800)
+
+ /* APB1 Module */
+-#define SUNXI_UART0_BASE (REGS_APB1_BASE + 0x0000)
+-#define SUNXI_UART1_BASE (REGS_APB1_BASE + 0x0400)
+-#define SUNXI_UART2_BASE (REGS_APB1_BASE + 0x0800)
+-#define SUNXI_UART3_BASE (REGS_APB1_BASE + 0x0C00)
+-#define SUNXI_UART4_BASE (REGS_APB1_BASE + 0x1000)
+-#define SUNXI_UART5_BASE (REGS_APB1_BASE + 0x1400)
+ #define SUNXI_TWI0_BASE (REGS_APB1_BASE + 0x2800)
+ #define SUNXI_TWI1_BASE (REGS_APB1_BASE + 0x2C00)
+ #define SUNXI_TWI2_BASE (REGS_APB1_BASE + 0x3000)
+@@ -100,7 +94,6 @@
+
+ /* RCPUS Module */
+ #define SUNXI_PRCM_BASE (REGS_RCPUS_BASE + 0x1400)
+-#define SUNXI_R_UART_BASE (REGS_RCPUS_BASE + 0x2800)
+ #define SUNXI_RSB_BASE (REGS_RCPUS_BASE + 0x3400)
+
+ /* Misc. */
+diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h b/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h
+index b13be2c4e8..961a3b37c9 100644
+--- a/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h
++++ b/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h
+@@ -10,10 +10,6 @@
+ #define SUNXI_CCM_BASE 0x02001000
+ #define SUNXI_TIMER_BASE 0x02050000
+
+-#define SUNXI_UART0_BASE 0x02500000
+-#define SUNXI_UART1_BASE 0x02500400
+-#define SUNXI_UART2_BASE 0x02500800
+-#define SUNXI_UART3_BASE 0x02500C00
+ #define SUNXI_TWI0_BASE 0x02502000
+ #define SUNXI_TWI1_BASE 0x02502400
+ #define SUNXI_TWI2_BASE 0x02502800
+diff --git a/arch/arm/include/asm/arch-sunxi/serial.h b/arch/arm/include/asm/arch-sunxi/serial.h
+new file mode 100644
+index 0000000000..9386287b65
+--- /dev/null
++++ b/arch/arm/include/asm/arch-sunxi/serial.h
+@@ -0,0 +1,32 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * hardcoded UART base addresses for early SPL use
++ *
++ * Copyright (c) 2022 Arm Ltd.
++ */
++
++#ifndef SUNXI_SERIAL_MEMMAP_H
++#define SUNXI_SERIAL_MEMMAP_H
++
++#if defined(CONFIG_MACH_SUN9I)
++#define SUNXI_UART0_BASE 0x07000000
++#define SUNXI_R_UART_BASE 0x08002800
++#elif defined(CONFIG_SUN50I_GEN_H6)
++#define SUNXI_UART0_BASE 0x05000000
++#define SUNXI_R_UART_BASE 0x07080000
++#elif defined(CONFIG_MACH_SUNIV)
++#define SUNXI_UART0_BASE 0x01c25000
++#define SUNXI_R_UART_BASE 0
++#elif defined(CONFIG_SUNXI_GEN_NCAT2)
++#define SUNXI_UART0_BASE 0x02500000
++#define SUNXI_R_UART_BASE 0 // 0x07080000 (?>
++#else
++#define SUNXI_UART0_BASE 0x01c28000
++#define SUNXI_R_UART_BASE 0x01f02800
++#endif
++
++#define SUNXI_UART1_BASE (SUNXI_UART0_BASE + 0x400)
++#define SUNXI_UART2_BASE (SUNXI_UART0_BASE + 0x800)
++#define SUNXI_UART3_BASE (SUNXI_UART0_BASE + 0xc00)
++
++#endif /* SUNXI_SERIAL_MEMMAP_H */
+diff --git a/arch/arm/mach-sunxi/gtbus_sun9i.c b/arch/arm/mach-sunxi/gtbus_sun9i.c
+index cf011c4cfa..5624621b50 100644
+--- a/arch/arm/mach-sunxi/gtbus_sun9i.c
++++ b/arch/arm/mach-sunxi/gtbus_sun9i.c
+@@ -8,6 +8,7 @@
+
+ #include <common.h>
+ #include <asm/io.h>
++#include <asm/arch/cpu.h>
+ #include <asm/arch/gtbus_sun9i.h>
+ #include <asm/arch/sys_proto.h>
+
+diff --git a/arch/arm/mach-sunxi/timer.c b/arch/arm/mach-sunxi/timer.c
+index fc9d419a25..9a6f6c06d8 100644
+--- a/arch/arm/mach-sunxi/timer.c
++++ b/arch/arm/mach-sunxi/timer.c
+@@ -10,6 +10,7 @@
+ #include <time.h>
+ #include <asm/global_data.h>
+ #include <asm/io.h>
++#include <asm/arch/cpu.h>
+ #include <asm/arch/timer.h>
+ #include <linux/delay.h>
+
+diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h
+index d2d70f0fc2..b8ca77d031 100644
+--- a/include/configs/sunxi-common.h
++++ b/include/configs/sunxi-common.h
+@@ -12,7 +12,6 @@
+ #ifndef _SUNXI_COMMON_CONFIG_H
+ #define _SUNXI_COMMON_CONFIG_H
+
+-#include <asm/arch/cpu.h>
+ #include <linux/stringify.h>
+
+ /* Serial & console */
+@@ -24,6 +23,7 @@
+ #define CFG_SYS_NS16550_CLK 24000000
+ #endif
+ #if !CONFIG_IS_ENABLED(DM_SERIAL)
++#include <asm/arch/serial.h>
+ # define CFG_SYS_NS16550_COM1 SUNXI_UART0_BASE
+ # define CFG_SYS_NS16550_COM2 SUNXI_UART1_BASE
+ # define CFG_SYS_NS16550_COM3 SUNXI_UART2_BASE
+--
+2.20.1
+
--- /dev/null
+From 8cd3f54a64857a8f60033817859f87ded8e22918 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Fri, 21 Jul 2023 14:46:04 +0100
+Subject: [PATCH 4024/4052] riscv: dts: allwinner: Add the D1/D1s SoC
+ devicetree
+
+D1 (aka D1-H), D1s (aka F133), R528, and T113 are a family of SoCs based
+on a single die, or at a pair of dies derived from the same design.
+
+D1 and D1s contain a single T-HEAD Xuantie C906 CPU, whereas R528 and
+T113 contain a pair of Cortex-A7's. D1 and R528 are the full version of
+the chip with a BGA package, whereas D1s and T113 are low-pin-count QFP
+variants.
+
+Because the original design supported both ARM and RISC-V CPUs, some
+peripherals are duplicated. In addition, all variants except D1s contain
+a HiFi 4 DSP with its own set of peripherals.
+
+The devicetrees are organized to minimize duplication:
+ - Common perhiperals are described in sunxi-d1s-t113.dtsi
+ - DSP-related peripherals are described in sunxi-d1-t113.dtsi
+ - RISC-V specific hardware is described in sun20i-d1s.dtsi
+ - Functionality unique to the D1 variant is described in sun20i-d1.dtsi
+
+The SOC_PERIPHERAL_IRQ macro handles the different #interrupt-cells
+values between the ARM (GIC) and RISC-V (PLIC) versions of the SoC.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ arch/riscv/dts/sun20i-common-regulators.dtsi | 28 +
+ arch/riscv/dts/sun20i-d1.dtsi | 66 ++
+ arch/riscv/dts/sun20i-d1s.dtsi | 76 ++
+ arch/riscv/dts/sunxi-d1-t113.dtsi | 15 +
+ arch/riscv/dts/sunxi-d1s-t113.dtsi | 846 +++++++++++++++++++
+ include/dt-bindings/clock/sun20i-d1-r-ccu.h | 19 +
+ include/dt-bindings/reset/sun20i-d1-r-ccu.h | 16 +
+ 7 files changed, 1066 insertions(+)
+ create mode 100644 arch/riscv/dts/sun20i-common-regulators.dtsi
+ create mode 100644 arch/riscv/dts/sun20i-d1.dtsi
+ create mode 100644 arch/riscv/dts/sun20i-d1s.dtsi
+ create mode 100644 arch/riscv/dts/sunxi-d1-t113.dtsi
+ create mode 100644 arch/riscv/dts/sunxi-d1s-t113.dtsi
+ create mode 100644 include/dt-bindings/clock/sun20i-d1-r-ccu.h
+ create mode 100644 include/dt-bindings/reset/sun20i-d1-r-ccu.h
+
+diff --git a/arch/riscv/dts/sun20i-common-regulators.dtsi b/arch/riscv/dts/sun20i-common-regulators.dtsi
+new file mode 100644
+index 0000000000..9b03fca244
+--- /dev/null
++++ b/arch/riscv/dts/sun20i-common-regulators.dtsi
+@@ -0,0 +1,28 @@
++// SPDX-License-Identifier: (GPL-2.0+ or MIT)
++// Copyright (C) 2021-2022 Samuel Holland <samuel@sholland.org>
++
++/ {
++ reg_vcc: vcc {
++ compatible = "regulator-fixed";
++ regulator-name = "vcc";
++ regulator-min-microvolt = <5000000>;
++ regulator-max-microvolt = <5000000>;
++ };
++
++ reg_vcc_3v3: vcc-3v3 {
++ compatible = "regulator-fixed";
++ regulator-name = "vcc-3v3";
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++ vin-supply = <®_vcc>;
++ };
++};
++
++&pio {
++ vcc-pb-supply = <®_vcc_3v3>;
++ vcc-pc-supply = <®_vcc_3v3>;
++ vcc-pd-supply = <®_vcc_3v3>;
++ vcc-pe-supply = <®_vcc_3v3>;
++ vcc-pf-supply = <®_vcc_3v3>;
++ vcc-pg-supply = <®_vcc_3v3>;
++};
+diff --git a/arch/riscv/dts/sun20i-d1.dtsi b/arch/riscv/dts/sun20i-d1.dtsi
+new file mode 100644
+index 0000000000..97e7cbb325
+--- /dev/null
++++ b/arch/riscv/dts/sun20i-d1.dtsi
+@@ -0,0 +1,66 @@
++// SPDX-License-Identifier: (GPL-2.0+ or MIT)
++// Copyright (C) 2021-2022 Samuel Holland <samuel@sholland.org>
++
++#include "sun20i-d1s.dtsi"
++#include "sunxi-d1-t113.dtsi"
++
++/ {
++ soc {
++ lradc: keys@2009800 {
++ compatible = "allwinner,sun20i-d1-lradc",
++ "allwinner,sun50i-r329-lradc";
++ reg = <0x2009800 0x400>;
++ interrupts = <SOC_PERIPHERAL_IRQ(61) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_LRADC>;
++ resets = <&ccu RST_BUS_LRADC>;
++ status = "disabled";
++ };
++
++ i2s0: i2s@2032000 {
++ compatible = "allwinner,sun20i-d1-i2s",
++ "allwinner,sun50i-r329-i2s";
++ reg = <0x2032000 0x1000>;
++ interrupts = <SOC_PERIPHERAL_IRQ(26) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_I2S0>,
++ <&ccu CLK_I2S0>;
++ clock-names = "apb", "mod";
++ resets = <&ccu RST_BUS_I2S0>;
++ dmas = <&dma 3>, <&dma 3>;
++ dma-names = "rx", "tx";
++ status = "disabled";
++ #sound-dai-cells = <0>;
++ };
++ };
++};
++
++&pio {
++ /omit-if-no-ref/
++ dmic_pb11_d0_pin: dmic-pb11-d0-pin {
++ pins = "PB11";
++ function = "dmic";
++ };
++
++ /omit-if-no-ref/
++ dmic_pe17_clk_pin: dmic-pe17-clk-pin {
++ pins = "PE17";
++ function = "dmic";
++ };
++
++ /omit-if-no-ref/
++ i2c0_pb10_pins: i2c0-pb10-pins {
++ pins = "PB10", "PB11";
++ function = "i2c0";
++ };
++
++ /omit-if-no-ref/
++ i2c2_pb0_pins: i2c2-pb0-pins {
++ pins = "PB0", "PB1";
++ function = "i2c2";
++ };
++
++ /omit-if-no-ref/
++ uart0_pb8_pins: uart0-pb8-pins {
++ pins = "PB8", "PB9";
++ function = "uart0";
++ };
++};
+diff --git a/arch/riscv/dts/sun20i-d1s.dtsi b/arch/riscv/dts/sun20i-d1s.dtsi
+new file mode 100644
+index 0000000000..8275630af9
+--- /dev/null
++++ b/arch/riscv/dts/sun20i-d1s.dtsi
+@@ -0,0 +1,76 @@
++// SPDX-License-Identifier: (GPL-2.0+ or MIT)
++// Copyright (C) 2021-2022 Samuel Holland <samuel@sholland.org>
++
++#define SOC_PERIPHERAL_IRQ(nr) (nr + 16)
++
++#include "sunxi-d1s-t113.dtsi"
++
++/ {
++ cpus {
++ timebase-frequency = <24000000>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ cpu0: cpu@0 {
++ compatible = "thead,c906", "riscv";
++ device_type = "cpu";
++ reg = <0>;
++ clocks = <&ccu CLK_RISCV>;
++ d-cache-block-size = <64>;
++ d-cache-sets = <256>;
++ d-cache-size = <32768>;
++ i-cache-block-size = <64>;
++ i-cache-sets = <128>;
++ i-cache-size = <32768>;
++ mmu-type = "riscv,sv39";
++ operating-points-v2 = <&opp_table_cpu>;
++ riscv,isa = "rv64imafdc";
++ #cooling-cells = <2>;
++
++ cpu0_intc: interrupt-controller {
++ compatible = "riscv,cpu-intc";
++ interrupt-controller;
++ #address-cells = <0>;
++ #interrupt-cells = <1>;
++ };
++ };
++ };
++
++ opp_table_cpu: opp-table-cpu {
++ compatible = "operating-points-v2";
++
++ opp-408000000 {
++ opp-hz = /bits/ 64 <408000000>;
++ opp-microvolt = <900000 900000 1100000>;
++ };
++
++ opp-1080000000 {
++ opp-hz = /bits/ 64 <1008000000>;
++ opp-microvolt = <900000 900000 1100000>;
++ };
++ };
++
++ soc {
++ interrupt-parent = <&plic>;
++
++ riscv_wdt: watchdog@6011000 {
++ compatible = "allwinner,sun20i-d1-wdt";
++ reg = <0x6011000 0x20>;
++ interrupts = <SOC_PERIPHERAL_IRQ(131) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&dcxo>, <&rtc CLK_OSC32K>;
++ clock-names = "hosc", "losc";
++ };
++
++ plic: interrupt-controller@10000000 {
++ compatible = "allwinner,sun20i-d1-plic",
++ "thead,c900-plic";
++ reg = <0x10000000 0x4000000>;
++ interrupts-extended = <&cpu0_intc 11>,
++ <&cpu0_intc 9>;
++ interrupt-controller;
++ riscv,ndev = <175>;
++ #address-cells = <0>;
++ #interrupt-cells = <2>;
++ };
++ };
++};
+diff --git a/arch/riscv/dts/sunxi-d1-t113.dtsi b/arch/riscv/dts/sunxi-d1-t113.dtsi
+new file mode 100644
+index 0000000000..b7156123df
+--- /dev/null
++++ b/arch/riscv/dts/sunxi-d1-t113.dtsi
+@@ -0,0 +1,15 @@
++// SPDX-License-Identifier: (GPL-2.0+ or MIT)
++// Copyright (C) 2021-2022 Samuel Holland <samuel@sholland.org>
++
++/ {
++ soc {
++ dsp_wdt: watchdog@1700400 {
++ compatible = "allwinner,sun20i-d1-wdt";
++ reg = <0x1700400 0x20>;
++ interrupts = <SOC_PERIPHERAL_IRQ(122) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&dcxo>, <&rtc CLK_OSC32K>;
++ clock-names = "hosc", "losc";
++ status = "reserved";
++ };
++ };
++};
+diff --git a/arch/riscv/dts/sunxi-d1s-t113.dtsi b/arch/riscv/dts/sunxi-d1s-t113.dtsi
+new file mode 100644
+index 0000000000..922e8e0e2c
+--- /dev/null
++++ b/arch/riscv/dts/sunxi-d1s-t113.dtsi
+@@ -0,0 +1,846 @@
++// SPDX-License-Identifier: (GPL-2.0+ or MIT)
++// Copyright (C) 2021-2022 Samuel Holland <samuel@sholland.org>
++
++#include <dt-bindings/clock/sun6i-rtc.h>
++#include <dt-bindings/clock/sun8i-de2.h>
++#include <dt-bindings/clock/sun8i-tcon-top.h>
++#include <dt-bindings/clock/sun20i-d1-ccu.h>
++#include <dt-bindings/clock/sun20i-d1-r-ccu.h>
++#include <dt-bindings/interrupt-controller/irq.h>
++#include <dt-bindings/reset/sun8i-de2.h>
++#include <dt-bindings/reset/sun20i-d1-ccu.h>
++#include <dt-bindings/reset/sun20i-d1-r-ccu.h>
++
++/ {
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ dcxo: dcxo-clk {
++ compatible = "fixed-clock";
++ clock-output-names = "dcxo";
++ #clock-cells = <0>;
++ };
++
++ de: display-engine {
++ compatible = "allwinner,sun20i-d1-display-engine";
++ allwinner,pipelines = <&mixer0>, <&mixer1>;
++ status = "disabled";
++ };
++
++ soc {
++ compatible = "simple-bus";
++ ranges;
++ dma-noncoherent;
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ pio: pinctrl@2000000 {
++ compatible = "allwinner,sun20i-d1-pinctrl";
++ reg = <0x2000000 0x800>;
++ interrupts = <SOC_PERIPHERAL_IRQ(69) IRQ_TYPE_LEVEL_HIGH>,
++ <SOC_PERIPHERAL_IRQ(71) IRQ_TYPE_LEVEL_HIGH>,
++ <SOC_PERIPHERAL_IRQ(73) IRQ_TYPE_LEVEL_HIGH>,
++ <SOC_PERIPHERAL_IRQ(75) IRQ_TYPE_LEVEL_HIGH>,
++ <SOC_PERIPHERAL_IRQ(77) IRQ_TYPE_LEVEL_HIGH>,
++ <SOC_PERIPHERAL_IRQ(79) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_APB0>,
++ <&dcxo>,
++ <&rtc CLK_OSC32K>;
++ clock-names = "apb", "hosc", "losc";
++ gpio-controller;
++ interrupt-controller;
++ #gpio-cells = <3>;
++ #interrupt-cells = <3>;
++
++ /omit-if-no-ref/
++ clk_pg11_pin: clk-pg11-pin {
++ pins = "PG11";
++ function = "clk";
++ };
++
++ /omit-if-no-ref/
++ dsi_4lane_pins: dsi-4lane-pins {
++ pins = "PD0", "PD1", "PD2", "PD3", "PD4", "PD5",
++ "PD6", "PD7", "PD8", "PD9";
++ drive-strength = <30>;
++ function = "dsi";
++ };
++
++ /omit-if-no-ref/
++ lcd_rgb666_pins: lcd-rgb666-pins {
++ pins = "PD0", "PD1", "PD2", "PD3", "PD4", "PD5",
++ "PD6", "PD7", "PD8", "PD9", "PD10", "PD11",
++ "PD12", "PD13", "PD14", "PD15", "PD16", "PD17",
++ "PD18", "PD19", "PD20", "PD21";
++ function = "lcd0";
++ };
++
++ /omit-if-no-ref/
++ mmc0_pins: mmc0-pins {
++ pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5";
++ function = "mmc0";
++ };
++
++ /omit-if-no-ref/
++ mmc1_pins: mmc1-pins {
++ pins = "PG0", "PG1", "PG2", "PG3", "PG4", "PG5";
++ function = "mmc1";
++ };
++
++ /omit-if-no-ref/
++ mmc2_pins: mmc2-pins {
++ pins = "PC2", "PC3", "PC4", "PC5", "PC6", "PC7";
++ function = "mmc2";
++ };
++
++ /omit-if-no-ref/
++ rgmii_pe_pins: rgmii-pe-pins {
++ pins = "PE0", "PE1", "PE2", "PE3", "PE4",
++ "PE5", "PE6", "PE7", "PE8", "PE9",
++ "PE11", "PE12", "PE13", "PE14", "PE15";
++ function = "emac";
++ };
++
++ /omit-if-no-ref/
++ rmii_pe_pins: rmii-pe-pins {
++ pins = "PE0", "PE1", "PE2", "PE3", "PE4",
++ "PE5", "PE6", "PE7", "PE8", "PE9";
++ function = "emac";
++ };
++
++ /omit-if-no-ref/
++ uart1_pg6_pins: uart1-pg6-pins {
++ pins = "PG6", "PG7";
++ function = "uart1";
++ };
++
++ /omit-if-no-ref/
++ uart1_pg8_rts_cts_pins: uart1-pg8-rts-cts-pins {
++ pins = "PG8", "PG9";
++ function = "uart1";
++ };
++
++ /omit-if-no-ref/
++ uart3_pb_pins: uart3-pb-pins {
++ pins = "PB6", "PB7";
++ function = "uart3";
++ };
++ };
++
++ ccu: clock-controller@2001000 {
++ compatible = "allwinner,sun20i-d1-ccu";
++ reg = <0x2001000 0x1000>;
++ clocks = <&dcxo>,
++ <&rtc CLK_OSC32K>,
++ <&rtc CLK_IOSC>;
++ clock-names = "hosc", "losc", "iosc";
++ #clock-cells = <1>;
++ #reset-cells = <1>;
++ };
++
++ dmic: dmic@2031000 {
++ compatible = "allwinner,sun20i-d1-dmic",
++ "allwinner,sun50i-h6-dmic";
++ reg = <0x2031000 0x400>;
++ interrupts = <SOC_PERIPHERAL_IRQ(24) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_DMIC>,
++ <&ccu CLK_DMIC>;
++ clock-names = "bus", "mod";
++ resets = <&ccu RST_BUS_DMIC>;
++ dmas = <&dma 8>;
++ dma-names = "rx";
++ status = "disabled";
++ #sound-dai-cells = <0>;
++ };
++
++ i2s1: i2s@2033000 {
++ compatible = "allwinner,sun20i-d1-i2s",
++ "allwinner,sun50i-r329-i2s";
++ reg = <0x2033000 0x1000>;
++ interrupts = <SOC_PERIPHERAL_IRQ(27) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_I2S1>,
++ <&ccu CLK_I2S1>;
++ clock-names = "apb", "mod";
++ resets = <&ccu RST_BUS_I2S1>;
++ dmas = <&dma 4>, <&dma 4>;
++ dma-names = "rx", "tx";
++ status = "disabled";
++ #sound-dai-cells = <0>;
++ };
++
++ i2s2: i2s@2034000 {
++ compatible = "allwinner,sun20i-d1-i2s",
++ "allwinner,sun50i-r329-i2s";
++ reg = <0x2034000 0x1000>;
++ interrupts = <SOC_PERIPHERAL_IRQ(28) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_I2S2>,
++ <&ccu CLK_I2S2>;
++ clock-names = "apb", "mod";
++ resets = <&ccu RST_BUS_I2S2>;
++ dmas = <&dma 5>, <&dma 5>;
++ dma-names = "rx", "tx";
++ status = "disabled";
++ #sound-dai-cells = <0>;
++ };
++
++ timer: timer@2050000 {
++ compatible = "allwinner,sun20i-d1-timer",
++ "allwinner,sun8i-a23-timer";
++ reg = <0x2050000 0xa0>;
++ interrupts = <SOC_PERIPHERAL_IRQ(59) IRQ_TYPE_LEVEL_HIGH>,
++ <SOC_PERIPHERAL_IRQ(60) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&dcxo>;
++ };
++
++ wdt: watchdog@20500a0 {
++ compatible = "allwinner,sun20i-d1-wdt-reset",
++ "allwinner,sun20i-d1-wdt";
++ reg = <0x20500a0 0x20>;
++ interrupts = <SOC_PERIPHERAL_IRQ(63) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&dcxo>, <&rtc CLK_OSC32K>;
++ clock-names = "hosc", "losc";
++ status = "reserved";
++ };
++
++ uart0: serial@2500000 {
++ compatible = "snps,dw-apb-uart";
++ reg = <0x2500000 0x400>;
++ reg-io-width = <4>;
++ reg-shift = <2>;
++ interrupts = <SOC_PERIPHERAL_IRQ(2) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_UART0>;
++ resets = <&ccu RST_BUS_UART0>;
++ dmas = <&dma 14>, <&dma 14>;
++ dma-names = "tx", "rx";
++ status = "disabled";
++ };
++
++ uart1: serial@2500400 {
++ compatible = "snps,dw-apb-uart";
++ reg = <0x2500400 0x400>;
++ reg-io-width = <4>;
++ reg-shift = <2>;
++ interrupts = <SOC_PERIPHERAL_IRQ(3) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_UART1>;
++ resets = <&ccu RST_BUS_UART1>;
++ dmas = <&dma 15>, <&dma 15>;
++ dma-names = "tx", "rx";
++ status = "disabled";
++ };
++
++ uart2: serial@2500800 {
++ compatible = "snps,dw-apb-uart";
++ reg = <0x2500800 0x400>;
++ reg-io-width = <4>;
++ reg-shift = <2>;
++ interrupts = <SOC_PERIPHERAL_IRQ(4) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_UART2>;
++ resets = <&ccu RST_BUS_UART2>;
++ dmas = <&dma 16>, <&dma 16>;
++ dma-names = "tx", "rx";
++ status = "disabled";
++ };
++
++ uart3: serial@2500c00 {
++ compatible = "snps,dw-apb-uart";
++ reg = <0x2500c00 0x400>;
++ reg-io-width = <4>;
++ reg-shift = <2>;
++ interrupts = <SOC_PERIPHERAL_IRQ(5) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_UART3>;
++ resets = <&ccu RST_BUS_UART3>;
++ dmas = <&dma 17>, <&dma 17>;
++ dma-names = "tx", "rx";
++ status = "disabled";
++ };
++
++ uart4: serial@2501000 {
++ compatible = "snps,dw-apb-uart";
++ reg = <0x2501000 0x400>;
++ reg-io-width = <4>;
++ reg-shift = <2>;
++ interrupts = <SOC_PERIPHERAL_IRQ(6) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_UART4>;
++ resets = <&ccu RST_BUS_UART4>;
++ dmas = <&dma 18>, <&dma 18>;
++ dma-names = "tx", "rx";
++ status = "disabled";
++ };
++
++ uart5: serial@2501400 {
++ compatible = "snps,dw-apb-uart";
++ reg = <0x2501400 0x400>;
++ reg-io-width = <4>;
++ reg-shift = <2>;
++ interrupts = <SOC_PERIPHERAL_IRQ(7) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_UART5>;
++ resets = <&ccu RST_BUS_UART5>;
++ dmas = <&dma 19>, <&dma 19>;
++ dma-names = "tx", "rx";
++ status = "disabled";
++ };
++
++ i2c0: i2c@2502000 {
++ compatible = "allwinner,sun20i-d1-i2c",
++ "allwinner,sun8i-v536-i2c",
++ "allwinner,sun6i-a31-i2c";
++ reg = <0x2502000 0x400>;
++ interrupts = <SOC_PERIPHERAL_IRQ(9) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_I2C0>;
++ resets = <&ccu RST_BUS_I2C0>;
++ dmas = <&dma 43>, <&dma 43>;
++ dma-names = "rx", "tx";
++ status = "disabled";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++
++ i2c1: i2c@2502400 {
++ compatible = "allwinner,sun20i-d1-i2c",
++ "allwinner,sun8i-v536-i2c",
++ "allwinner,sun6i-a31-i2c";
++ reg = <0x2502400 0x400>;
++ interrupts = <SOC_PERIPHERAL_IRQ(10) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_I2C1>;
++ resets = <&ccu RST_BUS_I2C1>;
++ dmas = <&dma 44>, <&dma 44>;
++ dma-names = "rx", "tx";
++ status = "disabled";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++
++ i2c2: i2c@2502800 {
++ compatible = "allwinner,sun20i-d1-i2c",
++ "allwinner,sun8i-v536-i2c",
++ "allwinner,sun6i-a31-i2c";
++ reg = <0x2502800 0x400>;
++ interrupts = <SOC_PERIPHERAL_IRQ(11) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_I2C2>;
++ resets = <&ccu RST_BUS_I2C2>;
++ dmas = <&dma 45>, <&dma 45>;
++ dma-names = "rx", "tx";
++ status = "disabled";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++
++ i2c3: i2c@2502c00 {
++ compatible = "allwinner,sun20i-d1-i2c",
++ "allwinner,sun8i-v536-i2c",
++ "allwinner,sun6i-a31-i2c";
++ reg = <0x2502c00 0x400>;
++ interrupts = <SOC_PERIPHERAL_IRQ(12) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_I2C3>;
++ resets = <&ccu RST_BUS_I2C3>;
++ dmas = <&dma 46>, <&dma 46>;
++ dma-names = "rx", "tx";
++ status = "disabled";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++
++ syscon: syscon@3000000 {
++ compatible = "allwinner,sun20i-d1-system-control";
++ reg = <0x3000000 0x1000>;
++ ranges;
++ #address-cells = <1>;
++ #size-cells = <1>;
++ };
++
++ dma: dma-controller@3002000 {
++ compatible = "allwinner,sun20i-d1-dma";
++ reg = <0x3002000 0x1000>;
++ interrupts = <SOC_PERIPHERAL_IRQ(50) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_DMA>, <&ccu CLK_MBUS_DMA>;
++ clock-names = "bus", "mbus";
++ resets = <&ccu RST_BUS_DMA>;
++ dma-channels = <16>;
++ dma-requests = <48>;
++ #dma-cells = <1>;
++ };
++
++ sid: efuse@3006000 {
++ compatible = "allwinner,sun20i-d1-sid";
++ reg = <0x3006000 0x1000>;
++ #address-cells = <1>;
++ #size-cells = <1>;
++ };
++
++ crypto: crypto@3040000 {
++ compatible = "allwinner,sun20i-d1-crypto";
++ reg = <0x3040000 0x800>;
++ interrupts = <SOC_PERIPHERAL_IRQ(52) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_CE>,
++ <&ccu CLK_CE>,
++ <&ccu CLK_MBUS_CE>,
++ <&rtc CLK_IOSC>;
++ clock-names = "bus", "mod", "ram", "trng";
++ resets = <&ccu RST_BUS_CE>;
++ };
++
++ mbus: dram-controller@3102000 {
++ compatible = "allwinner,sun20i-d1-mbus";
++ reg = <0x3102000 0x1000>,
++ <0x3103000 0x1000>;
++ reg-names = "mbus", "dram";
++ interrupts = <SOC_PERIPHERAL_IRQ(43) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_MBUS>,
++ <&ccu CLK_DRAM>,
++ <&ccu CLK_BUS_DRAM>;
++ clock-names = "mbus", "dram", "bus";
++ dma-ranges = <0 0x40000000 0x80000000>;
++ #address-cells = <1>;
++ #size-cells = <1>;
++ #interconnect-cells = <1>;
++ };
++
++ mmc0: mmc@4020000 {
++ compatible = "allwinner,sun20i-d1-mmc";
++ reg = <0x4020000 0x1000>;
++ interrupts = <SOC_PERIPHERAL_IRQ(40) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_MMC0>, <&ccu CLK_MMC0>;
++ clock-names = "ahb", "mmc";
++ resets = <&ccu RST_BUS_MMC0>;
++ reset-names = "ahb";
++ cap-sd-highspeed;
++ max-frequency = <150000000>;
++ no-mmc;
++ status = "disabled";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++
++ mmc1: mmc@4021000 {
++ compatible = "allwinner,sun20i-d1-mmc";
++ reg = <0x4021000 0x1000>;
++ interrupts = <SOC_PERIPHERAL_IRQ(41) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_MMC1>, <&ccu CLK_MMC1>;
++ clock-names = "ahb", "mmc";
++ resets = <&ccu RST_BUS_MMC1>;
++ reset-names = "ahb";
++ cap-sd-highspeed;
++ max-frequency = <150000000>;
++ no-mmc;
++ status = "disabled";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++
++ mmc2: mmc@4022000 {
++ compatible = "allwinner,sun20i-d1-emmc",
++ "allwinner,sun50i-a100-emmc";
++ reg = <0x4022000 0x1000>;
++ interrupts = <SOC_PERIPHERAL_IRQ(42) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_MMC2>, <&ccu CLK_MMC2>;
++ clock-names = "ahb", "mmc";
++ resets = <&ccu RST_BUS_MMC2>;
++ reset-names = "ahb";
++ cap-mmc-highspeed;
++ max-frequency = <150000000>;
++ mmc-ddr-1_8v;
++ mmc-ddr-3_3v;
++ no-sd;
++ no-sdio;
++ status = "disabled";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++
++ usb_otg: usb@4100000 {
++ compatible = "allwinner,sun20i-d1-musb",
++ "allwinner,sun8i-a33-musb";
++ reg = <0x4100000 0x400>;
++ interrupts = <SOC_PERIPHERAL_IRQ(29) IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "mc";
++ clocks = <&ccu CLK_BUS_OTG>;
++ resets = <&ccu RST_BUS_OTG>;
++ extcon = <&usbphy 0>;
++ phys = <&usbphy 0>;
++ phy-names = "usb";
++ status = "disabled";
++ };
++
++ usbphy: phy@4100400 {
++ compatible = "allwinner,sun20i-d1-usb-phy";
++ reg = <0x4100400 0x100>,
++ <0x4101800 0x100>,
++ <0x4200800 0x100>;
++ reg-names = "phy_ctrl",
++ "pmu0",
++ "pmu1";
++ clocks = <&dcxo>,
++ <&dcxo>;
++ clock-names = "usb0_phy",
++ "usb1_phy";
++ resets = <&ccu RST_USB_PHY0>,
++ <&ccu RST_USB_PHY1>;
++ reset-names = "usb0_reset",
++ "usb1_reset";
++ status = "disabled";
++ #phy-cells = <1>;
++ };
++
++ ehci0: usb@4101000 {
++ compatible = "allwinner,sun20i-d1-ehci",
++ "generic-ehci";
++ reg = <0x4101000 0x100>;
++ interrupts = <SOC_PERIPHERAL_IRQ(30) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_OHCI0>,
++ <&ccu CLK_BUS_EHCI0>,
++ <&ccu CLK_USB_OHCI0>;
++ resets = <&ccu RST_BUS_OHCI0>,
++ <&ccu RST_BUS_EHCI0>;
++ phys = <&usbphy 0>;
++ phy-names = "usb";
++ status = "disabled";
++ };
++
++ ohci0: usb@4101400 {
++ compatible = "allwinner,sun20i-d1-ohci",
++ "generic-ohci";
++ reg = <0x4101400 0x100>;
++ interrupts = <SOC_PERIPHERAL_IRQ(31) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_OHCI0>,
++ <&ccu CLK_USB_OHCI0>;
++ resets = <&ccu RST_BUS_OHCI0>;
++ phys = <&usbphy 0>;
++ phy-names = "usb";
++ status = "disabled";
++ };
++
++ ehci1: usb@4200000 {
++ compatible = "allwinner,sun20i-d1-ehci",
++ "generic-ehci";
++ reg = <0x4200000 0x100>;
++ interrupts = <SOC_PERIPHERAL_IRQ(33) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_OHCI1>,
++ <&ccu CLK_BUS_EHCI1>,
++ <&ccu CLK_USB_OHCI1>;
++ resets = <&ccu RST_BUS_OHCI1>,
++ <&ccu RST_BUS_EHCI1>;
++ phys = <&usbphy 1>;
++ phy-names = "usb";
++ status = "disabled";
++ };
++
++ ohci1: usb@4200400 {
++ compatible = "allwinner,sun20i-d1-ohci",
++ "generic-ohci";
++ reg = <0x4200400 0x100>;
++ interrupts = <SOC_PERIPHERAL_IRQ(34) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_OHCI1>,
++ <&ccu CLK_USB_OHCI1>;
++ resets = <&ccu RST_BUS_OHCI1>;
++ phys = <&usbphy 1>;
++ phy-names = "usb";
++ status = "disabled";
++ };
++
++ emac: ethernet@4500000 {
++ compatible = "allwinner,sun20i-d1-emac",
++ "allwinner,sun50i-a64-emac";
++ reg = <0x4500000 0x10000>;
++ interrupts = <SOC_PERIPHERAL_IRQ(46) IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "macirq";
++ clocks = <&ccu CLK_BUS_EMAC>;
++ clock-names = "stmmaceth";
++ resets = <&ccu RST_BUS_EMAC>;
++ reset-names = "stmmaceth";
++ syscon = <&syscon>;
++ status = "disabled";
++
++ mdio: mdio {
++ compatible = "snps,dwmac-mdio";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++ };
++
++ display_clocks: clock-controller@5000000 {
++ compatible = "allwinner,sun20i-d1-de2-clk",
++ "allwinner,sun50i-h5-de2-clk";
++ reg = <0x5000000 0x10000>;
++ clocks = <&ccu CLK_BUS_DE>, <&ccu CLK_DE>;
++ clock-names = "bus", "mod";
++ resets = <&ccu RST_BUS_DE>;
++ #clock-cells = <1>;
++ #reset-cells = <1>;
++ };
++
++ mixer0: mixer@5100000 {
++ compatible = "allwinner,sun20i-d1-de2-mixer-0";
++ reg = <0x5100000 0x100000>;
++ clocks = <&display_clocks CLK_BUS_MIXER0>,
++ <&display_clocks CLK_MIXER0>;
++ clock-names = "bus", "mod";
++ resets = <&display_clocks RST_MIXER0>;
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ mixer0_out: port@1 {
++ reg = <1>;
++
++ mixer0_out_tcon_top_mixer0: endpoint {
++ remote-endpoint = <&tcon_top_mixer0_in_mixer0>;
++ };
++ };
++ };
++ };
++
++ mixer1: mixer@5200000 {
++ compatible = "allwinner,sun20i-d1-de2-mixer-1";
++ reg = <0x5200000 0x100000>;
++ clocks = <&display_clocks CLK_BUS_MIXER1>,
++ <&display_clocks CLK_MIXER1>;
++ clock-names = "bus", "mod";
++ resets = <&display_clocks RST_MIXER1>;
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ mixer1_out: port@1 {
++ reg = <1>;
++
++ mixer1_out_tcon_top_mixer1: endpoint {
++ remote-endpoint = <&tcon_top_mixer1_in_mixer1>;
++ };
++ };
++ };
++ };
++
++ dsi: dsi@5450000 {
++ compatible = "allwinner,sun20i-d1-mipi-dsi",
++ "allwinner,sun50i-a100-mipi-dsi";
++ reg = <0x5450000 0x1000>;
++ interrupts = <SOC_PERIPHERAL_IRQ(92) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_MIPI_DSI>,
++ <&tcon_top CLK_TCON_TOP_DSI>;
++ clock-names = "bus", "mod";
++ resets = <&ccu RST_BUS_MIPI_DSI>;
++ phys = <&dphy>;
++ phy-names = "dphy";
++ status = "disabled";
++
++ port {
++ dsi_in_tcon_lcd0: endpoint {
++ remote-endpoint = <&tcon_lcd0_out_dsi>;
++ };
++ };
++ };
++
++ dphy: phy@5451000 {
++ compatible = "allwinner,sun20i-d1-mipi-dphy",
++ "allwinner,sun50i-a100-mipi-dphy";
++ reg = <0x5451000 0x1000>;
++ interrupts = <SOC_PERIPHERAL_IRQ(92) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_MIPI_DSI>,
++ <&ccu CLK_MIPI_DSI>;
++ clock-names = "bus", "mod";
++ resets = <&ccu RST_BUS_MIPI_DSI>;
++ #phy-cells = <0>;
++ };
++
++ tcon_top: tcon-top@5460000 {
++ compatible = "allwinner,sun20i-d1-tcon-top";
++ reg = <0x5460000 0x1000>;
++ clocks = <&ccu CLK_BUS_DPSS_TOP>,
++ <&ccu CLK_TCON_TV>,
++ <&ccu CLK_TVE>,
++ <&ccu CLK_TCON_LCD0>;
++ clock-names = "bus", "tcon-tv0", "tve0", "dsi";
++ clock-output-names = "tcon-top-tv0", "tcon-top-dsi";
++ resets = <&ccu RST_BUS_DPSS_TOP>;
++ #clock-cells = <1>;
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ tcon_top_mixer0_in: port@0 {
++ reg = <0>;
++
++ tcon_top_mixer0_in_mixer0: endpoint {
++ remote-endpoint = <&mixer0_out_tcon_top_mixer0>;
++ };
++ };
++
++ tcon_top_mixer0_out: port@1 {
++ reg = <1>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ tcon_top_mixer0_out_tcon_lcd0: endpoint@0 {
++ reg = <0>;
++ remote-endpoint = <&tcon_lcd0_in_tcon_top_mixer0>;
++ };
++
++ tcon_top_mixer0_out_tcon_tv0: endpoint@2 {
++ reg = <2>;
++ remote-endpoint = <&tcon_tv0_in_tcon_top_mixer0>;
++ };
++ };
++
++ tcon_top_mixer1_in: port@2 {
++ reg = <2>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ tcon_top_mixer1_in_mixer1: endpoint@1 {
++ reg = <1>;
++ remote-endpoint = <&mixer1_out_tcon_top_mixer1>;
++ };
++ };
++
++ tcon_top_mixer1_out: port@3 {
++ reg = <3>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ tcon_top_mixer1_out_tcon_lcd0: endpoint@0 {
++ reg = <0>;
++ remote-endpoint = <&tcon_lcd0_in_tcon_top_mixer1>;
++ };
++
++ tcon_top_mixer1_out_tcon_tv0: endpoint@2 {
++ reg = <2>;
++ remote-endpoint = <&tcon_tv0_in_tcon_top_mixer1>;
++ };
++ };
++
++ tcon_top_hdmi_in: port@4 {
++ reg = <4>;
++
++ tcon_top_hdmi_in_tcon_tv0: endpoint {
++ remote-endpoint = <&tcon_tv0_out_tcon_top_hdmi>;
++ };
++ };
++
++ tcon_top_hdmi_out: port@5 {
++ reg = <5>;
++ };
++ };
++ };
++
++ tcon_lcd0: lcd-controller@5461000 {
++ compatible = "allwinner,sun20i-d1-tcon-lcd";
++ reg = <0x5461000 0x1000>;
++ interrupts = <SOC_PERIPHERAL_IRQ(90) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_TCON_LCD0>,
++ <&ccu CLK_TCON_LCD0>;
++ clock-names = "ahb", "tcon-ch0";
++ clock-output-names = "tcon-pixel-clock";
++ resets = <&ccu RST_BUS_TCON_LCD0>,
++ <&ccu RST_BUS_LVDS0>;
++ reset-names = "lcd", "lvds";
++ #clock-cells = <0>;
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ tcon_lcd0_in: port@0 {
++ reg = <0>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ tcon_lcd0_in_tcon_top_mixer0: endpoint@0 {
++ reg = <0>;
++ remote-endpoint = <&tcon_top_mixer0_out_tcon_lcd0>;
++ };
++
++ tcon_lcd0_in_tcon_top_mixer1: endpoint@1 {
++ reg = <1>;
++ remote-endpoint = <&tcon_top_mixer1_out_tcon_lcd0>;
++ };
++ };
++
++ tcon_lcd0_out: port@1 {
++ reg = <1>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ tcon_lcd0_out_dsi: endpoint@1 {
++ reg = <1>;
++ remote-endpoint = <&dsi_in_tcon_lcd0>;
++ };
++ };
++ };
++ };
++
++ tcon_tv0: lcd-controller@5470000 {
++ compatible = "allwinner,sun20i-d1-tcon-tv";
++ reg = <0x5470000 0x1000>;
++ interrupts = <SOC_PERIPHERAL_IRQ(91) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_TCON_TV>,
++ <&tcon_top CLK_TCON_TOP_TV0>;
++ clock-names = "ahb", "tcon-ch1";
++ resets = <&ccu RST_BUS_TCON_TV>;
++ reset-names = "lcd";
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ tcon_tv0_in: port@0 {
++ reg = <0>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ tcon_tv0_in_tcon_top_mixer0: endpoint@0 {
++ reg = <0>;
++ remote-endpoint = <&tcon_top_mixer0_out_tcon_tv0>;
++ };
++
++ tcon_tv0_in_tcon_top_mixer1: endpoint@1 {
++ reg = <1>;
++ remote-endpoint = <&tcon_top_mixer1_out_tcon_tv0>;
++ };
++ };
++
++ tcon_tv0_out: port@1 {
++ reg = <1>;
++
++ tcon_tv0_out_tcon_top_hdmi: endpoint {
++ remote-endpoint = <&tcon_top_hdmi_in_tcon_tv0>;
++ };
++ };
++ };
++ };
++
++ ppu: power-controller@7001000 {
++ compatible = "allwinner,sun20i-d1-ppu";
++ reg = <0x7001000 0x1000>;
++ clocks = <&r_ccu CLK_BUS_R_PPU>;
++ resets = <&r_ccu RST_BUS_R_PPU>;
++ #power-domain-cells = <1>;
++ };
++
++ r_ccu: clock-controller@7010000 {
++ compatible = "allwinner,sun20i-d1-r-ccu";
++ reg = <0x7010000 0x400>;
++ clocks = <&dcxo>,
++ <&rtc CLK_OSC32K>,
++ <&rtc CLK_IOSC>,
++ <&ccu CLK_PLL_PERIPH0_DIV3>;
++ clock-names = "hosc", "losc", "iosc", "pll-periph";
++ #clock-cells = <1>;
++ #reset-cells = <1>;
++ };
++
++ rtc: rtc@7090000 {
++ compatible = "allwinner,sun20i-d1-rtc",
++ "allwinner,sun50i-r329-rtc";
++ reg = <0x7090000 0x400>;
++ interrupts = <SOC_PERIPHERAL_IRQ(144) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&r_ccu CLK_BUS_R_RTC>,
++ <&dcxo>,
++ <&r_ccu CLK_R_AHB>;
++ clock-names = "bus", "hosc", "ahb";
++ #clock-cells = <1>;
++ };
++ };
++};
+diff --git a/include/dt-bindings/clock/sun20i-d1-r-ccu.h b/include/dt-bindings/clock/sun20i-d1-r-ccu.h
+new file mode 100644
+index 0000000000..4c2697fd32
+--- /dev/null
++++ b/include/dt-bindings/clock/sun20i-d1-r-ccu.h
+@@ -0,0 +1,19 @@
++/* SPDX-License-Identifier: (GPL-2.0+ or MIT) */
++/*
++ * Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
++ */
++
++#ifndef _DT_BINDINGS_CLK_SUN20I_D1_R_CCU_H_
++#define _DT_BINDINGS_CLK_SUN20I_D1_R_CCU_H_
++
++#define CLK_R_AHB 0
++
++#define CLK_BUS_R_TIMER 2
++#define CLK_BUS_R_TWD 3
++#define CLK_BUS_R_PPU 4
++#define CLK_R_IR_RX 5
++#define CLK_BUS_R_IR_RX 6
++#define CLK_BUS_R_RTC 7
++#define CLK_BUS_R_CPUCFG 8
++
++#endif /* _DT_BINDINGS_CLK_SUN20I_D1_R_CCU_H_ */
+diff --git a/include/dt-bindings/reset/sun20i-d1-r-ccu.h b/include/dt-bindings/reset/sun20i-d1-r-ccu.h
+new file mode 100644
+index 0000000000..d93d6423d2
+--- /dev/null
++++ b/include/dt-bindings/reset/sun20i-d1-r-ccu.h
+@@ -0,0 +1,16 @@
++/* SPDX-License-Identifier: (GPL-2.0+ or MIT) */
++/*
++ * Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
++ */
++
++#ifndef _DT_BINDINGS_RST_SUN20I_D1_R_CCU_H_
++#define _DT_BINDINGS_RST_SUN20I_D1_R_CCU_H_
++
++#define RST_BUS_R_TIMER 0
++#define RST_BUS_R_TWD 1
++#define RST_BUS_R_PPU 2
++#define RST_BUS_R_IR_RX 3
++#define RST_BUS_R_RTC 4
++#define RST_BUS_R_CPUCFG 5
++
++#endif /* _DT_BINDINGS_RST_SUN20I_D1_R_CCU_H_ */
+--
+2.20.1
+
--- /dev/null
+From 032126b42512e91b2c8e918fd33a3ed7e96ac8df Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 21 Jul 2023 14:46:05 +0100
+Subject: [PATCH 4025/4052] ARM: dts: sunxi: add Allwinner T113-s SoC .dtsi
+
+The Allwinner T113-s SoC is apparently using the same (or at least a very
+similar) die as the D1/D1s, but replaces the single RISC-V core with
+two Arm Cortex-A7 cores.
+Since the D1 core .dtsi already describes all common peripherals, we
+just need a DT describing the ARM specific peripherals: the CPU cores,
+the Generic Timer, the GIC and the PMU.
+We include the core .dtsi directly from the riscv DT directory.
+
+The ARM core version of the DT specifies the CPUX watchdog as
+"reserved", which means it won't be recognised by U-Boot. Override this
+in our generic sunxi-u-boot.dtsi, to let U-Boot pick up this watchdog,
+so that the generic reset driver will work.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+Reviewed-by: Sam Edwards <CFSworks@gmail.com>
+Tested-by: Sam Edwards <CFSworks@gmail.com>
+---
+ arch/arm/dts/sun8i-t113s.dtsi | 59 ++++++++++++++++++++++++++++++++++
+ arch/arm/dts/sunxi-u-boot.dtsi | 7 ++++
+ 2 files changed, 66 insertions(+)
+ create mode 100644 arch/arm/dts/sun8i-t113s.dtsi
+
+diff --git a/arch/arm/dts/sun8i-t113s.dtsi b/arch/arm/dts/sun8i-t113s.dtsi
+new file mode 100644
+index 0000000000..ce00883130
+--- /dev/null
++++ b/arch/arm/dts/sun8i-t113s.dtsi
+@@ -0,0 +1,59 @@
++// SPDX-License-Identifier: (GPL-2.0+ or MIT)
++// Copyright (C) 2022 Arm Ltd.
++
++#define SOC_PERIPHERAL_IRQ(nr) GIC_SPI nr
++
++#include <dt-bindings/interrupt-controller/arm-gic.h>
++#include <../../riscv/dts/sunxi-d1s-t113.dtsi>
++#include <../../riscv/dts/sunxi-d1-t113.dtsi>
++
++/ {
++ interrupt-parent = <&gic>;
++
++ cpus {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ cpu0: cpu@0 {
++ compatible = "arm,cortex-a7";
++ device_type = "cpu";
++ reg = <0>;
++ clocks = <&ccu CLK_CPUX>;
++ clock-names = "cpu";
++ };
++
++ cpu1: cpu@1 {
++ compatible = "arm,cortex-a7";
++ device_type = "cpu";
++ reg = <1>;
++ clocks = <&ccu CLK_CPUX>;
++ clock-names = "cpu";
++ };
++ };
++
++ gic: interrupt-controller@1c81000 {
++ compatible = "arm,gic-400";
++ reg = <0x03021000 0x1000>,
++ <0x03022000 0x2000>,
++ <0x03024000 0x2000>,
++ <0x03026000 0x2000>;
++ interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
++ interrupt-controller;
++ #interrupt-cells = <3>;
++ };
++
++ timer {
++ compatible = "arm,armv7-timer";
++ interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
++ <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
++ <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
++ <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
++ };
++
++ pmu {
++ compatible = "arm,cortex-a7-pmu";
++ interrupts = <GIC_SPI 172 IRQ_TYPE_LEVEL_HIGH>,
++ <GIC_SPI 173 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-affinity = <&cpu0>, <&cpu1>;
++ };
++};
+diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi
+index e959eb2a40..27de5b8eac 100644
+--- a/arch/arm/dts/sunxi-u-boot.dtsi
++++ b/arch/arm/dts/sunxi-u-boot.dtsi
+@@ -23,6 +23,13 @@
+ };
+ };
+
++/* Let U-Boot be the firmware layer that controls the watchdog. */
++#ifdef CONFIG_MACH_SUN8I_R528
++&wdt {
++ status = "okay";
++};
++#endif
++
+ &binman {
+ u-boot-sunxi-with-spl {
+ filename = "u-boot-sunxi-with-spl.bin";
+--
+2.20.1
+
--- /dev/null
+From dd2f024850f1da845073eaa90867b7250837f3e1 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 21 Jul 2023 14:46:06 +0100
+Subject: [PATCH 4026/4052] sunxi: add MangoPi MQ-R board support
+
+This copies the T113s specific DTs from the Linux kernel tree
+(v6.4-rc1), and adds a defconfig to get the board booted.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+---
+ arch/arm/dts/Makefile | 2 +
+ .../arm/dts/sun8i-t113s-mangopi-mq-r-t113.dts | 35 +++++
+ arch/arm/dts/sunxi-d1s-t113-mangopi-mq-r.dtsi | 126 ++++++++++++++++++
+ configs/mangopi_mq_r_defconfig | 15 +++
+ 4 files changed, 178 insertions(+)
+ create mode 100644 arch/arm/dts/sun8i-t113s-mangopi-mq-r-t113.dts
+ create mode 100644 arch/arm/dts/sunxi-d1s-t113-mangopi-mq-r.dtsi
+ create mode 100644 configs/mangopi_mq_r_defconfig
+
+diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
+index c160e884bf..41ee9c4c65 100644
+--- a/arch/arm/dts/Makefile
++++ b/arch/arm/dts/Makefile
+@@ -710,6 +710,8 @@ dtb-$(CONFIG_MACH_SUN8I_V3S) += \
+ sun8i-s3-pinecube.dtb \
+ sun8i-v3-sl631-imx179.dtb \
+ sun8i-v3s-licheepi-zero.dtb
++dtb-$(CONFIG_MACH_SUN8I_R528) += \
++ sun8i-t113s-mangopi-mq-r-t113.dtb
+ dtb-$(CONFIG_MACH_SUN50I_H5) += \
+ sun50i-h5-bananapi-m2-plus.dtb \
+ sun50i-h5-emlid-neutis-n5-devboard.dtb \
+diff --git a/arch/arm/dts/sun8i-t113s-mangopi-mq-r-t113.dts b/arch/arm/dts/sun8i-t113s-mangopi-mq-r-t113.dts
+new file mode 100644
+index 0000000000..94e24b5926
+--- /dev/null
++++ b/arch/arm/dts/sun8i-t113s-mangopi-mq-r-t113.dts
+@@ -0,0 +1,35 @@
++// SPDX-License-Identifier: (GPL-2.0+ or MIT)
++// Copyright (C) 2022 Arm Ltd.
++
++#include <dt-bindings/interrupt-controller/irq.h>
++
++/dts-v1/;
++
++#include "sun8i-t113s.dtsi"
++#include "sunxi-d1s-t113-mangopi-mq-r.dtsi"
++
++/ {
++ model = "MangoPi MQ-R-T113";
++ compatible = "widora,mangopi-mq-r-t113", "allwinner,sun8i-t113s";
++
++ aliases {
++ ethernet0 = &rtl8189ftv;
++ };
++};
++
++&cpu0 {
++ cpu-supply = <®_vcc_core>;
++};
++
++&cpu1 {
++ cpu-supply = <®_vcc_core>;
++};
++
++&mmc1 {
++ rtl8189ftv: wifi@1 {
++ reg = <1>;
++ interrupt-parent = <&pio>;
++ interrupts = <6 10 IRQ_TYPE_LEVEL_LOW>; /* PG10 = WL_WAKE_AP */
++ interrupt-names = "host-wake";
++ };
++};
+diff --git a/arch/arm/dts/sunxi-d1s-t113-mangopi-mq-r.dtsi b/arch/arm/dts/sunxi-d1s-t113-mangopi-mq-r.dtsi
+new file mode 100644
+index 0000000000..e9bc749488
+--- /dev/null
++++ b/arch/arm/dts/sunxi-d1s-t113-mangopi-mq-r.dtsi
+@@ -0,0 +1,126 @@
++// SPDX-License-Identifier: (GPL-2.0+ or MIT)
++// Copyright (C) 2022 Arm Ltd.
++/*
++ * Common peripherals and configurations for MangoPi MQ-R boards.
++ */
++
++#include <dt-bindings/gpio/gpio.h>
++#include <dt-bindings/leds/common.h>
++
++/ {
++ aliases {
++ serial3 = &uart3;
++ };
++
++ chosen {
++ stdout-path = "serial3:115200n8";
++ };
++
++ leds {
++ compatible = "gpio-leds";
++
++ led-0 {
++ color = <LED_COLOR_ID_BLUE>;
++ function = LED_FUNCTION_STATUS;
++ gpios = <&pio 3 22 GPIO_ACTIVE_LOW>; /* PD22 */
++ };
++ };
++
++ /* board wide 5V supply directly from the USB-C socket */
++ reg_vcc5v: regulator-5v {
++ compatible = "regulator-fixed";
++ regulator-name = "vcc-5v";
++ regulator-min-microvolt = <5000000>;
++ regulator-max-microvolt = <5000000>;
++ regulator-always-on;
++ };
++
++ /* SY8008 DC/DC regulator on the board */
++ reg_3v3: regulator-3v3 {
++ compatible = "regulator-fixed";
++ regulator-name = "vcc-3v3";
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++ vin-supply = <®_vcc5v>;
++ };
++
++ /* SY8008 DC/DC regulator on the board, also supplying VDD-SYS */
++ reg_vcc_core: regulator-core {
++ compatible = "regulator-fixed";
++ regulator-name = "vcc-core";
++ regulator-min-microvolt = <880000>;
++ regulator-max-microvolt = <880000>;
++ vin-supply = <®_vcc5v>;
++ };
++
++ /* XC6206 LDO on the board */
++ reg_avdd2v8: regulator-avdd {
++ compatible = "regulator-fixed";
++ regulator-name = "avdd2v8";
++ regulator-min-microvolt = <2800000>;
++ regulator-max-microvolt = <2800000>;
++ vin-supply = <®_3v3>;
++ };
++
++ wifi_pwrseq: wifi-pwrseq {
++ compatible = "mmc-pwrseq-simple";
++ reset-gpios = <&pio 6 12 GPIO_ACTIVE_LOW>; /* PG12 */
++ };
++};
++
++&dcxo {
++ clock-frequency = <24000000>;
++};
++
++&ehci1 {
++ status = "okay";
++};
++
++&mmc0 {
++ pinctrl-0 = <&mmc0_pins>;
++ pinctrl-names = "default";
++ vmmc-supply = <®_3v3>;
++ cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>;
++ disable-wp;
++ bus-width = <4>;
++ status = "okay";
++};
++
++&mmc1 {
++ pinctrl-0 = <&mmc1_pins>;
++ pinctrl-names = "default";
++ vmmc-supply = <®_3v3>;
++ non-removable;
++ bus-width = <4>;
++ mmc-pwrseq = <&wifi_pwrseq>;
++ status = "okay";
++};
++
++&ohci1 {
++ status = "okay";
++};
++
++&pio {
++ vcc-pb-supply = <®_3v3>;
++ vcc-pd-supply = <®_3v3>;
++ vcc-pe-supply = <®_avdd2v8>;
++ vcc-pf-supply = <®_3v3>;
++ vcc-pg-supply = <®_3v3>;
++};
++
++&uart3 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&uart3_pb_pins>;
++ status = "okay";
++};
++
++/* The USB-C socket has its CC pins pulled to GND, so is hardwired as a UFP. */
++&usb_otg {
++ dr_mode = "peripheral";
++ status = "okay";
++};
++
++&usbphy {
++ usb1_vbus-supply = <®_vcc5v>;
++ status = "okay";
++};
+diff --git a/configs/mangopi_mq_r_defconfig b/configs/mangopi_mq_r_defconfig
+new file mode 100644
+index 0000000000..66ae639326
+--- /dev/null
++++ b/configs/mangopi_mq_r_defconfig
+@@ -0,0 +1,15 @@
++CONFIG_ARM=y
++CONFIG_ARCH_SUNXI=y
++CONFIG_DEFAULT_DEVICE_TREE="sun8i-t113s-mangopi-mq-r-t113"
++CONFIG_SPL=y
++CONFIG_MACH_SUN8I_R528=y
++CONFIG_DRAM_CLK=792
++CONFIG_DRAM_ZQ=8092667
++CONFIG_SUNXI_MINIMUM_DRAM_MB=128
++# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
++CONFIG_DRAM_SUNXI_ODT_EN=0
++CONFIG_DRAM_SUNXI_TPR0=0x004a2195
++CONFIG_DRAM_SUNXI_TPR11=0x340000
++CONFIG_DRAM_SUNXI_TPR12=0x46
++CONFIG_DRAM_SUNXI_TPR13=0x34000100
++CONFIG_CONS_INDEX=4
+--
+2.20.1
+
--- /dev/null
+From 243168af9a9a30516a5826b383bd86312cc246bf Mon Sep 17 00:00:00 2001
+From: Yegor Yefremov <yegorslists@googlemail.com>
+Date: Wed, 28 Nov 2012 11:15:18 +0100
+Subject: [PATCH 4027/4052] net: add ICPlus PHY driver
+
+The driver code was taken from Linux kernel source:
+drivers/net/phy/icplus.c
+
+Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
+Signed-off-by: Yegor Yefremov <yegorslists@googlemail.com>
+---
+ drivers/net/phy/Kconfig | 3 ++
+ drivers/net/phy/Makefile | 1 +
+ drivers/net/phy/icplus.c | 94 ++++++++++++++++++++++++++++++++++++++++
+ drivers/net/phy/phy.c | 3 ++
+ include/phy.h | 1 +
+ 5 files changed, 102 insertions(+)
+ create mode 100644 drivers/net/phy/icplus.c
+
+diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
+index 5eaff053a0..93980398f1 100644
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -165,6 +165,9 @@ config PHY_DAVICOM
+ config PHY_ET1011C
+ bool "LSI TruePHY ET1011C support"
+
++config PHY_ICPLUS
++ bool "IC+ IP101 Ethernet PHY support"
++
+ config PHY_LXT
+ bool "LXT971 Ethernet PHY support"
+
+diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
+index d38e99e717..5b6b78631d 100644
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -18,6 +18,7 @@ obj-$(CONFIG_PHY_CORTINA) += cortina.o
+ obj-$(CONFIG_PHY_CORTINA_ACCESS) += ca_phy.o
+ obj-$(CONFIG_PHY_DAVICOM) += davicom.o
+ obj-$(CONFIG_PHY_ET1011C) += et1011c.o
++obj-$(CONFIG_PHY_ICPLUS) += icplus.o
+ obj-$(CONFIG_PHY_LXT) += lxt.o
+ obj-$(CONFIG_PHY_MARVELL) += marvell.o
+ obj-$(CONFIG_PHY_MICREL_KSZ8XXX) += micrel_ksz8xxx.o
+diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c
+new file mode 100644
+index 0000000000..dd5c59259d
+--- /dev/null
++++ b/drivers/net/phy/icplus.c
+@@ -0,0 +1,94 @@
++/*
++ * ICPlus PHY drivers
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ *
++ * Copyright (c) 2007 Freescale Semiconductor, Inc.
++ *
++ */
++#include <phy.h>
++
++/* IP101A/G - IP1001 */
++#define IP10XX_SPEC_CTRL_STATUS 16 /* Spec. Control Register */
++#define IP1001_SPEC_CTRL_STATUS_2 20 /* IP1001 Spec. Control Reg 2 */
++#define IP1001_PHASE_SEL_MASK 3 /* IP1001 RX/TXPHASE_SEL */
++#define IP1001_APS_ON 11 /* IP1001 APS Mode bit */
++#define IP101A_G_APS_ON 2 /* IP101A/G APS Mode bit */
++#define IP101A_G_IRQ_CONF_STATUS 0x11 /* Conf Info IRQ & Status Reg */
++#define IP101A_G_IRQ_PIN_USED (1<<15) /* INTR pin used */
++#define IP101A_G_IRQ_DEFAULT IP101A_G_IRQ_PIN_USED
++
++static int ip1001_config(struct phy_device *phydev)
++{
++ int c;
++
++ /* Enable Auto Power Saving mode */
++ c = phy_read(phydev, MDIO_DEVAD_NONE, IP1001_SPEC_CTRL_STATUS_2);
++ if (c < 0)
++ return c;
++ c |= IP1001_APS_ON;
++ c = phy_write(phydev, MDIO_DEVAD_NONE, IP1001_SPEC_CTRL_STATUS_2, c);
++ if (c < 0)
++ return c;
++
++ /* INTR pin used: speed/link/duplex will cause an interrupt */
++ c = phy_write(phydev, MDIO_DEVAD_NONE, IP101A_G_IRQ_CONF_STATUS,
++ IP101A_G_IRQ_DEFAULT);
++ if (c < 0)
++ return c;
++
++ if (phydev->interface == PHY_INTERFACE_MODE_RGMII) {
++ /*
++ * Additional delay (2ns) used to adjust RX clock phase
++ * at RGMII interface
++ */
++ c = phy_read(phydev, MDIO_DEVAD_NONE, IP10XX_SPEC_CTRL_STATUS);
++ if (c < 0)
++ return c;
++
++ c |= IP1001_PHASE_SEL_MASK;
++ c = phy_write(phydev, MDIO_DEVAD_NONE, IP10XX_SPEC_CTRL_STATUS,
++ c);
++ if (c < 0)
++ return c;
++ }
++
++ return 0;
++}
++
++static int ip1001_startup(struct phy_device *phydev)
++{
++ genphy_update_link(phydev);
++ genphy_parse_link(phydev);
++
++ return 0;
++}
++static struct phy_driver IP1001_driver = {
++ .name = "ICPlus IP1001",
++ .uid = 0x02430d90,
++ .mask = 0x0ffffff0,
++ .features = PHY_GBIT_FEATURES,
++ .config = &ip1001_config,
++ .startup = &ip1001_startup,
++ .shutdown = &genphy_shutdown,
++};
++
++int phy_icplus_init(void)
++{
++ phy_register(&IP1001_driver);
++
++ return 0;
++}
+diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
+index 80230b907c..9e22db3fc8 100644
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -514,6 +514,9 @@ int phy_init(void)
+ #ifdef CONFIG_PHY_ET1011C
+ phy_et1011c_init();
+ #endif
++#ifdef CONFIG_PHY_ICPLUS
++ phy_icplus_init();
++#endif
+ #ifdef CONFIG_PHY_LXT
+ phy_lxt_init();
+ #endif
+diff --git a/include/phy.h b/include/phy.h
+index 87aa86c2e7..749285f15e 100644
+--- a/include/phy.h
++++ b/include/phy.h
+@@ -325,6 +325,7 @@ int phy_cortina_init(void);
+ int phy_cortina_access_init(void);
+ int phy_davicom_init(void);
+ int phy_et1011c_init(void);
++int phy_icplus_init(void);
+ int phy_lxt_init(void);
+ int phy_marvell_init(void);
+ int phy_micrel_ksz8xxx_init(void);
+--
+2.20.1
+
--- /dev/null
+From 424386ebda2d0fb0e00bb192572a7050bf5b084b Mon Sep 17 00:00:00 2001
+From: Yanhong Wang <yanhong.wang@starfivetech.com>
+Date: Thu, 25 May 2023 17:36:27 +0800
+Subject: [PATCH 4028/4052] net: phy: Add driver for Motorcomm yt8531 gigabit
+ ethernet phy
+
+Add a driver for the motorcomm yt8531 gigabit ethernet phy. We have
+verified the driver on StarFive VisionFive2 board.
+
+Signed-off-by: Yanhong Wang <yanhong.wang@starfivetech.com>
+Reviewed-by: Ramon Fried <rfried.dev@gmail.com>
+---
+ drivers/net/phy/Kconfig | 6 +
+ drivers/net/phy/Makefile | 1 +
+ drivers/net/phy/motorcomm.c | 437 ++++++++++++++++++++++++++++++++++++
+ 3 files changed, 444 insertions(+)
+ create mode 100644 drivers/net/phy/motorcomm.c
+
+diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
+index 93980398f1..0a99682c27 100644
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -215,6 +215,12 @@ config PHY_MICREL_KSZ8XXX
+
+ endif # PHY_MICREL
+
++config PHY_MOTORCOMM
++ tristate "Motorcomm PHYs"
++ help
++ Enables support for Motorcomm network PHYs.
++ Currently supports the YT8531 Gigabit Ethernet PHYs.
++
+ config PHY_MSCC
+ bool "Microsemi Corp Ethernet PHYs support"
+
+diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
+index 5b6b78631d..9637a138f8 100644
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -24,6 +24,7 @@ obj-$(CONFIG_PHY_MARVELL) += marvell.o
+ obj-$(CONFIG_PHY_MICREL_KSZ8XXX) += micrel_ksz8xxx.o
+ obj-$(CONFIG_PHY_MICREL_KSZ90X1) += micrel_ksz90x1.o
+ obj-$(CONFIG_PHY_MESON_GXL) += meson-gxl.o
++obj-$(CONFIG_PHY_MOTORCOMM) += motorcomm.o
+ obj-$(CONFIG_PHY_NATSEMI) += natsemi.o
+ obj-$(CONFIG_PHY_NXP_C45_TJA11XX) += nxp-c45-tja11xx.o
+ obj-$(CONFIG_PHY_NXP_TJA11XX) += nxp-tja11xx.o
+diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c
+new file mode 100644
+index 0000000000..e822fd76f2
+--- /dev/null
++++ b/drivers/net/phy/motorcomm.c
+@@ -0,0 +1,437 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Motorcomm 8531 PHY driver.
++ *
++ * Copyright (C) 2023 StarFive Technology Co., Ltd.
++ */
++
++#include <config.h>
++#include <common.h>
++#include <malloc.h>
++#include <phy.h>
++#include <linux/bitfield.h>
++
++#define PHY_ID_YT8531 0x4f51e91b
++#define PHY_ID_MASK GENMASK(31, 0)
++
++/* Extended Register's Address Offset Register */
++#define YTPHY_PAGE_SELECT 0x1E
++
++/* Extended Register's Data Register */
++#define YTPHY_PAGE_DATA 0x1F
++
++#define YTPHY_SYNCE_CFG_REG 0xA012
++
++#define YTPHY_DTS_OUTPUT_CLK_DIS 0
++#define YTPHY_DTS_OUTPUT_CLK_25M 25000000
++#define YTPHY_DTS_OUTPUT_CLK_125M 125000000
++
++#define YT8531_SCR_SYNCE_ENABLE BIT(6)
++/* 1b0 output 25m clock *default*
++ * 1b1 output 125m clock
++ */
++#define YT8531_SCR_CLK_FRE_SEL_125M BIT(4)
++#define YT8531_SCR_CLK_SRC_MASK GENMASK(3, 1)
++#define YT8531_SCR_CLK_SRC_PLL_125M 0
++#define YT8531_SCR_CLK_SRC_UTP_RX 1
++#define YT8531_SCR_CLK_SRC_SDS_RX 2
++#define YT8531_SCR_CLK_SRC_CLOCK_FROM_DIGITAL 3
++#define YT8531_SCR_CLK_SRC_REF_25M 4
++#define YT8531_SCR_CLK_SRC_SSC_25M 5
++
++/* 1b0 use original tx_clk_rgmii *default*
++ * 1b1 use inverted tx_clk_rgmii.
++ */
++#define YT8531_RC1R_TX_CLK_SEL_INVERTED BIT(14)
++#define YT8531_RC1R_RX_DELAY_MASK GENMASK(13, 10)
++#define YT8531_RC1R_FE_TX_DELAY_MASK GENMASK(7, 4)
++#define YT8531_RC1R_GE_TX_DELAY_MASK GENMASK(3, 0)
++#define YT8531_RC1R_RGMII_0_000_NS 0
++#define YT8531_RC1R_RGMII_0_150_NS 1
++#define YT8531_RC1R_RGMII_0_300_NS 2
++#define YT8531_RC1R_RGMII_0_450_NS 3
++#define YT8531_RC1R_RGMII_0_600_NS 4
++#define YT8531_RC1R_RGMII_0_750_NS 5
++#define YT8531_RC1R_RGMII_0_900_NS 6
++#define YT8531_RC1R_RGMII_1_050_NS 7
++#define YT8531_RC1R_RGMII_1_200_NS 8
++#define YT8531_RC1R_RGMII_1_350_NS 9
++#define YT8531_RC1R_RGMII_1_500_NS 10
++#define YT8531_RC1R_RGMII_1_650_NS 11
++#define YT8531_RC1R_RGMII_1_800_NS 12
++#define YT8531_RC1R_RGMII_1_950_NS 13
++#define YT8531_RC1R_RGMII_2_100_NS 14
++#define YT8531_RC1R_RGMII_2_250_NS 15
++
++/* Phy gmii clock gating Register */
++#define YT8531_CLOCK_GATING_REG 0xC
++#define YT8531_CGR_RX_CLK_EN BIT(12)
++
++/* Specific Status Register */
++#define YTPHY_SPECIFIC_STATUS_REG 0x11
++#define YTPHY_DUPLEX_MASK BIT(13)
++#define YTPHY_DUPLEX_SHIFT 13
++#define YTPHY_SPEED_MODE_MASK GENMASK(15, 14)
++#define YTPHY_SPEED_MODE_SHIFT 14
++
++#define YT8531_EXTREG_SLEEP_CONTROL1_REG 0x27
++#define YT8531_ESC1R_SLEEP_SW BIT(15)
++#define YT8531_ESC1R_PLLON_SLP BIT(14)
++
++#define YT8531_RGMII_CONFIG1_REG 0xA003
++
++#define YT8531_CHIP_CONFIG_REG 0xA001
++#define YT8531_CCR_SW_RST BIT(15)
++/* 1b0 disable 1.9ns rxc clock delay *default*
++ * 1b1 enable 1.9ns rxc clock delay
++ */
++#define YT8531_CCR_RXC_DLY_EN BIT(8)
++#define YT8531_CCR_RXC_DLY_1_900_NS 1900
++
++/* bits in struct ytphy_plat_priv->flag */
++#define TX_CLK_ADJ_ENABLED BIT(0)
++#define AUTO_SLEEP_DISABLED BIT(1)
++#define KEEP_PLL_ENABLED BIT(2)
++#define TX_CLK_10_INVERTED BIT(3)
++#define TX_CLK_100_INVERTED BIT(4)
++#define TX_CLK_1000_INVERTED BIT(5)
++
++struct ytphy_plat_priv {
++ u32 rx_delay_ps;
++ u32 tx_delay_ps;
++ u32 clk_out_frequency;
++ u32 flag;
++};
++
++/**
++ * struct ytphy_cfg_reg_map - map a config value to a register value
++ * @cfg: value in device configuration
++ * @reg: value in the register
++ */
++struct ytphy_cfg_reg_map {
++ u32 cfg;
++ u32 reg;
++};
++
++static const struct ytphy_cfg_reg_map ytphy_rgmii_delays[] = {
++ /* for tx delay / rx delay with YT8531_CCR_RXC_DLY_EN is not set. */
++ { 0, YT8531_RC1R_RGMII_0_000_NS },
++ { 150, YT8531_RC1R_RGMII_0_150_NS },
++ { 300, YT8531_RC1R_RGMII_0_300_NS },
++ { 450, YT8531_RC1R_RGMII_0_450_NS },
++ { 600, YT8531_RC1R_RGMII_0_600_NS },
++ { 750, YT8531_RC1R_RGMII_0_750_NS },
++ { 900, YT8531_RC1R_RGMII_0_900_NS },
++ { 1050, YT8531_RC1R_RGMII_1_050_NS },
++ { 1200, YT8531_RC1R_RGMII_1_200_NS },
++ { 1350, YT8531_RC1R_RGMII_1_350_NS },
++ { 1500, YT8531_RC1R_RGMII_1_500_NS },
++ { 1650, YT8531_RC1R_RGMII_1_650_NS },
++ { 1800, YT8531_RC1R_RGMII_1_800_NS },
++ { 1950, YT8531_RC1R_RGMII_1_950_NS }, /* default tx/rx delay */
++ { 2100, YT8531_RC1R_RGMII_2_100_NS },
++ { 2250, YT8531_RC1R_RGMII_2_250_NS },
++
++ /* only for rx delay with YT8531_CCR_RXC_DLY_EN is set. */
++ { 0 + YT8531_CCR_RXC_DLY_1_900_NS, YT8531_RC1R_RGMII_0_000_NS },
++ { 150 + YT8531_CCR_RXC_DLY_1_900_NS, YT8531_RC1R_RGMII_0_150_NS },
++ { 300 + YT8531_CCR_RXC_DLY_1_900_NS, YT8531_RC1R_RGMII_0_300_NS },
++ { 450 + YT8531_CCR_RXC_DLY_1_900_NS, YT8531_RC1R_RGMII_0_450_NS },
++ { 600 + YT8531_CCR_RXC_DLY_1_900_NS, YT8531_RC1R_RGMII_0_600_NS },
++ { 750 + YT8531_CCR_RXC_DLY_1_900_NS, YT8531_RC1R_RGMII_0_750_NS },
++ { 900 + YT8531_CCR_RXC_DLY_1_900_NS, YT8531_RC1R_RGMII_0_900_NS },
++ { 1050 + YT8531_CCR_RXC_DLY_1_900_NS, YT8531_RC1R_RGMII_1_050_NS },
++ { 1200 + YT8531_CCR_RXC_DLY_1_900_NS, YT8531_RC1R_RGMII_1_200_NS },
++ { 1350 + YT8531_CCR_RXC_DLY_1_900_NS, YT8531_RC1R_RGMII_1_350_NS },
++ { 1500 + YT8531_CCR_RXC_DLY_1_900_NS, YT8531_RC1R_RGMII_1_500_NS },
++ { 1650 + YT8531_CCR_RXC_DLY_1_900_NS, YT8531_RC1R_RGMII_1_650_NS },
++ { 1800 + YT8531_CCR_RXC_DLY_1_900_NS, YT8531_RC1R_RGMII_1_800_NS },
++ { 1950 + YT8531_CCR_RXC_DLY_1_900_NS, YT8531_RC1R_RGMII_1_950_NS },
++ { 2100 + YT8531_CCR_RXC_DLY_1_900_NS, YT8531_RC1R_RGMII_2_100_NS },
++ { 2250 + YT8531_CCR_RXC_DLY_1_900_NS, YT8531_RC1R_RGMII_2_250_NS }
++};
++
++static u32 ytphy_get_delay_reg_value(struct phy_device *phydev,
++ u32 val,
++ u16 *rxc_dly_en)
++{
++ int tb_size = ARRAY_SIZE(ytphy_rgmii_delays);
++ int tb_size_half = tb_size / 2;
++ int i;
++
++ /* when rxc_dly_en is NULL, it is get the delay for tx, only half of
++ * tb_size is valid.
++ */
++ if (!rxc_dly_en)
++ tb_size = tb_size_half;
++
++ for (i = 0; i < tb_size; i++) {
++ if (ytphy_rgmii_delays[i].cfg == val) {
++ if (rxc_dly_en && i < tb_size_half)
++ *rxc_dly_en = 0;
++ return ytphy_rgmii_delays[i].reg;
++ }
++ }
++
++ pr_warn("Unsupported value %d, using default (%u)\n",
++ val, YT8531_RC1R_RGMII_1_950_NS);
++
++ /* when rxc_dly_en is not NULL, it is get the delay for rx.
++ * The rx default in dts and ytphy_rgmii_clk_delay_config is 1950 ps,
++ * so YT8531_CCR_RXC_DLY_EN should not be set.
++ */
++ if (rxc_dly_en)
++ *rxc_dly_en = 0;
++
++ return YT8531_RC1R_RGMII_1_950_NS;
++}
++
++static int ytphy_modify_ext(struct phy_device *phydev, u16 regnum, u16 mask,
++ u16 set)
++{
++ int ret;
++
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, YTPHY_PAGE_SELECT, regnum);
++ if (ret < 0)
++ return ret;
++
++ return phy_modify(phydev, MDIO_DEVAD_NONE, YTPHY_PAGE_DATA, mask, set);
++}
++
++static int ytphy_rgmii_clk_delay_config(struct phy_device *phydev)
++{
++ struct ytphy_plat_priv *priv = phydev->priv;
++ u16 rxc_dly_en = YT8531_CCR_RXC_DLY_EN;
++ u32 rx_reg, tx_reg;
++ u16 mask, val = 0;
++ int ret;
++
++ rx_reg = ytphy_get_delay_reg_value(phydev, priv->rx_delay_ps,
++ &rxc_dly_en);
++ tx_reg = ytphy_get_delay_reg_value(phydev, priv->tx_delay_ps,
++ NULL);
++
++ switch (phydev->interface) {
++ case PHY_INTERFACE_MODE_RGMII:
++ rxc_dly_en = 0;
++ break;
++ case PHY_INTERFACE_MODE_RGMII_RXID:
++ val |= FIELD_PREP(YT8531_RC1R_RX_DELAY_MASK, rx_reg);
++ break;
++ case PHY_INTERFACE_MODE_RGMII_TXID:
++ rxc_dly_en = 0;
++ val |= FIELD_PREP(YT8531_RC1R_GE_TX_DELAY_MASK, tx_reg);
++ break;
++ case PHY_INTERFACE_MODE_RGMII_ID:
++ val |= FIELD_PREP(YT8531_RC1R_RX_DELAY_MASK, rx_reg) |
++ FIELD_PREP(YT8531_RC1R_GE_TX_DELAY_MASK, tx_reg);
++ break;
++ default: /* do not support other modes */
++ return -EOPNOTSUPP;
++ }
++
++ ret = ytphy_modify_ext(phydev, YT8531_CHIP_CONFIG_REG,
++ YT8531_CCR_RXC_DLY_EN, rxc_dly_en);
++ if (ret < 0)
++ return ret;
++
++ /* Generally, it is not necessary to adjust YT8531_RC1R_FE_TX_DELAY */
++ mask = YT8531_RC1R_RX_DELAY_MASK | YT8531_RC1R_GE_TX_DELAY_MASK;
++ return ytphy_modify_ext(phydev, YT8531_RGMII_CONFIG1_REG, mask, val);
++}
++
++static int yt8531_parse_status(struct phy_device *phydev)
++{
++ int val;
++ int speed, speed_mode;
++
++ val = phy_read(phydev, MDIO_DEVAD_NONE, YTPHY_SPECIFIC_STATUS_REG);
++ if (val < 0)
++ return val;
++
++ speed_mode = (val & YTPHY_SPEED_MODE_MASK) >> YTPHY_SPEED_MODE_SHIFT;
++ switch (speed_mode) {
++ case 2:
++ speed = SPEED_1000;
++ break;
++ case 1:
++ speed = SPEED_100;
++ break;
++ default:
++ speed = SPEED_10;
++ break;
++ }
++
++ phydev->speed = speed;
++ phydev->duplex = (val & YTPHY_DUPLEX_MASK) >> YTPHY_DUPLEX_SHIFT;
++
++ return 0;
++}
++
++static int yt8531_startup(struct phy_device *phydev)
++{
++ struct ytphy_plat_priv *priv = phydev->priv;
++ u16 val = 0;
++ int ret;
++
++ ret = genphy_update_link(phydev);
++ if (ret)
++ return ret;
++
++ ret = yt8531_parse_status(phydev);
++ if (ret)
++ return ret;
++
++ if (phydev->speed < 0)
++ return -EINVAL;
++
++ if (!(priv->flag & TX_CLK_ADJ_ENABLED))
++ return 0;
++
++ switch (phydev->speed) {
++ case SPEED_1000:
++ if (priv->flag & TX_CLK_1000_INVERTED)
++ val = YT8531_RC1R_TX_CLK_SEL_INVERTED;
++ break;
++ case SPEED_100:
++ if (priv->flag & TX_CLK_100_INVERTED)
++ val = YT8531_RC1R_TX_CLK_SEL_INVERTED;
++ break;
++ case SPEED_10:
++ if (priv->flag & TX_CLK_10_INVERTED)
++ val = YT8531_RC1R_TX_CLK_SEL_INVERTED;
++ break;
++ default:
++ printf("UNKNOWN SPEED\n");
++ return -EINVAL;
++ }
++
++ ret = ytphy_modify_ext(phydev, YT8531_RGMII_CONFIG1_REG,
++ YT8531_RC1R_TX_CLK_SEL_INVERTED, val);
++ if (ret < 0)
++ pr_warn("Modify TX_CLK_SEL err:%d\n", ret);
++
++ return 0;
++}
++
++static void ytphy_dt_parse(struct phy_device *phydev)
++{
++ struct ytphy_plat_priv *priv = phydev->priv;
++
++ priv->clk_out_frequency = ofnode_read_u32_default(phydev->node,
++ "motorcomm,clk-out-frequency-hz",
++ YTPHY_DTS_OUTPUT_CLK_DIS);
++ priv->rx_delay_ps = ofnode_read_u32_default(phydev->node,
++ "rx-internal-delay-ps",
++ YT8531_RC1R_RGMII_1_950_NS);
++ priv->tx_delay_ps = ofnode_read_u32_default(phydev->node,
++ "tx-internal-delay-ps",
++ YT8531_RC1R_RGMII_1_950_NS);
++
++ if (ofnode_read_bool(phydev->node, "motorcomm,auto-sleep-disabled"))
++ priv->flag |= AUTO_SLEEP_DISABLED;
++
++ if (ofnode_read_bool(phydev->node, "motorcomm,keep-pll-enabled"))
++ priv->flag |= KEEP_PLL_ENABLED;
++
++ if (ofnode_read_bool(phydev->node, "motorcomm,tx-clk-adj-enabled"))
++ priv->flag |= TX_CLK_ADJ_ENABLED;
++
++ if (ofnode_read_bool(phydev->node, "motorcomm,tx-clk-10-inverted"))
++ priv->flag |= TX_CLK_10_INVERTED;
++
++ if (ofnode_read_bool(phydev->node, "motorcomm,tx-clk-100-inverted"))
++ priv->flag |= TX_CLK_100_INVERTED;
++
++ if (ofnode_read_bool(phydev->node, "motorcomm,tx-clk-1000-inverted"))
++ priv->flag |= TX_CLK_1000_INVERTED;
++}
++
++static int yt8531_config(struct phy_device *phydev)
++{
++ struct ytphy_plat_priv *priv = phydev->priv;
++ u16 mask, val;
++ int ret;
++
++ ret = genphy_config_aneg(phydev);
++ if (ret < 0)
++ return ret;
++
++ ytphy_dt_parse(phydev);
++ switch (priv->clk_out_frequency) {
++ case YTPHY_DTS_OUTPUT_CLK_DIS:
++ mask = YT8531_SCR_SYNCE_ENABLE;
++ val = 0;
++ break;
++ case YTPHY_DTS_OUTPUT_CLK_25M:
++ mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK |
++ YT8531_SCR_CLK_FRE_SEL_125M;
++ val = YT8531_SCR_SYNCE_ENABLE |
++ FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
++ YT8531_SCR_CLK_SRC_REF_25M);
++ break;
++ case YTPHY_DTS_OUTPUT_CLK_125M:
++ mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK |
++ YT8531_SCR_CLK_FRE_SEL_125M;
++ val = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_FRE_SEL_125M |
++ FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
++ YT8531_SCR_CLK_SRC_PLL_125M);
++ break;
++ default:
++ pr_warn("Freq err:%u\n", priv->clk_out_frequency);
++ return -EINVAL;
++ }
++
++ ret = ytphy_modify_ext(phydev, YTPHY_SYNCE_CFG_REG, mask,
++ val);
++ if (ret < 0)
++ return ret;
++
++ ret = ytphy_rgmii_clk_delay_config(phydev);
++ if (ret < 0)
++ return ret;
++
++ if (priv->flag & AUTO_SLEEP_DISABLED) {
++ /* disable auto sleep */
++ ret = ytphy_modify_ext(phydev,
++ YT8531_EXTREG_SLEEP_CONTROL1_REG,
++ YT8531_ESC1R_SLEEP_SW, 0);
++ if (ret < 0)
++ return ret;
++ }
++
++ if (priv->flag & KEEP_PLL_ENABLED) {
++ /* enable RXC clock when no wire plug */
++ ret = ytphy_modify_ext(phydev,
++ YT8531_CLOCK_GATING_REG,
++ YT8531_CGR_RX_CLK_EN, 0);
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
++
++static int yt8531_probe(struct phy_device *phydev)
++{
++ struct ytphy_plat_priv *priv;
++
++ priv = calloc(1, sizeof(struct ytphy_plat_priv));
++ if (!priv)
++ return -ENOMEM;
++
++ phydev->priv = priv;
++
++ return 0;
++}
++
++U_BOOT_PHY_DRIVER(motorcomm8531) = {
++ .name = "YT8531 Gigabit Ethernet",
++ .uid = PHY_ID_YT8531,
++ .mask = PHY_ID_MASK,
++ .features = PHY_GBIT_FEATURES,
++ .probe = &yt8531_probe,
++ .config = &yt8531_config,
++ .startup = &yt8531_startup,
++ .shutdown = &genphy_shutdown,
++};
+--
+2.20.1
+
--- /dev/null
+From d7448cb6d2a3dfe8c76f4807fba748c1c83a387c Mon Sep 17 00:00:00 2001
+From: Zoltan HERPAI <wigyori@uid0.hu>
+Date: Mon, 5 Jun 2023 18:15:15 +0200
+Subject: [PATCH 4029/4052] net: phy: backport and update driver for Motorcomm
+ yt8531 phy
+
+Don't use U_BOOT_PHY_DRIVER yet.
+
+Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
+---
+ drivers/net/phy/motorcomm.c | 9 ++++++++-
+ drivers/net/phy/phy.c | 3 +++
+ include/phy.h | 1 +
+ 3 files changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c
+index e822fd76f2..eb2dd65c4a 100644
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -425,7 +425,7 @@ static int yt8531_probe(struct phy_device *phydev)
+ return 0;
+ }
+
+-U_BOOT_PHY_DRIVER(motorcomm8531) = {
++static struct phy_driver YT8531_driver = {
+ .name = "YT8531 Gigabit Ethernet",
+ .uid = PHY_ID_YT8531,
+ .mask = PHY_ID_MASK,
+@@ -435,3 +435,10 @@ U_BOOT_PHY_DRIVER(motorcomm8531) = {
+ .startup = &yt8531_startup,
+ .shutdown = &genphy_shutdown,
+ };
++
++int phy_motorcomm_init(void)
++{
++ phy_register(&YT8531_driver);
++
++ return 0;
++}
+diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
+index 9e22db3fc8..3613c3dd17 100644
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -532,6 +532,9 @@ int phy_init(void)
+ #ifdef CONFIG_PHY_MESON_GXL
+ phy_meson_gxl_init();
+ #endif
++#ifdef CONFIG_PHY_MOTORCOMM
++ phy_motorcomm_init();
++#endif
+ #ifdef CONFIG_PHY_NATSEMI
+ phy_natsemi_init();
+ #endif
+diff --git a/include/phy.h b/include/phy.h
+index 749285f15e..05208d7f3d 100644
+--- a/include/phy.h
++++ b/include/phy.h
+@@ -331,6 +331,7 @@ int phy_marvell_init(void);
+ int phy_micrel_ksz8xxx_init(void);
+ int phy_micrel_ksz90x1_init(void);
+ int phy_meson_gxl_init(void);
++int phy_motorcomm_init(void);
+ int phy_natsemi_init(void);
+ int phy_nxp_c45_tja11xx_init(void);
+ int phy_nxp_tja11xx_init(void);
+--
+2.20.1
+
--- /dev/null
+From e711e33258167beee01ae3768eff708eb72bcf6b Mon Sep 17 00:00:00 2001
+From: Maxim Kiselev <bigunclemax@gmail.com>
+Date: Fri, 19 May 2023 16:40:07 +0300
+Subject: [PATCH 4030/4052] sunxi: SPL SPI: Add SPI boot support for the
+ Allwinner R528/T113 SoCs
+
+R528/T113 SoCs uses the same SPI IP as the H6, also have the same clocks
+and reset bits layout, but the CCU base is different. Another difference
+is that the new SoCs do not have a clock divider inside. Instead of this
+we should configure sample mode depending on input clock rate.
+
+The pin assignment is also different: the H6 uses PC0, the R528/T113 PC4
+instead. This makes for a change in spi0_pinmux_setup() routine.
+
+This patch extends the H6/H616 #ifdef guards to also cover the R528/T113,
+using the shared CONFIG_SUNXI_GEN_NCAT2 and CONFIG_MACH_SUN8I_R528
+symbols. Also use CONFIG_SUNXI_GEN_NCAT2 symbol for the Kconfig
+dependency.
+
+Signed-off-by: Maxim Kiselev <bigunclemax@gmail.com>
+Tested-by: Sam Edwards <CFSworks@gmail.com>
+---
+ arch/arm/mach-sunxi/Kconfig | 2 +-
+ arch/arm/mach-sunxi/spl_spi_sunxi.c | 78 +++++++++++++++++++++--------
+ 2 files changed, 58 insertions(+), 22 deletions(-)
+
+diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
+index 77b510cdfe..81d2a173d0 100644
+--- a/arch/arm/mach-sunxi/Kconfig
++++ b/arch/arm/mach-sunxi/Kconfig
+@@ -997,7 +997,7 @@ config SPL_STACK_R_ADDR
+
+ config SPL_SPI_SUNXI
+ bool "Support for SPI Flash on Allwinner SoCs in SPL"
+- depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUNXI_H3_H5 || MACH_SUN50I || MACH_SUN8I_R40 || SUN50I_GEN_H6 || MACH_SUNIV
++ depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUNXI_H3_H5 || MACH_SUN50I || MACH_SUN8I_R40 || SUN50I_GEN_H6 || MACH_SUNIV || SUNXI_GEN_NCAT2
+ help
+ Enable support for SPI Flash. This option allows SPL to read from
+ sunxi SPI Flash. It uses the same method as the boot ROM, so does
+diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c b/arch/arm/mach-sunxi/spl_spi_sunxi.c
+index c2410dd7bb..3cfbf56d59 100644
+--- a/arch/arm/mach-sunxi/spl_spi_sunxi.c
++++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c
+@@ -73,18 +73,27 @@
+ #define SUN6I_CTL_ENABLE BIT(0)
+ #define SUN6I_CTL_MASTER BIT(1)
+ #define SUN6I_CTL_SRST BIT(31)
++#define SUN6I_TCR_SDM BIT(13)
+ #define SUN6I_TCR_XCH BIT(31)
+
+ /*****************************************************************************/
+
+-#define CCM_AHB_GATING0 (0x01C20000 + 0x60)
+-#define CCM_H6_SPI_BGR_REG (0x03001000 + 0x96c)
+-#ifdef CONFIG_SUN50I_GEN_H6
+-#define CCM_SPI0_CLK (0x03001000 + 0x940)
++#if defined(CONFIG_SUN50I_GEN_H6)
++#define CCM_BASE 0x03001000
++#elif defined(CONFIG_SUNXI_GEN_NCAT2)
++#define CCM_BASE 0x02001000
+ #else
+-#define CCM_SPI0_CLK (0x01C20000 + 0xA0)
++#define CCM_BASE 0x01C20000
+ #endif
+-#define SUN6I_BUS_SOFT_RST_REG0 (0x01C20000 + 0x2C0)
++
++#define CCM_AHB_GATING0 (CCM_BASE + 0x60)
++#define CCM_H6_SPI_BGR_REG (CCM_BASE + 0x96c)
++#if defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2)
++#define CCM_SPI0_CLK (CCM_BASE + 0x940)
++#else
++#define CCM_SPI0_CLK (CCM_BASE + 0xA0)
++#endif
++#define SUN6I_BUS_SOFT_RST_REG0 (CCM_BASE + 0x2C0)
+
+ #define AHB_RESET_SPI0_SHIFT 20
+ #define AHB_GATE_OFFSET_SPI0 20
+@@ -102,17 +111,22 @@
+ */
+ static void spi0_pinmux_setup(unsigned int pin_function)
+ {
+- /* All chips use PC0 and PC2. */
+- sunxi_gpio_set_cfgpin(SUNXI_GPC(0), pin_function);
++ /* All chips use PC2. And all chips use PC0, except R528/T113 */
++ if (!IS_ENABLED(CONFIG_MACH_SUN8I_R528))
++ sunxi_gpio_set_cfgpin(SUNXI_GPC(0), pin_function);
++
+ sunxi_gpio_set_cfgpin(SUNXI_GPC(2), pin_function);
+
+- /* All chips except H6 and H616 use PC1. */
+- if (!IS_ENABLED(CONFIG_SUN50I_GEN_H6))
++ /* All chips except H6/H616/R528/T113 use PC1. */
++ if (!IS_ENABLED(CONFIG_SUN50I_GEN_H6) &&
++ !IS_ENABLED(CONFIG_MACH_SUN8I_R528))
+ sunxi_gpio_set_cfgpin(SUNXI_GPC(1), pin_function);
+
+- if (IS_ENABLED(CONFIG_MACH_SUN50I_H6))
++ if (IS_ENABLED(CONFIG_MACH_SUN50I_H6) ||
++ IS_ENABLED(CONFIG_MACH_SUN8I_R528))
+ sunxi_gpio_set_cfgpin(SUNXI_GPC(5), pin_function);
+- if (IS_ENABLED(CONFIG_MACH_SUN50I_H616))
++ if (IS_ENABLED(CONFIG_MACH_SUN50I_H616) ||
++ IS_ENABLED(CONFIG_MACH_SUN8I_R528))
+ sunxi_gpio_set_cfgpin(SUNXI_GPC(4), pin_function);
+
+ /* Older generations use PC23 for CS, newer ones use PC3. */
+@@ -126,7 +140,8 @@ static void spi0_pinmux_setup(unsigned int pin_function)
+ static bool is_sun6i_gen_spi(void)
+ {
+ return IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I) ||
+- IS_ENABLED(CONFIG_SUN50I_GEN_H6);
++ IS_ENABLED(CONFIG_SUN50I_GEN_H6) ||
++ IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2);
+ }
+
+ static uintptr_t spi0_base_address(void)
+@@ -137,6 +152,9 @@ static uintptr_t spi0_base_address(void)
+ if (IS_ENABLED(CONFIG_SUN50I_GEN_H6))
+ return 0x05010000;
+
++ if (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2))
++ return 0x04025000;
++
+ if (!is_sun6i_gen_spi() ||
+ IS_ENABLED(CONFIG_MACH_SUNIV))
+ return 0x01C05000;
+@@ -152,23 +170,30 @@ static void spi0_enable_clock(void)
+ uintptr_t base = spi0_base_address();
+
+ /* Deassert SPI0 reset on SUN6I */
+- if (IS_ENABLED(CONFIG_SUN50I_GEN_H6))
++ if (IS_ENABLED(CONFIG_SUN50I_GEN_H6) ||
++ IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2))
+ setbits_le32(CCM_H6_SPI_BGR_REG, (1U << 16) | 0x1);
+ else if (is_sun6i_gen_spi())
+ setbits_le32(SUN6I_BUS_SOFT_RST_REG0,
+ (1 << AHB_RESET_SPI0_SHIFT));
+
+ /* Open the SPI0 gate */
+- if (!IS_ENABLED(CONFIG_SUN50I_GEN_H6))
++ if (!IS_ENABLED(CONFIG_SUN50I_GEN_H6) &&
++ !IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2))
+ setbits_le32(CCM_AHB_GATING0, (1 << AHB_GATE_OFFSET_SPI0));
+
+ if (IS_ENABLED(CONFIG_MACH_SUNIV)) {
+ /* Divide by 32, clock source is AHB clock 200MHz */
+ writel(SPI0_CLK_DIV_BY_32, base + SUN6I_SPI0_CCTL);
+ } else {
+- /* Divide by 4 */
+- writel(SPI0_CLK_DIV_BY_4, base + (is_sun6i_gen_spi() ?
+- SUN6I_SPI0_CCTL : SUN4I_SPI0_CCTL));
++ /* New SoCs do not have a clock divider inside */
++ if (!IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)) {
++ /* Divide by 4 */
++ writel(SPI0_CLK_DIV_BY_4,
++ base + (is_sun6i_gen_spi() ? SUN6I_SPI0_CCTL :
++ SUN4I_SPI0_CCTL));
++ }
++
+ /* 24MHz from OSC24M */
+ writel((1 << 31), CCM_SPI0_CLK);
+ }
+@@ -180,6 +205,14 @@ static void spi0_enable_clock(void)
+ /* Wait for completion */
+ while (readl(base + SUN6I_SPI0_GCR) & SUN6I_CTL_SRST)
+ ;
++
++ /*
++ * For new SoCs we should configure sample mode depending on
++ * input clock. As 24MHz from OSC24M is used, we could use
++ * normal sample mode by setting SDM bit in the TCR register
++ */
++ if (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2))
++ setbits_le32(base + SUN6I_SPI0_TCR, SUN6I_TCR_SDM);
+ } else {
+ /* Enable SPI in the master mode and reset FIFO */
+ setbits_le32(base + SUN4I_SPI0_CTL, SUN4I_CTL_MASTER |
+@@ -206,11 +239,13 @@ static void spi0_disable_clock(void)
+ writel(0, CCM_SPI0_CLK);
+
+ /* Close the SPI0 gate */
+- if (!IS_ENABLED(CONFIG_SUN50I_GEN_H6))
++ if (!IS_ENABLED(CONFIG_SUN50I_GEN_H6) &&
++ !IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2))
+ clrbits_le32(CCM_AHB_GATING0, (1 << AHB_GATE_OFFSET_SPI0));
+
+ /* Assert SPI0 reset on SUN6I */
+- if (IS_ENABLED(CONFIG_SUN50I_GEN_H6))
++ if (IS_ENABLED(CONFIG_SUN50I_GEN_H6) ||
++ IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2))
+ clrbits_le32(CCM_H6_SPI_BGR_REG, (1U << 16) | 0x1);
+ else if (is_sun6i_gen_spi())
+ clrbits_le32(SUN6I_BUS_SOFT_RST_REG0,
+@@ -224,7 +259,8 @@ static void spi0_init(void)
+ if (IS_ENABLED(CONFIG_MACH_SUN50I) ||
+ IS_ENABLED(CONFIG_SUN50I_GEN_H6))
+ pin_function = SUN50I_GPC_SPI0;
+- else if (IS_ENABLED(CONFIG_MACH_SUNIV))
++ else if (IS_ENABLED(CONFIG_MACH_SUNIV) ||
++ IS_ENABLED(CONFIG_MACH_SUN8I_R528))
+ pin_function = SUNIV_GPC_SPI0;
+
+ spi0_pinmux_setup(pin_function);
+--
+2.20.1
+
--- /dev/null
+From 66aa5102c53c8e462fa5311c7abd252defc74b96 Mon Sep 17 00:00:00 2001
+From: Maxim Kiselev <bigunclemax@gmail.com>
+Date: Fri, 19 May 2023 16:40:08 +0300
+Subject: [PATCH 4031/4052] spi: sunxi: Add support for R329/D1/R528/T113 SPI
+ controller
+
+These SoCs have two SPI controllers that are quite similar to the SPI
+on previous Allwinner SoCs. The main difference is that new SoCs
+don't have a clock divider (SPI_CCR register) inside SPI IP.
+
+Instead SPI sample mode should be configured depending on the input clock.
+
+For now SPI input clock source selection is not supported by this driver,
+and only HOSC@24MHz can be used as input clock. Therefore, according to
+the, manual we could change the SPI sample mode from delay half
+cycle(default) to normal.
+
+This patch adds a quirk for this kind of SPI controllers
+
+Signed-off-by: Maxim Kiselev <bigunclemax@gmail.com>
+Tested-by: Sam Edwards <CFSworks@gmail.com>
+---
+ drivers/spi/spi-sunxi.c | 34 +++++++++++++++++++++++++++++++++-
+ 1 file changed, 33 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/spi/spi-sunxi.c b/drivers/spi/spi-sunxi.c
+index c56d82d998..9ec6b359e2 100644
+--- a/drivers/spi/spi-sunxi.c
++++ b/drivers/spi/spi-sunxi.c
+@@ -117,6 +117,8 @@ enum sun4i_spi_bits {
+ SPI_TCR_XCH,
+ SPI_TCR_CS_MANUAL,
+ SPI_TCR_CS_LEVEL,
++ SPI_TCR_SDC,
++ SPI_TCR_SDM,
+ SPI_FCR_TF_RST,
+ SPI_FCR_RF_RST,
+ SPI_FSR_RF_CNT_MASK,
+@@ -128,6 +130,7 @@ struct sun4i_spi_variant {
+ u32 fifo_depth;
+ bool has_soft_reset;
+ bool has_burst_ctl;
++ bool has_clk_ctl;
+ };
+
+ struct sun4i_spi_plat {
+@@ -302,7 +305,19 @@ static int sun4i_spi_claim_bus(struct udevice *dev)
+ setbits_le32(SPI_REG(priv, SPI_TCR), SPI_BIT(priv, SPI_TCR_CS_MANUAL) |
+ SPI_BIT(priv, SPI_TCR_CS_ACTIVE_LOW));
+
+- sun4i_spi_set_speed_mode(dev->parent);
++ if (priv->variant->has_clk_ctl) {
++ sun4i_spi_set_speed_mode(dev->parent);
++ } else {
++ /*
++ * At this moment there is no ability to change input clock.
++ * Therefore, we can only use default HOSC@24MHz clock and
++ * set SPI sampling mode to normal
++ */
++ clrsetbits_le32(SPI_REG(priv, SPI_TCR),
++ SPI_BIT(priv, SPI_TCR_SDC) |
++ SPI_BIT(priv, SPI_TCR_SDM),
++ SPI_BIT(priv, SPI_TCR_SDM));
++ }
+
+ return 0;
+ }
+@@ -516,6 +531,8 @@ static const u32 sun6i_spi_bits[] = {
+ [SPI_TCR_CS_MASK] = 0x30,
+ [SPI_TCR_CS_MANUAL] = BIT(6),
+ [SPI_TCR_CS_LEVEL] = BIT(7),
++ [SPI_TCR_SDC] = BIT(11),
++ [SPI_TCR_SDM] = BIT(13),
+ [SPI_TCR_XCH] = BIT(31),
+ [SPI_FCR_RF_RST] = BIT(15),
+ [SPI_FCR_TF_RST] = BIT(31),
+@@ -526,6 +543,7 @@ static const struct sun4i_spi_variant sun4i_a10_spi_variant = {
+ .regs = sun4i_spi_regs,
+ .bits = sun4i_spi_bits,
+ .fifo_depth = 64,
++ .has_clk_ctl = true,
+ };
+
+ static const struct sun4i_spi_variant sun6i_a31_spi_variant = {
+@@ -534,6 +552,7 @@ static const struct sun4i_spi_variant sun6i_a31_spi_variant = {
+ .fifo_depth = 128,
+ .has_soft_reset = true,
+ .has_burst_ctl = true,
++ .has_clk_ctl = true,
+ };
+
+ static const struct sun4i_spi_variant sun8i_h3_spi_variant = {
+@@ -542,6 +561,15 @@ static const struct sun4i_spi_variant sun8i_h3_spi_variant = {
+ .fifo_depth = 64,
+ .has_soft_reset = true,
+ .has_burst_ctl = true,
++ .has_clk_ctl = true,
++};
++
++static const struct sun4i_spi_variant sun50i_r329_spi_variant = {
++ .regs = sun6i_spi_regs,
++ .bits = sun6i_spi_bits,
++ .fifo_depth = 64,
++ .has_soft_reset = true,
++ .has_burst_ctl = true,
+ };
+
+ static const struct udevice_id sun4i_spi_ids[] = {
+@@ -557,6 +585,10 @@ static const struct udevice_id sun4i_spi_ids[] = {
+ .compatible = "allwinner,sun8i-h3-spi",
+ .data = (ulong)&sun8i_h3_spi_variant,
+ },
++ {
++ .compatible = "allwinner,sun50i-r329-spi",
++ .data = (ulong)&sun50i_r329_spi_variant,
++ },
+ { /* sentinel */ }
+ };
+
+--
+2.20.1
+
--- /dev/null
+From b447d68238499753c42c5baa9f42db778673a023 Mon Sep 17 00:00:00 2001
+From: Maxim Kiselev <bigunclemax@gmail.com>
+Date: Fri, 19 May 2023 16:40:09 +0300
+Subject: [PATCH 4032/4052] riscv: dts: allwinner: d1: Add SPI controllers node
+
+Some boards form the MangoPi family (MQ\MQ-Dual\MQ-R) may have
+an optional SPI flash that connects to the SPI0 controller.
+
+This controller is the same for R329/D1/R528/T113s SoCs and
+should be supported by the sun50i-r329-spi driver.
+
+So let's add its DT nodes.
+
+Signed-off-by: Maxim Kiselev <bigunclemax@gmail.com>
+Reviewed-by: Sam Edwards <CFSworks@gmail.com>
+---
+ arch/riscv/dts/sunxi-d1s-t113.dtsi | 37 ++++++++++++++++++++++++++++++
+ 1 file changed, 37 insertions(+)
+
+diff --git a/arch/riscv/dts/sunxi-d1s-t113.dtsi b/arch/riscv/dts/sunxi-d1s-t113.dtsi
+index 922e8e0e2c..1bb1e5cae6 100644
+--- a/arch/riscv/dts/sunxi-d1s-t113.dtsi
++++ b/arch/riscv/dts/sunxi-d1s-t113.dtsi
+@@ -108,6 +108,12 @@
+ function = "emac";
+ };
+
++ /omit-if-no-ref/
++ spi0_pins: spi0-pins {
++ pins = "PC2", "PC3", "PC4", "PC5";
++ function = "spi0";
++ };
++
+ /omit-if-no-ref/
+ uart1_pg6_pins: uart1-pg6-pins {
+ pins = "PG6", "PG7";
+@@ -447,6 +453,37 @@
+ #size-cells = <0>;
+ };
+
++ spi0: spi@4025000 {
++ compatible = "allwinner,sun20i-d1-spi",
++ "allwinner,sun50i-r329-spi";
++ reg = <0x04025000 0x1000>;
++ interrupts = <SOC_PERIPHERAL_IRQ(15) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_SPI0>, <&ccu CLK_SPI0>;
++ clock-names = "ahb", "mod";
++ dmas = <&dma 22>, <&dma 22>;
++ dma-names = "rx", "tx";
++ resets = <&ccu RST_BUS_SPI0>;
++ status = "disabled";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++
++ spi1: spi@4026000 {
++ compatible = "allwinner,sun20i-d1-spi-dbi",
++ "allwinner,sun50i-r329-spi-dbi",
++ "allwinner,sun50i-r329-spi";
++ reg = <0x04026000 0x1000>;
++ interrupts = <SOC_PERIPHERAL_IRQ(16) IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&ccu CLK_BUS_SPI1>, <&ccu CLK_SPI1>;
++ clock-names = "ahb", "mod";
++ dmas = <&dma 23>, <&dma 23>;
++ dma-names = "rx", "tx";
++ resets = <&ccu RST_BUS_SPI1>;
++ status = "disabled";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++
+ usb_otg: usb@4100000 {
+ compatible = "allwinner,sun20i-d1-musb",
+ "allwinner,sun8i-a33-musb";
+--
+2.20.1
+
--- /dev/null
+From 0abff85e4947d6ef306f9e11ad07a7db91fa2700 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Wed, 27 Jul 2022 16:36:13 +0800
+Subject: [PATCH 4033/4052] mtd: spi-nand: backport from upstream kernel
+
+Backport new features from upstream kernel
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ drivers/mtd/nand/spi/Kconfig | 8 +
+ drivers/mtd/nand/spi/core.c | 101 ++++++----
+ drivers/mtd/nand/spi/gigadevice.c | 322 ++++++++++++++++++++++++++----
+ drivers/mtd/nand/spi/macronix.c | 173 +++++++++++++---
+ drivers/mtd/nand/spi/micron.c | 50 ++---
+ drivers/mtd/nand/spi/toshiba.c | 66 +++---
+ drivers/mtd/nand/spi/winbond.c | 171 +++++++++++++---
+ include/linux/mtd/spinand.h | 86 +++++---
+ 8 files changed, 753 insertions(+), 224 deletions(-)
+
+diff --git a/drivers/mtd/nand/spi/Kconfig b/drivers/mtd/nand/spi/Kconfig
+index 0777dfdf0a..bbd0981968 100644
+--- a/drivers/mtd/nand/spi/Kconfig
++++ b/drivers/mtd/nand/spi/Kconfig
+@@ -5,3 +5,11 @@ menuconfig MTD_SPI_NAND
+ select SPI_MEM
+ help
+ This is the framework for the SPI NAND device drivers.
++
++config MTD_SPI_NAND_W25N01KV
++ tristate "Winbond W25N01KV Support"
++ select MTD_SPI_NAND
++ default n
++ help
++ Winbond W25N01KV share the same ID with W25N01GV. However, they have
++ different attributes.
+diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
+index 134bf22c80..46ce48e0ea 100644
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -17,6 +17,7 @@
+ #include <linux/mtd/spinand.h>
+ #include <linux/of.h>
+ #include <linux/slab.h>
++#include <linux/string.h>
+ #include <linux/spi/spi.h>
+ #include <linux/spi/spi-mem.h>
+ #else
+@@ -451,10 +452,11 @@ out:
+ return status & STATUS_BUSY ? -ETIMEDOUT : 0;
+ }
+
+-static int spinand_read_id_op(struct spinand_device *spinand, u8 *buf)
++static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr,
++ u8 ndummy, u8 *buf)
+ {
+- struct spi_mem_op op = SPINAND_READID_OP(0, spinand->scratchbuf,
+- SPINAND_MAX_ID_LEN);
++ struct spi_mem_op op = SPINAND_READID_OP(
++ naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN);
+ int ret;
+
+ ret = spi_mem_exec_op(spinand->slave, &op);
+@@ -464,18 +466,6 @@ static int spinand_read_id_op(struct spinand_device *spinand, u8 *buf)
+ return ret;
+ }
+
+-static int spinand_reset_op(struct spinand_device *spinand)
+-{
+- struct spi_mem_op op = SPINAND_RESET_OP;
+- int ret;
+-
+- ret = spi_mem_exec_op(spinand->slave, &op);
+- if (ret)
+- return ret;
+-
+- return spinand_wait(spinand, NULL);
+-}
+-
+ static int spinand_lock_block(struct spinand_device *spinand, u8 lock)
+ {
+ return spinand_write_reg_op(spinand, REG_BLOCK_LOCK, lock);
+@@ -836,24 +826,63 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = {
+ &winbond_spinand_manufacturer,
+ };
+
+-static int spinand_manufacturer_detect(struct spinand_device *spinand)
++static int spinand_manufacturer_match(struct spinand_device *spinand,
++ enum spinand_readid_method rdid_method)
+ {
++ u8 *id = spinand->id.data;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(spinand_manufacturers); i++) {
+- ret = spinand_manufacturers[i]->ops->detect(spinand);
+- if (ret > 0) {
+- spinand->manufacturer = spinand_manufacturers[i];
+- return 0;
+- } else if (ret < 0) {
+- return ret;
+- }
++ const struct spinand_manufacturer *manufacturer =
++ spinand_manufacturers[i];
++
++ if (id[0] != manufacturer->id)
++ continue;
++
++ ret = spinand_match_and_init(spinand,
++ manufacturer->chips,
++ manufacturer->nchips,
++ rdid_method);
++ if (ret < 0)
++ continue;
++
++ spinand->manufacturer = manufacturer;
++ return 0;
+ }
+
+ return -ENOTSUPP;
+ }
+
++static int spinand_id_detect(struct spinand_device *spinand)
++{
++ u8 *id = spinand->id.data;
++ int ret;
++
++ ret = spinand_read_id_op(spinand, 0, 0, id);
++ if (ret)
++ return ret;
++ ret = spinand_manufacturer_match(spinand, SPINAND_READID_METHOD_OPCODE);
++ if (!ret)
++ return 0;
++
++ ret = spinand_read_id_op(spinand, 1, 0, id);
++ if (ret)
++ return ret;
++ ret = spinand_manufacturer_match(spinand,
++ SPINAND_READID_METHOD_OPCODE_ADDR);
++ if (!ret)
++ return 0;
++
++ ret = spinand_read_id_op(spinand, 0, 1, id);
++ if (ret)
++ return ret;
++ ret = spinand_manufacturer_match(spinand,
++ SPINAND_READID_METHOD_OPCODE_DUMMY);
++
++ return ret;
++}
++
+ static int spinand_manufacturer_init(struct spinand_device *spinand)
+ {
+ if (spinand->manufacturer->ops->init)
+@@ -909,9 +938,9 @@ spinand_select_op_variant(struct spinand_device *spinand,
+ * @spinand: SPI NAND object
+ * @table: SPI NAND device description table
+ * @table_size: size of the device description table
++ * @rdid_method: read id method to match
+ *
+- * Should be used by SPI NAND manufacturer drivers when they want to find a
+- * match between a device ID retrieved through the READ_ID command and an
++ * Match between a device ID retrieved through the READ_ID command and an
+ * entry in the SPI NAND description table. If a match is found, the spinand
+ * object will be initialized with information provided by the matching
+ * spinand_info entry.
+@@ -920,8 +949,10 @@ spinand_select_op_variant(struct spinand_device *spinand,
+ */
+ int spinand_match_and_init(struct spinand_device *spinand,
+ const struct spinand_info *table,
+- unsigned int table_size, u8 devid)
++ unsigned int table_size,
++ enum spinand_readid_method rdid_method)
+ {
++ u8 *id = spinand->id.data;
+ struct nand_device *nand = spinand_to_nand(spinand);
+ unsigned int i;
+
+@@ -929,13 +960,17 @@ int spinand_match_and_init(struct spinand_device *spinand,
+ const struct spinand_info *info = &table[i];
+ const struct spi_mem_op *op;
+
+- if (devid != info->devid)
++ if (rdid_method != info->devid.method)
++ continue;
++
++ if (memcmp(id + 1, info->devid.id, info->devid.len))
+ continue;
+
+ nand->memorg = table[i].memorg;
+ nand->eccreq = table[i].eccreq;
+ spinand->eccinfo = table[i].eccinfo;
+ spinand->flags = table[i].flags;
++ spinand->id.len = 1 + table[i].devid.len;
+ spinand->select_target = table[i].select_target;
+
+ op = spinand_select_op_variant(spinand,
+@@ -967,17 +1002,7 @@ static int spinand_detect(struct spinand_device *spinand)
+ struct nand_device *nand = spinand_to_nand(spinand);
+ int ret;
+
+- ret = spinand_reset_op(spinand);
+- if (ret)
+- return ret;
+-
+- ret = spinand_read_id_op(spinand, spinand->id.data);
+- if (ret)
+- return ret;
+-
+- spinand->id.len = SPINAND_MAX_ID_LEN;
+-
+- ret = spinand_manufacturer_detect(spinand);
++ ret = spinand_id_detect(spinand);
+ if (ret) {
+ dev_err(spinand->slave->dev, "unknown raw ID %*phN\n",
+ SPINAND_MAX_ID_LEN, spinand->id.data);
+diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c
+index a2c93486f4..5357f02ddc 100644
+--- a/drivers/mtd/nand/spi/gigadevice.c
++++ b/drivers/mtd/nand/spi/gigadevice.c
+@@ -22,8 +22,13 @@
+
+ #define GD5FXGQXXEXXG_REG_STATUS2 0xf0
+
++#define GD5FXGQ4UXFXXG_STATUS_ECC_MASK (7 << 4)
++#define GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS (0 << 4)
++#define GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS (1 << 4)
++#define GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR (7 << 4)
++
+ /* Q4 devices, QUADIO: Dummy bytes valid for 1 and 2 GBit variants */
+-static SPINAND_OP_VARIANTS(gd5fxgq4_read_cache_variants,
++static SPINAND_OP_VARIANTS(read_cache_variants,
+ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
+@@ -31,8 +36,17 @@ static SPINAND_OP_VARIANTS(gd5fxgq4_read_cache_variants,
+ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
+
+-/* Q5 devices, QUADIO: Dummy bytes only valid for 1 GBit variants */
+-static SPINAND_OP_VARIANTS(gd5f1gq5_read_cache_variants,
++static SPINAND_OP_VARIANTS(read_cache_variants_f,
++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0));
++
++/* For Q5 devices, QUADIO use different dummy byte settings */
++/* Q5 1Gb */
++static SPINAND_OP_VARIANTS(dummy2_read_cache_variants,
+ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
+@@ -40,6 +54,15 @@ static SPINAND_OP_VARIANTS(gd5f1gq5_read_cache_variants,
+ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
+
++/* Q5 2Gb & 4Gb */
++static SPINAND_OP_VARIANTS(dummy4_read_cache_variants,
++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 4, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 2, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
++
+ static SPINAND_OP_VARIANTS(write_cache_variants,
+ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
+ SPINAND_PROG_LOAD(true, 0, NULL, 0));
+@@ -48,7 +71,65 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
+ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
+ SPINAND_PROG_LOAD(false, 0, NULL, 0));
+
+-static int gd5fxgqxxexxg_ooblayout_ecc(struct mtd_info *mtd, int section,
++static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *region)
++{
++ if (section > 3)
++ return -ERANGE;
++
++ region->offset = (16 * section) + 8;
++ region->length = 8;
++
++ return 0;
++}
++
++static int gd5fxgq4xa_ooblayout_free(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *region)
++{
++ if (section > 3)
++ return -ERANGE;
++
++ if (section) {
++ region->offset = 16 * section;
++ region->length = 8;
++ } else {
++ /* section 0 has one byte reserved for bad block mark */
++ region->offset = 1;
++ region->length = 7;
++ }
++ return 0;
++}
++
++static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = {
++ .ecc = gd5fxgq4xa_ooblayout_ecc,
++ .rfree = gd5fxgq4xa_ooblayout_free,
++};
++
++static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand,
++ u8 status)
++{
++ switch (status & STATUS_ECC_MASK) {
++ case STATUS_ECC_NO_BITFLIPS:
++ return 0;
++
++ case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
++ /* 1-7 bits are flipped. return the maximum. */
++ return 7;
++
++ case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
++ return 8;
++
++ case STATUS_ECC_UNCOR_ERROR:
++ return -EBADMSG;
++
++ default:
++ break;
++ }
++
++ return -EINVAL;
++}
++
++static int gd5fxgqx_variant2_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+ {
+ if (section)
+@@ -60,7 +141,7 @@ static int gd5fxgqxxexxg_ooblayout_ecc(struct mtd_info *mtd, int section,
+ return 0;
+ }
+
+-static int gd5fxgqxxexxg_ooblayout_free(struct mtd_info *mtd, int section,
++static int gd5fxgqx_variant2_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+ {
+ if (section)
+@@ -73,7 +154,13 @@ static int gd5fxgqxxexxg_ooblayout_free(struct mtd_info *mtd, int section,
+ return 0;
+ }
+
+-static int gd5fxgq4xexxg_ecc_get_status(struct spinand_device *spinand,
++/* Valid for Q4/Q5 and Q6 (untested) devices */
++static const struct mtd_ooblayout_ops gd5fxgqx_variant2_ooblayout = {
++ .ecc = gd5fxgqx_variant2_ooblayout_ecc,
++ .rfree = gd5fxgqx_variant2_ooblayout_free,
++};
++
++static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand,
+ u8 status)
+ {
+ u8 status2;
+@@ -152,59 +239,214 @@ static int gd5fxgq5xexxg_ecc_get_status(struct spinand_device *spinand,
+ return -EINVAL;
+ }
+
+-static const struct mtd_ooblayout_ops gd5fxgqxxexxg_ooblayout = {
+- .ecc = gd5fxgqxxexxg_ooblayout_ecc,
+- .rfree = gd5fxgqxxexxg_ooblayout_free,
++static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand,
++ u8 status)
++{
++ switch (status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) {
++ case GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS:
++ return 0;
++
++ case GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS:
++ return 3;
++
++ case GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR:
++ return -EBADMSG;
++
++ default: /* (2 << 4) through (6 << 4) are 4-8 corrected errors */
++ return ((status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) >> 4) + 2;
++ }
++
++ return -EINVAL;
++}
++
++static int esmt_1_ooblayout_ecc(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *region)
++{
++ if (section > 3)
++ return -ERANGE;
++
++ region->offset = (16 * section) + 8;
++ region->length = 8;
++
++ return 0;
++}
++
++static int esmt_1_ooblayout_free(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *region)
++{
++ if (section > 3)
++ return -ERANGE;
++
++ region->offset = (16 * section) + 2;
++ region->length = 6;
++
++ return 0;
++}
++
++static const struct mtd_ooblayout_ops esmt_1_ooblayout = {
++ .ecc = esmt_1_ooblayout_ecc,
++ .rfree = esmt_1_ooblayout_free,
+ };
+
+ static const struct spinand_info gigadevice_spinand_table[] = {
+- SPINAND_INFO("GD5F1GQ4UExxG", 0xd1,
+- NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
++ SPINAND_INFO("F50L1G41LB",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01),
++ NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+- SPINAND_INFO_OP_VARIANTS(&gd5fxgq4_read_cache_variants,
++ SPINAND_INFO_OP_VARIANTS(&dummy2_read_cache_variants,
+ &write_cache_variants,
+ &update_cache_variants),
+ 0,
+- SPINAND_ECCINFO(&gd5fxgqxxexxg_ooblayout,
+- gd5fxgq4xexxg_ecc_get_status)),
+- SPINAND_INFO("GD5F1GQ5UExxG", 0x51,
++ SPINAND_ECCINFO(&esmt_1_ooblayout, NULL)),
++ SPINAND_INFO("GD5F1GQ4xA",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf1),
++ NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
++ gd5fxgq4xa_ecc_get_status)),
++ SPINAND_INFO("GD5F2GQ4xA",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf2),
++ NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
++ gd5fxgq4xa_ecc_get_status)),
++ SPINAND_INFO("GD5F4GQ4xA",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf4),
++ NAND_MEMORG(1, 2048, 64, 64, 4096, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
++ gd5fxgq4xa_ecc_get_status)),
++ SPINAND_INFO("GD5F1GQ4UExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd1),
++ NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq4uexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F1GQ4UFxxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48),
++ NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq4ufxxg_ecc_get_status)),
++ SPINAND_INFO("GD5F1GQ5UExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51),
+ NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
+ NAND_ECCREQ(4, 512),
+- SPINAND_INFO_OP_VARIANTS(&gd5f1gq5_read_cache_variants,
++ SPINAND_INFO_OP_VARIANTS(&dummy2_read_cache_variants,
+ &write_cache_variants,
+ &update_cache_variants),
+- 0,
+- SPINAND_ECCINFO(&gd5fxgqxxexxg_ooblayout,
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq5xexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F2GQ5UExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x52),
++ NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
++ NAND_ECCREQ(4, 512),
++ SPINAND_INFO_OP_VARIANTS(&dummy4_read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq5xexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F4GQ6UExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x55),
++ NAND_MEMORG(1, 2048, 128, 64, 4096, 1, 1, 1),
++ NAND_ECCREQ(4, 512),
++ SPINAND_INFO_OP_VARIANTS(&dummy4_read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq5xexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F1GM7UExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x91),
++ NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq4uexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F2GM7UExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x92),
++ NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq4uexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F4GM8UExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x95),
++ NAND_MEMORG(1, 2048, 128, 64, 4096, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq4uexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F1GQ5UExxH",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x31),
++ NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
++ NAND_ECCREQ(4, 512),
++ SPINAND_INFO_OP_VARIANTS(&dummy2_read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq5xexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F2GQ5UExxH",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x32),
++ NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1),
++ NAND_ECCREQ(4, 512),
++ SPINAND_INFO_OP_VARIANTS(&dummy4_read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq5xexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F4GQ6UExxH",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35),
++ NAND_MEMORG(1, 2048, 64, 64, 4096, 1, 1, 1),
++ NAND_ECCREQ(4, 512),
++ SPINAND_INFO_OP_VARIANTS(&dummy4_read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+ gd5fxgq5xexxg_ecc_get_status)),
+ };
+
+-static int gigadevice_spinand_detect(struct spinand_device *spinand)
+-{
+- u8 *id = spinand->id.data;
+- int ret;
+-
+- /*
+- * For GD NANDs, There is an address byte needed to shift in before IDs
+- * are read out, so the first byte in raw_id is dummy.
+- */
+- if (id[1] != SPINAND_MFR_GIGADEVICE)
+- return 0;
+-
+- ret = spinand_match_and_init(spinand, gigadevice_spinand_table,
+- ARRAY_SIZE(gigadevice_spinand_table),
+- id[2]);
+- if (ret)
+- return ret;
+-
+- return 1;
+-}
+-
+ static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {
+- .detect = gigadevice_spinand_detect,
+ };
+
+ const struct spinand_manufacturer gigadevice_spinand_manufacturer = {
+ .id = SPINAND_MFR_GIGADEVICE,
+ .name = "GigaDevice",
++ .chips = gigadevice_spinand_table,
++ .nchips = ARRAY_SIZE(gigadevice_spinand_table),
+ .ops = &gigadevice_spinand_manuf_ops,
+ };
+diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c
+index 6d643a8000..34e622d6e9 100644
+--- a/drivers/mtd/nand/spi/macronix.c
++++ b/drivers/mtd/nand/spi/macronix.c
+@@ -105,7 +105,8 @@ static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand,
+ }
+
+ static const struct spinand_info macronix_spinand_table[] = {
+- SPINAND_INFO("MX35LF1GE4AB", 0x12,
++ SPINAND_INFO("MX35LF1GE4AB",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x12),
+ NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
+ NAND_ECCREQ(4, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -114,7 +115,8 @@ static const struct spinand_info macronix_spinand_table[] = {
+ SPINAND_HAS_QE_BIT,
+ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+ mx35lf1ge4ab_ecc_get_status)),
+- SPINAND_INFO("MX35LF2GE4AB", 0x22,
++ SPINAND_INFO("MX35LF2GE4AB",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x22),
+ NAND_MEMORG(1, 2048, 64, 64, 2048, 2, 1, 1),
+ NAND_ECCREQ(4, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -122,7 +124,96 @@ static const struct spinand_info macronix_spinand_table[] = {
+ &update_cache_variants),
+ SPINAND_HAS_QE_BIT,
+ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
+- SPINAND_INFO("MX35UF4GE4AD", 0xb7,
++ SPINAND_INFO("MX35LF2GE4AD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x26),
++ NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++ mx35lf1ge4ab_ecc_get_status)),
++ SPINAND_INFO("MX35LF4GE4AD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x37),
++ NAND_MEMORG(1, 4096, 128, 64, 2048, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++ mx35lf1ge4ab_ecc_get_status)),
++ SPINAND_INFO("MX35LF1G24AD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14),
++ NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
++ SPINAND_INFO("MX35LF2G24AD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24),
++ NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
++ SPINAND_INFO("MX35LF4G24AD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35),
++ NAND_MEMORG(1, 4096, 256, 64, 2048, 2, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
++ SPINAND_INFO("MX31LF1GE4BC",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x1e),
++ NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++ mx35lf1ge4ab_ecc_get_status)),
++ SPINAND_INFO("MX31UF1GE4BC",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x9e),
++ NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++ mx35lf1ge4ab_ecc_get_status)),
++
++ SPINAND_INFO("MX35LF2G14AC",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x20),
++ NAND_MEMORG(1, 2048, 64, 64, 2048, 2, 1, 1),
++ NAND_ECCREQ(4, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++ mx35lf1ge4ab_ecc_get_status)),
++ SPINAND_INFO("MX35UF4G24AD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb5),
++ NAND_MEMORG(1, 4096, 256, 64, 2048, 2, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++ mx35lf1ge4ab_ecc_get_status)),
++ SPINAND_INFO("MX35UF4GE4AD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb7),
+ NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -131,7 +222,28 @@ static const struct spinand_info macronix_spinand_table[] = {
+ SPINAND_HAS_QE_BIT,
+ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+ mx35lf1ge4ab_ecc_get_status)),
+- SPINAND_INFO("MX35UF2GE4AD", 0xa6,
++ SPINAND_INFO("MX35UF2G14AC",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa0),
++ NAND_MEMORG(1, 2048, 64, 64, 2048, 2, 1, 1),
++ NAND_ECCREQ(4, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++ mx35lf1ge4ab_ecc_get_status)),
++ SPINAND_INFO("MX35UF2G24AD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa4),
++ NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++ mx35lf1ge4ab_ecc_get_status)),
++ SPINAND_INFO("MX35UF2GE4AD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa6),
+ NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -140,16 +252,38 @@ static const struct spinand_info macronix_spinand_table[] = {
+ SPINAND_HAS_QE_BIT,
+ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+ mx35lf1ge4ab_ecc_get_status)),
+- SPINAND_INFO("MX35UF2GE4AC", 0xa2,
++ SPINAND_INFO("MX35UF2GE4AC",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa2),
+ NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1),
+ NAND_ECCREQ(4, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++ mx35lf1ge4ab_ecc_get_status)),
++ SPINAND_INFO("MX35UF1G14AC",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x90),
++ NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
++ NAND_ECCREQ(4, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++ mx35lf1ge4ab_ecc_get_status)),
++ SPINAND_INFO("MX35UF1G24AD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x94),
++ NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+ &write_cache_variants,
+ &update_cache_variants),
+ SPINAND_HAS_QE_BIT,
+ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+ mx35lf1ge4ab_ecc_get_status)),
+- SPINAND_INFO("MX35UF1GE4AD", 0x96,
++ SPINAND_INFO("MX35UF1GE4AD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x96),
+ NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -158,7 +292,8 @@ static const struct spinand_info macronix_spinand_table[] = {
+ SPINAND_HAS_QE_BIT,
+ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+ mx35lf1ge4ab_ecc_get_status)),
+- SPINAND_INFO("MX35UF1GE4AC", 0x92,
++ SPINAND_INFO("MX35UF1GE4AC",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92),
+ NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
+ NAND_ECCREQ(4, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -170,33 +305,13 @@ static const struct spinand_info macronix_spinand_table[] = {
+
+ };
+
+-static int macronix_spinand_detect(struct spinand_device *spinand)
+-{
+- u8 *id = spinand->id.data;
+- int ret;
+-
+- /*
+- * Macronix SPI NAND read ID needs a dummy byte, so the first byte in
+- * raw_id is garbage.
+- */
+- if (id[1] != SPINAND_MFR_MACRONIX)
+- return 0;
+-
+- ret = spinand_match_and_init(spinand, macronix_spinand_table,
+- ARRAY_SIZE(macronix_spinand_table),
+- id[2]);
+- if (ret)
+- return ret;
+-
+- return 1;
+-}
+-
+ static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = {
+- .detect = macronix_spinand_detect,
+ };
+
+ const struct spinand_manufacturer macronix_spinand_manufacturer = {
+ .id = SPINAND_MFR_MACRONIX,
+ .name = "Macronix",
++ .chips = macronix_spinand_table,
++ .nchips = ARRAY_SIZE(macronix_spinand_table),
+ .ops = ¯onix_spinand_manuf_ops,
+ };
+diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c
+index 6bacf14aaf..cecd3db958 100644
+--- a/drivers/mtd/nand/spi/micron.c
++++ b/drivers/mtd/nand/spi/micron.c
+@@ -120,7 +120,8 @@ static int micron_8_ecc_get_status(struct spinand_device *spinand,
+
+ static const struct spinand_info micron_spinand_table[] = {
+ /* M79A 2Gb 3.3V */
+- SPINAND_INFO("MT29F2G01ABAGD", 0x24,
++ SPINAND_INFO("MT29F2G01ABAGD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24),
+ NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -130,7 +131,8 @@ static const struct spinand_info micron_spinand_table[] = {
+ SPINAND_ECCINFO(µn_8_ooblayout,
+ micron_8_ecc_get_status)),
+ /* M79A 2Gb 1.8V */
+- SPINAND_INFO("MT29F2G01ABBGD", 0x25,
++ SPINAND_INFO("MT29F2G01ABBGD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25),
+ NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -140,7 +142,8 @@ static const struct spinand_info micron_spinand_table[] = {
+ SPINAND_ECCINFO(µn_8_ooblayout,
+ micron_8_ecc_get_status)),
+ /* M78A 1Gb 3.3V */
+- SPINAND_INFO("MT29F1G01ABAFD", 0x14,
++ SPINAND_INFO("MT29F1G01ABAFD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14),
+ NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -150,7 +153,8 @@ static const struct spinand_info micron_spinand_table[] = {
+ SPINAND_ECCINFO(µn_8_ooblayout,
+ micron_8_ecc_get_status)),
+ /* M78A 1Gb 1.8V */
+- SPINAND_INFO("MT29F1G01ABAFD", 0x15,
++ SPINAND_INFO("MT29F1G01ABAFD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15),
+ NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -160,7 +164,8 @@ static const struct spinand_info micron_spinand_table[] = {
+ SPINAND_ECCINFO(µn_8_ooblayout,
+ micron_8_ecc_get_status)),
+ /* M79A 4Gb 3.3V */
+- SPINAND_INFO("MT29F4G01ADAGD", 0x36,
++ SPINAND_INFO("MT29F4G01ADAGD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x36),
+ NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 2),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -171,7 +176,8 @@ static const struct spinand_info micron_spinand_table[] = {
+ micron_8_ecc_get_status),
+ SPINAND_SELECT_TARGET(micron_select_target)),
+ /* M70A 4Gb 3.3V */
+- SPINAND_INFO("MT29F4G01ABAFD", 0x34,
++ SPINAND_INFO("MT29F4G01ABAFD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34),
+ NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -181,7 +187,8 @@ static const struct spinand_info micron_spinand_table[] = {
+ SPINAND_ECCINFO(µn_8_ooblayout,
+ micron_8_ecc_get_status)),
+ /* M70A 4Gb 1.8V */
+- SPINAND_INFO("MT29F4G01ABBFD", 0x35,
++ SPINAND_INFO("MT29F4G01ABBFD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35),
+ NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -191,7 +198,8 @@ static const struct spinand_info micron_spinand_table[] = {
+ SPINAND_ECCINFO(µn_8_ooblayout,
+ micron_8_ecc_get_status)),
+ /* M70A 8Gb 3.3V */
+- SPINAND_INFO("MT29F8G01ADAFD", 0x46,
++ SPINAND_INFO("MT29F8G01ADAFD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x46),
+ NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 2),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -202,7 +210,8 @@ static const struct spinand_info micron_spinand_table[] = {
+ micron_8_ecc_get_status),
+ SPINAND_SELECT_TARGET(micron_select_target)),
+ /* M70A 8Gb 1.8V */
+- SPINAND_INFO("MT29F8G01ADBFD", 0x47,
++ SPINAND_INFO("MT29F8G01ADBFD",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x47),
+ NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 2),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -214,26 +223,6 @@ static const struct spinand_info micron_spinand_table[] = {
+ SPINAND_SELECT_TARGET(micron_select_target)),
+ };
+
+-static int micron_spinand_detect(struct spinand_device *spinand)
+-{
+- u8 *id = spinand->id.data;
+- int ret;
+-
+- /*
+- * Micron SPI NAND read ID need a dummy byte,
+- * so the first byte in raw_id is dummy.
+- */
+- if (id[1] != SPINAND_MFR_MICRON)
+- return 0;
+-
+- ret = spinand_match_and_init(spinand, micron_spinand_table,
+- ARRAY_SIZE(micron_spinand_table), id[2]);
+- if (ret)
+- return ret;
+-
+- return 1;
+-}
+-
+ static int micron_spinand_init(struct spinand_device *spinand)
+ {
+ /*
+@@ -248,12 +237,13 @@ static int micron_spinand_init(struct spinand_device *spinand)
+ }
+
+ static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = {
+- .detect = micron_spinand_detect,
+ .init = micron_spinand_init,
+ };
+
+ const struct spinand_manufacturer micron_spinand_manufacturer = {
+ .id = SPINAND_MFR_MICRON,
+ .name = "Micron",
++ .chips = micron_spinand_table,
++ .nchips = ARRAY_SIZE(micron_spinand_table),
+ .ops = µn_spinand_manuf_ops,
+ };
+diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c
+index c2cd3b426b..e057b08c70 100644
+--- a/drivers/mtd/nand/spi/toshiba.c
++++ b/drivers/mtd/nand/spi/toshiba.c
+@@ -111,7 +111,8 @@ static int tx58cxgxsxraix_ecc_get_status(struct spinand_device *spinand,
+
+ static const struct spinand_info toshiba_spinand_table[] = {
+ /* 3.3V 1Gb (1st generation) */
+- SPINAND_INFO("TC58CVG0S3HRAIG", 0xC2,
++ SPINAND_INFO("TC58CVG0S3HRAIG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xC2),
+ NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -121,7 +122,8 @@ static const struct spinand_info toshiba_spinand_table[] = {
+ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+ tx58cxgxsxraix_ecc_get_status)),
+ /* 3.3V 2Gb (1st generation) */
+- SPINAND_INFO("TC58CVG1S3HRAIG", 0xCB,
++ SPINAND_INFO("TC58CVG1S3HRAIG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCB),
+ NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -131,7 +133,8 @@ static const struct spinand_info toshiba_spinand_table[] = {
+ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+ tx58cxgxsxraix_ecc_get_status)),
+ /* 3.3V 4Gb (1st generation) */
+- SPINAND_INFO("TC58CVG2S0HRAIG", 0xCD,
++ SPINAND_INFO("TC58CVG2S0HRAIG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCD),
+ NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -141,7 +144,8 @@ static const struct spinand_info toshiba_spinand_table[] = {
+ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+ tx58cxgxsxraix_ecc_get_status)),
+ /* 1.8V 1Gb (1st generation) */
+- SPINAND_INFO("TC58CYG0S3HRAIG", 0xB2,
++ SPINAND_INFO("TC58CYG0S3HRAIG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xB2),
+ NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -151,7 +155,8 @@ static const struct spinand_info toshiba_spinand_table[] = {
+ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+ tx58cxgxsxraix_ecc_get_status)),
+ /* 1.8V 2Gb (1st generation) */
+- SPINAND_INFO("TC58CYG1S3HRAIG", 0xBB,
++ SPINAND_INFO("TC58CYG1S3HRAIG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBB),
+ NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -161,7 +166,8 @@ static const struct spinand_info toshiba_spinand_table[] = {
+ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+ tx58cxgxsxraix_ecc_get_status)),
+ /* 1.8V 4Gb (1st generation) */
+- SPINAND_INFO("TC58CYG2S0HRAIG", 0xBD,
++ SPINAND_INFO("TC58CYG2S0HRAIG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBD),
+ NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -176,7 +182,8 @@ static const struct spinand_info toshiba_spinand_table[] = {
+ * QE_BIT.
+ */
+ /* 3.3V 1Gb (2nd generation) */
+- SPINAND_INFO("TC58CVG0S3HRAIJ", 0xE2,
++ SPINAND_INFO("TC58CVG0S3HRAIJ",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE2),
+ NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -186,7 +193,8 @@ static const struct spinand_info toshiba_spinand_table[] = {
+ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+ tx58cxgxsxraix_ecc_get_status)),
+ /* 3.3V 2Gb (2nd generation) */
+- SPINAND_INFO("TC58CVG1S3HRAIJ", 0xEB,
++ SPINAND_INFO("TC58CVG1S3HRAIJ",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xEB),
+ NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -196,7 +204,8 @@ static const struct spinand_info toshiba_spinand_table[] = {
+ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+ tx58cxgxsxraix_ecc_get_status)),
+ /* 3.3V 4Gb (2nd generation) */
+- SPINAND_INFO("TC58CVG2S0HRAIJ", 0xED,
++ SPINAND_INFO("TC58CVG2S0HRAIJ",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xED),
+ NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -206,7 +215,8 @@ static const struct spinand_info toshiba_spinand_table[] = {
+ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+ tx58cxgxsxraix_ecc_get_status)),
+ /* 3.3V 8Gb (2nd generation) */
+- SPINAND_INFO("TH58CVG3S0HRAIJ", 0xE4,
++ SPINAND_INFO("TH58CVG3S0HRAIJ",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4),
+ NAND_MEMORG(1, 4096, 256, 64, 4096, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -216,7 +226,8 @@ static const struct spinand_info toshiba_spinand_table[] = {
+ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+ tx58cxgxsxraix_ecc_get_status)),
+ /* 1.8V 1Gb (2nd generation) */
+- SPINAND_INFO("TC58CYG0S3HRAIJ", 0xD2,
++ SPINAND_INFO("TC58CYG0S3HRAIJ",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xD2),
+ NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -226,7 +237,8 @@ static const struct spinand_info toshiba_spinand_table[] = {
+ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+ tx58cxgxsxraix_ecc_get_status)),
+ /* 1.8V 2Gb (2nd generation) */
+- SPINAND_INFO("TC58CYG1S3HRAIJ", 0xDB,
++ SPINAND_INFO("TC58CYG1S3HRAIJ",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xDB),
+ NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -236,7 +248,8 @@ static const struct spinand_info toshiba_spinand_table[] = {
+ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+ tx58cxgxsxraix_ecc_get_status)),
+ /* 1.8V 4Gb (2nd generation) */
+- SPINAND_INFO("TC58CYG2S0HRAIJ", 0xDD,
++ SPINAND_INFO("TC58CYG2S0HRAIJ",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xDD),
+ NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -246,7 +259,8 @@ static const struct spinand_info toshiba_spinand_table[] = {
+ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+ tx58cxgxsxraix_ecc_get_status)),
+ /* 1.8V 8Gb (2nd generation) */
+- SPINAND_INFO("TH58CYG3S0HRAIJ", 0xD4,
++ SPINAND_INFO("TH58CYG3S0HRAIJ",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xD4),
+ NAND_MEMORG(1, 4096, 256, 64, 4096, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -257,33 +271,13 @@ static const struct spinand_info toshiba_spinand_table[] = {
+ tx58cxgxsxraix_ecc_get_status)),
+ };
+
+-static int toshiba_spinand_detect(struct spinand_device *spinand)
+-{
+- u8 *id = spinand->id.data;
+- int ret;
+-
+- /*
+- * Toshiba SPI NAND read ID needs a dummy byte,
+- * so the first byte in id is garbage.
+- */
+- if (id[1] != SPINAND_MFR_TOSHIBA)
+- return 0;
+-
+- ret = spinand_match_and_init(spinand, toshiba_spinand_table,
+- ARRAY_SIZE(toshiba_spinand_table),
+- id[2]);
+- if (ret)
+- return ret;
+-
+- return 1;
+-}
+-
+ static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = {
+- .detect = toshiba_spinand_detect,
+ };
+
+ const struct spinand_manufacturer toshiba_spinand_manufacturer = {
+ .id = SPINAND_MFR_TOSHIBA,
+ .name = "Toshiba",
++ .chips = toshiba_spinand_table,
++ .nchips = ARRAY_SIZE(toshiba_spinand_table),
+ .ops = &toshiba_spinand_manuf_ops,
+ };
+diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
+index c119486efb..7a108cb3e6 100644
+--- a/drivers/mtd/nand/spi/winbond.c
++++ b/drivers/mtd/nand/spi/winbond.c
+@@ -19,6 +19,25 @@
+
+ #define WINBOND_CFG_BUF_READ BIT(3)
+
++#define W25N02_N04KV_STATUS_ECC_MASK (3 << 4)
++#define W25N02_N04KV_STATUS_ECC_NO_BITFLIPS (0 << 4)
++#define W25N02_N04KV_STATUS_ECC_1_4_BITFLIPS (1 << 4)
++#define W25N02_N04KV_STATUS_ECC_5_8_BITFLIPS (3 << 4)
++#define W25N02_N04KV_STATUS_ECC_UNCOR_ERROR (2 << 4)
++
++#define W25N01_M02GV_STATUS_ECC_MASK (3 << 4)
++#define W25N01_M02GV_STATUS_ECC_NO_BITFLIPS (0 << 4)
++#define W25N01_M02GV_STATUS_ECC_1_BITFLIPS (1 << 4)
++#define W25N01_M02GV_STATUS_ECC_UNCOR_ERROR (2 << 4)
++
++#if IS_ENABLED(CONFIG_MTD_SPI_NAND_W25N01KV)
++#define W25N01KV_STATUS_ECC_MASK (3 << 4)
++#define W25N01KV_STATUS_ECC_NO_BITFLIPS (0 << 4)
++#define W25N01KV_STATUS_ECC_1_3_BITFLIPS (1 << 4)
++#define W25N01KV_STATUS_ECC_4_BITFLIPS (3 << 4)
++#define W25N01KV_STATUS_ECC_UNCOR_ERROR (2 << 4)
++#endif
++
+ static SPINAND_OP_VARIANTS(read_cache_variants,
+ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+@@ -35,6 +54,35 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
+ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
+ SPINAND_PROG_LOAD(false, 0, NULL, 0));
+
++static int w25n02kv_n04kv_ooblayout_ecc(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *region)
++{
++ if (section > 3)
++ return -ERANGE;
++
++ region->offset = (16 * section) + 64;
++ region->length = 16;
++
++ return 0;
++}
++
++static int w25n02kv_n04kv_ooblayout_free(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *region)
++{
++ if (section > 3)
++ return -ERANGE;
++
++ region->offset = (16 * section) + 2;
++ region->length = 14;
++
++ return 0;
++}
++
++static const struct mtd_ooblayout_ops w25n02kv_n04kv_ooblayout = {
++ .ecc = w25n02kv_n04kv_ooblayout_ecc,
++ .rfree = w25n02kv_n04kv_ooblayout_free,
++};
++
+ static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+ {
+@@ -78,8 +126,63 @@ static int w25m02gv_select_target(struct spinand_device *spinand,
+ return spi_mem_exec_op(spinand->slave, &op);
+ }
+
++#if IS_ENABLED(CONFIG_MTD_SPI_NAND_W25N01KV)
++static int w25n01kv_ecc_get_status(struct spinand_device *spinand,
++ u8 status)
++{
++ switch (status & W25N01KV_STATUS_ECC_MASK) {
++ case W25N01KV_STATUS_ECC_NO_BITFLIPS:
++ return 0;
++
++ case W25N01KV_STATUS_ECC_1_3_BITFLIPS:
++ return 3;
++
++ case W25N01KV_STATUS_ECC_4_BITFLIPS:
++ return 4;
++
++ case W25N01KV_STATUS_ECC_UNCOR_ERROR:
++ return -EBADMSG;
++
++ default:
++ break;
++ }
++
++ return -EINVAL;
++}
++#endif
++
++static int w25n02kv_n04kv_ecc_get_status(struct spinand_device *spinand,
++ u8 status)
++{
++ switch (status & W25N02_N04KV_STATUS_ECC_MASK) {
++ case W25N02_N04KV_STATUS_ECC_NO_BITFLIPS:
++ return 0;
++
++ case W25N02_N04KV_STATUS_ECC_1_4_BITFLIPS:
++ return 3;
++
++ case W25N02_N04KV_STATUS_ECC_5_8_BITFLIPS:
++ return 4;
++
++ /* W25N02_N04KV_use internal 8bit ECC algorithm.
++ * But the ECC strength is 4 bit requried.
++ * Return 3 if the bit bit flip count less than 5.
++ * Return 4 if the bit bit flip count more than 5 to 8.
++ */
++
++ case W25N02_N04KV_STATUS_ECC_UNCOR_ERROR:
++ return -EBADMSG;
++
++ default:
++ break;
++ }
++
++ return -EINVAL;
++}
++
+ static const struct spinand_info winbond_spinand_table[] = {
+- SPINAND_INFO("W25M02GV", 0xAB,
++ SPINAND_INFO("W25M02GV",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21),
+ NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 2),
+ NAND_ECCREQ(1, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -88,7 +191,19 @@ static const struct spinand_info winbond_spinand_table[] = {
+ 0,
+ SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
+ SPINAND_SELECT_TARGET(w25m02gv_select_target)),
+- SPINAND_INFO("W25N01GV", 0xAA,
++#if IS_ENABLED(CONFIG_MTD_SPI_NAND_W25N01KV)
++ SPINAND_INFO("W25N01KV",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21),
++ NAND_MEMORG(1, 2048, 96, 64, 1024, 1, 1, 1),
++ NAND_ECCREQ(4, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ 0,
++ SPINAND_ECCINFO(&w25n02kv_n04kv_ooblayout, w25n01kv_ecc_get_status)),
++#else
++ SPINAND_INFO("W25N01GV",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21),
+ NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
+ NAND_ECCREQ(1, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -96,32 +211,31 @@ static const struct spinand_info winbond_spinand_table[] = {
+ &update_cache_variants),
+ 0,
+ SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
+-};
+-
+-/**
+- * winbond_spinand_detect - initialize device related part in spinand_device
+- * struct if it is a Winbond device.
+- * @spinand: SPI NAND device structure
+- */
+-static int winbond_spinand_detect(struct spinand_device *spinand)
+-{
+- u8 *id = spinand->id.data;
+- int ret;
+-
+- /*
+- * Winbond SPI NAND read ID need a dummy byte,
+- * so the first byte in raw_id is dummy.
++#endif
++ SPINAND_INFO("W25N02KV",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22),
++ NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1),
++ NAND_ECCREQ(4, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ 0,
++ SPINAND_ECCINFO(&w25n02kv_n04kv_ooblayout,
++ w25n02kv_n04kv_ecc_get_status)),
++ /* W25N04KV has 2-die(lun), however, it can select die automatically.
++ * Treat it as single die here and double block size.
+ */
+- if (id[1] != SPINAND_MFR_WINBOND)
+- return 0;
+-
+- ret = spinand_match_and_init(spinand, winbond_spinand_table,
+- ARRAY_SIZE(winbond_spinand_table), id[2]);
+- if (ret)
+- return ret;
+-
+- return 1;
+-}
++ SPINAND_INFO("W25N04KV",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x23),
++ NAND_MEMORG(1, 2048, 128, 64, 4096, 2, 1, 1),
++ NAND_ECCREQ(4, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ 0,
++ SPINAND_ECCINFO(&w25n02kv_n04kv_ooblayout,
++ w25n02kv_n04kv_ecc_get_status)),
++};
+
+ static int winbond_spinand_init(struct spinand_device *spinand)
+ {
+@@ -142,12 +256,13 @@ static int winbond_spinand_init(struct spinand_device *spinand)
+ }
+
+ static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = {
+- .detect = winbond_spinand_detect,
+ .init = winbond_spinand_init,
+ };
+
+ const struct spinand_manufacturer winbond_spinand_manufacturer = {
+ .id = SPINAND_MFR_WINBOND,
+ .name = "Winbond",
++ .chips = winbond_spinand_table,
++ .nchips = ARRAY_SIZE(winbond_spinand_table),
+ .ops = &winbond_spinand_manuf_ops,
+ };
+diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
+index 15bcd59f34..3a138f977e 100644
+--- a/include/linux/mtd/spinand.h
++++ b/include/linux/mtd/spinand.h
+@@ -39,15 +39,15 @@
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_NO_DATA)
+
+-#define SPINAND_READID_OP(ndummy, buf, len) \
++#define SPINAND_READID_OP(naddr, ndummy, buf, len) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x9f, 1), \
+- SPI_MEM_OP_NO_ADDR, \
++ SPI_MEM_OP_ADDR(naddr, 0, 1), \
+ SPI_MEM_OP_DUMMY(ndummy, 1), \
+ SPI_MEM_OP_DATA_IN(len, buf, 1))
+
+ #define SPINAND_SET_FEATURE_OP(reg, valptr) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x1f, 1), \
+- SPI_MEM_OP_ADDR(1, reg, 1), \
++ SPI_MEM_OP_ADDR(1, reg, 1), \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_DATA_OUT(1, valptr, 1))
+
+@@ -75,18 +75,36 @@
+ SPI_MEM_OP_DUMMY(ndummy, 1), \
+ SPI_MEM_OP_DATA_IN(len, buf, 1))
+
++#define SPINAND_PAGE_READ_FROM_CACHE_OP_3A(fast, addr, ndummy, buf, len)\
++ SPI_MEM_OP(SPI_MEM_OP_CMD(fast ? 0x0b : 0x03, 1), \
++ SPI_MEM_OP_ADDR(3, addr, 1), \
++ SPI_MEM_OP_DUMMY(ndummy, 1), \
++ SPI_MEM_OP_DATA_IN(len, buf, 1))
++
+ #define SPINAND_PAGE_READ_FROM_CACHE_X2_OP(addr, ndummy, buf, len) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1), \
+ SPI_MEM_OP_ADDR(2, addr, 1), \
+ SPI_MEM_OP_DUMMY(ndummy, 1), \
+ SPI_MEM_OP_DATA_IN(len, buf, 2))
+
++#define SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(addr, ndummy, buf, len) \
++ SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1), \
++ SPI_MEM_OP_ADDR(3, addr, 1), \
++ SPI_MEM_OP_DUMMY(ndummy, 1), \
++ SPI_MEM_OP_DATA_IN(len, buf, 2))
++
+ #define SPINAND_PAGE_READ_FROM_CACHE_X4_OP(addr, ndummy, buf, len) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \
+ SPI_MEM_OP_ADDR(2, addr, 1), \
+ SPI_MEM_OP_DUMMY(ndummy, 1), \
+ SPI_MEM_OP_DATA_IN(len, buf, 4))
+
++#define SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(addr, ndummy, buf, len) \
++ SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \
++ SPI_MEM_OP_ADDR(3, addr, 1), \
++ SPI_MEM_OP_DUMMY(ndummy, 1), \
++ SPI_MEM_OP_DATA_IN(len, buf, 4))
++
+ #define SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(addr, ndummy, buf, len) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1), \
+ SPI_MEM_OP_ADDR(2, addr, 2), \
+@@ -153,37 +171,46 @@ struct spinand_device;
+ * @data: buffer containing the id bytes. Currently 4 bytes large, but can
+ * be extended if required
+ * @len: ID length
+- *
+- * struct_spinand_id->data contains all bytes returned after a READ_ID command,
+- * including dummy bytes if the chip does not emit ID bytes right after the
+- * READ_ID command. The responsibility to extract real ID bytes is left to
+- * struct_manufacurer_ops->detect().
+ */
+ struct spinand_id {
+ u8 data[SPINAND_MAX_ID_LEN];
+ int len;
+ };
+
++enum spinand_readid_method {
++ SPINAND_READID_METHOD_OPCODE,
++ SPINAND_READID_METHOD_OPCODE_ADDR,
++ SPINAND_READID_METHOD_OPCODE_DUMMY,
++};
++
++/**
++ * struct spinand_devid - SPI NAND device id structure
++ * @id: device id of current chip
++ * @len: number of bytes in device id
++ * @method: method to read chip id
++ * There are 3 possible variants:
++ * SPINAND_READID_METHOD_OPCODE: chip id is returned immediately
++ * after read_id opcode.
++ * SPINAND_READID_METHOD_OPCODE_ADDR: chip id is returned after
++ * read_id opcode + 1-byte address.
++ * SPINAND_READID_METHOD_OPCODE_DUMMY: chip id is returned after
++ * read_id opcode + 1 dummy byte.
++ */
++struct spinand_devid {
++ const u8 *id;
++ const u8 len;
++ const enum spinand_readid_method method;
++};
++
+ /**
+ * struct manufacurer_ops - SPI NAND manufacturer specific operations
+- * @detect: detect a SPI NAND device. Every time a SPI NAND device is probed
+- * the core calls the struct_manufacurer_ops->detect() hook of each
+- * registered manufacturer until one of them return 1. Note that
+- * the first thing to check in this hook is that the manufacturer ID
+- * in struct_spinand_device->id matches the manufacturer whose
+- * ->detect() hook has been called. Should return 1 if there's a
+- * match, 0 if the manufacturer ID does not match and a negative
+- * error code otherwise. When true is returned, the core assumes
+- * that properties of the NAND chip (spinand->base.memorg and
+- * spinand->base.eccreq) have been filled
+ * @init: initialize a SPI NAND device
+ * @cleanup: cleanup a SPI NAND device
+ *
+ * Each SPI NAND manufacturer driver should implement this interface so that
+- * NAND chips coming from this vendor can be detected and initialized properly.
++ * NAND chips coming from this vendor can be initialized properly.
+ */
+ struct spinand_manufacturer_ops {
+- int (*detect)(struct spinand_device *spinand);
+ int (*init)(struct spinand_device *spinand);
+ void (*cleanup)(struct spinand_device *spinand);
+ };
+@@ -192,11 +219,16 @@ struct spinand_manufacturer_ops {
+ * struct spinand_manufacturer - SPI NAND manufacturer instance
+ * @id: manufacturer ID
+ * @name: manufacturer name
++ * @devid_len: number of bytes in device ID
++ * @chips: supported SPI NANDs under current manufacturer
++ * @nchips: number of SPI NANDs available in chips array
+ * @ops: manufacturer operations
+ */
+ struct spinand_manufacturer {
+ u8 id;
+ char *name;
++ const struct spinand_info *chips;
++ const size_t nchips;
+ const struct spinand_manufacturer_ops *ops;
+ };
+
+@@ -268,7 +300,7 @@ struct spinand_ecc_info {
+ */
+ struct spinand_info {
+ const char *model;
+- u8 devid;
++ struct spinand_devid devid;
+ u32 flags;
+ struct nand_memory_organization memorg;
+ struct nand_ecc_req eccreq;
+@@ -282,6 +314,13 @@ struct spinand_info {
+ unsigned int target);
+ };
+
++#define SPINAND_ID(__method, ...) \
++ { \
++ .id = (const u8[]){ __VA_ARGS__ }, \
++ .len = sizeof((u8[]){ __VA_ARGS__ }), \
++ .method = __method, \
++ }
++
+ #define SPINAND_INFO_OP_VARIANTS(__read, __write, __update) \
+ { \
+ .read_cache = __read, \
+@@ -440,9 +479,10 @@ static inline void spinand_set_ofnode(struct spinand_device *spinand,
+ }
+ #endif /* __UBOOT__ */
+
+-int spinand_match_and_init(struct spinand_device *dev,
++int spinand_match_and_init(struct spinand_device *spinand,
+ const struct spinand_info *table,
+- unsigned int table_size, u8 devid);
++ unsigned int table_size,
++ enum spinand_readid_method rdid_method);
+
+ int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val);
+ int spinand_select_target(struct spinand_device *spinand, unsigned int target);
+--
+2.20.1
+
--- /dev/null
+From 0ecc2b9041144670a3fbaabd2c77cfe2cf85f930 Mon Sep 17 00:00:00 2001
+From: Sam Edwards <cfsworks@gmail.com>
+Date: Wed, 16 Aug 2023 10:34:16 -0700
+Subject: [PATCH 4034/4052] sunxi: psci: clean away preprocessor macros
+
+This patch restructures psci.c to get away from the "many different
+function definitions switched by #ifdef" paradigm to the preferred style
+of having a single function definition with `if (IS_ENABLED(...))` to
+make the optimizer include only the appropriate function bodies instead.
+
+There are no functional changes here.
+
+Signed-off-by: Sam Edwards <CFSworks@gmail.com>
+Reviewed-by: Andre Przywara <andre.przywara@arm.com>
+---
+ arch/arm/cpu/armv7/sunxi/psci.c | 102 +++++++++++++-------------------
+ 1 file changed, 42 insertions(+), 60 deletions(-)
+
+diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c
+index e1d3638b5c..7804e0933b 100644
+--- a/arch/arm/cpu/armv7/sunxi/psci.c
++++ b/arch/arm/cpu/armv7/sunxi/psci.c
+@@ -76,11 +76,8 @@ static void __secure __mdelay(u32 ms)
+ isb();
+ }
+
+-static void __secure clamp_release(u32 __maybe_unused *clamp)
++static void __secure clamp_release(u32 *clamp)
+ {
+-#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \
+- defined(CONFIG_MACH_SUN8I_H3) || \
+- defined(CONFIG_MACH_SUN8I_R40)
+ u32 tmp = 0x1ff;
+ do {
+ tmp >>= 1;
+@@ -88,83 +85,68 @@ static void __secure clamp_release(u32 __maybe_unused *clamp)
+ } while (tmp);
+
+ __mdelay(10);
+-#endif
+ }
+
+-static void __secure clamp_set(u32 __maybe_unused *clamp)
++static void __secure clamp_set(u32 *clamp)
+ {
+-#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \
+- defined(CONFIG_MACH_SUN8I_H3) || \
+- defined(CONFIG_MACH_SUN8I_R40)
+ writel(0xff, clamp);
+-#endif
+ }
+
+-static void __secure sunxi_power_switch(u32 *clamp, u32 *pwroff, bool on,
+- int cpu)
++static void __secure sunxi_set_entry_address(void *entry)
+ {
+- if (on) {
+- /* Release power clamp */
+- clamp_release(clamp);
+-
+- /* Clear power gating */
+- clrbits_le32(pwroff, BIT(cpu));
++ /* secondary core entry address is programmed differently on R40 */
++ if (IS_ENABLED(CONFIG_MACH_SUN8I_R40)) {
++ writel((u32)entry,
++ SUNXI_SRAMC_BASE + SUN8I_R40_SRAMC_SOFT_ENTRY_REG0);
+ } else {
+- /* Set power gating */
+- setbits_le32(pwroff, BIT(cpu));
++ struct sunxi_cpucfg_reg *cpucfg =
++ (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
+
+- /* Activate power clamp */
+- clamp_set(clamp);
++ writel((u32)entry, &cpucfg->priv0);
+ }
+ }
+
+-#ifdef CONFIG_MACH_SUN8I_R40
+-/* secondary core entry address is programmed differently on R40 */
+-static void __secure sunxi_set_entry_address(void *entry)
+-{
+- writel((u32)entry,
+- SUNXI_SRAMC_BASE + SUN8I_R40_SRAMC_SOFT_ENTRY_REG0);
+-}
+-#else
+-static void __secure sunxi_set_entry_address(void *entry)
++static void __secure sunxi_cpu_set_power(int cpu, bool on)
+ {
++ u32 *clamp = NULL;
++ u32 *pwroff;
+ struct sunxi_cpucfg_reg *cpucfg =
+ (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
+
+- writel((u32)entry, &cpucfg->priv0);
+-}
+-#endif
++ /* sun7i (A20) is different from other single cluster SoCs */
++ if (IS_ENABLED(CONFIG_MACH_SUN7I)) {
++ clamp = &cpucfg->cpu1_pwr_clamp;
++ pwroff = &cpucfg->cpu1_pwroff;
++ } else if (IS_ENABLED(CONFIG_MACH_SUN8I_R40)) {
++ clamp = (void *)cpucfg + SUN8I_R40_PWR_CLAMP(cpu);
++ pwroff = (void *)cpucfg + SUN8I_R40_PWROFF;
++ } else {
++ struct sunxi_prcm_reg *prcm =
++ (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
+
+-#ifdef CONFIG_MACH_SUN7I
+-/* sun7i (A20) is different from other single cluster SoCs */
+-static void __secure sunxi_cpu_set_power(int __always_unused cpu, bool on)
+-{
+- struct sunxi_cpucfg_reg *cpucfg =
+- (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
++ if (IS_ENABLED(CONFIG_MACH_SUN6I) ||
++ IS_ENABLED(CONFIG_MACH_SUN8I_H3))
++ clamp = &prcm->cpu_pwr_clamp[cpu];
+
+- sunxi_power_switch(&cpucfg->cpu1_pwr_clamp, &cpucfg->cpu1_pwroff,
+- on, 0);
+-}
+-#elif defined CONFIG_MACH_SUN8I_R40
+-static void __secure sunxi_cpu_set_power(int cpu, bool on)
+-{
+- struct sunxi_cpucfg_reg *cpucfg =
+- (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
++ pwroff = &prcm->cpu_pwroff;
++ }
+
+- sunxi_power_switch((void *)cpucfg + SUN8I_R40_PWR_CLAMP(cpu),
+- (void *)cpucfg + SUN8I_R40_PWROFF,
+- on, cpu);
+-}
+-#else /* ! CONFIG_MACH_SUN7I && ! CONFIG_MACH_SUN8I_R40 */
+-static void __secure sunxi_cpu_set_power(int cpu, bool on)
+-{
+- struct sunxi_prcm_reg *prcm =
+- (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
++ if (on) {
++ /* Release power clamp */
++ if (clamp)
++ clamp_release(clamp);
+
+- sunxi_power_switch(&prcm->cpu_pwr_clamp[cpu], &prcm->cpu_pwroff,
+- on, cpu);
++ /* Clear power gating */
++ clrbits_le32(pwroff, BIT(cpu));
++ } else {
++ /* Set power gating */
++ setbits_le32(pwroff, BIT(cpu));
++
++ /* Activate power clamp */
++ if (clamp)
++ clamp_set(clamp);
++ }
+ }
+-#endif /* CONFIG_MACH_SUN7I */
+
+ void __secure sunxi_cpu_power_off(u32 cpuid)
+ {
+--
+2.20.1
+
--- /dev/null
+From a6ee36e14e484a0a00e6b03ebbe1a380f41fdcf9 Mon Sep 17 00:00:00 2001
+From: Sam Edwards <cfsworks@gmail.com>
+Date: Wed, 16 Aug 2023 10:34:17 -0700
+Subject: [PATCH 4035/4052] sunxi: psci: refactor register access to separate
+ functions
+
+This is to prepare for R528, which does not have the typical
+"CPUCFG" block; it has a "CPUX" block which provides these
+same functions but is organized differently.
+
+Moving the hardware-access bits to their own functions separates the
+logic from the hardware so we can reuse the same logic.
+
+Signed-off-by: Sam Edwards <CFSworks@gmail.com>
+Reviewed-by: Andre Przywara <andre.przywara@arm.com>
+---
+ arch/arm/cpu/armv7/sunxi/psci.c | 66 +++++++++++++++++++++++----------
+ 1 file changed, 47 insertions(+), 19 deletions(-)
+
+diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c
+index 7804e0933b..e2845f21ab 100644
+--- a/arch/arm/cpu/armv7/sunxi/psci.c
++++ b/arch/arm/cpu/armv7/sunxi/psci.c
+@@ -92,7 +92,7 @@ static void __secure clamp_set(u32 *clamp)
+ writel(0xff, clamp);
+ }
+
+-static void __secure sunxi_set_entry_address(void *entry)
++static void __secure sunxi_cpu_set_entry(int __always_unused cpu, void *entry)
+ {
+ /* secondary core entry address is programmed differently on R40 */
+ if (IS_ENABLED(CONFIG_MACH_SUN8I_R40)) {
+@@ -148,30 +148,60 @@ static void __secure sunxi_cpu_set_power(int cpu, bool on)
+ }
+ }
+
+-void __secure sunxi_cpu_power_off(u32 cpuid)
++static void __secure sunxi_cpu_set_reset(int cpu, bool reset)
++{
++ struct sunxi_cpucfg_reg *cpucfg =
++ (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
++
++ writel(reset ? 0b00 : 0b11, &cpucfg->cpu[cpu].rst);
++}
++
++static void __secure sunxi_cpu_set_locking(int cpu, bool lock)
+ {
+ struct sunxi_cpucfg_reg *cpucfg =
+ (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
++
++ if (lock)
++ clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
++ else
++ setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
++}
++
++static bool __secure sunxi_cpu_poll_wfi(int cpu)
++{
++ struct sunxi_cpucfg_reg *cpucfg =
++ (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
++
++ return !!(readl(&cpucfg->cpu[cpu].status) & BIT(2));
++}
++
++static void __secure sunxi_cpu_invalidate_cache(int cpu)
++{
++ struct sunxi_cpucfg_reg *cpucfg =
++ (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
++
++ clrbits_le32(&cpucfg->gen_ctrl, BIT(cpu));
++}
++
++void __secure sunxi_cpu_power_off(u32 cpuid)
++{
+ u32 cpu = cpuid & 0x3;
+
+ /* Wait for the core to enter WFI */
+- while (1) {
+- if (readl(&cpucfg->cpu[cpu].status) & BIT(2))
+- break;
++ while (!sunxi_cpu_poll_wfi(cpu))
+ __mdelay(1);
+- }
+
+ /* Assert reset on target CPU */
+- writel(0, &cpucfg->cpu[cpu].rst);
++ sunxi_cpu_set_reset(cpu, true);
+
+ /* Lock CPU (Disable external debug access) */
+- clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
++ sunxi_cpu_set_locking(cpu, true);
+
+ /* Power down CPU */
+ sunxi_cpu_set_power(cpuid, false);
+
+- /* Unlock CPU (Disable external debug access) */
+- setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
++ /* Unlock CPU (Reenable external debug access) */
++ sunxi_cpu_set_locking(cpu, false);
+ }
+
+ static u32 __secure cp15_read_scr(void)
+@@ -228,33 +258,31 @@ out:
+ int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc,
+ u32 context_id)
+ {
+- struct sunxi_cpucfg_reg *cpucfg =
+- (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
+ u32 cpu = (mpidr & 0x3);
+
+ /* store target PC and context id */
+ psci_save(cpu, pc, context_id);
+
+ /* Set secondary core power on PC */
+- sunxi_set_entry_address(&psci_cpu_entry);
++ sunxi_cpu_set_entry(cpu, &psci_cpu_entry);
+
+ /* Assert reset on target CPU */
+- writel(0, &cpucfg->cpu[cpu].rst);
++ sunxi_cpu_set_reset(cpu, true);
+
+ /* Invalidate L1 cache */
+- clrbits_le32(&cpucfg->gen_ctrl, BIT(cpu));
++ sunxi_cpu_invalidate_cache(cpu);
+
+ /* Lock CPU (Disable external debug access) */
+- clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
++ sunxi_cpu_set_locking(cpu, true);
+
+ /* Power up target CPU */
+ sunxi_cpu_set_power(cpu, true);
+
+ /* De-assert reset on target CPU */
+- writel(BIT(1) | BIT(0), &cpucfg->cpu[cpu].rst);
++ sunxi_cpu_set_reset(cpu, false);
+
+- /* Unlock CPU (Disable external debug access) */
+- setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
++ /* Unlock CPU (Reenable external debug access) */
++ sunxi_cpu_set_locking(cpu, false);
+
+ return ARM_PSCI_RET_SUCCESS;
+ }
+--
+2.20.1
+
--- /dev/null
+From 6213b44b5dc472ceb28fb6082559a995e510d35b Mon Sep 17 00:00:00 2001
+From: Sam Edwards <cfsworks@gmail.com>
+Date: Wed, 16 Aug 2023 10:34:18 -0700
+Subject: [PATCH 4036/4052] sunxi: psci: stop modeling register layout with C
+ structs
+
+Since the sunxi support nowadays generally prefers #defined register
+offsets instead of modeling register layouts using C structs, now is a
+good time to do this for PSCI as well. This patch moves away from using
+the structs `sunxi_cpucfg_reg` and `sunxi_prcm_reg` in psci.c.
+
+The former struct and its associated header file existed only to support
+PSCI code, so also delete them altogether.
+
+Signed-off-by: Sam Edwards <CFSworks@gmail.com>
+---
+ arch/arm/cpu/armv7/sunxi/psci.c | 57 ++++++++------------
+ arch/arm/include/asm/arch-sunxi/cpucfg.h | 67 ------------------------
+ 2 files changed, 23 insertions(+), 101 deletions(-)
+ delete mode 100644 arch/arm/include/asm/arch-sunxi/cpucfg.h
+
+diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c
+index e2845f21ab..6ecdd05250 100644
+--- a/arch/arm/cpu/armv7/sunxi/psci.c
++++ b/arch/arm/cpu/armv7/sunxi/psci.c
+@@ -11,8 +11,6 @@
+ #include <asm/cache.h>
+
+ #include <asm/arch/cpu.h>
+-#include <asm/arch/cpucfg.h>
+-#include <asm/arch/prcm.h>
+ #include <asm/armv7.h>
+ #include <asm/gic.h>
+ #include <asm/io.h>
+@@ -27,6 +25,17 @@
+ #define GICD_BASE (SUNXI_GIC400_BASE + GIC_DIST_OFFSET)
+ #define GICC_BASE (SUNXI_GIC400_BASE + GIC_CPU_OFFSET_A15)
+
++/*
++ * Offsets into the CPUCFG block applicable to most SUNXIs.
++ */
++#define SUNXI_CPU_RST(cpu) (0x40 + (cpu) * 0x40 + 0x0)
++#define SUNXI_CPU_STATUS(cpu) (0x40 + (cpu) * 0x40 + 0x8)
++#define SUNXI_GEN_CTRL (0x184)
++#define SUNXI_PRIV0 (0x1a4)
++#define SUN7I_CPU1_PWR_CLAMP (0x1b0)
++#define SUN7I_CPU1_PWROFF (0x1b4)
++#define SUNXI_DBG_CTRL1 (0x1e4)
++
+ /*
+ * R40 is different from other single cluster SoCs.
+ *
+@@ -99,10 +108,7 @@ static void __secure sunxi_cpu_set_entry(int __always_unused cpu, void *entry)
+ writel((u32)entry,
+ SUNXI_SRAMC_BASE + SUN8I_R40_SRAMC_SOFT_ENTRY_REG0);
+ } else {
+- struct sunxi_cpucfg_reg *cpucfg =
+- (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
+-
+- writel((u32)entry, &cpucfg->priv0);
++ writel((u32)entry, SUNXI_CPUCFG_BASE + SUNXI_PRIV0);
+ }
+ }
+
+@@ -110,25 +116,20 @@ static void __secure sunxi_cpu_set_power(int cpu, bool on)
+ {
+ u32 *clamp = NULL;
+ u32 *pwroff;
+- struct sunxi_cpucfg_reg *cpucfg =
+- (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
+
+ /* sun7i (A20) is different from other single cluster SoCs */
+ if (IS_ENABLED(CONFIG_MACH_SUN7I)) {
+- clamp = &cpucfg->cpu1_pwr_clamp;
+- pwroff = &cpucfg->cpu1_pwroff;
++ clamp = (void *)SUNXI_CPUCFG_BASE + SUN7I_CPU1_PWR_CLAMP;
++ pwroff = (void *)SUNXI_CPUCFG_BASE + SUN7I_CPU1_PWROFF;
+ } else if (IS_ENABLED(CONFIG_MACH_SUN8I_R40)) {
+- clamp = (void *)cpucfg + SUN8I_R40_PWR_CLAMP(cpu);
+- pwroff = (void *)cpucfg + SUN8I_R40_PWROFF;
++ clamp = (void *)SUNXI_CPUCFG_BASE + SUN8I_R40_PWR_CLAMP(cpu);
++ pwroff = (void *)SUNXI_CPUCFG_BASE + SUN8I_R40_PWROFF;
+ } else {
+- struct sunxi_prcm_reg *prcm =
+- (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
+-
+ if (IS_ENABLED(CONFIG_MACH_SUN6I) ||
+ IS_ENABLED(CONFIG_MACH_SUN8I_H3))
+- clamp = &prcm->cpu_pwr_clamp[cpu];
++ clamp = (void *)SUNXI_PRCM_BASE + 0x140 + cpu * 0x4;
+
+- pwroff = &prcm->cpu_pwroff;
++ pwroff = (void *)SUNXI_PRCM_BASE + 0x100;
+ }
+
+ if (on) {
+@@ -150,37 +151,25 @@ static void __secure sunxi_cpu_set_power(int cpu, bool on)
+
+ static void __secure sunxi_cpu_set_reset(int cpu, bool reset)
+ {
+- struct sunxi_cpucfg_reg *cpucfg =
+- (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
+-
+- writel(reset ? 0b00 : 0b11, &cpucfg->cpu[cpu].rst);
++ writel(reset ? 0b00 : 0b11, SUNXI_CPUCFG_BASE + SUNXI_CPU_RST(cpu));
+ }
+
+ static void __secure sunxi_cpu_set_locking(int cpu, bool lock)
+ {
+- struct sunxi_cpucfg_reg *cpucfg =
+- (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
+-
+ if (lock)
+- clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
++ clrbits_le32(SUNXI_CPUCFG_BASE + SUNXI_DBG_CTRL1, BIT(cpu));
+ else
+- setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
++ setbits_le32(SUNXI_CPUCFG_BASE + SUNXI_DBG_CTRL1, BIT(cpu));
+ }
+
+ static bool __secure sunxi_cpu_poll_wfi(int cpu)
+ {
+- struct sunxi_cpucfg_reg *cpucfg =
+- (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
+-
+- return !!(readl(&cpucfg->cpu[cpu].status) & BIT(2));
++ return !!(readl(SUNXI_CPUCFG_BASE + SUNXI_CPU_STATUS(cpu)) & BIT(2));
+ }
+
+ static void __secure sunxi_cpu_invalidate_cache(int cpu)
+ {
+- struct sunxi_cpucfg_reg *cpucfg =
+- (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
+-
+- clrbits_le32(&cpucfg->gen_ctrl, BIT(cpu));
++ clrbits_le32(SUNXI_CPUCFG_BASE + SUNXI_GEN_CTRL, BIT(cpu));
+ }
+
+ void __secure sunxi_cpu_power_off(u32 cpuid)
+diff --git a/arch/arm/include/asm/arch-sunxi/cpucfg.h b/arch/arm/include/asm/arch-sunxi/cpucfg.h
+deleted file mode 100644
+index 4aaebe0a97..0000000000
+--- a/arch/arm/include/asm/arch-sunxi/cpucfg.h
++++ /dev/null
+@@ -1,67 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0+ */
+-/*
+- * Sunxi A31 CPUCFG register definition.
+- *
+- * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com
+- */
+-
+-#ifndef _SUNXI_CPUCFG_H
+-#define _SUNXI_CPUCFG_H
+-
+-#include <linux/compiler.h>
+-#include <linux/types.h>
+-
+-#ifndef __ASSEMBLY__
+-
+-struct __packed sunxi_cpucfg_cpu {
+- u32 rst; /* base + 0x0 */
+- u32 ctrl; /* base + 0x4 */
+- u32 status; /* base + 0x8 */
+- u8 res[0x34]; /* base + 0xc */
+-};
+-
+-struct __packed sunxi_cpucfg_reg {
+- u8 res0[0x40]; /* 0x000 */
+- struct sunxi_cpucfg_cpu cpu[4]; /* 0x040 */
+- u8 res1[0x44]; /* 0x140 */
+- u32 gen_ctrl; /* 0x184 */
+- u32 l2_status; /* 0x188 */
+- u8 res2[0x4]; /* 0x18c */
+- u32 event_in; /* 0x190 */
+- u8 res3[0xc]; /* 0x194 */
+- u32 super_standy_flag; /* 0x1a0 */
+- u32 priv0; /* 0x1a4 */
+- u32 priv1; /* 0x1a8 */
+- u8 res4[0x4]; /* 0x1ac */
+- u32 cpu1_pwr_clamp; /* 0x1b0 sun7i only */
+- u32 cpu1_pwroff; /* 0x1b4 sun7i only */
+- u8 res5[0x2c]; /* 0x1b8 */
+- u32 dbg_ctrl1; /* 0x1e4 */
+- u8 res6[0x18]; /* 0x1e8 */
+- u32 idle_cnt0_low; /* 0x200 */
+- u32 idle_cnt0_high; /* 0x204 */
+- u32 idle_cnt0_ctrl; /* 0x208 */
+- u8 res8[0x4]; /* 0x20c */
+- u32 idle_cnt1_low; /* 0x210 */
+- u32 idle_cnt1_high; /* 0x214 */
+- u32 idle_cnt1_ctrl; /* 0x218 */
+- u8 res9[0x4]; /* 0x21c */
+- u32 idle_cnt2_low; /* 0x220 */
+- u32 idle_cnt2_high; /* 0x224 */
+- u32 idle_cnt2_ctrl; /* 0x228 */
+- u8 res10[0x4]; /* 0x22c */
+- u32 idle_cnt3_low; /* 0x230 */
+- u32 idle_cnt3_high; /* 0x234 */
+- u32 idle_cnt3_ctrl; /* 0x238 */
+- u8 res11[0x4]; /* 0x23c */
+- u32 idle_cnt4_low; /* 0x240 */
+- u32 idle_cnt4_high; /* 0x244 */
+- u32 idle_cnt4_ctrl; /* 0x248 */
+- u8 res12[0x34]; /* 0x24c */
+- u32 cnt64_ctrl; /* 0x280 */
+- u32 cnt64_low; /* 0x284 */
+- u32 cnt64_high; /* 0x288 */
+-};
+-
+-#endif /* __ASSEMBLY__ */
+-#endif /* _SUNXI_CPUCFG_H */
+--
+2.20.1
+
--- /dev/null
+From 59c86f6f0c3abc711b018b1ad3795d601103cadc Mon Sep 17 00:00:00 2001
+From: Sam Edwards <cfsworks@gmail.com>
+Date: Wed, 16 Aug 2023 10:34:19 -0700
+Subject: [PATCH 4037/4052] sunxi: psci: implement PSCI on R528
+
+This patch adds the necessary code to make nonsec booting and PSCI
+secondary core management functional on the R528/T113.
+
+Signed-off-by: Sam Edwards <CFSworks@gmail.com>
+Tested-by: Maksim Kiselev <bigunclemax@gmail.com>
+---
+ arch/arm/cpu/armv7/sunxi/psci.c | 48 ++++++++++++++++++++++++++++++++-
+ arch/arm/mach-sunxi/Kconfig | 2 ++
+ include/configs/sunxi-common.h | 8 ++++++
+ 3 files changed, 57 insertions(+), 1 deletion(-)
+
+diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c
+index 6ecdd05250..b4ce4f6def 100644
+--- a/arch/arm/cpu/armv7/sunxi/psci.c
++++ b/arch/arm/cpu/armv7/sunxi/psci.c
+@@ -47,6 +47,19 @@
+ #define SUN8I_R40_PWR_CLAMP(cpu) (0x120 + (cpu) * 0x4)
+ #define SUN8I_R40_SRAMC_SOFT_ENTRY_REG0 (0xbc)
+
++/*
++ * R528 is also different, as it has both cores powered up (but held in reset
++ * state) after the SoC is reset. Like the R40, it uses a "soft" entry point
++ * address register, but unlike the R40, it uses a newer "CPUX" block to manage
++ * CPU state, rather than the older CPUCFG system.
++ */
++#define SUN8I_R528_SOFT_ENTRY (0x1c8)
++#define SUN8I_R528_C0_RST_CTRL (0x0000)
++#define SUN8I_R528_C0_CTRL_REG0 (0x0010)
++#define SUN8I_R528_C0_CPU_STATUS (0x0080)
++
++#define SUN8I_R528_C0_STATUS_STANDBYWFI (16)
++
+ static void __secure cp15_write_cntp_tval(u32 tval)
+ {
+ asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval));
+@@ -103,10 +116,13 @@ static void __secure clamp_set(u32 *clamp)
+
+ static void __secure sunxi_cpu_set_entry(int __always_unused cpu, void *entry)
+ {
+- /* secondary core entry address is programmed differently on R40 */
++ /* secondary core entry address is programmed differently on R40/528 */
+ if (IS_ENABLED(CONFIG_MACH_SUN8I_R40)) {
+ writel((u32)entry,
+ SUNXI_SRAMC_BASE + SUN8I_R40_SRAMC_SOFT_ENTRY_REG0);
++ } else if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) {
++ writel((u32)entry,
++ SUNXI_R_CPUCFG_BASE + SUN8I_R528_SOFT_ENTRY);
+ } else {
+ writel((u32)entry, SUNXI_CPUCFG_BASE + SUNXI_PRIV0);
+ }
+@@ -124,6 +140,9 @@ static void __secure sunxi_cpu_set_power(int cpu, bool on)
+ } else if (IS_ENABLED(CONFIG_MACH_SUN8I_R40)) {
+ clamp = (void *)SUNXI_CPUCFG_BASE + SUN8I_R40_PWR_CLAMP(cpu);
+ pwroff = (void *)SUNXI_CPUCFG_BASE + SUN8I_R40_PWROFF;
++ } else if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) {
++ /* R528 leaves both cores powered up, manages them via reset */
++ return;
+ } else {
+ if (IS_ENABLED(CONFIG_MACH_SUN6I) ||
+ IS_ENABLED(CONFIG_MACH_SUN8I_H3))
+@@ -151,11 +170,27 @@ static void __secure sunxi_cpu_set_power(int cpu, bool on)
+
+ static void __secure sunxi_cpu_set_reset(int cpu, bool reset)
+ {
++ if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) {
++ if (reset) {
++ clrbits_le32(SUNXI_CPUCFG_BASE + SUN8I_R528_C0_RST_CTRL,
++ BIT(cpu));
++ } else {
++ setbits_le32(SUNXI_CPUCFG_BASE + SUN8I_R528_C0_RST_CTRL,
++ BIT(cpu));
++ }
++ return;
++ }
++
+ writel(reset ? 0b00 : 0b11, SUNXI_CPUCFG_BASE + SUNXI_CPU_RST(cpu));
+ }
+
+ static void __secure sunxi_cpu_set_locking(int cpu, bool lock)
+ {
++ if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) {
++ /* Not required on R528 */
++ return;
++ }
++
+ if (lock)
+ clrbits_le32(SUNXI_CPUCFG_BASE + SUNXI_DBG_CTRL1, BIT(cpu));
+ else
+@@ -164,11 +199,22 @@ static void __secure sunxi_cpu_set_locking(int cpu, bool lock)
+
+ static bool __secure sunxi_cpu_poll_wfi(int cpu)
+ {
++ if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) {
++ return !!(readl(SUNXI_CPUCFG_BASE + SUN8I_R528_C0_CPU_STATUS) &
++ BIT(SUN8I_R528_C0_STATUS_STANDBYWFI + cpu));
++ }
++
+ return !!(readl(SUNXI_CPUCFG_BASE + SUNXI_CPU_STATUS(cpu)) & BIT(2));
+ }
+
+ static void __secure sunxi_cpu_invalidate_cache(int cpu)
+ {
++ if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) {
++ clrbits_le32(SUNXI_CPUCFG_BASE + SUN8I_R528_C0_CTRL_REG0,
++ BIT(cpu));
++ return;
++ }
++
+ clrbits_le32(SUNXI_CPUCFG_BASE + SUNXI_GEN_CTRL, BIT(cpu));
+ }
+
+diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
+index 81d2a173d0..7233ecbc1c 100644
+--- a/arch/arm/mach-sunxi/Kconfig
++++ b/arch/arm/mach-sunxi/Kconfig
+@@ -319,6 +319,8 @@ config MACH_SUN8I_R40
+ config MACH_SUN8I_R528
+ bool "sun8i (Allwinner R528)"
+ select CPU_V7A
++ select CPU_V7_HAS_NONSEC
++ select ARCH_SUPPORT_PSCI
+ select SUNXI_GEN_NCAT2
+ select SUNXI_NEW_PINCTRL
+ select MMC_SUNXI_HAS_NEW_MODE
+diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h
+index b8ca77d031..67eb0d25db 100644
+--- a/include/configs/sunxi-common.h
++++ b/include/configs/sunxi-common.h
+@@ -33,6 +33,14 @@
+
+ /* CPU */
+
++/*
++ * Newer ARM SoCs have moved the GIC, but have not updated their ARM cores to
++ * reflect the correct address in CBAR/PERIPHBASE.
++ */
++#if defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2)
++#define CFG_ARM_GIC_BASE_ADDRESS 0x03020000
++#endif
++
+ /*
+ * The DRAM Base differs between some models. We cannot use macros for the
+ * CONFIG_FOO defines which contain the DRAM base address since they end
+--
+2.20.1
+
--- /dev/null
+From 1ae024a941fcbb196a02cf78c710a2120e86f52c Mon Sep 17 00:00:00 2001
+From: Sam Edwards <cfsworks@gmail.com>
+Date: Wed, 16 Aug 2023 10:34:20 -0700
+Subject: [PATCH 4038/4052] HACK: sunxi: psci: be compatible with v1 of R528
+ patchset
+
+This is a hack for reviewer QoL. It is not being submitted for mainline
+inclusion.
+---
+ arch/arm/cpu/armv7/sunxi/psci.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c
+index b4ce4f6def..27bac291d5 100644
+--- a/arch/arm/cpu/armv7/sunxi/psci.c
++++ b/arch/arm/cpu/armv7/sunxi/psci.c
+@@ -60,6 +60,18 @@
+
+ #define SUN8I_R528_C0_STATUS_STANDBYWFI (16)
+
++/* 3 hacks for compatibility across v1/v2 of Andre's R528 support series */
++#ifndef SUNXI_R_CPUCFG_BASE
++#define SUNXI_R_CPUCFG_BASE 0
++#endif
++#ifndef SUNXI_PRCM_BASE
++#define SUNXI_PRCM_BASE 0
++#endif
++#if defined(SUNXI_CPUX_BASE) && defined(SUNXI_CPUCFG_BASE)
++#undef SUNXI_CPUCFG_BASE
++#define SUNXI_CPUCFG_BASE SUNXI_CPUX_BASE
++#endif
++
+ static void __secure cp15_write_cntp_tval(u32 tval)
+ {
+ asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval));
+--
+2.20.1
+
--- /dev/null
+From d71edd5b4c744fd7eae2973819765b5c6aced322 Mon Sep 17 00:00:00 2001
+From: Zoltan HERPAI <wigyori@uid0.hu>
+Date: Sat, 3 Jun 2023 00:52:04 +0200
+Subject: [PATCH 4039/4052] sunxi: add uart0_pins on Port E PE2/PE3 on D1s/T133
+
+Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
+---
+ arch/riscv/dts/sunxi-d1s-t113.dtsi | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/arch/riscv/dts/sunxi-d1s-t113.dtsi b/arch/riscv/dts/sunxi-d1s-t113.dtsi
+index 1bb1e5cae6..567aa43d70 100644
+--- a/arch/riscv/dts/sunxi-d1s-t113.dtsi
++++ b/arch/riscv/dts/sunxi-d1s-t113.dtsi
+@@ -131,6 +131,12 @@
+ pins = "PB6", "PB7";
+ function = "uart3";
+ };
++
++ /omit-if-no-ref/
++ uart0_pins: uart0-pins {
++ pins = "PE2", "PE3";
++ function = "uart0";
++ };
+ };
+
+ ccu: clock-controller@2001000 {
+--
+2.20.1
+
--- /dev/null
+From 759dea3364c28424cf8c94c327ffc60682d87ad3 Mon Sep 17 00:00:00 2001
+From: Zoltan HERPAI <wigyori@uid0.hu>
+Date: Sat, 3 Jun 2023 00:52:40 +0200
+Subject: [PATCH 4040/4052] sunxi: add support for MangoPI MQDual T113 variant
+
+Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
+---
+ arch/arm/dts/Makefile | 3 +-
+ .../dts/sun8i-t113s-mangopi-mqdual-t113.dts | 50 +++++++++++++++++++
+ configs/mangopi_mqdual_t113_defconfig | 17 +++++++
+ 3 files changed, 69 insertions(+), 1 deletion(-)
+ create mode 100644 arch/arm/dts/sun8i-t113s-mangopi-mqdual-t113.dts
+ create mode 100644 configs/mangopi_mqdual_t113_defconfig
+
+diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
+index 41ee9c4c65..4207f17601 100644
+--- a/arch/arm/dts/Makefile
++++ b/arch/arm/dts/Makefile
+@@ -711,7 +711,8 @@ dtb-$(CONFIG_MACH_SUN8I_V3S) += \
+ sun8i-v3-sl631-imx179.dtb \
+ sun8i-v3s-licheepi-zero.dtb
+ dtb-$(CONFIG_MACH_SUN8I_R528) += \
+- sun8i-t113s-mangopi-mq-r-t113.dtb
++ sun8i-t113s-mangopi-mq-r-t113.dtb \
++ sun8i-t113s-mangopi-mqdual-t113.dtb
+ dtb-$(CONFIG_MACH_SUN50I_H5) += \
+ sun50i-h5-bananapi-m2-plus.dtb \
+ sun50i-h5-emlid-neutis-n5-devboard.dtb \
+diff --git a/arch/arm/dts/sun8i-t113s-mangopi-mqdual-t113.dts b/arch/arm/dts/sun8i-t113s-mangopi-mqdual-t113.dts
+new file mode 100644
+index 0000000000..7de3ddb92f
+--- /dev/null
++++ b/arch/arm/dts/sun8i-t113s-mangopi-mqdual-t113.dts
+@@ -0,0 +1,50 @@
++// SPDX-License-Identifier: (GPL-2.0+ or MIT)
++// Copyright (C) 2022 Arm Ltd.
++
++#include <dt-bindings/interrupt-controller/irq.h>
++
++/dts-v1/;
++
++#include "sun8i-t113s.dtsi"
++#include "sunxi-d1s-t113-mangopi-mq-r.dtsi"
++
++/ {
++ model = "MangoPi MQDual T113";
++ compatible = "widora,mangopi-mqdual-t113", "allwinner,sun8i-t113s";
++
++ aliases {
++ serial0 = &uart0;
++ ethernet0 = &rtl8189ftv;
++ };
++
++ chosen {
++ stdout-path = "serial0:115200n8";
++ };
++};
++
++&cpu0 {
++ cpu-supply = <®_vcc_core>;
++};
++
++&cpu1 {
++ cpu-supply = <®_vcc_core>;
++};
++
++&mmc1 {
++ rtl8189ftv: wifi@1 {
++ reg = <1>;
++ interrupt-parent = <&pio>;
++ interrupts = <6 10 IRQ_TYPE_LEVEL_LOW>; /* PG10 = WL_WAKE_AP */
++ interrupt-names = "host-wake";
++ };
++};
++
++&uart0 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&uart0_pins>;
++ status = "okay";
++};
++
++&uart3 {
++ status = "disabled";
++};
+diff --git a/configs/mangopi_mqdual_t113_defconfig b/configs/mangopi_mqdual_t113_defconfig
+new file mode 100644
+index 0000000000..98b90c55f4
+--- /dev/null
++++ b/configs/mangopi_mqdual_t113_defconfig
+@@ -0,0 +1,17 @@
++CONFIG_ARM=y
++CONFIG_ARCH_SUNXI=y
++CONFIG_DEFAULT_DEVICE_TREE="sun8i-t113s-mangopi-mqdual-t113"
++CONFIG_SUNXI_MINIMUM_DRAM_MB=128
++CONFIG_SPL=y
++CONFIG_MACH_SUN8I_R528=y
++CONFIG_CONS_INDEX=1
++CONFIG_MMC0_CD_PIN="PF6"
++# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
++CONFIG_SYS_MONITOR_LEN=786432
++CONFIG_DRAM_CLK=792
++CONFIG_DRAM_ZQ=8092667
++CONFIG_DRAM_SUNXI_ODT_EN=0
++CONFIG_DRAM_SUNXI_TPR0=0x004a2195
++CONFIG_DRAM_SUNXI_TPR11=0x340000
++CONFIG_DRAM_SUNXI_TPR12=0x46
++CONFIG_DRAM_SUNXI_TPR13=0x34000100
+--
+2.20.1
+
--- /dev/null
+From 5e15e7c630e4710cf60432373ff0fe838ed58d79 Mon Sep 17 00:00:00 2001
+From: Zoltan HERPAI <wigyori@uid0.hu>
+Date: Sat, 3 Jun 2023 23:41:31 +0200
+Subject: [PATCH 4041/4052] sunxi: add support for UART5 in Port E group on
+ T133
+
+Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
+---
+ arch/arm/include/asm/arch-sunxi/serial.h | 1 +
+ arch/arm/mach-sunxi/board.c | 4 ++++
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 1 +
+ include/configs/sunxi-common.h | 3 +++
+ 4 files changed, 9 insertions(+)
+
+diff --git a/arch/arm/include/asm/arch-sunxi/serial.h b/arch/arm/include/asm/arch-sunxi/serial.h
+index 9386287b65..48d0f42a3b 100644
+--- a/arch/arm/include/asm/arch-sunxi/serial.h
++++ b/arch/arm/include/asm/arch-sunxi/serial.h
+@@ -20,6 +20,7 @@
+ #elif defined(CONFIG_SUNXI_GEN_NCAT2)
+ #define SUNXI_UART0_BASE 0x02500000
+ #define SUNXI_R_UART_BASE 0 // 0x07080000 (?>
++#define SUNXI_UART5_BASE (SUNXI_UART0_BASE + 0x1400)
+ #else
+ #define SUNXI_UART0_BASE 0x01c28000
+ #define SUNXI_R_UART_BASE 0x01f02800
+diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
+index 8980ffb509..50693d216b 100644
+--- a/arch/arm/mach-sunxi/board.c
++++ b/arch/arm/mach-sunxi/board.c
+@@ -175,6 +175,10 @@ static int gpio_init(void)
+ sunxi_gpio_set_cfgpin(SUNXI_GPL(2), SUN8I_GPL_R_UART);
+ sunxi_gpio_set_cfgpin(SUNXI_GPL(3), SUN8I_GPL_R_UART);
+ sunxi_gpio_set_pull(SUNXI_GPL(3), SUNXI_GPIO_PULL_UP);
++#elif CONFIG_CONS_INDEX == 6 && defined(CONFIG_MACH_SUN8I_R528)
++ sunxi_gpio_set_cfgpin(SUNXI_GPE(6), 9);
++ sunxi_gpio_set_cfgpin(SUNXI_GPE(7), 9);
++ sunxi_gpio_set_pull(SUNXI_GPE(7), SUNXI_GPIO_PULL_UP);
+ #elif CONFIG_CONS_INDEX == 2 && defined(CONFIG_MACH_SUN8I) && \
+ !defined(CONFIG_MACH_SUN8I_R40)
+ sunxi_gpio_set_cfgpin(SUNXI_GPG(6), SUN8I_GPG_UART1);
+diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+index 614cfe6b73..2717d79bc3 100644
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -748,6 +748,7 @@ static const struct sunxi_pinctrl_function sun20i_d1_pinctrl_functions[] = {
+ { "uart0", 6 }, /* PB2-PB3 */
+ #endif
+ { "uart3", 7 }, /* PB6-PB9 */
++ { "uart5", 3 }, /* PE6-PE7 */
+ };
+
+ static const struct sunxi_pinctrl_desc __maybe_unused sun20i_d1_pinctrl_desc = {
+diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h
+index 67eb0d25db..e6efc4fad8 100644
+--- a/include/configs/sunxi-common.h
++++ b/include/configs/sunxi-common.h
+@@ -29,6 +29,9 @@
+ # define CFG_SYS_NS16550_COM3 SUNXI_UART2_BASE
+ # define CFG_SYS_NS16550_COM4 SUNXI_UART3_BASE
+ # define CFG_SYS_NS16550_COM5 SUNXI_R_UART_BASE
++#if defined(CONFIG_SUNXI_GEN_NCAT2)
++# define CFG_SYS_NS16550_COM6 SUNXI_UART5_BASE
++#endif
+ #endif
+
+ /* CPU */
+--
+2.20.1
+
--- /dev/null
+From 316c22da82fd1d66f8c081a5cba039ebde7af433 Mon Sep 17 00:00:00 2001
+From: Zoltan HERPAI <wigyori@uid0.hu>
+Date: Sun, 4 Jun 2023 00:13:45 +0200
+Subject: [PATCH 4042/4052] sunxi: add MYIR MYD-YT113X board
+
+Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
+---
+ arch/arm/dts/Makefile | 3 +-
+ arch/arm/dts/sun8i-t113s-myir-myd-yt113x.dts | 68 ++++++++++++++++++++
+ configs/myir_myd_t113x_defconfig | 20 ++++++
+ 3 files changed, 90 insertions(+), 1 deletion(-)
+ create mode 100644 arch/arm/dts/sun8i-t113s-myir-myd-yt113x.dts
+ create mode 100644 configs/myir_myd_t113x_defconfig
+
+diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
+index 4207f17601..6fb49de59a 100644
+--- a/arch/arm/dts/Makefile
++++ b/arch/arm/dts/Makefile
+@@ -712,7 +712,8 @@ dtb-$(CONFIG_MACH_SUN8I_V3S) += \
+ sun8i-v3s-licheepi-zero.dtb
+ dtb-$(CONFIG_MACH_SUN8I_R528) += \
+ sun8i-t113s-mangopi-mq-r-t113.dtb \
+- sun8i-t113s-mangopi-mqdual-t113.dtb
++ sun8i-t113s-mangopi-mqdual-t113.dtb \
++ sun8i-t113s-myir-myd-yt113x.dtb
+ dtb-$(CONFIG_MACH_SUN50I_H5) += \
+ sun50i-h5-bananapi-m2-plus.dtb \
+ sun50i-h5-emlid-neutis-n5-devboard.dtb \
+diff --git a/arch/arm/dts/sun8i-t113s-myir-myd-yt113x.dts b/arch/arm/dts/sun8i-t113s-myir-myd-yt113x.dts
+new file mode 100644
+index 0000000000..afd0d8f532
+--- /dev/null
++++ b/arch/arm/dts/sun8i-t113s-myir-myd-yt113x.dts
+@@ -0,0 +1,68 @@
++// SPDX-License-Identifier: (GPL-2.0+ or MIT)
++// Copyright (C) 2022 Arm Ltd.
++
++#include <dt-bindings/interrupt-controller/irq.h>
++
++/dts-v1/;
++
++#include "sun8i-t113s.dtsi"
++#include "sunxi-d1s-t113-mangopi-mq-r.dtsi"
++
++/ {
++ model = "MYIR MYD-YT113X";
++ compatible = "myir,myd-yt113x", "myir,myc-yt113x", "allwinner,sun8i-t113s";
++
++ aliases {
++ serial5 = &uart5;
++ };
++
++ chosen {
++ stdout-path = "serial5:115200n8";
++ };
++};
++
++&cpu0 {
++ cpu-supply = <®_vcc_core>;
++};
++
++&cpu1 {
++ cpu-supply = <®_vcc_core>;
++};
++
++&mmc2_pins {
++ bias-pull-up;
++ drive-strength = <40>;
++};
++
++&mmc2 {
++ pinctrl-0 = <&mmc2_pins>;
++ pinctrl-names = "default";
++ vmmc-supply = <®_3v3>;
++ non-removable;
++ bus-width = <4>;
++ status = "okay";
++
++ emmc: emmc@0 {
++ reg = <0>;
++ compatible = "mmc-card";
++ broken-hpi;
++ };
++};
++
++&pio {
++ /omit-if-no-ref/
++ uart5_pins: uart5-pins {
++ pins = "PE6", "PE7";
++ function = "uart5";
++ };
++};
++
++&uart5 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&uart5_pins>;
++ status = "okay";
++};
++
++&uart3 {
++ status = "disabled";
++};
+diff --git a/configs/myir_myd_t113x_defconfig b/configs/myir_myd_t113x_defconfig
+new file mode 100644
+index 0000000000..2ae442c3ca
+--- /dev/null
++++ b/configs/myir_myd_t113x_defconfig
+@@ -0,0 +1,20 @@
++CONFIG_ARM=y
++CONFIG_ARCH_SUNXI=y
++CONFIG_DEFAULT_DEVICE_TREE="sun8i-t113s-myir-myd-yt113x"
++CONFIG_SUNXI_MINIMUM_DRAM_MB=128
++CONFIG_SPL=y
++CONFIG_MACH_SUN8I_R528=y
++CONFIG_CONS_INDEX=6
++CONFIG_MMC0_CD_PIN="PF6"
++CONFIG_MMC_SUNXI_SLOT_EXTRA=2
++# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
++CONFIG_SYS_MONITOR_LEN=786432
++CONFIG_DRAM_CLK=792
++CONFIG_DRAM_ZQ=8092667
++CONFIG_DRAM_SUNXI_ODT_EN=0
++CONFIG_DRAM_SUNXI_TPR0=0x004a2195
++CONFIG_DRAM_SUNXI_TPR11=0x340000
++CONFIG_DRAM_SUNXI_TPR12=0x46
++CONFIG_DRAM_SUNXI_TPR13=0x34000100
++CONFIG_USB_EHCI_HCD=y
++CONFIG_USB_OHCI_HCD=y
+--
+2.20.1
+
--- /dev/null
+From f3cfc41de396835ca4dbccbc54294c608ed9ff6f Mon Sep 17 00:00:00 2001
+From: Zoltan HERPAI <wigyori@uid0.hu>
+Date: Sat, 3 Jun 2023 23:57:46 +0200
+Subject: [PATCH 4043/4052] sunxi: add support for UART3 on PE pins
+
+Some boards use Port E pins for muxing the UART3 as console. Add a new
+Kconfig option allowing to select this (mimicking MMC_PINS_PH).
+
+Pinmux taken from https://bbs.aw-ol.com/assets/uploads/files/1648883311844-t113-s3_datasheet_v1.2.pdf
+
+Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
+---
+ arch/arm/mach-sunxi/Kconfig | 6 ++++++
+ arch/arm/mach-sunxi/board.c | 10 ++++++++--
+ arch/riscv/dts/sunxi-d1s-t113.dtsi | 6 ++++++
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 4 ++++
+ 4 files changed, 24 insertions(+), 2 deletions(-)
+
+diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
+index 7233ecbc1c..6f5e438713 100644
+--- a/arch/arm/mach-sunxi/Kconfig
++++ b/arch/arm/mach-sunxi/Kconfig
+@@ -666,6 +666,12 @@ config UART0_PORT_F
+ at the same time, the system can be only booted in the FEL mode.
+ Only enable this if you really know what you are doing.
+
++config UART3_PINS_PE
++ bool "Pins for uart3 are on Port E"
++ ---help---
++ Select this option for boards where uart3 uses the Port E pinmux.
++ (Some T113-S3 boards use uart3 as console.)
++
+ config OLD_SUNXI_KERNEL_COMPAT
+ bool "Enable workarounds for booting old kernels"
+ ---help---
+diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
+index 50693d216b..85dbad0552 100644
+--- a/arch/arm/mach-sunxi/board.c
++++ b/arch/arm/mach-sunxi/board.c
+@@ -168,16 +168,22 @@ static int gpio_init(void)
+ sunxi_gpio_set_cfgpin(SUNXI_GPB(1), SUN8I_GPB_UART2);
+ sunxi_gpio_set_pull(SUNXI_GPB(1), SUNXI_GPIO_PULL_UP);
+ #elif CONFIG_CONS_INDEX == 4 && defined(CONFIG_MACH_SUN8I_R528)
++#if defined(CONFIG_UART3_PINS_PE)
++ sunxi_gpio_set_cfgpin(SUNXI_GPE(8), 5);
++ sunxi_gpio_set_cfgpin(SUNXI_GPE(9), 5);
++ sunxi_gpio_set_pull(SUNXI_GPE(9), SUNXI_GPIO_PULL_UP);
++#else
+ sunxi_gpio_set_cfgpin(SUNXI_GPB(6), 7);
+ sunxi_gpio_set_cfgpin(SUNXI_GPB(7), 7);
+ sunxi_gpio_set_pull(SUNXI_GPB(7), SUNXI_GPIO_PULL_UP);
++#endif
+ #elif CONFIG_CONS_INDEX == 5 && defined(CONFIG_MACH_SUN8I)
+ sunxi_gpio_set_cfgpin(SUNXI_GPL(2), SUN8I_GPL_R_UART);
+ sunxi_gpio_set_cfgpin(SUNXI_GPL(3), SUN8I_GPL_R_UART);
+ sunxi_gpio_set_pull(SUNXI_GPL(3), SUNXI_GPIO_PULL_UP);
+ #elif CONFIG_CONS_INDEX == 6 && defined(CONFIG_MACH_SUN8I_R528)
+- sunxi_gpio_set_cfgpin(SUNXI_GPE(6), 9);
+- sunxi_gpio_set_cfgpin(SUNXI_GPE(7), 9);
++ sunxi_gpio_set_cfgpin(SUNXI_GPE(6), 3);
++ sunxi_gpio_set_cfgpin(SUNXI_GPE(7), 3);
+ sunxi_gpio_set_pull(SUNXI_GPE(7), SUNXI_GPIO_PULL_UP);
+ #elif CONFIG_CONS_INDEX == 2 && defined(CONFIG_MACH_SUN8I) && \
+ !defined(CONFIG_MACH_SUN8I_R40)
+diff --git a/arch/riscv/dts/sunxi-d1s-t113.dtsi b/arch/riscv/dts/sunxi-d1s-t113.dtsi
+index 567aa43d70..324353905f 100644
+--- a/arch/riscv/dts/sunxi-d1s-t113.dtsi
++++ b/arch/riscv/dts/sunxi-d1s-t113.dtsi
+@@ -132,6 +132,12 @@
+ function = "uart3";
+ };
+
++ /omit-if-no-ref/
++ uart3_pe_pins: uart3-pe-pins {
++ pins = "PE8", "PE9";
++ function = "uart3";
++ };
++
+ /omit-if-no-ref/
+ uart0_pins: uart0-pins {
+ pins = "PE2", "PE3";
+diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+index 2717d79bc3..e466808e4e 100644
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -747,7 +747,11 @@ static const struct sunxi_pinctrl_function sun20i_d1_pinctrl_functions[] = {
+ #else
+ { "uart0", 6 }, /* PB2-PB3 */
+ #endif
++#if IS_ENABLED(CONFIG_UART3_PINS_E)
++ { "uart3", 5 }, /* PE8-PE9 */
++else
+ { "uart3", 7 }, /* PB6-PB9 */
++#endif
+ { "uart5", 3 }, /* PE6-PE7 */
+ };
+
+--
+2.20.1
+
--- /dev/null
+From 4903155257eae57c25fa61e75630f71bf5823009 Mon Sep 17 00:00:00 2001
+From: Zoltan HERPAI <wigyori@uid0.hu>
+Date: Sat, 3 Jun 2023 23:42:33 +0200
+Subject: [PATCH 4044/4052] sunxi: add support for Rongpin RP-T113 board
+
+Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
+---
+ arch/arm/dts/Makefile | 3 +-
+ arch/arm/dts/sun8i-t113s-rongpin-rp-t113.dts | 99 ++++++++++++++++++++
+ configs/rongpin_rp_t113_defconfig | 18 ++++
+ 3 files changed, 119 insertions(+), 1 deletion(-)
+ create mode 100644 arch/arm/dts/sun8i-t113s-rongpin-rp-t113.dts
+ create mode 100644 configs/rongpin_rp_t113_defconfig
+
+diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
+index 6fb49de59a..be89021657 100644
+--- a/arch/arm/dts/Makefile
++++ b/arch/arm/dts/Makefile
+@@ -713,7 +713,8 @@ dtb-$(CONFIG_MACH_SUN8I_V3S) += \
+ dtb-$(CONFIG_MACH_SUN8I_R528) += \
+ sun8i-t113s-mangopi-mq-r-t113.dtb \
+ sun8i-t113s-mangopi-mqdual-t113.dtb \
+- sun8i-t113s-myir-myd-yt113x.dtb
++ sun8i-t113s-myir-myd-yt113x.dtb \
++ sun8i-t113s-rongpin-rp-t113.dtb
+ dtb-$(CONFIG_MACH_SUN50I_H5) += \
+ sun50i-h5-bananapi-m2-plus.dtb \
+ sun50i-h5-emlid-neutis-n5-devboard.dtb \
+diff --git a/arch/arm/dts/sun8i-t113s-rongpin-rp-t113.dts b/arch/arm/dts/sun8i-t113s-rongpin-rp-t113.dts
+new file mode 100644
+index 0000000000..3f7633210b
+--- /dev/null
++++ b/arch/arm/dts/sun8i-t113s-rongpin-rp-t113.dts
+@@ -0,0 +1,99 @@
++// SPDX-License-Identifier: (GPL-2.0+ or MIT)
++// Copyright (C) 2022 Arm Ltd.
++
++#include <dt-bindings/interrupt-controller/irq.h>
++#include <dt-bindings/gpio/gpio.h>
++
++/dts-v1/;
++
++#include "sun8i-t113s.dtsi"
++
++/ {
++ model = "Rongpin RP-T113";
++ compatible = "rongpin,rp-t113", "allwinner,sun8i-t113s";
++
++ aliases {
++ serial3 = &uart3;
++ };
++
++ chosen {
++ stdout-path = "serial3:115200n8";
++ };
++
++ /* board wide 5V supply directly from the USB-C socket */
++ reg_vcc5v: regulator-5v {
++ compatible = "regulator-fixed";
++ regulator-name = "vcc-5v";
++ regulator-min-microvolt = <5000000>;
++ regulator-max-microvolt = <5000000>;
++ regulator-always-on;
++ };
++
++ /* SY8008 DC/DC regulator on the board */
++ reg_3v3: regulator-3v3 {
++ compatible = "regulator-fixed";
++ regulator-name = "vcc-3v3";
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++ vin-supply = <®_vcc5v>;
++ };
++
++ /* SY8008 DC/DC regulator on the board, also supplying VDD-SYS */
++ reg_vcc_core: regulator-core {
++ compatible = "regulator-fixed";
++ regulator-name = "vcc-core";
++ regulator-min-microvolt = <880000>;
++ regulator-max-microvolt = <880000>;
++ vin-supply = <®_vcc5v>;
++ };
++
++ /* XC6206 LDO on the board */
++ reg_avdd2v8: regulator-avdd {
++ compatible = "regulator-fixed";
++ regulator-name = "avdd2v8";
++ regulator-min-microvolt = <2800000>;
++ regulator-max-microvolt = <2800000>;
++ vin-supply = <®_3v3>;
++ };
++};
++
++&cpu0 {
++ cpu-supply = <®_vcc_core>;
++};
++
++&cpu1 {
++ cpu-supply = <®_vcc_core>;
++};
++
++&pio {
++ vcc-pb-supply = <®_3v3>;
++ vcc-pd-supply = <®_3v3>;
++ vcc-pe-supply = <®_avdd2v8>;
++ vcc-pf-supply = <®_3v3>;
++ vcc-pg-supply = <®_3v3>;
++};
++
++&uart3 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&uart3_pe_pins>;
++ status = "okay";
++};
++
++&mmc0 {
++ pinctrl-0 = <&mmc0_pins>;
++ pinctrl-names = "default";
++ vmmc-supply = <®_3v3>;
++ cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>;
++ disable-wp;
++ bus-width = <4>;
++ status = "okay";
++};
++
++&mmc2 {
++ pinctrl-0 = <&mmc2_pins>;
++ pinctrl-names = "default";
++ vmmc-supply = <®_3v3>;
++ non-removable;
++ bus-width = <4>;
++ status = "okay";
++};
+diff --git a/configs/rongpin_rp_t113_defconfig b/configs/rongpin_rp_t113_defconfig
+new file mode 100644
+index 0000000000..9b5a5bcd65
+--- /dev/null
++++ b/configs/rongpin_rp_t113_defconfig
+@@ -0,0 +1,18 @@
++CONFIG_ARM=y
++CONFIG_ARCH_SUNXI=y
++CONFIG_DEFAULT_DEVICE_TREE="sun8i-t113s-rongpin-rp-t113"
++CONFIG_SUNXI_MINIMUM_DRAM_MB=128
++CONFIG_SPL=y
++CONFIG_MACH_SUN8I_R528=y
++CONFIG_CONS_INDEX=4
++CONFIG_MMC0_CD_PIN="PF6"
++# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
++CONFIG_SYS_MONITOR_LEN=786432
++CONFIG_DRAM_CLK=792
++CONFIG_DRAM_ZQ=8092667
++CONFIG_DRAM_SUNXI_ODT_EN=0
++CONFIG_DRAM_SUNXI_TPR0=0x004a2195
++CONFIG_DRAM_SUNXI_TPR11=0x340000
++CONFIG_DRAM_SUNXI_TPR12=0x46
++CONFIG_DRAM_SUNXI_TPR13=0x34000100
++CONFIG_UART3_PINS_PE=y
+--
+2.20.1
+
--- /dev/null
+From b13b4017649837d97c73e855448474a9792ab93d Mon Sep 17 00:00:00 2001
+From: Zoltan HERPAI <wigyori@uid0.hu>
+Date: Sat, 29 Jul 2023 11:23:47 +0200
+Subject: [PATCH 4045/4052] sunxi: add MYIR MYD-YT113X-SPI board
+
+Instead of eMMC, this board sports a 256Mb SPI NAND flash.
+
+Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
+---
+ arch/arm/dts/Makefile | 1 +
+ .../dts/sun8i-t113s-myir-myd-yt113x-spi.dts | 59 +++++++++++++++++++
+ configs/myir_myd_t113x-spi_defconfig | 38 ++++++++++++
+ 3 files changed, 98 insertions(+)
+ create mode 100644 arch/arm/dts/sun8i-t113s-myir-myd-yt113x-spi.dts
+ create mode 100644 configs/myir_myd_t113x-spi_defconfig
+
+diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
+index be89021657..267f7bc07e 100644
+--- a/arch/arm/dts/Makefile
++++ b/arch/arm/dts/Makefile
+@@ -714,6 +714,7 @@ dtb-$(CONFIG_MACH_SUN8I_R528) += \
+ sun8i-t113s-mangopi-mq-r-t113.dtb \
+ sun8i-t113s-mangopi-mqdual-t113.dtb \
+ sun8i-t113s-myir-myd-yt113x.dtb \
++ sun8i-t113s-myir-myd-yt113x-spi.dtb \
+ sun8i-t113s-rongpin-rp-t113.dtb
+ dtb-$(CONFIG_MACH_SUN50I_H5) += \
+ sun50i-h5-bananapi-m2-plus.dtb \
+diff --git a/arch/arm/dts/sun8i-t113s-myir-myd-yt113x-spi.dts b/arch/arm/dts/sun8i-t113s-myir-myd-yt113x-spi.dts
+new file mode 100644
+index 0000000000..f72bab29a6
+--- /dev/null
++++ b/arch/arm/dts/sun8i-t113s-myir-myd-yt113x-spi.dts
+@@ -0,0 +1,59 @@
++// SPDX-License-Identifier: (GPL-2.0+ or MIT)
++// Copyright (C) 2022 Arm Ltd.
++
++#include <dt-bindings/interrupt-controller/irq.h>
++
++/dts-v1/;
++
++#include "sun8i-t113s.dtsi"
++#include "sunxi-d1s-t113-mangopi-mq-r.dtsi"
++
++/ {
++ model = "MYIR MYD-YT113X (SPI)";
++ compatible = "myir,myd-yt113x", "myir,myc-yt113x", "allwinner,sun8i-t113s";
++
++ aliases {
++ serial5 = &uart5;
++ };
++
++ chosen {
++ stdout-path = "serial5:115200n8";
++ };
++};
++
++&cpu0 {
++ cpu-supply = <®_vcc_core>;
++};
++
++&cpu1 {
++ cpu-supply = <®_vcc_core>;
++};
++
++&pio {
++ /omit-if-no-ref/
++ uart5_pins: uart5-pins {
++ pins = "PE6", "PE7";
++ function = "uart5";
++ };
++};
++
++&uart5 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&uart5_pins>;
++ status = "okay";
++};
++
++&uart3 {
++ status = "disabled";
++};
++
++&spi0 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&spi0_pins>;
++ status = "okay";
++ spi_nand@0 {
++ compatible = "spi-nand";
++ reg = <0>;
++ spi-max-frequency = <52000000>;
++ };
++};
+diff --git a/configs/myir_myd_t113x-spi_defconfig b/configs/myir_myd_t113x-spi_defconfig
+new file mode 100644
+index 0000000000..a433fe0449
+--- /dev/null
++++ b/configs/myir_myd_t113x-spi_defconfig
+@@ -0,0 +1,38 @@
++CONFIG_ARM=y
++CONFIG_ARCH_SUNXI=y
++CONFIG_DEFAULT_DEVICE_TREE="sun8i-t113s-myir-myd-yt113x-spi"
++CONFIG_SUNXI_MINIMUM_DRAM_MB=128
++CONFIG_SPL=y
++CONFIG_SPL_SPI_SUNXI=y
++CONFIG_MTD_SPI_NAND=y
++CONFIG_MACH_SUN8I_R528=y
++CONFIG_CONS_INDEX=6
++CONFIG_MMC0_CD_PIN="PF6"
++# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
++CONFIG_SYS_MONITOR_LEN=786432
++CONFIG_DRAM_CLK=792
++CONFIG_DRAM_ZQ=8092667
++CONFIG_DRAM_SUNXI_ODT_EN=0
++CONFIG_DRAM_SUNXI_TPR0=0x004a2195
++CONFIG_DRAM_SUNXI_TPR11=0x340000
++CONFIG_DRAM_SUNXI_TPR12=0x46
++CONFIG_DRAM_SUNXI_TPR13=0x34000100
++CONFIG_CLK_SUN20I_D1=y
++CONFIG_PHY_MOTORCOMM=y
++CONFIG_SUN8I_EMAC=y
++CONFIG_RGMII=y
++CONFIG_RMII=y
++CONFIG_MTD=y
++CONFIG_DM_MTD=y
++CONFIG_SYS_MTDPARTS_RUNTIME=y
++CONFIG_NAND_STM32_FMC2=y
++CONFIG_SYS_NAND_ONFI_DETECTION=y
++CONFIG_MTD_SPI_NAND=y
++CONFIG_DM_SPI_FLASH=y
++CONFIG_SPI_FLASH_MACRONIX=y
++CONFIG_SPI_FLASH_SPANSION=y
++CONFIG_SPI_FLASH_STMICRO=y
++CONFIG_SPI_FLASH_WINBOND=y
++# CONFIG_SPI_FLASH_USE_4K_SECTORS is not set
++CONFIG_SPI_FLASH_MTD=y
++CONFIG_SPI=y
+--
+2.20.1
+
--- /dev/null
+From b3a2b860f3f0760ef356d312f01f68a68c05294b Mon Sep 17 00:00:00 2001
+From: Zoltan HERPAI <wigyori@uid0.hu>
+Date: Sat, 29 Jul 2023 11:29:26 +0200
+Subject: [PATCH 4046/4052] sunxi: add support for emac on PG pins
+
+Some boards use Port G pins for muxing EMAC.
+
+Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
+---
+ arch/riscv/dts/sunxi-d1s-t113.dtsi | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/arch/riscv/dts/sunxi-d1s-t113.dtsi b/arch/riscv/dts/sunxi-d1s-t113.dtsi
+index 324353905f..2426c71f25 100644
+--- a/arch/riscv/dts/sunxi-d1s-t113.dtsi
++++ b/arch/riscv/dts/sunxi-d1s-t113.dtsi
+@@ -101,6 +101,14 @@
+ function = "emac";
+ };
+
++ /omit-if-no-ref/
++ rgmii_pg_pins: rgmii-pg-pins {
++ pins = "PG0", "PG1", "PG2", "PG3", "PG4",
++ "PG5", "PG6", "PG7", "PG8", "PG9",
++ "PG11", "PG12", "PG13", "PG14", "PG15";
++ function = "emac";
++ };
++
+ /omit-if-no-ref/
+ rmii_pe_pins: rmii-pe-pins {
+ pins = "PE0", "PE1", "PE2", "PE3", "PE4",
+--
+2.20.1
+
--- /dev/null
+From 4a461963583fe845500c9e5227fa47811cab7136 Mon Sep 17 00:00:00 2001
+From: Zoltan HERPAI <wigyori@uid0.hu>
+Date: Sat, 29 Jul 2023 11:34:19 +0200
+Subject: [PATCH 4047/4052] sunxi: add ethernet support on MYIR MYD-YT113X-SPI
+
+Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
+---
+ .../dts/sun8i-t113s-myir-myd-yt113x-spi.dts | 34 +++++++++++++++++++
+ 1 file changed, 34 insertions(+)
+
+diff --git a/arch/arm/dts/sun8i-t113s-myir-myd-yt113x-spi.dts b/arch/arm/dts/sun8i-t113s-myir-myd-yt113x-spi.dts
+index f72bab29a6..431d5593d6 100644
+--- a/arch/arm/dts/sun8i-t113s-myir-myd-yt113x-spi.dts
++++ b/arch/arm/dts/sun8i-t113s-myir-myd-yt113x-spi.dts
+@@ -19,6 +19,23 @@
+ chosen {
+ stdout-path = "serial5:115200n8";
+ };
++
++ reg_vcc5v: regulator-5v {
++ compatible = "regulator-fixed";
++ regulator-name = "vcc-5v";
++ regulator-min-microvolt = <5000000>;
++ regulator-max-microvolt = <5000000>;
++ regulator-always-on;
++ };
++
++ /* SY8008 DC/DC regulator on the board */
++ reg_3v3: regulator-3v3 {
++ compatible = "regulator-fixed";
++ regulator-name = "vcc-3v3";
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++ vin-supply = <®_vcc5v>;
++ };
+ };
+
+ &cpu0 {
+@@ -57,3 +74,20 @@
+ spi-max-frequency = <52000000>;
+ };
+ };
++
++&emac {
++ pinctrl-names = "default";
++ pinctrl-0 = <&rgmii_pg_pins>;
++ phy-supply = <®_3v3>;
++ phy-handle = <&ext_rgmii_phy>;
++ phy-mode = "rgmii-id";
++
++ status = "okay";
++};
++
++&mdio {
++ ext_rgmii_phy: ethernet-phy@4 {
++ compatible = "ethernet-phy-ieee802.3-c22";
++ reg = <4>;
++ };
++};
+--
+2.20.1
+
--- /dev/null
+From 01d239cb0daaaca75ffc71adecd19a55e755c296 Mon Sep 17 00:00:00 2001
+From: Zoltan HERPAI <wigyori@uid0.hu>
+Date: Sun, 4 Jun 2023 15:40:42 +0200
+Subject: [PATCH 4048/4052] sunxi: enable emac on Rongpin RP-T113
+
+The emac is connected to an IC+ IP101 PHY, for which the driver
+has been re-added (it was removed in 2014).
+
+Currently the driver init fails with the below, so further tweaking
+will be required.
+
+CPU: Allwinner R528 (SUN8I)
+Model: Rongpin RP-T113
+DRAM: 128 MiB
+Core: 36 devices, 15 uclasses, devicetree: separate
+MMC: mmc@4020000: 0, mmc@4022000: 1
+Loading Environment from FAT... Unable to read "uboot.env" from mmc0:1...
+In: serial@2500c00
+Out: serial@2500c00
+Err: serial@2500c00
+Net: eth_sun8i_emac ethernet@4500000: failed to get TX clock
+No ethernet found.
+
+Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
+---
+ arch/arm/dts/sun8i-t113s-rongpin-rp-t113.dts | 28 ++++++++++++++++++++
+ configs/rongpin_rp_t113_defconfig | 3 +++
+ 2 files changed, 31 insertions(+)
+
+diff --git a/arch/arm/dts/sun8i-t113s-rongpin-rp-t113.dts b/arch/arm/dts/sun8i-t113s-rongpin-rp-t113.dts
+index 3f7633210b..348ae5eec6 100644
+--- a/arch/arm/dts/sun8i-t113s-rongpin-rp-t113.dts
++++ b/arch/arm/dts/sun8i-t113s-rongpin-rp-t113.dts
+@@ -55,6 +55,16 @@
+ regulator-max-microvolt = <2800000>;
+ vin-supply = <®_3v3>;
+ };
++
++ reg_gmac_3v3: gmac-3v3 {
++ compatible = "regulator-fixed";
++ regulator-name = "gmac-3v3";
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++ startup-delay-us = <100000>;
++ enable-active-high;
++ gpio = <&pio 3 6 GPIO_ACTIVE_HIGH>; /* PD6 */
++ };
+ };
+
+ &cpu0 {
+@@ -79,6 +89,17 @@
+ status = "okay";
+ };
+
++&emac {
++ pinctrl-names = "default";
++ pinctrl-0 = <&rmii_pe_pins>;
++
++ phy-supply = <®_3v3>;
++ phy-handle = <&ext_rgmii_phy>;
++ phy-mode = "rmii";
++
++ status = "okay";
++};
++
+ &mmc0 {
+ pinctrl-0 = <&mmc0_pins>;
+ pinctrl-names = "default";
+@@ -97,3 +118,10 @@
+ bus-width = <4>;
+ status = "okay";
+ };
++
++&mdio {
++ ext_rgmii_phy: ethernet-phy@1 {
++ compatible = "ethernet-phy-ieee802.3-c22";
++ reg = <1>;
++ };
++};
+diff --git a/configs/rongpin_rp_t113_defconfig b/configs/rongpin_rp_t113_defconfig
+index 9b5a5bcd65..e234934f50 100644
+--- a/configs/rongpin_rp_t113_defconfig
++++ b/configs/rongpin_rp_t113_defconfig
+@@ -16,3 +16,6 @@ CONFIG_DRAM_SUNXI_TPR11=0x340000
+ CONFIG_DRAM_SUNXI_TPR12=0x46
+ CONFIG_DRAM_SUNXI_TPR13=0x34000100
+ CONFIG_UART3_PINS_PE=y
++CONFIG_SUN8I_EMAC=y
++CONFIG_PHY_ICPLUS=y
++CONFIG_RMII=y
+--
+2.20.1
+
--- /dev/null
+From fac8490193f271477ee4a9a3d749862461e7051c Mon Sep 17 00:00:00 2001
+From: Zoltan HERPAI <wigyori@uid0.hu>
+Date: Mon, 5 Jun 2023 17:57:15 +0200
+Subject: [PATCH 4049/4052] sunxi: enable gmac on MYIR MYD-YT113X with the
+ YT8531 PHY
+
+The gmac is connected to a Motorcomm YT8531, for which the driver
+has been picked from Starfive and ported over.
+
+Support is not yet added in DTS, only for compile testing.
+
+Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
+---
+ configs/myir_myd_t113x_defconfig | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/configs/myir_myd_t113x_defconfig b/configs/myir_myd_t113x_defconfig
+index 2ae442c3ca..8bffcb7acd 100644
+--- a/configs/myir_myd_t113x_defconfig
++++ b/configs/myir_myd_t113x_defconfig
+@@ -18,3 +18,12 @@ CONFIG_DRAM_SUNXI_TPR12=0x46
+ CONFIG_DRAM_SUNXI_TPR13=0x34000100
+ CONFIG_USB_EHCI_HCD=y
+ CONFIG_USB_OHCI_HCD=y
++CONFIG_PHY_MOTORCOMM=y
++CONFIG_SUN8I_EMAC=y
++CONFIG_RGMII=y
++CONFIG_RMII=y
++CONFIG_SUPPORT_EMMC_BOOT=y
++CONFIG_MMC_IO_VOLTAGE=y
++CONFIG_SPL_MMC_IO_VOLTAGE=y
++CONFIG_MMC_HS200_SUPPORT=y
++CONFIG_SPL_MMC_HS200_SUPPORT=y
+--
+2.20.1
+
--- /dev/null
+From 2fac27b1c5de843377d9413425a079a6ce3a13e3 Mon Sep 17 00:00:00 2001
+From: Zoltan HERPAI <wigyori@uid0.hu>
+Date: Sat, 26 Aug 2023 17:45:03 +0200
+Subject: [PATCH 4050/4052] arm: dts: add partition table for MYIR
+ MYD-YT113X-SPI
+
+The original bootloader reports the following as the partition table:
+
+device nand0 <nand>, # parts = 4
+ #: name size offset mask_flags
+ 0: boot0 0x00100000 0x00000000 1
+ 1: uboot 0x00300000 0x00100000 1
+ 2: secure_storage 0x00100000 0x00400000 1
+ 3: sys 0x0fb00000 0x00500000 0
+
+Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
+---
+ .../dts/sun8i-t113s-myir-myd-yt113x-spi.dts | 26 +++++++++++++++++++
+ 1 file changed, 26 insertions(+)
+
+diff --git a/arch/arm/dts/sun8i-t113s-myir-myd-yt113x-spi.dts b/arch/arm/dts/sun8i-t113s-myir-myd-yt113x-spi.dts
+index 431d5593d6..a5fd8c6bea 100644
+--- a/arch/arm/dts/sun8i-t113s-myir-myd-yt113x-spi.dts
++++ b/arch/arm/dts/sun8i-t113s-myir-myd-yt113x-spi.dts
+@@ -72,6 +72,32 @@
+ compatible = "spi-nand";
+ reg = <0>;
+ spi-max-frequency = <52000000>;
++
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ partition@0 {
++ label = "boot0";
++ reg = <0x0 0x100000>;
++ };
++
++ partition@100000 {
++ label = "uboot";
++ reg = <0x0 0x300000>;
++ };
++
++ partition@400000 {
++ label = "secure_storage";
++ reg = <0x0 0x400000>;
++ };
++
++ partition@500000 {
++ label = "sys";
++ reg = <0x0 0xfb00000>;
++ };
++ };
+ };
+ };
+
+--
+2.20.1
+
--- /dev/null
+From 4e383529bbbb24d359afa9c06864c890f466b212 Mon Sep 17 00:00:00 2001
+From: Zoltan HERPAI <wigyori@uid0.hu>
+Date: Sat, 26 Aug 2023 17:46:22 +0200
+Subject: [PATCH 4051/4052] configs: enable UBI support for MYIR MYD-YT113X-SPI
+
+Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
+---
+ configs/myir_myd_t113x-spi_defconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/configs/myir_myd_t113x-spi_defconfig b/configs/myir_myd_t113x-spi_defconfig
+index a433fe0449..a1c2a82c87 100644
+--- a/configs/myir_myd_t113x-spi_defconfig
++++ b/configs/myir_myd_t113x-spi_defconfig
+@@ -36,3 +36,4 @@ CONFIG_SPI_FLASH_WINBOND=y
+ # CONFIG_SPI_FLASH_USE_4K_SECTORS is not set
+ CONFIG_SPI_FLASH_MTD=y
+ CONFIG_SPI=y
++CONFIG_MTD_UBI_FASTMAP=y
+--
+2.20.1
+
--- /dev/null
+From ac986d0e4da7ccdf48c7d2369531c8bd0bd1b57d Mon Sep 17 00:00:00 2001
+From: Zoltan HERPAI <wigyori@uid0.hu>
+Date: Sat, 26 Aug 2023 21:09:17 +0200
+Subject: [PATCH 4052/4052] sunxi: r528/d1/t113: add SDC2 pinmux on PC2-7 pins
+
+Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
+---
+ board/sunxi/board.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/board/sunxi/board.c b/board/sunxi/board.c
+index 661137f43c..c00629f9b5 100644
+--- a/board/sunxi/board.c
++++ b/board/sunxi/board.c
+@@ -424,6 +424,13 @@ static void mmc_pinmux_setup(int sdc)
+ sunxi_gpio_set_cfgpin(SUNXI_GPC(24), SUNXI_GPC_SDC2);
+ sunxi_gpio_set_pull(SUNXI_GPC(24), SUNXI_GPIO_PULL_UP);
+ sunxi_gpio_set_drv(SUNXI_GPC(24), 2);
++#elif defined(CONFIG_MACH_SUN8I_R528)
++ /* SDC2: PC2-PC7 */
++ for (pin = SUNXI_GPC(2); pin <= SUNXI_GPC(7); pin++) {
++ sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_SDC2);
++ sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
++ sunxi_gpio_set_drv(pin, 2);
++ }
+ #elif defined(CONFIG_MACH_SUN8I) || defined(CONFIG_MACH_SUN50I)
+ /* SDC2: PC5-PC6, PC8-PC16 */
+ for (pin = SUNXI_GPC(5); pin <= SUNXI_GPC(6); pin++) {
+--
+2.20.1
+
--- /dev/null
+setenv fdt_high ffffffff
+setenv mmc_rootpart 2
+part uuid mmc ${mmc_bootdev}:${mmc_rootpart} uuid
+setenv loadkernel fatload mmc \$mmc_bootdev \$kernel_addr_r uImage
+setenv loaddtb fatload mmc \$mmc_bootdev \$fdt_addr_r dtb
+setenv bootargs console=ttyS3,115200 earlyprintk root=PARTUUID=${uuid} rootwait
+setenv uenvcmd run loadkernel \&\& run loaddtb \&\& bootm \$kernel_addr_r - \$fdt_addr_r
+run uenvcmd
--- /dev/null
+setenv fdt_high ffffffff
+setenv mmc_rootpart 2
+part uuid mmc ${mmc_bootdev}:${mmc_rootpart} uuid
+setenv loadkernel fatload mmc \$mmc_bootdev \$kernel_addr_r uImage
+setenv loaddtb fatload mmc \$mmc_bootdev \$fdt_addr_r dtb
+setenv bootargs console=ttyS5,115200 earlyprintk root=PARTUUID=${uuid} rootwait
+setenv uenvcmd run loadkernel \&\& run loaddtb \&\& bootm \$kernel_addr_r - \$fdt_addr_r
+run uenvcmd