Merge patch series "airoha: Add initial support AN7581"

Christian Marangi <ansuelsmth@gmail.com> says:

This little series adds initial support for Airoha AN7581 SoC.

With the help of some backport patch, this use OF_UPSTREAM
directly.

Posting this to have the targer and the very basic driver.

Ethernet, SNAND and eMMC support is already ready downstream
and will be posted shortly after this gets approved.

Having the first driver ready permits to separately push
dedicate series for SNAND, eMMC and Ethrnet as they all depends
on basic support of clock and reset and nothing else.

Link: https://lore.kernel.org/r/20250314185941.27834-1-ansuelsmth@gmail.com
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 32b80da..3ed829e 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -600,6 +600,13 @@
 	prompt "Target select"
 	default TARGET_HIKEY
 
+config ARCH_AIROHA
+	bool "Airoha SoCs"
+	select DM
+	select OF_CONTROL
+	help
+	  Support for the Airoha soc.
+
 config ARCH_AT91
 	bool "Atmel AT91"
 	select GPIO_EXTRA_HEADER
@@ -2253,6 +2260,8 @@
 	  Path within the source directory to the kwbimage.cfg file to use
 	  when packaging the U-Boot image for use.
 
+source "arch/arm/mach-airoha/Kconfig"
+
 source "arch/arm/mach-apple/Kconfig"
 
 source "arch/arm/mach-aspeed/Kconfig"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 7334e79..5ecadb2 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -51,6 +51,7 @@
 
 # Machine directory name.  This list is sorted alphanumerically
 # by CONFIG_* macro name.
+machine-$(CONFIG_ARCH_AIROHA)		+= airoha
 machine-$(CONFIG_ARCH_APPLE)		+= apple
 machine-$(CONFIG_ARCH_ASPEED)		+= aspeed
 machine-$(CONFIG_ARCH_AT91)		+= at91
diff --git a/arch/arm/dts/an7581-u-boot.dtsi b/arch/arm/dts/an7581-u-boot.dtsi
new file mode 100644
index 0000000..0316b73
--- /dev/null
+++ b/arch/arm/dts/an7581-u-boot.dtsi
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/ {
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		atf-reserved-memory@80000000 {
+			no-map;
+			reg = <0x0 0x80000000 0x0 0x40000>;
+		};
+	};
+};
+
+&uart1 {
+	bootph-all;
+};
diff --git a/arch/arm/dts/en7581-evb-u-boot.dtsi b/arch/arm/dts/en7581-evb-u-boot.dtsi
new file mode 100644
index 0000000..ebd3b8b
--- /dev/null
+++ b/arch/arm/dts/en7581-evb-u-boot.dtsi
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/ {
+	/* When running as a first-stage bootloader this isn't filled in automatically */
+	memory@80000000 {
+		device_type = "memory";
+		reg = <0x0 0x80000000 0x0 0x20000000>;
+	};
+};
+
+#include "an7581-u-boot.dtsi"
diff --git a/arch/arm/mach-airoha/Kconfig b/arch/arm/mach-airoha/Kconfig
new file mode 100644
index 0000000..be3562a
--- /dev/null
+++ b/arch/arm/mach-airoha/Kconfig
@@ -0,0 +1,32 @@
+if ARCH_AIROHA
+
+config SYS_VENDOR
+	default "airoha"
+
+choice
+	prompt "Airoha board select"
+
+config TARGET_AN7581
+	bool "Airoha AN7581 SoC"
+	select ARM64
+	help
+	  The Airoha EN7581 is a ARM-based SoC with a quad-core Cortex-A7
+	  including NEON and GPU, Mali-450 graphics, several DDR3 options,
+	  crypto engine, built-in Wi-Fi / Bluetooth combo chip, JPEG decoder,
+	  video interfaces supporting HDMI and MIPI, and video codec support.
+	  Peripherals include Gigabit Ethernet, switch, USB3.0 and OTG, PCIe,
+	  I2S, PCM, S/PDIF, UART, SPI, I2C, IR TX/RX, and PWM.
+
+endchoice
+
+config SYS_SOC
+	default "an7581" if TARGET_AN7581
+
+config SYS_BOARD
+	default "an7581" if TARGET_AN7581
+
+config SYS_CONFIG_NAME
+	default "an7581" if TARGET_AN7581
+
+endif
+
diff --git a/arch/arm/mach-airoha/Makefile b/arch/arm/mach-airoha/Makefile
new file mode 100644
index 0000000..215a300
--- /dev/null
+++ b/arch/arm/mach-airoha/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier:	GPL-2.0
+
+obj-y	+= cpu.o
+
+obj-$(CONFIG_TARGET_AN7581) += an7581/
diff --git a/arch/arm/mach-airoha/an7581/Makefile b/arch/arm/mach-airoha/an7581/Makefile
new file mode 100644
index 0000000..886ab7e
--- /dev/null
+++ b/arch/arm/mach-airoha/an7581/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier:	GPL-2.0
+
+obj-y += init.o
diff --git a/arch/arm/mach-airoha/an7581/init.c b/arch/arm/mach-airoha/an7581/init.c
new file mode 100644
index 0000000..cefe9c6
--- /dev/null
+++ b/arch/arm/mach-airoha/an7581/init.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <fdtdec.h>
+#include <init.h>
+#include <asm/armv8/mmu.h>
+#include <asm/system.h>
+
+int print_cpuinfo(void)
+{
+	printf("CPU:   Airoha AN7581\n");
+	return 0;
+}
+
+int dram_init(void)
+{
+	return fdtdec_setup_mem_size_base();
+}
+
+int dram_init_banksize(void)
+{
+	return fdtdec_setup_memory_banksize();
+}
+
+void reset_cpu(ulong addr)
+{
+	psci_system_reset();
+}
+
+static struct mm_region an7581_mem_map[] = {
+	{
+		/* DDR */
+		.virt = 0x80000000UL,
+		.phys = 0x80000000UL,
+		.size = 0x80000000UL,
+		.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_OUTER_SHARE,
+	}, {
+		.virt = 0x00000000UL,
+		.phys = 0x00000000UL,
+		.size = 0x20000000UL,
+		.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
+			 PTE_BLOCK_NON_SHARE |
+			 PTE_BLOCK_PXN | PTE_BLOCK_UXN
+	}, {
+		0,
+	}
+};
+struct mm_region *mem_map = an7581_mem_map;
diff --git a/arch/arm/mach-airoha/cpu.c b/arch/arm/mach-airoha/cpu.c
new file mode 100644
index 0000000..a578e96
--- /dev/null
+++ b/arch/arm/mach-airoha/cpu.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <cpu_func.h>
+#include <dm.h>
+#include <init.h>
+#include <wdt.h>
+#include <dm/uclass-internal.h>
+
+int arch_cpu_init(void)
+{
+	icache_enable();
+
+	return 0;
+}
+
+void enable_caches(void)
+{
+	/* Enable D-cache. I-cache is already enabled in start.S */
+	dcache_enable();
+}
diff --git a/board/airoha/an7581/MAINTAINERS b/board/airoha/an7581/MAINTAINERS
new file mode 100644
index 0000000..28ec2fb
--- /dev/null
+++ b/board/airoha/an7581/MAINTAINERS
@@ -0,0 +1,5 @@
+AN7581
+M:	Christian Marangi <ansuelsmth@gmail.com>
+S:	Maintained
+N:      airoha
+N:      an7581
diff --git a/board/airoha/an7581/Makefile b/board/airoha/an7581/Makefile
new file mode 100644
index 0000000..70f8db7
--- /dev/null
+++ b/board/airoha/an7581/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier:	GPL-2.0
+
+obj-y	+= an7581_rfb.o
diff --git a/board/airoha/an7581/an7581_rfb.c b/board/airoha/an7581/an7581_rfb.c
new file mode 100644
index 0000000..aa73679
--- /dev/null
+++ b/board/airoha/an7581/an7581_rfb.c
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Christian Marangi <ansuelsmth@gmail.com>
+ */
+
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int board_init(void)
+{
+	/* address of boot parameters */
+	gd->bd->bi_boot_params = CFG_SYS_SDRAM_BASE + 0x100;
+
+	return 0;
+}
diff --git a/configs/an7581_evb_defconfig b/configs/an7581_evb_defconfig
new file mode 100644
index 0000000..f09b5b6
--- /dev/null
+++ b/configs/an7581_evb_defconfig
@@ -0,0 +1,78 @@
+CONFIG_ARM=y
+CONFIG_ARCH_AIROHA=y
+CONFIG_TEXT_BASE=0x81E00000
+CONFIG_SYS_MALLOC_F_LEN=0x4000
+CONFIG_NR_DRAM_BANKS=1
+CONFIG_ENV_SIZE=0x4000
+CONFIG_ENV_OFFSET=0x7c000
+CONFIG_DM_GPIO=y
+CONFIG_DEFAULT_DEVICE_TREE="airoha/en7581-evb"
+CONFIG_DM_RESET=y
+CONFIG_SYS_LOAD_ADDR=0x81800000
+CONFIG_BUILD_TARGET="u-boot.bin"
+# CONFIG_EFI_LOADER is not set
+CONFIG_FIT=y
+CONFIG_FIT_VERBOSE=y
+CONFIG_BOOTDELAY=3
+CONFIG_DEFAULT_FDT_FILE="en7581-evb"
+CONFIG_SYS_PBSIZE=1049
+CONFIG_SYS_CONSOLE_IS_IN_ENV=y
+# CONFIG_DISPLAY_BOARDINFO is not set
+CONFIG_HUSH_PARSER=y
+CONFIG_SYS_PROMPT="U-Boot> "
+CONFIG_SYS_MAXARGS=8
+CONFIG_CMD_BOOTZ=y
+CONFIG_CMD_BOOTMENU=y
+# CONFIG_CMD_ELF is not set
+# CONFIG_CMD_XIMG is not set
+CONFIG_CMD_BIND=y
+CONFIG_CMD_GPIO=y
+CONFIG_CMD_MMC=y
+CONFIG_CMD_MTD=y
+CONFIG_CMD_SF_TEST=y
+CONFIG_CMD_SPI=y
+# CONFIG_CMD_SETEXPR is not set
+CONFIG_CMD_PING=y
+CONFIG_CMD_EXT4=y
+CONFIG_CMD_FAT=y
+CONFIG_CMD_FS_GENERIC=y
+CONFIG_CMD_MTDPARTS=y
+CONFIG_CMD_LOG=y
+CONFIG_OF_UPSTREAM=y
+CONFIG_ENV_OVERWRITE=y
+CONFIG_ENV_IS_IN_MMC=y
+CONFIG_SYS_RELOC_GD_ENV_ADDR=y
+CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
+CONFIG_NET_RANDOM_ETHADDR=y
+CONFIG_REGMAP=y
+CONFIG_SYSCON=y
+CONFIG_CLK=y
+CONFIG_DMA=y
+CONFIG_LED=y
+CONFIG_LED_GPIO=y
+CONFIG_MMC_HS200_SUPPORT=y
+CONFIG_MTD=y
+CONFIG_DM_MTD=y
+CONFIG_MTD_SPI_NAND=y
+CONFIG_DM_SPI_FLASH=y
+CONFIG_SPI_FLASH_EON=y
+CONFIG_SPI_FLASH_GIGADEVICE=y
+CONFIG_SPI_FLASH_ISSI=y
+CONFIG_SPI_FLASH_MACRONIX=y
+CONFIG_SPI_FLASH_SPANSION=y
+CONFIG_SPI_FLASH_STMICRO=y
+CONFIG_SPI_FLASH_WINBOND=y
+CONFIG_SPI_FLASH_MTD=y
+CONFIG_PHYLIB=y
+CONFIG_PHY=y
+CONFIG_PINCTRL=y
+CONFIG_PINCONF=y
+CONFIG_POWER_DOMAIN=y
+CONFIG_DM_REGULATOR=y
+CONFIG_DM_REGULATOR_FIXED=y
+CONFIG_RAM=y
+CONFIG_DM_SERIAL=y
+CONFIG_SYS_NS16550=y
+CONFIG_SPI=y
+CONFIG_DM_SPI=y
+CONFIG_SHA512=y
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index fe0e49f..8411205 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -14,6 +14,7 @@
 obj-$(CONFIG_$(PHASE_)CLK_STUB) += clk-stub.o
 
 obj-y += adi/
+obj-y += airoha/
 obj-y += analogbits/
 obj-y += imx/
 obj-$(CONFIG_CLK_JH7110) += starfive/
diff --git a/drivers/clk/airoha/Makefile b/drivers/clk/airoha/Makefile
new file mode 100644
index 0000000..1744c5f
--- /dev/null
+++ b/drivers/clk/airoha/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+# Core
+obj-$(CONFIG_ARCH_AIROHA) += clk-airoha.o
diff --git a/drivers/clk/airoha/clk-airoha.c b/drivers/clk/airoha/clk-airoha.c
new file mode 100644
index 0000000..1b2c4c9
--- /dev/null
+++ b/drivers/clk/airoha/clk-airoha.c
@@ -0,0 +1,454 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Based on the Linux clk-en7523.c but majorly reworked
+ * for U-Boot that doesn't require CCF subsystem.
+ *
+ * Major modification, support for set_rate, realtime
+ * get_rate and split for reset part to a different driver.
+ *
+ * Author: Lorenzo Bianconi <lorenzo@kernel.org> (original driver)
+ *	   Christian Marangi <ansuelsmth@gmail.com>
+ */
+
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dm/devres.h>
+#include <dm/device_compat.h>
+#include <dm/lists.h>
+#include <regmap.h>
+#include <syscon.h>
+
+#include <dt-bindings/clock/en7523-clk.h>
+
+#define REG_GSW_CLK_DIV_SEL		0x1b4
+#define REG_EMI_CLK_DIV_SEL		0x1b8
+#define REG_BUS_CLK_DIV_SEL		0x1bc
+#define REG_SPI_CLK_DIV_SEL		0x1c4
+#define REG_SPI_CLK_FREQ_SEL		0x1c8
+#define REG_NPU_CLK_DIV_SEL		0x1fc
+
+#define REG_NP_SCU_PCIC			0x88
+#define REG_NP_SCU_SSTR			0x9c
+#define REG_PCIE_XSI0_SEL_MASK		GENMASK(14, 13)
+#define REG_PCIE_XSI1_SEL_MASK		GENMASK(12, 11)
+#define REG_CRYPTO_CLKSRC2		0x20c
+
+#define EN7581_MAX_CLKS			9
+
+struct airoha_clk_desc {
+	int id;
+	const char *name;
+	u32 base_reg;
+	u8 base_bits;
+	u8 base_shift;
+	union {
+		const unsigned int *base_values;
+		unsigned int base_value;
+	};
+	size_t n_base_values;
+
+	u16 div_reg;
+	u8 div_bits;
+	u8 div_shift;
+	u16 div_val0;
+	u8 div_step;
+	u8 div_offset;
+};
+
+struct airoha_clk_priv {
+	struct regmap *chip_scu_map;
+	struct airoha_clk_soc_data *data;
+};
+
+struct airoha_clk_soc_data {
+	u32 num_clocks;
+	const struct airoha_clk_desc *descs;
+};
+
+static const u32 gsw_base[] = { 400000000, 500000000 };
+static const u32 slic_base[] = { 100000000, 3125000 };
+
+static const u32 emi7581_base[] = { 540000000, 480000000, 400000000, 300000000 };
+static const u32 bus7581_base[] = { 600000000, 540000000 };
+static const u32 npu7581_base[] = { 800000000, 750000000, 720000000, 600000000 };
+static const u32 crypto_base[] = { 540000000, 480000000 };
+static const u32 emmc7581_base[] = { 200000000, 150000000 };
+
+static const struct airoha_clk_desc en7581_base_clks[EN7581_MAX_CLKS] = {
+	[EN7523_CLK_GSW] = {
+		.id = EN7523_CLK_GSW,
+		.name = "gsw",
+
+		.base_reg = REG_GSW_CLK_DIV_SEL,
+		.base_bits = 1,
+		.base_shift = 8,
+		.base_values = gsw_base,
+		.n_base_values = ARRAY_SIZE(gsw_base),
+
+		.div_bits = 3,
+		.div_shift = 0,
+		.div_step = 1,
+		.div_offset = 1,
+	},
+	[EN7523_CLK_EMI] = {
+		.id = EN7523_CLK_EMI,
+		.name = "emi",
+
+		.base_reg = REG_EMI_CLK_DIV_SEL,
+		.base_bits = 2,
+		.base_shift = 8,
+		.base_values = emi7581_base,
+		.n_base_values = ARRAY_SIZE(emi7581_base),
+
+		.div_bits = 3,
+		.div_shift = 0,
+		.div_step = 1,
+		.div_offset = 1,
+	},
+	[EN7523_CLK_BUS] = {
+		.id = EN7523_CLK_BUS,
+		.name = "bus",
+
+		.base_reg = REG_BUS_CLK_DIV_SEL,
+		.base_bits = 1,
+		.base_shift = 8,
+		.base_values = bus7581_base,
+		.n_base_values = ARRAY_SIZE(bus7581_base),
+
+		.div_bits = 3,
+		.div_shift = 0,
+		.div_step = 1,
+		.div_offset = 1,
+	},
+	[EN7523_CLK_SLIC] = {
+		.id = EN7523_CLK_SLIC,
+		.name = "slic",
+
+		.base_reg = REG_SPI_CLK_FREQ_SEL,
+		.base_bits = 1,
+		.base_shift = 0,
+		.base_values = slic_base,
+		.n_base_values = ARRAY_SIZE(slic_base),
+
+		.div_reg = REG_SPI_CLK_DIV_SEL,
+		.div_bits = 5,
+		.div_shift = 24,
+		.div_val0 = 20,
+		.div_step = 2,
+	},
+	[EN7523_CLK_SPI] = {
+		.id = EN7523_CLK_SPI,
+		.name = "spi",
+
+		.base_reg = REG_SPI_CLK_DIV_SEL,
+
+		.base_value = 400000000,
+
+		.div_bits = 5,
+		.div_shift = 8,
+		.div_val0 = 40,
+		.div_step = 2,
+	},
+	[EN7523_CLK_NPU] = {
+		.id = EN7523_CLK_NPU,
+		.name = "npu",
+
+		.base_reg = REG_NPU_CLK_DIV_SEL,
+		.base_bits = 2,
+		.base_shift = 8,
+		.base_values = npu7581_base,
+		.n_base_values = ARRAY_SIZE(npu7581_base),
+
+		.div_bits = 3,
+		.div_shift = 0,
+		.div_step = 1,
+		.div_offset = 1,
+	},
+	[EN7523_CLK_CRYPTO] = {
+		.id = EN7523_CLK_CRYPTO,
+		.name = "crypto",
+
+		.base_reg = REG_CRYPTO_CLKSRC2,
+		.base_bits = 1,
+		.base_shift = 0,
+		.base_values = crypto_base,
+		.n_base_values = ARRAY_SIZE(crypto_base),
+	},
+	[EN7581_CLK_EMMC] = {
+		.id = EN7581_CLK_EMMC,
+		.name = "emmc",
+
+		.base_reg = REG_CRYPTO_CLKSRC2,
+		.base_bits = 1,
+		.base_shift = 12,
+		.base_values = emmc7581_base,
+		.n_base_values = ARRAY_SIZE(emmc7581_base),
+	}
+};
+
+static u32 airoha_clk_get_base_rate(const struct airoha_clk_desc *desc, u32 val)
+{
+	if (!desc->base_bits)
+		return desc->base_value;
+
+	val >>= desc->base_shift;
+	val &= (1 << desc->base_bits) - 1;
+
+	if (val >= desc->n_base_values)
+		return 0;
+
+	return desc->base_values[val];
+}
+
+static u32 airoha_clk_get_div(const struct airoha_clk_desc *desc, u32 val)
+{
+	if (!desc->div_bits)
+		return 1;
+
+	val >>= desc->div_shift;
+	val &= (1 << desc->div_bits) - 1;
+
+	if (!val && desc->div_val0)
+		return desc->div_val0;
+
+	return (val + desc->div_offset) * desc->div_step;
+}
+
+static int airoha_clk_enable(struct clk *clk)
+{
+	struct airoha_clk_priv *priv = dev_get_priv(clk->dev);
+	struct airoha_clk_soc_data *data = priv->data;
+	int id = clk->id;
+
+	if (id > data->num_clocks)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int airoha_clk_disable(struct clk *clk)
+{
+	return 0;
+}
+
+static ulong airoha_clk_get_rate(struct clk *clk)
+{
+	struct airoha_clk_priv *priv = dev_get_priv(clk->dev);
+	struct airoha_clk_soc_data *data = priv->data;
+	const struct airoha_clk_desc *desc;
+	struct regmap *map = priv->chip_scu_map;
+	int id = clk->id;
+	u32 reg, val;
+	ulong rate;
+	int ret;
+
+	if (id > data->num_clocks) {
+		dev_err(clk->dev, "Invalid clk ID %d\n", id);
+		return 0;
+	}
+
+	desc = &data->descs[id];
+
+	ret = regmap_read(map, desc->base_reg, &val);
+	if (ret) {
+		dev_err(clk->dev, "Failed to read reg for clock %s\n",
+			desc->name);
+		return 0;
+	}
+
+	rate = airoha_clk_get_base_rate(desc, val);
+
+	reg = desc->div_reg ? desc->div_reg : desc->base_reg;
+	ret = regmap_read(map, reg, &val);
+	if (ret) {
+		dev_err(clk->dev, "Failed to read reg for clock %s\n",
+			desc->name);
+		return 0;
+	}
+
+	rate /= airoha_clk_get_div(desc, val);
+
+	return rate;
+}
+
+static int airoha_clk_search_rate(const struct airoha_clk_desc *desc, int div,
+				  ulong rate)
+{
+	int i;
+
+	/* Single base rate */
+	if (!desc->base_bits) {
+		if (rate != desc->base_value / div)
+			goto err;
+
+		return 0;
+	}
+
+	/* Check every base rate with provided divisor */
+	for (i = 0; i < desc->n_base_values; i++)
+		if (rate == desc->base_values[i] / div)
+			return i;
+
+err:
+	return -EINVAL;
+}
+
+static ulong airoha_clk_set_rate(struct clk *clk, ulong rate)
+{
+	struct airoha_clk_priv *priv = dev_get_priv(clk->dev);
+	struct airoha_clk_soc_data *data = priv->data;
+	const struct airoha_clk_desc *desc;
+	struct regmap *map = priv->chip_scu_map;
+	int div_val, base_val;
+	u32 reg, val, mask;
+	int id = clk->id;
+	int div;
+	int ret;
+
+	if (id > data->num_clocks) {
+		dev_err(clk->dev, "Invalid clk ID %d\n", id);
+		return 0;
+	}
+
+	desc = &data->descs[id];
+
+	if (!desc->base_bits && !desc->div_bits) {
+		dev_err(clk->dev, "Can't set rate for fixed clock %s\n",
+			desc->name);
+		return 0;
+	}
+
+	if (!desc->div_bits) {
+		/* Divisor not supported, just search in base rate */
+		div_val = 0;
+		base_val = airoha_clk_search_rate(desc, 1, rate);
+		if (base_val < 0) {
+			dev_err(clk->dev, "Invalid rate for clock %s\n",
+				desc->name);
+			return 0;
+		}
+	} else {
+		div_val = 0;
+
+		/* Check if div0 satisfy the request */
+		if (desc->div_val0) {
+			base_val = airoha_clk_search_rate(desc, desc->div_val0,
+							  rate);
+			if (base_val >= 0) {
+				div_val = 0;
+				goto apply;
+			}
+
+			/* Skip checking first divisor val */
+			div_val = 1;
+		}
+
+		/* Simulate rate with every divisor supported */
+		for (div_val = div_val + desc->div_offset;
+		     div_val < BIT(desc->div_bits) - 1; div_val++) {
+			div = div_val * desc->div_step;
+
+			base_val = airoha_clk_search_rate(desc, div, rate);
+			if (base_val >= 0)
+				break;
+		}
+
+		if (div_val == BIT(desc->div_bits) - 1) {
+			dev_err(clk->dev, "Invalid rate for clock %s\n",
+				desc->name);
+			return 0;
+		}
+	}
+
+apply:
+	if (desc->div_bits) {
+		reg = desc->div_reg ? desc->div_reg : desc->base_reg;
+
+		mask = (BIT(desc->div_bits) - 1) << desc->div_shift;
+		val = div_val << desc->div_shift;
+
+		ret = regmap_update_bits(map, reg, mask, val);
+		if (ret) {
+			dev_err(clk->dev, "Failed to update div reg for clock %s\n",
+				desc->name);
+			return 0;
+		}
+	}
+
+	if (desc->base_bits) {
+		mask = (BIT(desc->base_bits) - 1) << desc->base_shift;
+		val = base_val << desc->base_shift;
+
+		ret = regmap_update_bits(map, desc->base_reg, mask, val);
+		if (ret) {
+			dev_err(clk->dev, "Failed to update reg for clock %s\n",
+				desc->name);
+			return 0;
+		}
+	}
+
+	return rate;
+}
+
+const struct clk_ops airoha_clk_ops = {
+	.enable = airoha_clk_enable,
+	.disable = airoha_clk_disable,
+	.get_rate = airoha_clk_get_rate,
+	.set_rate = airoha_clk_set_rate,
+};
+
+static int airoha_clk_probe(struct udevice *dev)
+{
+	struct airoha_clk_priv *priv = dev_get_priv(dev);
+	ofnode chip_scu_node;
+
+	chip_scu_node = ofnode_by_compatible(ofnode_null(),
+					     "airoha,en7581-chip-scu");
+	if (!ofnode_valid(chip_scu_node))
+		return -EINVAL;
+
+	priv->chip_scu_map = syscon_node_to_regmap(chip_scu_node);
+	if (IS_ERR(priv->chip_scu_map))
+		return PTR_ERR(priv->chip_scu_map);
+
+	priv->data = (void *)dev_get_driver_data(dev);
+
+	return 0;
+}
+
+static int airoha_clk_bind(struct udevice *dev)
+{
+	struct udevice *rst_dev;
+	int ret = 0;
+
+	if (CONFIG_IS_ENABLED(RESET_AIROHA)) {
+		ret = device_bind_driver_to_node(dev, "airoha-reset", "reset",
+						 dev_ofnode(dev), &rst_dev);
+		if (ret)
+			debug("Warning: failed to bind reset controller\n");
+	}
+
+	return ret;
+}
+
+static const struct airoha_clk_soc_data en7581_data = {
+	.num_clocks = ARRAY_SIZE(en7581_base_clks),
+	.descs = en7581_base_clks,
+};
+
+static const struct udevice_id airoha_clk_ids[] = {
+	{ .compatible = "airoha,en7581-scu",
+	  .data = (ulong)&en7581_data,
+	},
+	{ }
+};
+
+U_BOOT_DRIVER(airoha_clk) = {
+	.name = "clk-airoha",
+	.id = UCLASS_CLK,
+	.of_match = airoha_clk_ids,
+	.probe = airoha_clk_probe,
+	.bind = airoha_clk_bind,
+	.priv_auto = sizeof(struct airoha_clk_priv),
+	.ops = &airoha_clk_ops,
+};
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index b408f19..a0d079c 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -49,6 +49,13 @@
 	  Enable support for manipulating Tegra's on-SoC reset signals via IPC
 	  requests to the BPMP (Boot and Power Management Processor).
 
+config RESET_AIROHA
+	bool "Reset controller driver for Airoha SoCs"
+	depends on DM_RESET && ARCH_AIROHA
+	default y
+	help
+	  Support for reset controller on Airoha SoCs.
+
 config RESET_TI_SCI
 	bool "TI System Control Interface (TI SCI) reset driver"
 	depends on DM_RESET && TI_SCI_PROTOCOL
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 8cab734..b949439 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -10,6 +10,7 @@
 obj-$(CONFIG_STM32_RESET) += stm32-reset.o
 obj-$(CONFIG_TEGRA_CAR_RESET) += tegra-car-reset.o
 obj-$(CONFIG_TEGRA186_RESET) += tegra186-reset.o
+obj-$(CONFIG_RESET_AIROHA) += reset-airoha.o
 obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o
 obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o
 obj-$(CONFIG_RESET_BCM6345) += reset-bcm6345.o
diff --git a/drivers/reset/reset-airoha.c b/drivers/reset/reset-airoha.c
new file mode 100644
index 0000000..e878af6
--- /dev/null
+++ b/drivers/reset/reset-airoha.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Based on Linux drivers/clk/clk-en7523.c reworked
+ * and detached to a dedicated driver
+ *
+ * Author: Lorenzo Bianconi <lorenzo@kernel.org> (original driver)
+ *	   Christian Marangi <ansuelsmth@gmail.com>
+ */
+
+#include <dm.h>
+#include <linux/io.h>
+#include <reset-uclass.h>
+
+#include <dt-bindings/reset/airoha,en7581-reset.h>
+
+#define RST_NR_PER_BANK			32
+
+#define REG_RESET_CONTROL2		0x830
+#define REG_RESET_CONTROL1		0x834
+
+struct airoha_reset_priv {
+	const u16 *bank_ofs;
+	const u16 *idx_map;
+	void __iomem *base;
+};
+
+static const u16 en7581_rst_ofs[] = {
+	REG_RESET_CONTROL2,
+	REG_RESET_CONTROL1,
+};
+
+static const u16 en7581_rst_map[] = {
+	/* RST_CTRL2 */
+	[EN7581_XPON_PHY_RST]		= 0,
+	[EN7581_CPU_TIMER2_RST]		= 2,
+	[EN7581_HSUART_RST]		= 3,
+	[EN7581_UART4_RST]		= 4,
+	[EN7581_UART5_RST]		= 5,
+	[EN7581_I2C2_RST]		= 6,
+	[EN7581_XSI_MAC_RST]		= 7,
+	[EN7581_XSI_PHY_RST]		= 8,
+	[EN7581_NPU_RST]		= 9,
+	[EN7581_I2S_RST]		= 10,
+	[EN7581_TRNG_RST]		= 11,
+	[EN7581_TRNG_MSTART_RST]	= 12,
+	[EN7581_DUAL_HSI0_RST]		= 13,
+	[EN7581_DUAL_HSI1_RST]		= 14,
+	[EN7581_HSI_RST]		= 15,
+	[EN7581_DUAL_HSI0_MAC_RST]	= 16,
+	[EN7581_DUAL_HSI1_MAC_RST]	= 17,
+	[EN7581_HSI_MAC_RST]		= 18,
+	[EN7581_WDMA_RST]		= 19,
+	[EN7581_WOE0_RST]		= 20,
+	[EN7581_WOE1_RST]		= 21,
+	[EN7581_HSDMA_RST]		= 22,
+	[EN7581_TDMA_RST]		= 24,
+	[EN7581_EMMC_RST]		= 25,
+	[EN7581_SOE_RST]		= 26,
+	[EN7581_PCIE2_RST]		= 27,
+	[EN7581_XFP_MAC_RST]		= 28,
+	[EN7581_USB_HOST_P1_RST]	= 29,
+	[EN7581_USB_HOST_P1_U3_PHY_RST]	= 30,
+	/* RST_CTRL1 */
+	[EN7581_PCM1_ZSI_ISI_RST]	= RST_NR_PER_BANK + 0,
+	[EN7581_FE_PDMA_RST]		= RST_NR_PER_BANK + 1,
+	[EN7581_FE_QDMA_RST]		= RST_NR_PER_BANK + 2,
+	[EN7581_PCM_SPIWP_RST]		= RST_NR_PER_BANK + 4,
+	[EN7581_CRYPTO_RST]		= RST_NR_PER_BANK + 6,
+	[EN7581_TIMER_RST]		= RST_NR_PER_BANK + 8,
+	[EN7581_PCM1_RST]		= RST_NR_PER_BANK + 11,
+	[EN7581_UART_RST]		= RST_NR_PER_BANK + 12,
+	[EN7581_GPIO_RST]		= RST_NR_PER_BANK + 13,
+	[EN7581_GDMA_RST]		= RST_NR_PER_BANK + 14,
+	[EN7581_I2C_MASTER_RST]		= RST_NR_PER_BANK + 16,
+	[EN7581_PCM2_ZSI_ISI_RST]	= RST_NR_PER_BANK + 17,
+	[EN7581_SFC_RST]		= RST_NR_PER_BANK + 18,
+	[EN7581_UART2_RST]		= RST_NR_PER_BANK + 19,
+	[EN7581_GDMP_RST]		= RST_NR_PER_BANK + 20,
+	[EN7581_FE_RST]			= RST_NR_PER_BANK + 21,
+	[EN7581_USB_HOST_P0_RST]	= RST_NR_PER_BANK + 22,
+	[EN7581_GSW_RST]		= RST_NR_PER_BANK + 23,
+	[EN7581_SFC2_PCM_RST]		= RST_NR_PER_BANK + 25,
+	[EN7581_PCIE0_RST]		= RST_NR_PER_BANK + 26,
+	[EN7581_PCIE1_RST]		= RST_NR_PER_BANK + 27,
+	[EN7581_CPU_TIMER_RST]		= RST_NR_PER_BANK + 28,
+	[EN7581_PCIE_HB_RST]		= RST_NR_PER_BANK + 29,
+	[EN7581_XPON_MAC_RST]		= RST_NR_PER_BANK + 31,
+};
+
+static int airoha_reset_update(struct airoha_reset_priv *priv,
+			       unsigned long id, bool assert)
+{
+	void __iomem *addr = priv->base + priv->bank_ofs[id / RST_NR_PER_BANK];
+	u32 val;
+
+	val = readl(addr);
+	if (assert)
+		val |= BIT(id % RST_NR_PER_BANK);
+	else
+		val &= ~BIT(id % RST_NR_PER_BANK);
+	writel(val, addr);
+
+	return 0;
+}
+
+static int airoha_reset_assert(struct reset_ctl *reset_ctl)
+{
+	struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev);
+	int id = reset_ctl->id;
+
+	return airoha_reset_update(priv, id, true);
+}
+
+static int airoha_reset_deassert(struct reset_ctl *reset_ctl)
+{
+	struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev);
+	int id = reset_ctl->id;
+
+	return airoha_reset_update(priv, id, false);
+}
+
+static int airoha_reset_status(struct reset_ctl *reset_ctl)
+{
+	struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev);
+	int id = reset_ctl->id;
+	void __iomem *addr;
+
+	addr = priv->base + priv->bank_ofs[id / RST_NR_PER_BANK];
+
+	return !!(readl(addr) & BIT(id % RST_NR_PER_BANK));
+}
+
+static int airoha_reset_xlate(struct reset_ctl *reset_ctl,
+			      struct ofnode_phandle_args *args)
+{
+	struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev);
+
+	if (args->args[0] >= ARRAY_SIZE(en7581_rst_map))
+		return -EINVAL;
+
+	reset_ctl->id = priv->idx_map[args->args[0]];
+
+	return 0;
+}
+
+static struct reset_ops airoha_reset_ops = {
+	.of_xlate = airoha_reset_xlate,
+	.rst_assert = airoha_reset_assert,
+	.rst_deassert = airoha_reset_deassert,
+	.rst_status = airoha_reset_status,
+};
+
+static int airoha_reset_probe(struct udevice *dev)
+{
+	struct airoha_reset_priv *priv = dev_get_priv(dev);
+
+	priv->base = dev_remap_addr(dev);
+	if (!priv->base)
+		return -ENOMEM;
+
+	priv->bank_ofs = en7581_rst_ofs;
+	priv->idx_map = en7581_rst_map;
+
+	return 0;
+}
+
+U_BOOT_DRIVER(airoha_reset) = {
+	.name = "airoha-reset",
+	.id = UCLASS_RESET,
+	.probe = airoha_reset_probe,
+	.ops = &airoha_reset_ops,
+	.priv_auto = sizeof(struct airoha_reset_priv),
+};
diff --git a/dts/upstream/include/dt-bindings/clock/en7523-clk.h b/dts/upstream/include/dt-bindings/clock/en7523-clk.h
index 717d23a..edfa640 100644
--- a/dts/upstream/include/dt-bindings/clock/en7523-clk.h
+++ b/dts/upstream/include/dt-bindings/clock/en7523-clk.h
@@ -12,6 +12,6 @@
 #define EN7523_CLK_CRYPTO	6
 #define EN7523_CLK_PCIE		7
 
-#define EN7523_NUM_CLOCKS	8
+#define EN7581_CLK_EMMC		8
 
 #endif /* _DT_BINDINGS_CLOCK_AIROHA_EN7523_H_ */
diff --git a/dts/upstream/src/arm64/airoha/en7581.dtsi b/dts/upstream/src/arm64/airoha/en7581.dtsi
index 55eb176..f584409 100644
--- a/dts/upstream/src/arm64/airoha/en7581.dtsi
+++ b/dts/upstream/src/arm64/airoha/en7581.dtsi
@@ -2,6 +2,7 @@
 
 #include <dt-bindings/interrupt-controller/irq.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/clock/en7523-clk.h>
 
 / {
 	interrupt-parent = <&gic>;
@@ -142,6 +143,13 @@
 			interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_LOW>;
 		};
 
+		scuclk: clock-controller@1fa20000 {
+			compatible = "airoha,en7581-scu";
+			reg = <0x0 0x1fb00000 0x0 0x970>;
+			#clock-cells = <1>;
+			#reset-cells = <1>;
+		};
+
 		uart1: serial@1fbf0000 {
 			compatible = "ns16550";
 			reg = <0x0 0x1fbf0000 0x0 0x30>;
diff --git a/include/configs/an7581.h b/include/configs/an7581.h
new file mode 100644
index 0000000..64f04c9
--- /dev/null
+++ b/include/configs/an7581.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Configuration for Airoha AN7581
+ */
+
+#ifndef __AN7581_H
+#define __AN7581_H
+
+#include <linux/sizes.h>
+
+#define CFG_SYS_UBOOT_BASE		CONFIG_TEXT_BASE
+
+#define CFG_SYS_INIT_RAM_ADDR           CONFIG_TEXT_BASE
+#define CFG_SYS_INIT_RAM_SIZE           SZ_2M
+
+/* DRAM */
+#define CFG_SYS_SDRAM_BASE		0x80000000
+
+#endif