summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/Kconfig5
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/airoha/clk-airoha.c6
-rw-r--r--drivers/clk/spacemit/Kconfig23
-rw-r--r--drivers/clk/spacemit/Makefile7
-rw-r--r--drivers/clk/spacemit/clk-k1.c1878
-rw-r--r--drivers/clk/spacemit/clk_common.h79
-rw-r--r--drivers/clk/spacemit/clk_ddn.c93
-rw-r--r--drivers/clk/spacemit/clk_ddn.h53
-rw-r--r--drivers/clk/spacemit/clk_mix.c403
-rw-r--r--drivers/clk/spacemit/clk_mix.h224
-rw-r--r--drivers/clk/spacemit/clk_pll.c157
-rw-r--r--drivers/clk/spacemit/clk_pll.h81
-rw-r--r--drivers/clk/ti/clk-ctrl.c53
-rw-r--r--drivers/core/device.c2
-rw-r--r--drivers/core/of_access.c2
-rw-r--r--drivers/core/ofnode.c4
-rw-r--r--drivers/core/tag.c4
-rw-r--r--drivers/gpio/Kconfig8
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/spacemit_gpio.c253
-rw-r--r--drivers/i2c/Kconfig7
-rw-r--r--drivers/i2c/Makefile1
-rw-r--r--drivers/i2c/k1_i2c.c523
-rw-r--r--drivers/i2c/k1_i2c.h69
-rw-r--r--drivers/misc/qfw_acpi.c1
-rw-r--r--drivers/mmc/dw_mmc.c6
-rw-r--r--drivers/mmc/sdhci.c2
-rw-r--r--drivers/mtd/spi/Makefile5
-rw-r--r--drivers/mtd/spi/sf_probe.c8
-rw-r--r--drivers/pinctrl/Kconfig21
-rw-r--r--drivers/pinctrl/Makefile1
-rw-r--r--drivers/pinctrl/aspeed/Makefile2
-rw-r--r--drivers/pinctrl/aspeed/pinctrl_ast2700_soc0.c623
-rw-r--r--drivers/pinctrl/aspeed/pinctrl_ast2700_soc1.c1642
-rw-r--r--drivers/pinctrl/spacemit/Kconfig9
-rw-r--r--drivers/pinctrl/spacemit/Makefile2
-rw-r--r--drivers/pinctrl/spacemit/pinctrl-k1.c550
-rw-r--r--drivers/power/pmic/Kconfig8
-rw-r--r--drivers/power/pmic/Makefile1
-rw-r--r--drivers/power/pmic/pmic_spacemit_p1.c94
-rw-r--r--drivers/power/regulator/Kconfig50
-rw-r--r--drivers/power/regulator/Makefile5
-rw-r--r--drivers/power/regulator/mpq8785.c494
-rw-r--r--drivers/power/regulator/pmbus_generic.c90
-rw-r--r--drivers/power/regulator/pmbus_helper.c315
-rw-r--r--drivers/power/regulator/pmbus_helper.h90
-rw-r--r--drivers/power/regulator/regulator-uclass.c27
-rw-r--r--drivers/power/regulator/sandbox.c11
-rw-r--r--drivers/power/regulator/sandbox_pmbus.c171
-rw-r--r--drivers/power/regulator/spacemit_p1_regulator.c464
-rw-r--r--drivers/reset/Kconfig7
-rw-r--r--drivers/reset/Makefile2
-rw-r--r--drivers/reset/reset-spacemit-k1.c548
-rw-r--r--drivers/reset/spacemit/Makefile5
-rw-r--r--drivers/reset/spacemit/reset-spacemit-k1.c289
-rw-r--r--drivers/serial/serial_lpuart.c6
-rw-r--r--drivers/spi/designware_spi.c12
-rw-r--r--drivers/spi/fsl_qspi.c9
-rw-r--r--drivers/thermal/Kconfig8
-rw-r--r--drivers/thermal/Makefile1
-rw-r--r--drivers/thermal/pmbus_thermal.c33
62 files changed, 8957 insertions, 592 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index e69b0543817..145e4cfa2de 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -277,11 +277,12 @@ source "drivers/clk/mvebu/Kconfig"
source "drivers/clk/owl/Kconfig"
source "drivers/clk/qcom/Kconfig"
source "drivers/clk/renesas/Kconfig"
-source "drivers/clk/sophgo/Kconfig"
-source "drivers/clk/sunxi/Kconfig"
source "drivers/clk/sifive/Kconfig"
+source "drivers/clk/sophgo/Kconfig"
+source "drivers/clk/spacemit/Kconfig"
source "drivers/clk/starfive/Kconfig"
source "drivers/clk/stm32/Kconfig"
+source "drivers/clk/sunxi/Kconfig"
source "drivers/clk/tegra/Kconfig"
source "drivers/clk/ti/Kconfig"
source "drivers/clk/thead/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index c37ef75d420..2bfa789e48a 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_CLK_RENESAS) += renesas/
obj-$(CONFIG_$(PHASE_)CLK_SCMI) += clk_scmi.o
obj-$(CONFIG_CLK_SIFIVE) += sifive/
obj-$(CONFIG_CLK_SOPHGO) += sophgo/
+obj-$(CONFIG_CLK_SPACEMIT) += spacemit/
obj-$(CONFIG_CLK_SUNXI) += sunxi/
obj-$(CONFIG_CLK_UNIPHIER) += uniphier/
obj-$(CONFIG_CLK_VERSACLOCK) += clk_versaclock.o
diff --git a/drivers/clk/airoha/clk-airoha.c b/drivers/clk/airoha/clk-airoha.c
index 49dbca82135..89a8b4cb1cf 100644
--- a/drivers/clk/airoha/clk-airoha.c
+++ b/drivers/clk/airoha/clk-airoha.c
@@ -327,7 +327,7 @@ static int airoha_clk_enable(struct clk *clk)
struct airoha_clk_soc_data *data = priv->data;
int id = clk->id;
- if (id > data->num_clocks)
+ if (id >= data->num_clocks)
return -EINVAL;
return 0;
@@ -349,7 +349,7 @@ static ulong airoha_clk_get_rate(struct clk *clk)
ulong rate;
int ret;
- if (id > data->num_clocks) {
+ if (id >= data->num_clocks) {
dev_err(clk->dev, "Invalid clk ID %d\n", id);
return 0;
}
@@ -412,7 +412,7 @@ static ulong airoha_clk_set_rate(struct clk *clk, ulong rate)
int div;
int ret;
- if (id > data->num_clocks) {
+ if (id >= data->num_clocks) {
dev_err(clk->dev, "Invalid clk ID %d\n", id);
return 0;
}
diff --git a/drivers/clk/spacemit/Kconfig b/drivers/clk/spacemit/Kconfig
new file mode 100644
index 00000000000..25a3c1e4a45
--- /dev/null
+++ b/drivers/clk/spacemit/Kconfig
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (c) 2025, Junhui Liu <[email protected]>
+
+config CLK_SPACEMIT
+ bool "Clock support for SpacemiT SoCs"
+ depends on CLK || COMPILE_TEST
+ select REGMAP
+ select LIB_RATIONAL
+ help
+ This enables support clock driver for Spacemit SoC
+ family.
+
+if CLK_SPACEMIT
+
+config CLK_SPACEMIT_K1
+ bool "SpacemiT K1 clock support"
+ select CLK_CCF
+ help
+ This enables support clock driver for Spacemit K1 SoC.
+ It's based on Common Clock Framework.
+
+endif
diff --git a/drivers/clk/spacemit/Makefile b/drivers/clk/spacemit/Makefile
new file mode 100644
index 00000000000..824e94d1f74
--- /dev/null
+++ b/drivers/clk/spacemit/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2025 Junhui Liu <[email protected]>
+
+obj-$(CONFIG_CLK_SPACEMIT) += clk_ddn.o clk_mix.o clk_pll.o
+
+obj-$(CONFIG_CLK_SPACEMIT_K1) += clk-k1.o
diff --git a/drivers/clk/spacemit/clk-k1.c b/drivers/clk/spacemit/clk-k1.c
new file mode 100644
index 00000000000..07adc126ee3
--- /dev/null
+++ b/drivers/clk/spacemit/clk-k1.c
@@ -0,0 +1,1878 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024 SpacemiT Technology Co. Ltd
+ * Copyright (c) 2024-2025 Haylen Chu <[email protected]>
+ * Copyright (c) 2025 Junhui Liu <[email protected]>
+ * Copyright (c) 2025-2026 RISCstar Ltd.
+ *
+ * Authors: Haylen Chu <[email protected]>
+ */
+
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/lists.h>
+#include <regmap.h>
+#include <linux/clk-provider.h>
+#include <soc/spacemit/k1-reset.h>
+#include <soc/spacemit/k1-syscon.h>
+
+#include "clk_common.h"
+#include "clk_ddn.h"
+#include "clk_mix.h"
+#include "clk_pll.h"
+
+#include <dt-bindings/clock/spacemit,k1-syscon.h>
+
+#define K1_PLL_ID 100
+#define K1_MPMU_ID 200
+#define K1_APBC_ID 300
+#define K1_APMU_ID 400
+
+struct spacemit_ccu_data {
+ struct clk **clks;
+ size_t num;
+ unsigned long offset;
+};
+
+/* APBS clocks start, APBS region contains and only contains all PLL clocks */
+
+/*
+ * PLL{1,2} must run at fixed frequencies to provide clocks in correct rates for
+ * peripherals.
+ */
+static const struct ccu_pll_rate_tbl pll1_rate_tbl[] = {
+ CCU_PLL_RATE(2457600000UL, 0x0050dd64, 0x330ccccd),
+};
+
+#if IS_ENABLED(CONFIG_SPL_BUILD)
+CCU_PLL_DEFINE(CLK_PLL1, pll1, pll1, "clock-24m", pll1_rate_tbl,
+ APBS_PLL1_SWCR1, APBS_PLL1_SWCR3, MPMU_POSR, POSR_PLL1_LOCK,
+ CLK_SET_RATE_GATE);
+
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D4, pll1_d4, pll1_d4, "pll1", APBS_PLL1_SWCR2,
+ BIT(3), 4, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D6, pll1_d6, pll1_d6, "pll1", APBS_PLL1_SWCR2,
+ BIT(5), 6, 1);
+CCU_FACTOR_GATE_FLAGS_DEFINE(CLK_PLL1_D8, pll1_d8, pll1_d8, "pll1",
+ APBS_PLL1_SWCR2, BIT(7), 8, 1, CLK_IS_CRITICAL);
+#else
+static const struct ccu_pll_rate_tbl pll2_rate_tbl[] = {
+ CCU_PLL_RATE(3000000000UL, 0x0050dd66, 0x3fe00000),
+};
+
+static const struct ccu_pll_rate_tbl pll3_rate_tbl[] = {
+ CCU_PLL_RATE(1600000000UL, 0x0050cd61, 0x43eaaaab),
+ CCU_PLL_RATE(1800000000UL, 0x0050cd61, 0x4b000000),
+ CCU_PLL_RATE(2000000000UL, 0x0050dd62, 0x2aeaaaab),
+ CCU_PLL_RATE(2457600000UL, 0x0050dd64, 0x330ccccd),
+ CCU_PLL_RATE(3000000000UL, 0x0050dd66, 0x3fe00000),
+ CCU_PLL_RATE(3200000000UL, 0x0050dd67, 0x43eaaaab),
+};
+
+CCU_PLL_DEFINE(CLK_PLL1, pll1, pll1, "clock-24m", pll1_rate_tbl,
+ APBS_PLL1_SWCR1, APBS_PLL1_SWCR3, MPMU_POSR, POSR_PLL1_LOCK,
+ CLK_SET_RATE_GATE);
+CCU_PLL_DEFINE(CLK_PLL2, pll2, pll2, "clock-24m", pll2_rate_tbl,
+ APBS_PLL2_SWCR1, APBS_PLL2_SWCR3, MPMU_POSR, POSR_PLL2_LOCK,
+ CLK_SET_RATE_GATE);
+CCU_PLL_DEFINE(CLK_PLL3, pll3, pll3, "clock-24m", pll3_rate_tbl,
+ APBS_PLL3_SWCR1, APBS_PLL3_SWCR3, MPMU_POSR, POSR_PLL3_LOCK,
+ CLK_SET_RATE_GATE);
+
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D2, pll1_d2, pll1_d2, "pll1", APBS_PLL1_SWCR2,
+ BIT(1), 2, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D3, pll1_d3, pll1_d3, "pll1", APBS_PLL1_SWCR2,
+ BIT(2), 3, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D4, pll1_d4, pll1_d4, "pll1", APBS_PLL1_SWCR2,
+ BIT(3), 4, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D5, pll1_d5, pll1_d5, "pll1", APBS_PLL1_SWCR2,
+ BIT(4), 5, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D6, pll1_d6, pll1_d6, "pll1", APBS_PLL1_SWCR2,
+ BIT(5), 6, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D7, pll1_d7, pll1_d7, "pll1", APBS_PLL1_SWCR2,
+ BIT(6), 7, 1);
+CCU_FACTOR_GATE_FLAGS_DEFINE(CLK_PLL1_D8, pll1_d8, pll1_d8, "pll1",
+ APBS_PLL1_SWCR2, BIT(7), 8, 1, CLK_IS_CRITICAL);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D11, pll1_d11_223p4, pll1_d11_223p4, "pll1",
+ APBS_PLL1_SWCR2, BIT(15), 11, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D13, pll1_d13_189, pll1_d13_189, "pll1",
+ APBS_PLL1_SWCR2, BIT(16), 13, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D23, pll1_d23_106p8, pll1_d23_106p8, "pll1",
+ APBS_PLL1_SWCR2, BIT(20), 23, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D64, pll1_d64_38p4, pll1_d64_38p4, "pll1",
+ APBS_PLL1_SWCR2, BIT(0), 64, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D10_AUD, pll1_aud_245p7, pll1_aud_245p7, "pll1",
+ APBS_PLL1_SWCR2, BIT(10), 10, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_D100_AUD, pll1_aud_24p5, pll1_aud_24p5, "pll1",
+ APBS_PLL1_SWCR2, BIT(11), 100, 1);
+
+CCU_FACTOR_GATE_DEFINE(CLK_PLL2_D1, pll2_d1, pll2_d1, "pll2", APBS_PLL2_SWCR2,
+ BIT(0), 1, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL2_D2, pll2_d2, pll2_d2, "pll2", APBS_PLL2_SWCR2,
+ BIT(1), 2, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL2_D3, pll2_d3, pll2_d3, "pll2", APBS_PLL2_SWCR2,
+ BIT(2), 3, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL2_D4, pll2_d4, pll2_d4, "pll2", APBS_PLL2_SWCR2,
+ BIT(3), 4, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL2_D5, pll2_d5, pll2_d5, "pll2", APBS_PLL2_SWCR2,
+ BIT(4), 5, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL2_D6, pll2_d6, pll2_d6, "pll2", APBS_PLL2_SWCR2,
+ BIT(5), 6, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL2_D7, pll2_d7, pll2_d7, "pll2", APBS_PLL2_SWCR2,
+ BIT(6), 7, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL2_D8, pll2_d8, pll2_d8, "pll2", APBS_PLL2_SWCR2,
+ BIT(7), 8, 1);
+
+CCU_FACTOR_GATE_DEFINE(CLK_PLL3_D1, pll3_d1, pll3_d1, "pll3", APBS_PLL3_SWCR2,
+ BIT(0), 1, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL3_D2, pll3_d2, pll3_d2, "pll3", APBS_PLL3_SWCR2,
+ BIT(1), 2, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL3_D3, pll3_d3, pll3_d3, "pll3", APBS_PLL3_SWCR2,
+ BIT(2), 3, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL3_D4, pll3_d4, pll3_d4, "pll3", APBS_PLL3_SWCR2,
+ BIT(3), 4, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL3_D5, pll3_d5, pll3_d5, "pll3", APBS_PLL3_SWCR2,
+ BIT(4), 5, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL3_D6, pll3_d6, pll3_d6, "pll3", APBS_PLL3_SWCR2,
+ BIT(5), 6, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL3_D7, pll3_d7, pll3_d7, "pll3", APBS_PLL3_SWCR2,
+ BIT(6), 7, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL3_D8, pll3_d8, pll3_d8, "pll3", APBS_PLL3_SWCR2,
+ BIT(7), 8, 1);
+
+CCU_FACTOR_DEFINE(CLK_PLL3_20, pll3_20, pll3_20, "pll3_d8", 20, 1);
+CCU_FACTOR_DEFINE(CLK_PLL3_40, pll3_40, pll3_40, "pll3_d8", 10, 1);
+CCU_FACTOR_DEFINE(CLK_PLL3_80, pll3_80, pll3_80, "pll3_d8", 5, 1);
+#endif
+/* APBS clocks end */
+
+/* MPMU clocks start */
+#if IS_ENABLED(CONFIG_SPL_BUILD)
+CCU_GATE_DEFINE(CLK_PLL1_614, pll1_d4_614p4, pll1_d4_614p4, "pll1_d4",
+ MPMU_ACGR, BIT(15), 0);
+CCU_GATE_DEFINE(CLK_PLL1_409P6, pll1_d6_409p6, pll1_d6_409p6, "pll1_d6",
+ MPMU_ACGR, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PLL1_307P2, pll1_d8_307p2, pll1_d8_307p2, "pll1_d8",
+ MPMU_ACGR, BIT(13), 0);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_31P5, pll1_d78_31p5, pll1_d78_31p5,
+ "pll1_d4", MPMU_ACGR, BIT(6), 39, 2);
+CCU_DDN_DEFINE(CLK_SLOW_UART2, slow_uart2_48, slow_uart2_48,
+ "pll1_d4_614p4", MPMU_SUCCR_1,
+ CCU_DDN_MASK(16, 13), 16, CCU_DDN_MASK(0, 13), 0, 2, 0);
+#else
+CCU_GATE_DEFINE(CLK_PLL1_307P2, pll1_d8_307p2, pll1_d8_307p2, "pll1_d8",
+ MPMU_ACGR, BIT(13), 0);
+
+CCU_FACTOR_DEFINE(CLK_PLL1_76P8, pll1_d32_76p8, pll1_d32_76p8, "pll1_d8_307p2",
+ 4, 1);
+
+CCU_FACTOR_DEFINE(CLK_PLL1_61P44, pll1_d40_61p44, pll1_d40_61p44,
+ "pll1_d8_307p2", 5, 1);
+
+CCU_FACTOR_DEFINE(CLK_PLL1_153P6, pll1_d16_153p6, pll1_d16_153p6,
+ "pll1_d8", 2, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_102P4, pll1_d24_102p4, pll1_d24_102p4,
+ "pll1_d8", MPMU_ACGR, BIT(12), 3, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_51P2, pll1_d48_51p2, pll1_d48_51p2,
+ "pll1_d8", MPMU_ACGR, BIT(7), 6, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_51P2_AP, pll1_d48_51p2_ap, pll1_d48_51p2_ap,
+ "pll1_d8", MPMU_ACGR, BIT(11), 6, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_57P6, pll1_m3d128_57p6, pll1_m3d128_57p6,
+ "pll1_d8", MPMU_ACGR, BIT(8), 16, 3);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_25P6, pll1_d96_25p6, pll1_d96_25p6,
+ "pll1_d8", MPMU_ACGR, BIT(4), 12, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_12P8, pll1_d192_12p8, pll1_d192_12p8,
+ "pll1_d8", MPMU_ACGR, BIT(3), 24, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_12P8_WDT, pll1_d192_12p8_wdt, pll1_d192_12p8_wdt,
+ "pll1_d8", MPMU_ACGR, BIT(19), 24, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_6P4, pll1_d384_6p4, pll1_d384_6p4,
+ "pll1_d8", MPMU_ACGR, BIT(2), 48, 1);
+
+CCU_FACTOR_DEFINE(CLK_PLL1_3P2, pll1_d768_3p2, pll1_d768_3p2,
+ "pll1_d384_6p4", 2, 1);
+CCU_FACTOR_DEFINE(CLK_PLL1_1P6, pll1_d1536_1p6, pll1_d1536_1p6,
+ "pll1_d384_6p4", 4, 1);
+CCU_FACTOR_DEFINE(CLK_PLL1_0P8, pll1_d3072_0p8, pll1_d3072_0p8,
+ "pll1_d384_6p4", 8, 1);
+
+CCU_GATE_DEFINE(CLK_PLL1_409P6, pll1_d6_409p6, pll1_d6_409p6, "pll1_d6",
+ MPMU_ACGR, BIT(0), 0);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_204P8, pll1_d12_204p8, pll1_d12_204p8,
+ "pll1_d6", MPMU_ACGR, BIT(5), 2, 1);
+
+CCU_GATE_DEFINE(CLK_PLL1_491, pll1_d5_491p52, pll1_d5_491p52, "pll1_d5",
+ MPMU_ACGR, BIT(21), 0);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_245P76, pll1_d10_245p76, pll1_d10_245p76,
+ "pll1_d5", MPMU_ACGR, BIT(18), 2, 1);
+
+CCU_GATE_DEFINE(CLK_PLL1_614, pll1_d4_614p4, pll1_d4_614p4, "pll1_d4",
+ MPMU_ACGR, BIT(15), 0);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_47P26, pll1_d52_47p26, pll1_d52_47p26,
+ "pll1_d4", MPMU_ACGR, BIT(10), 13, 1);
+CCU_FACTOR_GATE_DEFINE(CLK_PLL1_31P5, pll1_d78_31p5, pll1_d78_31p5,
+ "pll1_d4", MPMU_ACGR, BIT(6), 39, 2);
+
+CCU_GATE_DEFINE(CLK_PLL1_819, pll1_d3_819p2, pll1_d3_819p2, "pll1_d3",
+ MPMU_ACGR, BIT(14), 0);
+
+CCU_GATE_DEFINE(CLK_PLL1_1228, pll1_d2_1228p8, pll1_d2_1228p8, "pll1_d2",
+ MPMU_ACGR, BIT(16), 0);
+
+CCU_GATE_DEFINE(CLK_SLOW_UART, slow_uart, slow_uart, "clock-32k", MPMU_ACGR,
+ BIT(1), CLK_IGNORE_UNUSED);
+CCU_DDN_DEFINE(CLK_SLOW_UART1, slow_uart1_14p74, slow_uart1_14p74,
+ "pll1_d16_153p6", MPMU_SUCCR,
+ CCU_DDN_MASK(16, 13), 16, CCU_DDN_MASK(0, 13), 0, 2, 0);
+CCU_DDN_DEFINE(CLK_SLOW_UART2, slow_uart2_48, slow_uart2_48,
+ "pll1_d4_614p4", MPMU_SUCCR_1,
+ CCU_DDN_MASK(16, 13), 16, CCU_DDN_MASK(0, 13), 0, 2, 0);
+
+CCU_GATE_DEFINE(CLK_WDT, wdt_clk, wdt_clk, "pll1_d96_25p6", MPMU_WDTPCR,
+ BIT(1), 0);
+
+CCU_FACTOR_DEFINE(CLK_I2S_153P6, i2s_153p6, i2s_153p6, "pll1_d8_307p2", 2, 1);
+
+static const char * const i2s_153p6_base_parents[] = {
+ "i2s_153p6",
+ "pll1_d8_307p2",
+};
+
+CCU_MUX_DEFINE(CLK_I2S_153P6_BASE, i2s_153p6_base, i2s_153p6_base,
+ i2s_153p6_base_parents, ARRAY_SIZE(i2s_153p6_base_parents),
+ MPMU_FCCR, 29, 1, 0);
+
+static const char * const i2s_sysclk_src_parents[] = {
+ "pll1_d96_25p6",
+ "i2s_153p6_base"
+};
+
+CCU_MUX_GATE_DEFINE(CLK_I2S_SYSCLK_SRC, i2s_sysclk_src, i2s_sysclk_src,
+ i2s_sysclk_src_parents, ARRAY_SIZE(i2s_sysclk_src_parents),
+ MPMU_ISCCR, 30, 1, BIT(31), 0);
+
+CCU_DDN_DEFINE(CLK_I2S_SYSCLK, i2s_sysclk, i2s_sysclk, "i2s_sysclk_src",
+ MPMU_ISCCR, CCU_DDN_MASK(0, 15), 0, CCU_DDN_MASK(15, 12),
+ 15, 1, 0);
+
+CCU_FACTOR_DEFINE(CLK_I2S_BCLK_FACTOR, i2s_bclk_factor, i2s_bclk_factor,
+ "i2s_sysclk", 2, 1);
+/*
+ * Divider of i2s_bclk always implies a 1/2 factor, which is
+ * described by i2s_bclk_factor.
+ */
+CCU_DIV_GATE_DEFINE(CLK_I2S_BCLK, i2s_bclk, i2s_bclk, "i2s_bclk_factor",
+ MPMU_ISCCR, 27, 2, BIT(29), 0);
+
+static const char * const apb_parents[] = {
+ "pll1_d96_25p6",
+ "pll1_d48_51p2",
+ "pll1_d96_25p6",
+ "pll1_d24_102p4",
+};
+
+CCU_MUX_DEFINE(CLK_APB, apb_clk, apb_clk, apb_parents, ARRAY_SIZE(apb_parents),
+ MPMU_APBCSCR, 0, 2, 0);
+
+CCU_GATE_DEFINE(CLK_WDT_BUS, wdt_bus_clk, wdt_bus_clk, "apb_clk", MPMU_WDTPCR,
+ BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_RIPC, ripc_clk, ripc_clk, "apb_clk", MPMU_RIPCCR, 0x1, 0);
+#endif
+/* MPMU clocks end */
+
+/* APBC clocks start */
+#if IS_ENABLED(CONFIG_SPL_BUILD)
+static const char * const uart_clk_parents[] = {
+ "clock-dummy",
+ "clock-dummy",
+ "slow_uart2_48",
+};
+
+CCU_MUX_GATE_DEFINE(CLK_UART0, uart0_clk, uart0_clk, uart_clk_parents,
+ ARRAY_SIZE(uart_clk_parents), APBC_UART1_CLK_RST,
+ 4, 3, BIT(1) | BIT(0), 0);
+
+static const char * const twsi_parents[] = {
+ "pll1_d78_31p5",
+};
+
+CCU_MUX_GATE_DEFINE(CLK_TWSI2, twsi2_clk, twsi2_clk, twsi_parents,
+ ARRAY_SIZE(twsi_parents), APBC_TWSI2_CLK_RST,
+ 4, 3, BIT(1) | BIT(0), 0);
+/*
+ * APBC_TWSI8_CLK_RST has a quirk that reading always results in zero.
+ * Combine functional and bus bits together as a gate to avoid sharing the
+ * write-only register between different clock hardwares.
+ */
+CCU_GATE_DEFINE(CLK_TWSI8, twsi8_clk, twsi8_clk, "pll1_d78_31p5",
+ APBC_TWSI8_CLK_RST, BIT(1) | BIT(0), 0);
+
+#else
+static const char * const uart_clk_parents[] = {
+ "pll1_m3d128_57p6",
+ "slow_uart1_14p74",
+ "slow_uart2_48",
+};
+
+CCU_MUX_GATE_DEFINE(CLK_UART0, uart0_clk, uart0_clk, uart_clk_parents,
+ ARRAY_SIZE(uart_clk_parents), APBC_UART1_CLK_RST,
+ 4, 3, BIT(1) | BIT(0), 0);
+
+static const char * const twsi_parents[] = {
+ "pll1_d78_31p5",
+ "pll1_d48_51p2",
+ "pll1_d40_61p44",
+};
+
+CCU_MUX_GATE_DEFINE(CLK_TWSI2, twsi2_clk, twsi2_clk, twsi_parents,
+ ARRAY_SIZE(twsi_parents), APBC_TWSI2_CLK_RST,
+ 4, 3, BIT(1) | BIT(0), 0);
+/*
+ * APBC_TWSI8_CLK_RST has a quirk that reading always results in zero.
+ * Combine functional and bus bits together as a gate to avoid sharing the
+ * write-only register between different clock hardwares.
+ */
+CCU_GATE_DEFINE(CLK_TWSI8, twsi8_clk, twsi8_clk, "pll1_d78_31p5",
+ APBC_TWSI8_CLK_RST, BIT(1) | BIT(0), 0);
+
+CCU_MUX_GATE_DEFINE(CLK_UART2, uart2_clk, uart2_clk, uart_clk_parents,
+ ARRAY_SIZE(uart_clk_parents), APBC_UART2_CLK_RST,
+ 4, 3, BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_UART3, uart3_clk, uart3_clk, uart_clk_parents,
+ ARRAY_SIZE(uart_clk_parents), APBC_UART3_CLK_RST,
+ 4, 3, BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_UART4, uart4_clk, uart4_clk, uart_clk_parents,
+ ARRAY_SIZE(uart_clk_parents), APBC_UART4_CLK_RST,
+ 4, 3, BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_UART5, uart5_clk, uart5_clk, uart_clk_parents,
+ ARRAY_SIZE(uart_clk_parents), APBC_UART5_CLK_RST,
+ 4, 3, BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_UART6, uart6_clk, uart6_clk, uart_clk_parents,
+ ARRAY_SIZE(uart_clk_parents), APBC_UART6_CLK_RST,
+ 4, 3, BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_UART7, uart7_clk, uart7_clk, uart_clk_parents,
+ ARRAY_SIZE(uart_clk_parents), APBC_UART7_CLK_RST,
+ 4, 3, BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_UART8, uart8_clk, uart8_clk, uart_clk_parents,
+ ARRAY_SIZE(uart_clk_parents), APBC_UART8_CLK_RST,
+ 4, 3, BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_UART9, uart9_clk, uart9_clk, uart_clk_parents,
+ ARRAY_SIZE(uart_clk_parents), APBC_UART9_CLK_RST,
+ 4, 3, BIT(1) | BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_GPIO, gpio_clk, gpio_clk, "clock-24m", APBC_GPIO_CLK_RST,
+ BIT(1) | BIT(0), 0);
+
+static const char * const pwm_parents[] = {
+ "pll1_d192_12p8",
+ "clock-32k",
+};
+
+CCU_MUX_GATE_DEFINE(CLK_PWM0, pwm0_clk, pwm0_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM0_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM1, pwm1_clk, pwm1_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM1_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM2, pwm2_clk, pwm2_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM2_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM3, pwm3_clk, pwm3_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM3_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM4, pwm4_clk, pwm4_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM4_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM5, pwm5_clk, pwm5_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM5_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM6, pwm6_clk, pwm6_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM6_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM7, pwm7_clk, pwm7_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM7_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM8, pwm8_clk, pwm8_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM8_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM9, pwm9_clk, pwm9_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM9_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM10, pwm10_clk, pwm10_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM10_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM11, pwm11_clk, pwm11_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM11_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM12, pwm12_clk, pwm12_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM12_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM13, pwm13_clk, pwm13_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM13_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM14, pwm14_clk, pwm14_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM14_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM15, pwm15_clk, pwm15_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM15_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM16, pwm16_clk, pwm16_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM16_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM17, pwm17_clk, pwm17_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM17_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM18, pwm18_clk, pwm18_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM18_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_PWM19, pwm19_clk, pwm19_clk, pwm_parents,
+ ARRAY_SIZE(pwm_parents), APBC_PWM19_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+
+static const char * const ssp_parents[] = {
+ "pll1_d384_6p4",
+ "pll1_d192_12p8",
+ "pll1_d96_25p6",
+ "pll1_d48_51p2",
+ "pll1_d768_3p2",
+ "pll1_d1536_1p6",
+ "pll1_d3072_0p8",
+};
+
+CCU_MUX_GATE_DEFINE(CLK_SSP3, ssp3_clk, ssp3_clk, ssp_parents,
+ ARRAY_SIZE(ssp_parents), APBC_SSP3_CLK_RST, 4, 3,
+ BIT(1), 0);
+
+CCU_GATE_DEFINE(CLK_RTC, rtc_clk, rtc_clk, "clock-32k", APBC_RTC_CLK_RST,
+ BIT(7) | BIT(1) | BIT(0), 0);
+
+CCU_MUX_GATE_DEFINE(CLK_TWSI0, twsi0_clk, twsi0_clk, twsi_parents,
+ ARRAY_SIZE(twsi_parents), APBC_TWSI0_CLK_RST,
+ 4, 3, BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_TWSI1, twsi1_clk, twsi1_clk, twsi_parents,
+ ARRAY_SIZE(twsi_parents), APBC_TWSI1_CLK_RST,
+ 4, 3, BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_TWSI4, twsi4_clk, twsi4_clk, twsi_parents,
+ ARRAY_SIZE(twsi_parents), APBC_TWSI4_CLK_RST,
+ 4, 3, BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_TWSI5, twsi5_clk, twsi5_clk, twsi_parents,
+ ARRAY_SIZE(twsi_parents), APBC_TWSI5_CLK_RST,
+ 4, 3, BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_TWSI6, twsi6_clk, twsi6_clk, twsi_parents,
+ ARRAY_SIZE(twsi_parents), APBC_TWSI6_CLK_RST,
+ 4, 3, BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_TWSI7, twsi7_clk, twsi7_clk, twsi_parents,
+ ARRAY_SIZE(twsi_parents), APBC_TWSI7_CLK_RST,
+ 4, 3, BIT(1) | BIT(0), 0);
+
+static const char * const timer_parents[] = {
+ "pll1_d192_12p8",
+ "clock-32k",
+ "pll1_d384_6p4",
+ "clock-3m",
+ "clock-1m",
+};
+
+CCU_MUX_GATE_DEFINE(CLK_TIMERS1, timers1_clk, timers1_clk, timer_parents,
+ ARRAY_SIZE(timer_parents), APBC_TIMERS1_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+CCU_MUX_GATE_DEFINE(CLK_TIMERS2, timers2_clk, timers2_clk, timer_parents,
+ ARRAY_SIZE(timer_parents), APBC_TIMERS2_CLK_RST, 4, 3,
+ BIT(1) | BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_AIB, aib_clk, aib_clk, "clock-24m", APBC_AIB_CLK_RST,
+ BIT(1) | BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_ONEWIRE, onewire_clk, onewire_clk, "clock-24m",
+ APBC_ONEWIRE_CLK_RST, BIT(1) | BIT(0), 0);
+
+/*
+ * When i2s_bclk is selected as the parent clock of sspa,
+ * the hardware requires bit3 to be set
+ */
+CCU_GATE_DEFINE(CLK_SSPA0_I2S_BCLK, sspa0_i2s_bclk, sspa0_i2s_bclk, "i2s_bclk",
+ APBC_SSPA0_CLK_RST, BIT(3), 0);
+CCU_GATE_DEFINE(CLK_SSPA1_I2S_BCLK, sspa1_i2s_bclk, sspa1_i2s_bclk, "i2s_bclk",
+ APBC_SSPA1_CLK_RST, BIT(3), 0);
+
+static const char * const sspa0_parents[] = {
+ "pll1_d384_6p4",
+ "pll1_d192_12p8",
+ "pll1_d96_25p6",
+ "pll1_d48_51p2",
+ "pll1_d768_3p2",
+ "pll1_d1536_1p6",
+ "pll1_d3072_0p8",
+ "sspa0_i2s_bclk",
+};
+
+CCU_MUX_GATE_DEFINE(CLK_SSPA0, sspa0_clk, sspa0_clk, sspa0_parents,
+ ARRAY_SIZE(sspa0_parents), APBC_SSPA0_CLK_RST, 4, 3,
+ BIT(1), 0);
+
+static const char * const sspa1_parents[] = {
+ "pll1_d384_6p4",
+ "pll1_d192_12p8",
+ "pll1_d96_25p6",
+ "pll1_d48_51p2",
+ "pll1_d768_3p2",
+ "pll1_d1536_1p6",
+ "pll1_d3072_0p8",
+ "sspa1_i2s_bclk",
+};
+
+CCU_MUX_GATE_DEFINE(CLK_SSPA1, sspa1_clk, sspa1_clk, sspa1_parents,
+ ARRAY_SIZE(sspa1_parents), APBC_SSPA1_CLK_RST, 4, 3,
+ BIT(1), 0);
+
+CCU_GATE_DEFINE(CLK_DRO, dro_clk, dro_clk, "apb_clk", APBC_DRO_CLK_RST,
+ BIT(1) | BIT(0), 0);
+CCU_GATE_DEFINE(CLK_IR, ir_clk, ir_clk, "apb_clk", APBC_IR_CLK_RST,
+ BIT(1) | BIT(0), 0);
+CCU_GATE_DEFINE(CLK_TSEN, tsen_clk, tsen_clk, "apb_clk", APBC_TSEN_CLK_RST,
+ BIT(1) | BIT(0), 0);
+CCU_GATE_DEFINE(CLK_IPC_AP2AUD, ipc_ap2aud_clk, ipc_ap2aud_clk, "apb_clk",
+ APBC_IPC_AP2AUD_CLK_RST, BIT(1) | BIT(0), 0);
+
+static const char * const can_parents[] = {
+ "pll3_20",
+ "pll3_40",
+ "pll3_80",
+};
+
+CCU_MUX_GATE_DEFINE(CLK_CAN0, can0_clk, can0_clk, can_parents,
+ ARRAY_SIZE(can_parents), APBC_CAN0_CLK_RST, 4, 3,
+ BIT(1), 0);
+CCU_GATE_DEFINE(CLK_CAN0_BUS, can0_bus_clk, can0_bus_clk, "clock-24m",
+ APBC_CAN0_CLK_RST, BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_UART0_BUS, uart0_bus_clk, uart0_bus_clk, "apb_clk",
+ APBC_UART1_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_UART2_BUS, uart2_bus_clk, uart2_bus_clk, "apb_clk",
+ APBC_UART2_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_UART3_BUS, uart3_bus_clk, uart3_bus_clk, "apb_clk",
+ APBC_UART3_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_UART4_BUS, uart4_bus_clk, uart4_bus_clk, "apb_clk",
+ APBC_UART4_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_UART5_BUS, uart5_bus_clk, uart5_bus_clk, "apb_clk",
+ APBC_UART5_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_UART6_BUS, uart6_bus_clk, uart6_bus_clk, "apb_clk",
+ APBC_UART6_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_UART7_BUS, uart7_bus_clk, uart7_bus_clk, "apb_clk",
+ APBC_UART7_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_UART8_BUS, uart8_bus_clk, uart8_bus_clk, "apb_clk",
+ APBC_UART8_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_UART9_BUS, uart9_bus_clk, uart9_bus_clk, "apb_clk",
+ APBC_UART9_CLK_RST, BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_GPIO_BUS, gpio_bus_clk, gpio_bus_clk, "apb_clk",
+ APBC_GPIO_CLK_RST, BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_PWM0_BUS, pwm0_bus_clk, pwm0_bus_clk, "apb_clk",
+ APBC_PWM0_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM1_BUS, pwm1_bus_clk, pwm1_bus_clk, "apb_clk",
+ APBC_PWM1_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM2_BUS, pwm2_bus_clk, pwm2_bus_clk, "apb_clk",
+ APBC_PWM2_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM3_BUS, pwm3_bus_clk, pwm3_bus_clk, "apb_clk",
+ APBC_PWM3_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM4_BUS, pwm4_bus_clk, pwm4_bus_clk, "apb_clk",
+ APBC_PWM4_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM5_BUS, pwm5_bus_clk, pwm5_bus_clk, "apb_clk",
+ APBC_PWM5_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM6_BUS, pwm6_bus_clk, pwm6_bus_clk, "apb_clk",
+ APBC_PWM6_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM7_BUS, pwm7_bus_clk, pwm7_bus_clk, "apb_clk",
+ APBC_PWM7_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM8_BUS, pwm8_bus_clk, pwm8_bus_clk, "apb_clk",
+ APBC_PWM8_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM9_BUS, pwm9_bus_clk, pwm9_bus_clk, "apb_clk",
+ APBC_PWM9_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM10_BUS, pwm10_bus_clk, pwm10_bus_clk, "apb_clk",
+ APBC_PWM10_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM11_BUS, pwm11_bus_clk, pwm11_bus_clk, "apb_clk",
+ APBC_PWM11_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM12_BUS, pwm12_bus_clk, pwm12_bus_clk, "apb_clk",
+ APBC_PWM12_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM13_BUS, pwm13_bus_clk, pwm13_bus_clk, "apb_clk",
+ APBC_PWM13_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM14_BUS, pwm14_bus_clk, pwm14_bus_clk, "apb_clk",
+ APBC_PWM14_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM15_BUS, pwm15_bus_clk, pwm15_bus_clk, "apb_clk",
+ APBC_PWM15_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM16_BUS, pwm16_bus_clk, pwm16_bus_clk, "apb_clk",
+ APBC_PWM16_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM17_BUS, pwm17_bus_clk, pwm17_bus_clk, "apb_clk",
+ APBC_PWM17_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM18_BUS, pwm18_bus_clk, pwm18_bus_clk, "apb_clk",
+ APBC_PWM18_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_PWM19_BUS, pwm19_bus_clk, pwm19_bus_clk, "apb_clk",
+ APBC_PWM19_CLK_RST, BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_SSP3_BUS, ssp3_bus_clk, ssp3_bus_clk, "apb_clk",
+ APBC_SSP3_CLK_RST, BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_RTC_BUS, rtc_bus_clk, rtc_bus_clk, "apb_clk",
+ APBC_RTC_CLK_RST, BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_TWSI0_BUS, twsi0_bus_clk, twsi0_bus_clk, "apb_clk",
+ APBC_TWSI0_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_TWSI1_BUS, twsi1_bus_clk, twsi1_bus_clk, "apb_clk",
+ APBC_TWSI1_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_TWSI2_BUS, twsi2_bus_clk, twsi2_bus_clk, "apb_clk",
+ APBC_TWSI2_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_TWSI4_BUS, twsi4_bus_clk, twsi4_bus_clk, "apb_clk",
+ APBC_TWSI4_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_TWSI5_BUS, twsi5_bus_clk, twsi5_bus_clk, "apb_clk",
+ APBC_TWSI5_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_TWSI6_BUS, twsi6_bus_clk, twsi6_bus_clk, "apb_clk",
+ APBC_TWSI6_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_TWSI7_BUS, twsi7_bus_clk, twsi7_bus_clk, "apb_clk",
+ APBC_TWSI7_CLK_RST, BIT(0), 0);
+CCU_FACTOR_DEFINE(CLK_TWSI8_BUS, twsi8_bus_clk, twsi8_bus_clk, "apb_clk", 1, 1);
+
+CCU_GATE_DEFINE(CLK_TIMERS1_BUS, timers1_bus_clk, timers1_bus_clk, "apb_clk",
+ APBC_TIMERS1_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_TIMERS2_BUS, timers2_bus_clk, timers2_bus_clk, "apb_clk",
+ APBC_TIMERS2_CLK_RST, BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_AIB_BUS, aib_bus_clk, aib_bus_clk, "apb_clk",
+ APBC_AIB_CLK_RST, BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_ONEWIRE_BUS, onewire_bus_clk, onewire_bus_clk, "apb_clk",
+ APBC_ONEWIRE_CLK_RST, BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_SSPA0_BUS, sspa0_bus_clk, sspa0_bus_clk, "apb_clk",
+ APBC_SSPA0_CLK_RST, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_SSPA1_BUS, sspa1_bus_clk, sspa1_bus_clk, "apb_clk",
+ APBC_SSPA1_CLK_RST, BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_TSEN_BUS, tsen_bus_clk, tsen_bus_clk, "apb_clk",
+ APBC_TSEN_CLK_RST, BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_IPC_AP2AUD_BUS, ipc_ap2aud_bus_clk, ipc_ap2aud_bus_clk,
+ "apb_clk", APBC_IPC_AP2AUD_CLK_RST, BIT(0), 0);
+#endif
+/* APBC clocks end */
+
+/* APMU clocks start */
+#if IS_ENABLED(CONFIG_SPL_BUILD)
+static const char * const pmua_aclk_parents[] = {
+ "clock-dummy",
+ "pll1_d8_307p2",
+};
+
+CCU_MUX_DIV_FC_DEFINE(CLK_PMUA_ACLK, pmua_aclk, pmua_aclk, pmua_aclk_parents,
+ ARRAY_SIZE(pmua_aclk_parents),
+ APMU_ACLK_CLK_CTRL, APMU_ACLK_CLK_CTRL, 1, 2, BIT(4),
+ 0, 1, 0);
+
+CCU_GATE_DEFINE(CLK_SDH_AXI, sdh_axi_aclk, sdh_axi_aclk, "pmua_aclk",
+ APMU_SDH0_CLK_RES_CTRL, BIT(3), 0);
+
+static const char * const sdh01_parents[] = {
+ "pll1_d6_409p6",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_SDH0, sdh0_clk, sdh0_clk, sdh01_parents,
+ ARRAY_SIZE(sdh01_parents),
+ APMU_SDH0_CLK_RES_CTRL,
+ APMU_SDH0_CLK_RES_CTRL, 8, 3, BIT(11), 5, 3,
+ BIT(4), 0);
+
+static const char * const sdh2_parents[] = {
+ "pll1_d6_409p6",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_SDH2, sdh2_clk, sdh2_clk, sdh2_parents,
+ ARRAY_SIZE(sdh2_parents),
+ APMU_SDH2_CLK_RES_CTRL,
+ APMU_SDH2_CLK_RES_CTRL, 8, 3, BIT(11), 5, 3,
+ BIT(4), 0);
+#else
+static const char * const pmua_aclk_parents[] = {
+ "pll1_d10_245p76",
+ "pll1_d8_307p2",
+};
+
+CCU_MUX_DIV_FC_DEFINE(CLK_PMUA_ACLK, pmua_aclk, pmua_aclk, pmua_aclk_parents,
+ ARRAY_SIZE(pmua_aclk_parents),
+ APMU_ACLK_CLK_CTRL, APMU_ACLK_CLK_CTRL, 1, 2, BIT(4),
+ 0, 1, 0);
+
+static const char * const emmc_parents[] = {
+ "pll1_d6_409p6",
+ "pll1_d4_614p4",
+ "pll1_d52_47p26",
+ "pll1_d3_819p2",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_EMMC, emmc_clk, emmc_clk, emmc_parents,
+ ARRAY_SIZE(emmc_parents),
+ APMU_PMUA_EM_CLK_RES_CTRL,
+ APMU_PMUA_EM_CLK_RES_CTRL, 8, 3, BIT(11),
+ 6, 2, BIT(4), 0);
+CCU_DIV_GATE_DEFINE(CLK_EMMC_X, emmc_x_clk, emmc_x_clk, "pll1_d2_1228p8",
+ APMU_PMUA_EM_CLK_RES_CTRL, 12,
+ 3, BIT(15), 0);
+
+CCU_GATE_DEFINE(CLK_EMMC_BUS, emmc_bus_clk, emmc_bus_clk, "pmua_aclk",
+ APMU_PMUA_EM_CLK_RES_CTRL, BIT(3), 0);
+
+static const char * const cci550_clk_parents[] = {
+ "pll1_d5_491p52",
+ "pll1_d4_614p4",
+ "pll1_d3_819p2",
+ "pll2_d3",
+};
+
+CCU_MUX_DIV_FC_DEFINE(CLK_CCI550, cci550_clk, cci550_clk, cci550_clk_parents,
+ ARRAY_SIZE(cci550_clk_parents),
+ APMU_CCI550_CLK_CTRL, APMU_CCI550_CLK_CTRL, 8, 3,
+ BIT(12), 0, 2, CLK_IS_CRITICAL);
+
+static const char * const cpu_c0_hi_clk_parents[] = {
+ "pll3_d2",
+ "pll3_d1",
+};
+
+CCU_MUX_DEFINE(CLK_CPU_C0_HI, cpu_c0_hi_clk, cpu_c0_hi_clk,
+ cpu_c0_hi_clk_parents, ARRAY_SIZE(cpu_c0_hi_clk_parents),
+ APMU_CPU_C0_CLK_CTRL, 13, 1, 0);
+static const char * const cpu_c0_clk_parents[] = {
+ "pll1_d4_614p4",
+ "pll1_d3_819p2",
+ "pll1_d6_409p6",
+ "pll1_d5_491p52",
+ "pll1_d2_1228p8",
+ "pll3_d3",
+ "pll2_d3",
+ "cpu_c0_hi_clk",
+};
+
+CCU_MUX_FC_DEFINE(CLK_CPU_C0_CORE, cpu_c0_core_clk, cpu_c0_core_clk,
+ cpu_c0_clk_parents, ARRAY_SIZE(cpu_c0_clk_parents),
+ APMU_CPU_C0_CLK_CTRL, APMU_CPU_C0_CLK_CTRL,
+ BIT(12), 0, 3, CLK_IS_CRITICAL);
+CCU_DIV_DEFINE(CLK_CPU_C0_ACE, cpu_c0_ace_clk, cpu_c0_ace_clk,
+ "cpu_c0_core_clk", APMU_CPU_C0_CLK_CTRL, 6, 3, CLK_IS_CRITICAL);
+CCU_DIV_DEFINE(CLK_CPU_C0_TCM, cpu_c0_tcm_clk, cpu_c0_tcm_clk,
+ "cpu_c0_core_clk", APMU_CPU_C0_CLK_CTRL, 9, 3, CLK_IS_CRITICAL);
+
+static const char * const cpu_c1_hi_clk_parents[] = {
+ "pll3_d2",
+ "pll3_d1",
+};
+
+CCU_MUX_DEFINE(CLK_CPU_C1_HI, cpu_c1_hi_clk, cpu_c1_hi_clk,
+ cpu_c1_hi_clk_parents, ARRAY_SIZE(cpu_c1_hi_clk_parents),
+ APMU_CPU_C1_CLK_CTRL, 13, 1, 0);
+static const char * const cpu_c1_clk_parents[] = {
+ "pll1_d4_614p4",
+ "pll1_d3_819p2",
+ "pll1_d6_409p6",
+ "pll1_d5_491p52",
+ "pll1_d2_1228p8",
+ "pll3_d3",
+ "pll2_d3",
+ "cpu_c1_hi_clk",
+};
+
+CCU_MUX_FC_DEFINE(CLK_CPU_C1_CORE, cpu_c1_core_clk, cpu_c1_core_clk,
+ cpu_c1_clk_parents, ARRAY_SIZE(cpu_c1_clk_parents),
+ APMU_CPU_C1_CLK_CTRL, APMU_CPU_C1_CLK_CTRL,
+ BIT(12), 0, 3, CLK_IS_CRITICAL);
+CCU_DIV_DEFINE(CLK_CPU_C1_ACE, cpu_c1_ace_clk, cpu_c1_ace_clk,
+ "cpu_c1_core_clk", APMU_CPU_C1_CLK_CTRL, 6, 3, CLK_IS_CRITICAL);
+
+static const char * const jpg_parents[] = {
+ "pll1_d4_614p4",
+ "pll1_d6_409p6",
+ "pll1_d5_491p52",
+ "pll1_d3_819p2",
+ "pll1_d2_1228p8",
+ "pll2_d4",
+ "pll2_d3",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_JPG, jpg_clk, jpg_clk, jpg_parents,
+ ARRAY_SIZE(jpg_parents),
+ APMU_JPG_CLK_RES_CTRL,
+ APMU_JPG_CLK_RES_CTRL,
+ 5, 3, BIT(15), 2, 3, BIT(1), 0);
+
+static const char * const ccic2phy_parents[] = {
+ "pll1_d24_102p4",
+ "pll1_d48_51p2_ap",
+};
+
+CCU_MUX_GATE_DEFINE(CLK_CCIC2PHY, ccic2phy_clk, ccic2phy_clk, ccic2phy_parents,
+ ARRAY_SIZE(ccic2phy_parents), APMU_CSI_CCIC2_CLK_RES_CTRL,
+ 7, 1, BIT(5), 0);
+
+static const char * const ccic3phy_parents[] = {
+ "pll1_d24_102p4",
+ "pll1_d48_51p2_ap",
+};
+
+CCU_MUX_GATE_DEFINE(CLK_CCIC3PHY, ccic3phy_clk, ccic3phy_clk, ccic3phy_parents,
+ ARRAY_SIZE(ccic3phy_parents), APMU_CSI_CCIC2_CLK_RES_CTRL,
+ 31, 1, BIT(30), 0);
+
+static const char * const csi_parents[] = {
+ "pll1_d5_491p52",
+ "pll1_d6_409p6",
+ "pll1_d4_614p4",
+ "pll1_d3_819p2",
+ "pll2_d2",
+ "pll2_d3",
+ "pll2_d4",
+ "pll1_d2_1228p8",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_CSI, csi_clk, csi_clk, csi_parents,
+ ARRAY_SIZE(csi_parents),
+ APMU_CSI_CCIC2_CLK_RES_CTRL,
+ APMU_CSI_CCIC2_CLK_RES_CTRL, 20, 3, BIT(15),
+ 16, 3, BIT(4), 0);
+
+static const char * const camm_parents[] = {
+ "pll1_d8_307p2",
+ "pll2_d5",
+ "pll1_d6_409p6",
+ "clock-24m",
+};
+
+CCU_MUX_DIV_GATE_DEFINE(CLK_CAMM0, camm0_clk, camm0_clk, camm_parents,
+ ARRAY_SIZE(camm_parents),
+ APMU_CSI_CCIC2_CLK_RES_CTRL, 23, 4, 8, 2,
+ BIT(28), 0);
+CCU_MUX_DIV_GATE_DEFINE(CLK_CAMM1, camm1_clk, camm1_clk, camm_parents,
+ ARRAY_SIZE(camm_parents),
+ APMU_CSI_CCIC2_CLK_RES_CTRL, 23, 4, 8, 2,
+ BIT(6), 0);
+CCU_MUX_DIV_GATE_DEFINE(CLK_CAMM2, camm2_clk, camm2_clk, camm_parents,
+ ARRAY_SIZE(camm_parents),
+ APMU_CSI_CCIC2_CLK_RES_CTRL, 23, 4, 8, 2,
+ BIT(3), 0);
+
+static const char * const isp_cpp_parents[] = {
+ "pll1_d8_307p2",
+ "pll1_d6_409p6",
+};
+
+CCU_MUX_DIV_GATE_DEFINE(CLK_ISP_CPP, isp_cpp_clk, isp_cpp_clk, isp_cpp_parents,
+ ARRAY_SIZE(isp_cpp_parents),
+ APMU_ISP_CLK_RES_CTRL, 24, 2, 26, 1,
+ BIT(28), 0);
+static const char * const isp_bus_parents[] = {
+ "pll1_d6_409p6",
+ "pll1_d5_491p52",
+ "pll1_d8_307p2",
+ "pll1_d10_245p76",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_ISP_BUS, isp_bus_clk, isp_bus_clk,
+ isp_bus_parents, ARRAY_SIZE(isp_cpp_parents),
+ APMU_ISP_CLK_RES_CTRL,
+ APMU_ISP_CLK_RES_CTRL, 18, 3, BIT(23),
+ 21, 2, BIT(17), 0);
+static const char * const isp_parents[] = {
+ "pll1_d6_409p6",
+ "pll1_d5_491p52",
+ "pll1_d4_614p4",
+ "pll1_d8_307p2",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_ISP, isp_clk, isp_clk, isp_parents,
+ ARRAY_SIZE(isp_parents),
+ APMU_ISP_CLK_RES_CTRL,
+ APMU_ISP_CLK_RES_CTRL,
+ 4, 3, BIT(7), 8, 2, BIT(1), 0);
+
+static const char * const dpumclk_parents[] = {
+ "pll1_d6_409p6",
+ "pll1_d5_491p52",
+ "pll1_d4_614p4",
+ "pll1_d8_307p2",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_DPU_MCLK, dpu_mclk, dpu_mclk,
+ dpumclk_parents, ARRAY_SIZE(dpumclk_parents),
+ APMU_LCD_CLK_RES_CTRL2, APMU_LCD_CLK_RES_CTRL1,
+ 1, 4, BIT(29), 5, 3, BIT(0), 0);
+
+static const char * const dpuesc_parents[] = {
+ "pll1_d48_51p2_ap",
+ "pll1_d52_47p26",
+ "pll1_d96_25p6",
+ "pll1_d32_76p8",
+};
+
+CCU_MUX_GATE_DEFINE(CLK_DPU_ESC, dpu_esc_clk, dpu_esc_clk, dpuesc_parents,
+ ARRAY_SIZE(dpuesc_parents), APMU_LCD_CLK_RES_CTRL1, 0, 2,
+ BIT(2), 0);
+
+static const char * const dpubit_parents[] = {
+ "pll1_d3_819p2",
+ "pll2_d2",
+ "pll2_d3",
+ "pll1_d2_1228p8",
+ "pll2_d4",
+ "pll2_d5",
+ "pll2_d7",
+ "pll2_d8",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_DPU_BIT, dpu_bit_clk, dpu_bit_clk,
+ dpubit_parents, ARRAY_SIZE(dpubit_parents),
+ APMU_LCD_CLK_RES_CTRL1,
+ APMU_LCD_CLK_RES_CTRL1, 17, 3, BIT(31),
+ 20, 3, BIT(16), 0);
+
+static const char * const dpupx_parents[] = {
+ "pll1_d6_409p6",
+ "pll1_d5_491p52",
+ "pll1_d4_614p4",
+ "pll1_d8_307p2",
+ "pll2_d7",
+ "pll2_d8",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_DPU_PXCLK, dpu_pxclk, dpu_pxclk,
+ dpupx_parents, ARRAY_SIZE(dpupx_parents),
+ APMU_LCD_CLK_RES_CTRL2, APMU_LCD_CLK_RES_CTRL1,
+ 17, 4, BIT(30), 21, 3, BIT(16), 0);
+
+CCU_GATE_DEFINE(CLK_DPU_HCLK, dpu_hclk, dpu_hclk, "pmua_aclk",
+ APMU_LCD_CLK_RES_CTRL1, BIT(5), 0);
+
+static const char * const dpu_spi_parents[] = {
+ "pll1_d8_307p2",
+ "pll1_d6_409p6",
+ "pll1_d10_245p76",
+ "pll1_d11_223p4",
+ "pll1_d13_189",
+ "pll1_d23_106p8",
+ "pll2_d3",
+ "pll2_d5",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_DPU_SPI, dpu_spi_clk, dpu_spi_clk,
+ dpu_spi_parents, ARRAY_SIZE(dpu_spi_parents),
+ APMU_LCD_SPI_CLK_RES_CTRL,
+ APMU_LCD_SPI_CLK_RES_CTRL, 8, 3,
+ BIT(7), 12, 3, BIT(1), 0);
+CCU_GATE_DEFINE(CLK_DPU_SPI_HBUS, dpu_spi_hbus_clk, dpu_spi_hbus_clk,
+ "pmua_aclk", APMU_LCD_SPI_CLK_RES_CTRL, BIT(3), 0);
+CCU_GATE_DEFINE(CLK_DPU_SPIBUS, dpu_spi_bus_clk, dpu_spi_bus_clk,
+ "pmua_aclk", APMU_LCD_SPI_CLK_RES_CTRL, BIT(5), 0);
+CCU_GATE_DEFINE(CLK_DPU_SPI_ACLK, dpu_spi_aclk, dpu_spi_aclk,
+ "pmua_aclk", APMU_LCD_SPI_CLK_RES_CTRL, BIT(6), 0);
+
+static const char * const v2d_parents[] = {
+ "pll1_d5_491p52",
+ "pll1_d6_409p6",
+ "pll1_d8_307p2",
+ "pll1_d4_614p4",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_V2D, v2d_clk, v2d_clk, v2d_parents,
+ ARRAY_SIZE(v2d_parents),
+ APMU_LCD_CLK_RES_CTRL1,
+ APMU_LCD_CLK_RES_CTRL1, 9, 3, BIT(28), 12, 2,
+ BIT(8), 0);
+
+static const char * const ccic_4x_parents[] = {
+ "pll1_d5_491p52",
+ "pll1_d6_409p6",
+ "pll1_d4_614p4",
+ "pll1_d3_819p2",
+ "pll2_d2",
+ "pll2_d3",
+ "pll2_d4",
+ "pll1_d2_1228p8",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_CCIC_4X, ccic_4x_clk, ccic_4x_clk,
+ ccic_4x_parents, ARRAY_SIZE(ccic_4x_parents),
+ APMU_CCIC_CLK_RES_CTRL,
+ APMU_CCIC_CLK_RES_CTRL, 18, 3,
+ BIT(15), 23, 2, BIT(4), 0);
+
+static const char * const ccic1phy_parents[] = {
+ "pll1_d24_102p4",
+ "pll1_d48_51p2_ap",
+};
+
+CCU_MUX_GATE_DEFINE(CLK_CCIC1PHY, ccic1phy_clk, ccic1phy_clk, ccic1phy_parents,
+ ARRAY_SIZE(ccic1phy_parents), APMU_CCIC_CLK_RES_CTRL, 7, 1,
+ BIT(5), 0);
+
+CCU_GATE_DEFINE(CLK_SDH_AXI, sdh_axi_aclk, sdh_axi_aclk, "pmua_aclk",
+ APMU_SDH0_CLK_RES_CTRL, BIT(3), 0);
+static const char * const sdh01_parents[] = {
+ "pll1_d6_409p6",
+ "pll1_d4_614p4",
+ "pll2_d8",
+ "pll2_d5",
+ "pll1_d11_223p4",
+ "pll1_d13_189",
+ "pll1_d23_106p8",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_SDH0, sdh0_clk, sdh0_clk, sdh01_parents,
+ ARRAY_SIZE(sdh01_parents),
+ APMU_SDH0_CLK_RES_CTRL,
+ APMU_SDH0_CLK_RES_CTRL, 8, 3, BIT(11), 5, 3,
+ BIT(4), 0);
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_SDH1, sdh1_clk, sdh1_clk, sdh01_parents,
+ ARRAY_SIZE(sdh01_parents),
+ APMU_SDH1_CLK_RES_CTRL,
+ APMU_SDH1_CLK_RES_CTRL, 8, 3, BIT(11), 5, 3,
+ BIT(4), 0);
+static const char * const sdh2_parents[] = {
+ "pll1_d6_409p6",
+ "pll1_d4_614p4",
+ "pll2_d8",
+ "pll1_d3_819p2",
+ "pll1_d11_223p4",
+ "pll1_d13_189",
+ "pll1_d23_106p8",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_SDH2, sdh2_clk, sdh2_clk, sdh2_parents,
+ ARRAY_SIZE(sdh2_parents),
+ APMU_SDH2_CLK_RES_CTRL,
+ APMU_SDH2_CLK_RES_CTRL, 8, 3, BIT(11), 5, 3,
+ BIT(4), 0);
+
+CCU_GATE_DEFINE(CLK_USB_AXI, usb_axi_clk, usb_axi_clk, "pmua_aclk",
+ APMU_USB_CLK_RES_CTRL, BIT(1), 0);
+CCU_GATE_DEFINE(CLK_USB_P1, usb_p1_aclk, usb_p1_aclk, "pmua_aclk",
+ APMU_USB_CLK_RES_CTRL, BIT(5), 0);
+CCU_GATE_DEFINE(CLK_USB30, usb30_clk, usb30_clk, "pmua_aclk",
+ APMU_USB_CLK_RES_CTRL, BIT(8), 0);
+
+static const char * const qspi_parents[] = {
+ "pll1_d6_409p6",
+ "pll2_d8",
+ "pll1_d8_307p2",
+ "pll1_d10_245p76",
+ "pll1_d11_223p4",
+ "pll1_d23_106p8",
+ "pll1_d5_491p52",
+ "pll1_d13_189",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_QSPI, qspi_clk, qspi_clk, qspi_parents,
+ ARRAY_SIZE(qspi_parents),
+ APMU_QSPI_CLK_RES_CTRL,
+ APMU_QSPI_CLK_RES_CTRL, 9, 3, BIT(12), 6, 3,
+ BIT(4), 0);
+CCU_GATE_DEFINE(CLK_QSPI_BUS, qspi_bus_clk, qspi_bus_clk, "pmua_aclk",
+ APMU_QSPI_CLK_RES_CTRL, BIT(3), 0);
+CCU_GATE_DEFINE(CLK_DMA, dma_clk, dma_clk, "pmua_aclk", APMU_DMA_CLK_RES_CTRL,
+ BIT(3), 0);
+
+static const char * const aes_parents[] = {
+ "pll1_d12_204p8",
+ "pll1_d24_102p4",
+};
+
+CCU_MUX_GATE_DEFINE(CLK_AES, aes_clk, aes_clk, aes_parents,
+ ARRAY_SIZE(aes_parents), APMU_AES_CLK_RES_CTRL, 6, 1,
+ BIT(5), 0);
+
+static const char * const vpu_parents[] = {
+ "pll1_d4_614p4",
+ "pll1_d5_491p52",
+ "pll1_d3_819p2",
+ "pll1_d6_409p6",
+ "pll3_d6",
+ "pll2_d3",
+ "pll2_d4",
+ "pll2_d5",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_VPU, vpu_clk, vpu_clk, vpu_parents,
+ ARRAY_SIZE(vpu_parents), APMU_VPU_CLK_RES_CTRL,
+ APMU_VPU_CLK_RES_CTRL, 13, 3, BIT(21), 10, 3,
+ BIT(3), 0);
+
+static const char * const gpu_parents[] = {
+ "pll1_d4_614p4",
+ "pll1_d5_491p52",
+ "pll1_d3_819p2",
+ "pll1_d6_409p6",
+ "pll3_d6",
+ "pll2_d3",
+ "pll2_d4",
+ "pll2_d5",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_GPU, gpu_clk, gpu_clk, gpu_parents,
+ ARRAY_SIZE(gpu_parents), APMU_GPU_CLK_RES_CTRL,
+ APMU_GPU_CLK_RES_CTRL, 12, 3, BIT(15), 18, 3,
+ BIT(4), 0);
+
+static const char * const audio_parents[] = {
+ "pll1_aud_245p7",
+ "pll1_d8_307p2",
+ "pll1_d6_409p6",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_AUDIO, audio_clk, audio_clk, audio_parents,
+ ARRAY_SIZE(audio_parents),
+ APMU_AUDIO_CLK_RES_CTRL,
+ APMU_AUDIO_CLK_RES_CTRL, 4, 3, BIT(15),
+ 7, 3, BIT(12), 0);
+
+static const char * const hdmi_parents[] = {
+ "pll1_d6_409p6",
+ "pll1_d5_491p52",
+ "pll1_d4_614p4",
+ "pll1_d8_307p2",
+};
+
+CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(CLK_HDMI, hdmi_mclk, hdmi_mclk, hdmi_parents,
+ ARRAY_SIZE(hdmi_parents),
+ APMU_HDMI_CLK_RES_CTRL,
+ APMU_HDMI_CLK_RES_CTRL, 1, 4, BIT(29), 5,
+ 3, BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_PCIE0_MASTER, pcie0_master_clk, pcie0_master_clk,
+ "pmua_aclk", APMU_PCIE_CLK_RES_CTRL_0, BIT(2), 0);
+CCU_GATE_DEFINE(CLK_PCIE0_SLAVE, pcie0_slave_clk, pcie0_slave_clk, "pmua_aclk",
+ APMU_PCIE_CLK_RES_CTRL_0, BIT(1), 0);
+CCU_GATE_DEFINE(CLK_PCIE0_DBI, pcie0_dbi_clk, pcie0_dbi_clk, "pmua_aclk",
+ APMU_PCIE_CLK_RES_CTRL_0, BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_PCIE1_MASTER, pcie1_master_clk, pcie1_master_clk,
+ "pmua_aclk", APMU_PCIE_CLK_RES_CTRL_1, BIT(2), 0);
+CCU_GATE_DEFINE(CLK_PCIE1_SLAVE, pcie1_slave_clk, pcie1_slave_clk, "pmua_aclk",
+ APMU_PCIE_CLK_RES_CTRL_1, BIT(1), 0);
+CCU_GATE_DEFINE(CLK_PCIE1_DBI, pcie1_dbi_clk, pcie1_dbi_clk, "pmua_aclk",
+ APMU_PCIE_CLK_RES_CTRL_1, BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_PCIE2_MASTER, pcie2_master_clk, pcie2_master_clk,
+ "pmua_aclk", APMU_PCIE_CLK_RES_CTRL_2, BIT(2), 0);
+CCU_GATE_DEFINE(CLK_PCIE2_SLAVE, pcie2_slave_clk, pcie2_slave_clk, "pmua_aclk",
+ APMU_PCIE_CLK_RES_CTRL_2, BIT(1), 0);
+CCU_GATE_DEFINE(CLK_PCIE2_DBI, pcie2_dbi_clk, pcie2_dbi_clk, "pmua_aclk",
+ APMU_PCIE_CLK_RES_CTRL_2, BIT(0), 0);
+
+CCU_GATE_DEFINE(CLK_EMAC0_BUS, emac0_bus_clk, emac0_bus_clk, "pmua_aclk",
+ APMU_EMAC0_CLK_RES_CTRL, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_EMAC0_PTP, emac0_ptp_clk, emac0_ptp_clk, "pll2_d6",
+ APMU_EMAC0_CLK_RES_CTRL, BIT(15), 0);
+CCU_GATE_DEFINE(CLK_EMAC1_BUS, emac1_bus_clk, emac1_bus_clk, "pmua_aclk",
+ APMU_EMAC1_CLK_RES_CTRL, BIT(0), 0);
+CCU_GATE_DEFINE(CLK_EMAC1_PTP, emac1_ptp_clk, emac1_ptp_clk, "pll2_d6",
+ APMU_EMAC1_CLK_RES_CTRL, BIT(15), 0);
+
+#endif
+/* APMU clocks end */
+
+#if IS_ENABLED(CONFIG_SPL_BUILD)
+static struct clk *k1_ccu_pll_clks[] = {
+ &pll1.common.clk,
+ &pll1_d4.common.clk,
+ &pll1_d6.common.clk,
+ &pll1_d8.common.clk,
+};
+#else
+static struct clk *k1_ccu_pll_clks[] = {
+ &pll1.common.clk,
+ &pll2.common.clk,
+ &pll3.common.clk,
+ &pll1_d2.common.clk,
+ &pll1_d3.common.clk,
+ &pll1_d4.common.clk,
+ &pll1_d5.common.clk,
+ &pll1_d6.common.clk,
+ &pll1_d7.common.clk,
+ &pll1_d8.common.clk,
+ &pll1_d11_223p4.common.clk,
+ &pll1_d13_189.common.clk,
+ &pll1_d23_106p8.common.clk,
+ &pll1_d64_38p4.common.clk,
+ &pll1_aud_245p7.common.clk,
+ &pll1_aud_24p5.common.clk,
+ &pll2_d1.common.clk,
+ &pll2_d2.common.clk,
+ &pll2_d3.common.clk,
+ &pll2_d4.common.clk,
+ &pll2_d5.common.clk,
+ &pll2_d6.common.clk,
+ &pll2_d7.common.clk,
+ &pll2_d8.common.clk,
+ &pll3_d1.common.clk,
+ &pll3_d2.common.clk,
+ &pll3_d3.common.clk,
+ &pll3_d4.common.clk,
+ &pll3_d5.common.clk,
+ &pll3_d6.common.clk,
+ &pll3_d7.common.clk,
+ &pll3_d8.common.clk,
+ &pll3_80.common.clk,
+ &pll3_40.common.clk,
+ &pll3_20.common.clk,
+};
+#endif
+
+static const struct spacemit_ccu_data k1_ccu_pll_data = {
+ .clks = k1_ccu_pll_clks,
+ .num = ARRAY_SIZE(k1_ccu_pll_clks),
+ .offset = K1_PLL_ID,
+};
+
+#if IS_ENABLED(CONFIG_SPL_BUILD)
+static struct clk *k1_ccu_mpmu_clks[] = {
+ &pll1_d4_614p4.common.clk,
+ &pll1_d6_409p6.common.clk,
+ &pll1_d8_307p2.common.clk,
+ &pll1_d78_31p5.common.clk,
+ &slow_uart2_48.common.clk,
+};
+#else
+static struct clk *k1_ccu_mpmu_clks[] = {
+ &pll1_d8_307p2.common.clk,
+ &pll1_d32_76p8.common.clk,
+ &pll1_d40_61p44.common.clk,
+ &pll1_d16_153p6.common.clk,
+ &pll1_d24_102p4.common.clk,
+ &pll1_d48_51p2.common.clk,
+ &pll1_d48_51p2_ap.common.clk,
+ &pll1_m3d128_57p6.common.clk,
+ &pll1_d96_25p6.common.clk,
+ &pll1_d192_12p8.common.clk,
+ &pll1_d192_12p8_wdt.common.clk,
+ &pll1_d384_6p4.common.clk,
+ &pll1_d768_3p2.common.clk,
+ &pll1_d1536_1p6.common.clk,
+ &pll1_d3072_0p8.common.clk,
+ &pll1_d6_409p6.common.clk,
+ &pll1_d12_204p8.common.clk,
+ &pll1_d5_491p52.common.clk,
+ &pll1_d10_245p76.common.clk,
+ &pll1_d4_614p4.common.clk,
+ &pll1_d52_47p26.common.clk,
+ &pll1_d78_31p5.common.clk,
+ &pll1_d3_819p2.common.clk,
+ &pll1_d2_1228p8.common.clk,
+ &slow_uart.common.clk,
+ &slow_uart1_14p74.common.clk,
+ &slow_uart2_48.common.clk,
+ &wdt_clk.common.clk,
+ &apb_clk.common.clk,
+ &ripc_clk.common.clk,
+ &i2s_153p6.common.clk,
+ &i2s_153p6_base.common.clk,
+ &i2s_sysclk_src.common.clk,
+ &i2s_sysclk.common.clk,
+ &i2s_bclk_factor.common.clk,
+ &i2s_bclk.common.clk,
+ &wdt_bus_clk.common.clk,
+};
+#endif
+
+static const struct spacemit_ccu_data k1_ccu_mpmu_data = {
+ .clks = k1_ccu_mpmu_clks,
+ .num = ARRAY_SIZE(k1_ccu_mpmu_clks),
+ .offset = K1_MPMU_ID,
+};
+
+#if IS_ENABLED(CONFIG_SPL_BUILD)
+static struct clk *k1_ccu_apbc_clks[] = {
+ &uart0_clk.common.clk,
+ &twsi2_clk.common.clk,
+ &twsi8_clk.common.clk,
+};
+#else
+static struct clk *k1_ccu_apbc_clks[] = {
+ &uart0_clk.common.clk,
+ &uart2_clk.common.clk,
+ &uart3_clk.common.clk,
+ &uart4_clk.common.clk,
+ &uart5_clk.common.clk,
+ &uart6_clk.common.clk,
+ &uart7_clk.common.clk,
+ &uart8_clk.common.clk,
+ &uart9_clk.common.clk,
+ &gpio_clk.common.clk,
+ &pwm0_clk.common.clk,
+ &pwm1_clk.common.clk,
+ &pwm2_clk.common.clk,
+ &pwm3_clk.common.clk,
+ &pwm4_clk.common.clk,
+ &pwm5_clk.common.clk,
+ &pwm6_clk.common.clk,
+ &pwm7_clk.common.clk,
+ &pwm8_clk.common.clk,
+ &pwm9_clk.common.clk,
+ &pwm10_clk.common.clk,
+ &pwm11_clk.common.clk,
+ &pwm12_clk.common.clk,
+ &pwm13_clk.common.clk,
+ &pwm14_clk.common.clk,
+ &pwm15_clk.common.clk,
+ &pwm16_clk.common.clk,
+ &pwm17_clk.common.clk,
+ &pwm18_clk.common.clk,
+ &pwm19_clk.common.clk,
+ &ssp3_clk.common.clk,
+ &rtc_clk.common.clk,
+ &twsi0_clk.common.clk,
+ &twsi1_clk.common.clk,
+ &twsi2_clk.common.clk,
+ &twsi4_clk.common.clk,
+ &twsi5_clk.common.clk,
+ &twsi6_clk.common.clk,
+ &twsi7_clk.common.clk,
+ &twsi8_clk.common.clk,
+ &timers1_clk.common.clk,
+ &timers2_clk.common.clk,
+ &aib_clk.common.clk,
+ &onewire_clk.common.clk,
+ &sspa0_clk.common.clk,
+ &sspa1_clk.common.clk,
+ &dro_clk.common.clk,
+ &ir_clk.common.clk,
+ &tsen_clk.common.clk,
+ &ipc_ap2aud_clk.common.clk,
+ &can0_clk.common.clk,
+ &can0_bus_clk.common.clk,
+ &uart0_bus_clk.common.clk,
+ &uart2_bus_clk.common.clk,
+ &uart3_bus_clk.common.clk,
+ &uart4_bus_clk.common.clk,
+ &uart5_bus_clk.common.clk,
+ &uart6_bus_clk.common.clk,
+ &uart7_bus_clk.common.clk,
+ &uart8_bus_clk.common.clk,
+ &uart9_bus_clk.common.clk,
+ &gpio_bus_clk.common.clk,
+ &pwm0_bus_clk.common.clk,
+ &pwm1_bus_clk.common.clk,
+ &pwm2_bus_clk.common.clk,
+ &pwm3_bus_clk.common.clk,
+ &pwm4_bus_clk.common.clk,
+ &pwm5_bus_clk.common.clk,
+ &pwm6_bus_clk.common.clk,
+ &pwm7_bus_clk.common.clk,
+ &pwm8_bus_clk.common.clk,
+ &pwm9_bus_clk.common.clk,
+ &pwm10_bus_clk.common.clk,
+ &pwm11_bus_clk.common.clk,
+ &pwm12_bus_clk.common.clk,
+ &pwm13_bus_clk.common.clk,
+ &pwm14_bus_clk.common.clk,
+ &pwm15_bus_clk.common.clk,
+ &pwm16_bus_clk.common.clk,
+ &pwm17_bus_clk.common.clk,
+ &pwm18_bus_clk.common.clk,
+ &pwm19_bus_clk.common.clk,
+ &ssp3_bus_clk.common.clk,
+ &rtc_bus_clk.common.clk,
+ &twsi0_bus_clk.common.clk,
+ &twsi1_bus_clk.common.clk,
+ &twsi2_bus_clk.common.clk,
+ &twsi4_bus_clk.common.clk,
+ &twsi5_bus_clk.common.clk,
+ &twsi6_bus_clk.common.clk,
+ &twsi7_bus_clk.common.clk,
+ &twsi8_bus_clk.common.clk,
+ &timers1_bus_clk.common.clk,
+ &timers2_bus_clk.common.clk,
+ &aib_bus_clk.common.clk,
+ &onewire_bus_clk.common.clk,
+ &sspa0_bus_clk.common.clk,
+ &sspa1_bus_clk.common.clk,
+ &tsen_bus_clk.common.clk,
+ &ipc_ap2aud_bus_clk.common.clk,
+ &sspa0_i2s_bclk.common.clk,
+ &sspa1_i2s_bclk.common.clk,
+};
+#endif
+
+static const struct spacemit_ccu_data k1_ccu_apbc_data = {
+ .clks = k1_ccu_apbc_clks,
+ .num = ARRAY_SIZE(k1_ccu_apbc_clks),
+ .offset = K1_APBC_ID,
+};
+
+#if IS_ENABLED(CONFIG_SPL_BUILD)
+static struct clk *k1_ccu_apmu_clks[] = {
+ &pmua_aclk.common.clk,
+ &sdh_axi_aclk.common.clk,
+ &sdh0_clk.common.clk,
+ &sdh2_clk.common.clk,
+};
+#else
+static struct clk *k1_ccu_apmu_clks[] = {
+ &cci550_clk.common.clk,
+ &cpu_c0_hi_clk.common.clk,
+ &cpu_c0_core_clk.common.clk,
+ &cpu_c0_ace_clk.common.clk,
+ &cpu_c0_tcm_clk.common.clk,
+ &cpu_c1_hi_clk.common.clk,
+ &cpu_c1_core_clk.common.clk,
+ &cpu_c1_ace_clk.common.clk,
+ &ccic_4x_clk.common.clk,
+ &ccic1phy_clk.common.clk,
+ &pmua_aclk.common.clk,
+ &sdh_axi_aclk.common.clk,
+ &sdh0_clk.common.clk,
+ &sdh1_clk.common.clk,
+ &sdh2_clk.common.clk,
+ &usb_p1_aclk.common.clk,
+ &usb_axi_clk.common.clk,
+ &usb30_clk.common.clk,
+ &qspi_clk.common.clk,
+ &qspi_bus_clk.common.clk,
+ &dma_clk.common.clk,
+ &aes_clk.common.clk,
+ &vpu_clk.common.clk,
+ &gpu_clk.common.clk,
+ &emmc_clk.common.clk,
+ &emmc_x_clk.common.clk,
+ &audio_clk.common.clk,
+ &hdmi_mclk.common.clk,
+ &pcie0_master_clk.common.clk,
+ &pcie0_slave_clk.common.clk,
+ &pcie0_dbi_clk.common.clk,
+ &pcie1_master_clk.common.clk,
+ &pcie1_slave_clk.common.clk,
+ &pcie1_dbi_clk.common.clk,
+ &pcie2_master_clk.common.clk,
+ &pcie2_slave_clk.common.clk,
+ &pcie2_dbi_clk.common.clk,
+ &emac0_bus_clk.common.clk,
+ &emac0_ptp_clk.common.clk,
+ &emac1_bus_clk.common.clk,
+ &emac1_ptp_clk.common.clk,
+ &jpg_clk.common.clk,
+ &ccic2phy_clk.common.clk,
+ &ccic3phy_clk.common.clk,
+ &csi_clk.common.clk,
+ &camm0_clk.common.clk,
+ &camm1_clk.common.clk,
+ &camm2_clk.common.clk,
+ &isp_cpp_clk.common.clk,
+ &isp_bus_clk.common.clk,
+ &isp_clk.common.clk,
+ &dpu_mclk.common.clk,
+ &dpu_esc_clk.common.clk,
+ &dpu_bit_clk.common.clk,
+ &dpu_pxclk.common.clk,
+ &dpu_hclk.common.clk,
+ &dpu_spi_clk.common.clk,
+ &dpu_spi_hbus_clk.common.clk,
+ &dpu_spi_bus_clk.common.clk,
+ &dpu_spi_aclk.common.clk,
+ &v2d_clk.common.clk,
+ &emmc_bus_clk.common.clk,
+};
+#endif
+
+static int clk_k1_enable(struct clk *clk)
+{
+ const struct spacemit_ccu_data *data;
+ struct clk *c;
+ struct clk *pclk;
+ int ret, i;
+
+ data = (struct spacemit_ccu_data *)dev_get_driver_data(clk->dev);
+ for (i = 0; i < data->num; i++) {
+ if (clk->id == data->clks[i]->id) {
+ c = data->clks[i];
+ break;
+ }
+ }
+ if (i == data->num)
+ c = clk;
+
+ pclk = clk_get_parent(c);
+ if (!IS_ERR_OR_NULL(pclk)) {
+ ret = ccf_clk_enable(pclk);
+ if (ret)
+ return ret;
+ }
+ ret = ccu_gate_enable(c);
+ return ret;
+}
+
+static int clk_k1_disable(struct clk *clk)
+{
+ const struct spacemit_ccu_data *data;
+ struct clk *c;
+ struct clk *pclk;
+ int ret, i;
+
+ data = (struct spacemit_ccu_data *)dev_get_driver_data(clk->dev);
+ for (i = 0; i < data->num; i++) {
+ if (clk->id == data->clks[i]->id) {
+ c = data->clks[i];
+ break;
+ }
+ }
+ if (i == data->num)
+ c = clk;
+
+ pclk = clk_get_parent(c);
+ if (!IS_ERR_OR_NULL(pclk)) {
+ ret = ccf_clk_disable(pclk);
+ if (ret)
+ return ret;
+ }
+ ret = ccu_gate_disable(c);
+ return ret;
+}
+
+#define K1_CLK_OPS(name) \
+static const struct clk_ops k1_##name##_clk_ops = { \
+ .set_rate = ccf_clk_set_rate, \
+ .get_rate = ccf_clk_get_rate, \
+ .enable = clk_k1_enable, \
+ .disable = clk_k1_disable, \
+ .set_parent = ccf_clk_set_parent, \
+ .of_xlate = k1_##name##_clk_of_xlate, \
+}
+
+static const struct spacemit_ccu_data k1_ccu_apmu_data = {
+ .clks = k1_ccu_apmu_clks,
+ .num = ARRAY_SIZE(k1_ccu_apmu_clks),
+ .offset = K1_APMU_ID,
+};
+
+/*
+ * The K1 clock providers are split across separate syscon devices (PLL,
+ * MPMU, APMU, APBC) whose clocks parent across device boundaries. U-Boot's
+ * CCF resolves a parent by name at registration time and silently binds an
+ * orphan if the parent is not registered yet, so a controller must not
+ * register its clocks before the controllers owning its parents have probed.
+ * Force the parent controller to probe by its driver: this is robust against
+ * device tree node/phandle ordering and needs no helper "clocks" wiring in
+ * the syscon nodes.
+ */
+static int k1_clk_force_probe(const struct driver *drv)
+{
+ struct udevice *dev;
+
+ return uclass_get_device_by_driver(UCLASS_CLK, drv, &dev);
+}
+
+static int k1_clk_register(struct udevice *dev, struct regmap *regmap,
+ struct regmap *lock_regmap,
+ const struct spacemit_ccu_data *data)
+{
+ int i, ret;
+
+ for (i = 0; i < data->num; i++) {
+ struct clk *clk = data->clks[i];
+ struct ccu_common *common;
+
+ if (!clk)
+ continue;
+
+ common = clk_to_ccu_common(clk);
+ common->regmap = regmap;
+ common->lock_regmap = lock_regmap;
+
+ if (common->clk.id < K1_PLL_ID)
+ clk->id = common->clk.id + data->offset;
+ else
+ clk->id = common->clk.id;
+
+ ret = common->init(common);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int k1_pll_clk_probe(struct udevice *dev)
+{
+ struct regmap *base_regmap, *lock_regmap = NULL;
+ const struct spacemit_ccu_data *data;
+ int ret;
+
+ clk_register_fixed_rate(NULL, "clock-dummy", 0);
+
+ ret = regmap_init_mem(dev_ofnode(dev), &base_regmap);
+ if (ret)
+ return ret;
+
+ /*
+ * The lock status of PLLs locate in MPMU region, while PLLs themselves
+ * are in APBS region. Reference to MPMU syscon is required to check PLL
+ * status.
+ */
+ if (device_is_compatible(dev, "spacemit,k1-pll")) {
+ struct ofnode_phandle_args mpmu_args;
+
+ ret = dev_read_phandle_with_args(dev, "spacemit,mpmu", NULL, 0, 0,
+ &mpmu_args);
+ if (ret)
+ return ret;
+
+ ret = regmap_init_mem(mpmu_args.node, &lock_regmap);
+ if (ret)
+ return ret;
+ }
+
+ data = (struct spacemit_ccu_data *)dev_get_driver_data(dev);
+
+ ret = k1_clk_register(dev, base_regmap, lock_regmap, data);
+ if (ret)
+ return -EPROBE_DEFER;
+
+ return 0;
+}
+
+static int k1_mpmu_clk_probe(struct udevice *dev)
+{
+ struct regmap *base_regmap, *lock_regmap = NULL;
+ const struct spacemit_ccu_data *data;
+ int ret;
+
+ /* MPMU clocks parent off the PLL controller */
+ ret = k1_clk_force_probe(DM_DRIVER_GET(k1_pll_clk));
+ if (ret)
+ return ret;
+
+ ret = regmap_init_mem(dev_ofnode(dev), &base_regmap);
+ if (ret)
+ return ret;
+
+ data = (struct spacemit_ccu_data *)dev_get_driver_data(dev);
+
+ ret = k1_clk_register(dev, base_regmap, lock_regmap, data);
+ if (ret)
+ return -EPROBE_DEFER;
+
+ return 0;
+}
+
+static int k1_apmu_clk_probe(struct udevice *dev)
+{
+ struct regmap *base_regmap, *lock_regmap = NULL;
+ const struct spacemit_ccu_data *data;
+ int ret;
+
+ /* APMU clocks parent off the PLL and MPMU controllers */
+ ret = k1_clk_force_probe(DM_DRIVER_GET(k1_pll_clk));
+ if (ret)
+ return ret;
+ ret = k1_clk_force_probe(DM_DRIVER_GET(k1_mpmu_clk));
+ if (ret)
+ return ret;
+
+ ret = regmap_init_mem(dev_ofnode(dev), &base_regmap);
+ if (ret)
+ return ret;
+
+ data = (struct spacemit_ccu_data *)dev_get_driver_data(dev);
+
+ ret = k1_clk_register(dev, base_regmap, lock_regmap, data);
+ if (ret)
+ return -EPROBE_DEFER;
+
+ return 0;
+}
+
+static int k1_apbc_clk_probe(struct udevice *dev)
+{
+ struct regmap *base_regmap, *lock_regmap = NULL;
+ const struct spacemit_ccu_data *data;
+ int ret;
+
+ /* APBC clocks parent off the PLL, MPMU and APMU controllers */
+ ret = k1_clk_force_probe(DM_DRIVER_GET(k1_pll_clk));
+ if (ret)
+ return ret;
+ ret = k1_clk_force_probe(DM_DRIVER_GET(k1_mpmu_clk));
+ if (ret)
+ return ret;
+ ret = k1_clk_force_probe(DM_DRIVER_GET(k1_apmu_clk));
+ if (ret)
+ return ret;
+
+ ret = regmap_init_mem(dev_ofnode(dev), &base_regmap);
+ if (ret)
+ return ret;
+
+ data = (struct spacemit_ccu_data *)dev_get_driver_data(dev);
+
+ ret = k1_clk_register(dev, base_regmap, lock_regmap, data);
+ if (ret)
+ return -EPROBE_DEFER;
+
+ return 0;
+}
+
+static int k1_pll_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
+{
+ if (args->args_count > 1) {
+ debug("Invalid args_count: %d\n", args->args_count);
+ return -EINVAL;
+ }
+
+ if (args->args_count)
+ clk->id = K1_PLL_ID + args->args[0];
+ else
+ clk->id = K1_PLL_ID;
+
+ return 0;
+}
+
+static int k1_mpmu_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
+{
+ if (args->args_count > 1) {
+ debug("Invalid args_count: %d\n", args->args_count);
+ return -EINVAL;
+ }
+
+ if (args->args_count)
+ clk->id = K1_MPMU_ID + args->args[0];
+ else
+ clk->id = K1_MPMU_ID;
+
+ return 0;
+}
+
+static int k1_apbc_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
+{
+ if (args->args_count > 1) {
+ debug("Invalid args_count: %d\n", args->args_count);
+ return -EINVAL;
+ }
+
+ if (args->args_count)
+ clk->id = K1_APBC_ID + args->args[0];
+ else
+ clk->id = K1_APBC_ID;
+
+ return 0;
+}
+
+static int k1_apmu_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
+{
+ if (args->args_count > 1) {
+ debug("Invalid args_count: %d\n", args->args_count);
+ return -EINVAL;
+ }
+
+ if (args->args_count)
+ clk->id = K1_APMU_ID + args->args[0];
+ else
+ clk->id = K1_APMU_ID;
+
+ return 0;
+}
+
+static const struct udevice_id k1_pll_clk_match[] = {
+ { .compatible = "spacemit,k1-pll",
+ .data = (ulong)&k1_ccu_pll_data },
+ { /* sentinel */ },
+};
+
+K1_CLK_OPS(pll);
+
+U_BOOT_DRIVER(k1_pll_clk) = {
+ .name = "k1_pll_clk",
+ .id = UCLASS_CLK,
+ .of_match = k1_pll_clk_match,
+ .probe = k1_pll_clk_probe,
+ .ops = &k1_pll_clk_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+static int k1_mpmu_clk_bind(struct udevice *dev)
+{
+ return spacemit_k1_reset_bind(dev, SPACEMIT_K1_RESET_MPMU);
+}
+
+static int k1_apbc_clk_bind(struct udevice *dev)
+{
+ return spacemit_k1_reset_bind(dev, SPACEMIT_K1_RESET_APBC);
+}
+
+static int k1_apmu_clk_bind(struct udevice *dev)
+{
+ return spacemit_k1_reset_bind(dev, SPACEMIT_K1_RESET_APMU);
+}
+
+static int k1_apbc2_clk_bind(struct udevice *dev)
+{
+ return spacemit_k1_reset_bind(dev, SPACEMIT_K1_RESET_APBC2);
+}
+
+static const struct udevice_id k1_mpmu_clk_match[] = {
+ { .compatible = "spacemit,k1-syscon-mpmu",
+ .data = (ulong)&k1_ccu_mpmu_data },
+ { /* sentinel */ },
+};
+
+K1_CLK_OPS(mpmu);
+
+U_BOOT_DRIVER(k1_mpmu_clk) = {
+ .name = "k1_mpmu_clk",
+ .id = UCLASS_CLK,
+ .of_match = k1_mpmu_clk_match,
+ .bind = k1_mpmu_clk_bind,
+ .probe = k1_mpmu_clk_probe,
+ .ops = &k1_mpmu_clk_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+static const struct udevice_id k1_apbc_clk_match[] = {
+ { .compatible = "spacemit,k1-syscon-apbc",
+ .data = (ulong)&k1_ccu_apbc_data },
+ { /* sentinel */ },
+};
+
+K1_CLK_OPS(apbc);
+
+U_BOOT_DRIVER(k1_apbc_clk) = {
+ .name = "k1_apbc_clk",
+ .id = UCLASS_CLK,
+ .of_match = k1_apbc_clk_match,
+ .bind = k1_apbc_clk_bind,
+ .probe = k1_apbc_clk_probe,
+ .ops = &k1_apbc_clk_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+static const struct udevice_id k1_apmu_clk_match[] = {
+ { .compatible = "spacemit,k1-syscon-apmu",
+ .data = (ulong)&k1_ccu_apmu_data },
+ { /* sentinel */ },
+};
+
+K1_CLK_OPS(apmu);
+
+U_BOOT_DRIVER(k1_apmu_clk) = {
+ .name = "k1_apmu_clk",
+ .id = UCLASS_CLK,
+ .of_match = k1_apmu_clk_match,
+ .bind = k1_apmu_clk_bind,
+ .probe = k1_apmu_clk_probe,
+ .ops = &k1_apmu_clk_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+static const struct udevice_id k1_apbc2_clk_match[] = {
+ { .compatible = "spacemit,k1-syscon-apbc2" },
+ { /* sentinel */ },
+};
+
+U_BOOT_DRIVER(k1_apbc2_clk) = {
+ .name = "k1_apbc2_clk",
+ .id = UCLASS_CLK,
+ .of_match = k1_apbc2_clk_match,
+ .bind = k1_apbc2_clk_bind,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/spacemit/clk_common.h b/drivers/clk/spacemit/clk_common.h
new file mode 100644
index 00000000000..ea5ebf57784
--- /dev/null
+++ b/drivers/clk/spacemit/clk_common.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024 SpacemiT Technology Co. Ltd
+ * Copyright (c) 2024-2025 Haylen Chu <[email protected]>
+ * Copyright (c) 2025 Junhui Liu <[email protected]>
+ * Copyright (c) 2025-2026 RISCstar Ltd.
+ *
+ * Authors: Haylen Chu <[email protected]>
+ */
+
+#ifndef _CLK_COMMON_H_
+#define _CLK_COMMON_H_
+
+#include <linux/clk-provider.h>
+
+struct ccu_common;
+
+typedef int (*ccu_init_fn)(struct ccu_common *common);
+
+struct ccu_common {
+ struct regmap *regmap;
+ struct regmap *lock_regmap;
+ const char *name;
+ const char * const *parents;
+ size_t num_parents;
+ ccu_init_fn init;
+
+ union {
+ /* For DDN and MIX */
+ struct {
+ u32 reg_ctrl;
+ u32 reg_fc;
+ u32 mask_fc;
+ };
+
+ /* For PLL */
+ struct {
+ u32 reg_swcr1;
+ u32 reg_swcr3;
+ };
+ };
+
+ struct clk clk;
+};
+
+#define CCU_COMMON(_id, _name, _parent, _init, _flags) \
+ .name = #_name, \
+ .parents = (const char *[]) { _parent }, \
+ .num_parents = 1, \
+ .init = _init, \
+ .clk = { .flags = _flags, .id = _id, } \
+
+#define CCU_COMMON_PARENTS(_id, _name, _parents, _num_p, _init, _flags) \
+ .name = #_name, \
+ .parents = _parents, \
+ .num_parents = _num_p, \
+ .init = _init, \
+ .clk = { .flags = _flags, .id = _id, } \
+
+static inline struct ccu_common *clk_to_ccu_common(struct clk *clk)
+{
+ return container_of(clk, struct ccu_common, clk);
+}
+
+#define ccu_read(c, reg) \
+ ({ \
+ struct ccu_common * const __ccu = (c); \
+ u32 tmp; \
+ regmap_read(__ccu->regmap, __ccu->reg_##reg, &tmp); \
+ tmp; \
+ })
+#define ccu_update(c, reg, mask, val) \
+ ({ \
+ struct ccu_common * const __ccu = (c); \
+ regmap_update_bits(__ccu->regmap, __ccu->reg_##reg, \
+ mask, val); \
+ })
+
+#endif /* _CLK_COMMON_H_ */
diff --git a/drivers/clk/spacemit/clk_ddn.c b/drivers/clk/spacemit/clk_ddn.c
new file mode 100644
index 00000000000..7b93f30d5c3
--- /dev/null
+++ b/drivers/clk/spacemit/clk_ddn.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024 SpacemiT Technology Co. Ltd
+ * Copyright (c) 2024-2025 Haylen Chu <[email protected]>
+ * Copyright (c) 2025 Junhui Liu <[email protected]>
+ * Authors: Haylen Chu <[email protected]>
+ *
+ * DDN stands for "Divider Denominator Numerator", it's M/N clock with a
+ * constant x2 factor. This clock hardware follows the equation below,
+ *
+ * numerator Fin
+ * 2 * ------------- = -------
+ * denominator Fout
+ *
+ * Thus, Fout could be calculated with,
+ *
+ * Fin denominator
+ * Fout = ----- * -------------
+ * 2 numerator
+ */
+
+#include <dm/device.h>
+#include <regmap.h>
+#include <linux/clk-provider.h>
+#include <linux/rational.h>
+
+#include "clk_ddn.h"
+
+#define UBOOT_DM_SPACEMIT_CLK_DDN "spacemit_clk_ddn"
+
+static unsigned long ccu_ddn_calc_rate(unsigned long prate, unsigned long num,
+ unsigned long den, unsigned int pre_div)
+{
+ return prate * den / pre_div / num;
+}
+
+static unsigned long ccu_ddn_calc_best_rate(struct ccu_ddn *ddn,
+ unsigned long rate, unsigned long prate,
+ unsigned long *num, unsigned long *den)
+{
+ rational_best_approximation(rate, prate / ddn->pre_div,
+ ddn->den_mask >> ddn->den_shift,
+ ddn->num_mask >> ddn->num_shift,
+ den, num);
+ return ccu_ddn_calc_rate(prate, *num, *den, ddn->pre_div);
+}
+
+static unsigned long ccu_ddn_recalc_rate(struct clk *clk)
+{
+ struct ccu_ddn *ddn = clk_to_ccu_ddn(clk);
+ unsigned int val, num, den;
+
+ val = ccu_read(&ddn->common, ctrl);
+
+ num = (val & ddn->num_mask) >> ddn->num_shift;
+ den = (val & ddn->den_mask) >> ddn->den_shift;
+
+ return ccu_ddn_calc_rate(clk_get_parent_rate(clk), num, den, ddn->pre_div);
+}
+
+static unsigned long ccu_ddn_set_rate(struct clk *clk, unsigned long rate)
+{
+ struct ccu_ddn *ddn = clk_to_ccu_ddn(clk);
+ unsigned long num, den;
+
+ ccu_ddn_calc_best_rate(ddn, rate, clk_get_parent_rate(clk), &num, &den);
+
+ ccu_update(&ddn->common, ctrl,
+ ddn->num_mask | ddn->den_mask,
+ (num << ddn->num_shift) | (den << ddn->den_shift));
+
+ return 0;
+}
+
+static const struct clk_ops spacemit_clk_ddn_ops = {
+ .get_rate = ccu_ddn_recalc_rate,
+ .set_rate = ccu_ddn_set_rate,
+};
+
+int spacemit_ddn_init(struct ccu_common *common)
+{
+ struct clk *clk = &common->clk;
+
+ return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_DDN,
+ common->name, common->parents[0]);
+}
+
+U_BOOT_DRIVER(spacemit_clk_ddn) = {
+ .name = UBOOT_DM_SPACEMIT_CLK_DDN,
+ .id = UCLASS_CLK,
+ .ops = &spacemit_clk_ddn_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/spacemit/clk_ddn.h b/drivers/clk/spacemit/clk_ddn.h
new file mode 100644
index 00000000000..1330ced23b1
--- /dev/null
+++ b/drivers/clk/spacemit/clk_ddn.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024 SpacemiT Technology Co. Ltd
+ * Copyright (c) 2024-2025 Haylen Chu <[email protected]>
+ * Copyright (c) 2025 Junhui Liu <[email protected]>
+ * Copyright (c) 2025-2026 RISCstar Ltd.
+ *
+ * Authors: Haylen Chu <[email protected]>
+ */
+
+#ifndef _CLK_DDN_H_
+#define _CLK_DDN_H_
+
+#include <linux/clk-provider.h>
+
+#include "clk_common.h"
+
+struct ccu_ddn {
+ struct ccu_common common;
+ unsigned int num_mask;
+ unsigned int num_shift;
+ unsigned int den_mask;
+ unsigned int den_shift;
+ unsigned int pre_div;
+};
+
+#define CCU_DDN_MASK(_num_shift, _num_width) \
+ GENMASK((_num_shift) + (_num_width) - 1, _num_shift)
+
+#define CCU_DDN_DEFINE(_id, _var, _name, _parent, _reg_ctrl, _num_mask, \
+ _num_shift, _den_mask, _den_shift, _pre_div, _flags) \
+static struct ccu_ddn _var = { \
+ .common = { \
+ .reg_ctrl = _reg_ctrl, \
+ CCU_COMMON(_id, _name, _parent, spacemit_ddn_init, _flags) \
+ }, \
+ .num_mask = _num_mask, \
+ .num_shift = _num_shift, \
+ .den_mask = _den_mask, \
+ .den_shift = _den_shift, \
+ .pre_div = _pre_div, \
+}
+
+static inline struct ccu_ddn *clk_to_ccu_ddn(struct clk *clk)
+{
+ struct ccu_common *common = clk_to_ccu_common(clk);
+
+ return container_of(common, struct ccu_ddn, common);
+}
+
+int spacemit_ddn_init(struct ccu_common *common);
+
+#endif /* _CLK_DDN_H_ */
diff --git a/drivers/clk/spacemit/clk_mix.c b/drivers/clk/spacemit/clk_mix.c
new file mode 100644
index 00000000000..a1158512a92
--- /dev/null
+++ b/drivers/clk/spacemit/clk_mix.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024 SpacemiT Technology Co. Ltd
+ * Copyright (c) 2024-2025 Haylen Chu <[email protected]>
+ * Copyright (c) 2025 Junhui Liu <[email protected]>
+ * Authors: Haylen Chu <[email protected]>
+ *
+ * MIX clock type is the combination of mux, factor or divider, and gate
+ */
+
+#include <dm/device.h>
+#include <dm/uclass.h>
+#include <div64.h>
+#include <regmap.h>
+#include <linux/clk-provider.h>
+#include <linux/kernel.h>
+
+#include "clk_mix.h"
+
+#define UBOOT_DM_SPACEMIT_CLK_GATE "spacemit_clk_gate"
+#define UBOOT_DM_SPACEMIT_CLK_FACTOR "spacemit_clk_factor"
+#define UBOOT_DM_SPACEMIT_CLK_MUX "spacemit_clk_mux"
+#define UBOOT_DM_SPACEMIT_CLK_DIV "spacemit_clk_div"
+#define UBOOT_DM_SPACEMIT_CLK_FACTOR_GATE "spacemit_clk_factor_gate"
+#define UBOOT_DM_SPACEMIT_CLK_MUX_GATE "spacemit_clk_mux_gate"
+#define UBOOT_DM_SPACEMIT_CLK_DIV_GATE "spacemit_clk_div_gate"
+#define UBOOT_DM_SPACEMIT_CLK_MUX_DIV "spacemit_clk_mux_div"
+#define UBOOT_DM_SPACEMIT_CLK_MUX_DIV_GATE "spacemit_clk_mux_div_gate"
+
+#define MIX_FC_TIMEOUT_US 10000
+#define MIX_FC_DELAY_US 5
+
+int ccu_gate_disable(struct clk *clk)
+{
+ struct ccu_mix *mix = clk_to_ccu_mix(clk);
+
+ ccu_update(&mix->common, ctrl, mix->gate.mask, 0);
+
+ return 0;
+}
+
+int ccu_gate_enable(struct clk *clk)
+{
+ struct ccu_mix *mix = clk_to_ccu_mix(clk);
+ struct ccu_gate_config *gate = &mix->gate;
+
+ ccu_update(&mix->common, ctrl, gate->mask, gate->mask);
+
+ return 0;
+}
+
+static unsigned long ccu_factor_recalc_rate(struct clk *clk)
+{
+ struct ccu_mix *mix = clk_to_ccu_mix(clk);
+
+ return clk_get_parent_rate(clk) * mix->factor.mul / mix->factor.div;
+}
+
+static unsigned long ccu_div_recalc_rate(struct clk *clk)
+{
+ struct ccu_mix *mix = clk_to_ccu_mix(clk);
+ struct ccu_div_config *div = &mix->div;
+ unsigned long val;
+
+ val = ccu_read(&mix->common, ctrl) >> div->shift;
+ val &= (1 << div->width) - 1;
+
+ return divider_recalc_rate(clk, clk_get_parent_rate(clk), val, NULL, 0, div->width);
+}
+
+/*
+ * Some clocks require a "FC" (frequency change) bit to be set after changing
+ * their rates or reparenting. This bit will be automatically cleared by
+ * hardware in MIX_FC_TIMEOUT_US, which indicates the operation is completed.
+ */
+static int ccu_mix_trigger_fc(struct clk *clk)
+{
+ struct ccu_common *common = clk_to_ccu_common(clk);
+ unsigned int val;
+
+ if (common->reg_fc)
+ return 0;
+
+ ccu_update(common, fc, common->mask_fc, common->mask_fc);
+
+ return regmap_read_poll_timeout(common->regmap, common->reg_fc,
+ val, !(val & common->mask_fc),
+ MIX_FC_DELAY_US,
+ MIX_FC_TIMEOUT_US);
+}
+
+static unsigned long
+ccu_mix_calc_best_rate(struct clk *clk, unsigned long rate,
+ struct clk **best_parent,
+ unsigned long *best_parent_rate,
+ u32 *div_val)
+{
+ struct ccu_common *common = clk_to_ccu_common(clk);
+ struct ccu_mix *mix = clk_to_ccu_mix(clk);
+ unsigned int parent_num = common->num_parents;
+ struct ccu_div_config *div = &mix->div;
+ u32 div_max = 1 << div->width;
+ unsigned long best_rate = 0;
+
+ for (int i = 0; i < parent_num; i++) {
+ struct udevice *parent_dev;
+ unsigned long parent_rate;
+ struct clk *parent;
+
+ if (uclass_get_device_by_name(UCLASS_CLK, common->parents[i],
+ &parent_dev))
+ continue;
+ parent = dev_get_clk_ptr(parent_dev);
+ if (!parent)
+ continue;
+
+ parent_rate = clk_get_rate(parent);
+
+ for (int j = 1; j <= div_max; j++) {
+ unsigned long tmp = DIV_ROUND_CLOSEST_ULL(parent_rate, j);
+
+ if (abs(tmp - rate) < abs(best_rate - rate)) {
+ best_rate = tmp;
+
+ if (div_val)
+ *div_val = j - 1;
+
+ if (best_parent) {
+ *best_parent = parent;
+ *best_parent_rate = parent_rate;
+ }
+ }
+ }
+ }
+
+ return best_rate;
+}
+
+static unsigned long ccu_mix_set_rate(struct clk *clk, unsigned long rate)
+{
+ struct ccu_mix *mix = clk_to_ccu_mix(clk);
+ struct ccu_common *common = &mix->common;
+ struct ccu_div_config *div = &mix->div;
+ u32 current_div, target_div, mask;
+
+ ccu_mix_calc_best_rate(clk, rate, NULL, NULL, &target_div);
+
+ current_div = ccu_read(common, ctrl) >> div->shift;
+ current_div &= (1 << div->width) - 1;
+
+ if (current_div == target_div)
+ return 0;
+
+ mask = GENMASK(div->width + div->shift - 1, div->shift);
+
+ ccu_update(common, ctrl, mask, target_div << div->shift);
+
+ return ccu_mix_trigger_fc(clk);
+}
+
+static u8 ccu_mux_get_parent(struct clk *clk)
+{
+ struct ccu_mix *mix = clk_to_ccu_mix(clk);
+ struct ccu_mux_config *mux = &mix->mux;
+ u8 parent;
+
+ parent = ccu_read(&mix->common, ctrl) >> mux->shift;
+ parent &= (1 << mux->width) - 1;
+
+ return parent;
+}
+
+static int ccu_mux_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct ccu_common *common = clk_to_ccu_common(clk);
+ struct ccu_mix *mix = clk_to_ccu_mix(clk);
+ struct ccu_mux_config *mux = &mix->mux;
+ u32 mask;
+ int i = 0;
+
+ mask = GENMASK(mux->width + mux->shift - 1, mux->shift);
+
+ for (i = 0; i < common->num_parents; i++) {
+ if (!strcmp(parent->dev->name, common->parents[i]))
+ break;
+ }
+
+ if (i == common->num_parents)
+ return -EINVAL;
+
+ ccu_update(&mix->common, ctrl, mask, i << mux->shift);
+
+ return ccu_mix_trigger_fc(clk);
+}
+
+int spacemit_gate_init(struct ccu_common *common)
+{
+ struct clk *clk = &common->clk;
+
+ return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_GATE,
+ common->name, common->parents[0]);
+}
+
+static const struct clk_ops spacemit_clk_gate_ops = {
+ .disable = ccu_gate_disable,
+ .enable = ccu_gate_enable,
+ .get_rate = clk_generic_get_rate,
+};
+
+U_BOOT_DRIVER(spacemit_clk_gate) = {
+ .name = UBOOT_DM_SPACEMIT_CLK_GATE,
+ .id = UCLASS_CLK,
+ .ops = &spacemit_clk_gate_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+int spacemit_factor_init(struct ccu_common *common)
+{
+ struct clk *clk = &common->clk;
+
+ return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_FACTOR,
+ common->name, common->parents[0]);
+}
+
+static const struct clk_ops spacemit_clk_factor_ops = {
+ .get_rate = ccu_factor_recalc_rate,
+};
+
+U_BOOT_DRIVER(spacemit_clk_factor) = {
+ .name = UBOOT_DM_SPACEMIT_CLK_FACTOR,
+ .id = UCLASS_CLK,
+ .ops = &spacemit_clk_factor_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+int spacemit_mux_init(struct ccu_common *common)
+{
+ struct clk *clk = &common->clk;
+ u8 index;
+
+ index = ccu_mux_get_parent(clk);
+ if (index >= common->num_parents)
+ index = 0;
+
+ return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_MUX,
+ common->name, common->parents[index]);
+}
+
+static const struct clk_ops spacemit_clk_mux_ops = {
+ .set_parent = ccu_mux_set_parent,
+ .get_rate = clk_generic_get_rate,
+};
+
+U_BOOT_DRIVER(spacemit_clk_mux) = {
+ .name = UBOOT_DM_SPACEMIT_CLK_MUX,
+ .id = UCLASS_CLK,
+ .ops = &spacemit_clk_mux_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+int spacemit_div_init(struct ccu_common *common)
+{
+ struct clk *clk = &common->clk;
+
+ return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_DIV,
+ common->name, common->parents[0]);
+}
+
+static const struct clk_ops spacemit_clk_div_ops = {
+ .get_rate = ccu_div_recalc_rate,
+ .set_rate = ccu_mix_set_rate,
+};
+
+U_BOOT_DRIVER(spacemit_clk_div) = {
+ .name = UBOOT_DM_SPACEMIT_CLK_DIV,
+ .id = UCLASS_CLK,
+ .ops = &spacemit_clk_div_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+int spacemit_factor_gate_init(struct ccu_common *common)
+{
+ struct clk *clk = &common->clk;
+
+ return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_FACTOR_GATE,
+ common->name, common->parents[0]);
+}
+
+static const struct clk_ops spacemit_clk_factor_gate_ops = {
+ .disable = ccu_gate_disable,
+ .enable = ccu_gate_enable,
+ .get_rate = ccu_factor_recalc_rate,
+};
+
+U_BOOT_DRIVER(spacemit_clk_factor_gate) = {
+ .name = UBOOT_DM_SPACEMIT_CLK_FACTOR_GATE,
+ .id = UCLASS_CLK,
+ .ops = &spacemit_clk_factor_gate_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+int spacemit_mux_gate_init(struct ccu_common *common)
+{
+ struct clk *clk = &common->clk;
+ u8 index;
+
+ index = ccu_mux_get_parent(clk);
+ if (index >= common->num_parents)
+ index = 0;
+
+ return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_MUX_GATE,
+ common->name, common->parents[index]);
+}
+
+static const struct clk_ops spacemit_clk_mux_gate_ops = {
+ .disable = ccu_gate_disable,
+ .enable = ccu_gate_enable,
+ .set_parent = ccu_mux_set_parent,
+ .get_rate = clk_generic_get_rate,
+};
+
+U_BOOT_DRIVER(spacemit_clk_mux_gate) = {
+ .name = UBOOT_DM_SPACEMIT_CLK_MUX_GATE,
+ .id = UCLASS_CLK,
+ .ops = &spacemit_clk_mux_gate_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+int spacemit_div_gate_init(struct ccu_common *common)
+{
+ struct clk *clk = &common->clk;
+
+ return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_DIV_GATE,
+ common->name, common->parents[0]);
+}
+
+static const struct clk_ops spacemit_clk_div_gate_ops = {
+ .disable = ccu_gate_disable,
+ .enable = ccu_gate_enable,
+ .get_rate = ccu_div_recalc_rate,
+ .set_rate = ccu_mix_set_rate,
+};
+
+U_BOOT_DRIVER(spacemit_clk_div_gate) = {
+ .name = UBOOT_DM_SPACEMIT_CLK_DIV_GATE,
+ .id = UCLASS_CLK,
+ .ops = &spacemit_clk_div_gate_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+int spacemit_mux_div_init(struct ccu_common *common)
+{
+ struct clk *clk = &common->clk;
+ u8 index;
+
+ index = ccu_mux_get_parent(clk);
+ if (index >= common->num_parents)
+ index = 0;
+
+ return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_MUX_DIV,
+ common->name, common->parents[index]);
+}
+
+static const struct clk_ops spacemit_clk_mux_div_ops = {
+ .set_parent = ccu_mux_set_parent,
+ .get_rate = ccu_div_recalc_rate,
+ .set_rate = ccu_mix_set_rate,
+};
+
+U_BOOT_DRIVER(spacemit_clk_mux_div) = {
+ .name = UBOOT_DM_SPACEMIT_CLK_MUX_DIV,
+ .id = UCLASS_CLK,
+ .ops = &spacemit_clk_mux_div_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+int spacemit_mux_div_gate_init(struct ccu_common *common)
+{
+ struct clk *clk = &common->clk;
+ u8 index;
+
+ index = ccu_mux_get_parent(clk);
+ if (index >= common->num_parents)
+ index = 0;
+
+ return clk_register(clk, UBOOT_DM_SPACEMIT_CLK_MUX_DIV_GATE,
+ common->name, common->parents[index]);
+}
+
+static const struct clk_ops spacemit_clk_mux_div_gate_ops = {
+ .disable = ccu_gate_disable,
+ .enable = ccu_gate_enable,
+ .set_parent = ccu_mux_set_parent,
+ .get_rate = ccu_div_recalc_rate,
+ .set_rate = ccu_mix_set_rate,
+};
+
+U_BOOT_DRIVER(spacemit_clk_mux_div_gate) = {
+ .name = UBOOT_DM_SPACEMIT_CLK_MUX_DIV_GATE,
+ .id = UCLASS_CLK,
+ .ops = &spacemit_clk_mux_div_gate_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/spacemit/clk_mix.h b/drivers/clk/spacemit/clk_mix.h
new file mode 100644
index 00000000000..26a12cedd0d
--- /dev/null
+++ b/drivers/clk/spacemit/clk_mix.h
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024 SpacemiT Technology Co. Ltd
+ * Copyright (c) 2024-2025 Haylen Chu <[email protected]>
+ * Copyright (c) 2025 Junhui Liu <[email protected]>
+ * Copyright (c) 2025-2026 RISCstar Ltd.
+ *
+ * Authors: Haylen Chu <[email protected]>
+ */
+
+#ifndef _CLK_MIX_H_
+#define _CLK_MIX_H_
+
+#include <linux/clk-provider.h>
+
+#include "clk_common.h"
+
+/**
+ * struct ccu_gate_config - Gate configuration
+ *
+ * @mask: Mask to enable the gate. Some clocks may have more than one bit
+ * set in this field.
+ */
+struct ccu_gate_config {
+ u32 mask;
+};
+
+struct ccu_factor_config {
+ u32 div;
+ u32 mul;
+};
+
+struct ccu_mux_config {
+ u8 shift;
+ u8 width;
+};
+
+struct ccu_div_config {
+ u8 shift;
+ u8 width;
+};
+
+struct ccu_mix {
+ struct ccu_factor_config factor;
+ struct ccu_gate_config gate;
+ struct ccu_div_config div;
+ struct ccu_mux_config mux;
+ struct ccu_common common;
+};
+
+#define CCU_GATE_INIT(_mask) { .mask = _mask }
+#define CCU_FACTOR_INIT(_div, _mul) { .div = _div, .mul = _mul }
+#define CCU_MUX_INIT(_shift, _width) { .shift = _shift, .width = _width }
+#define CCU_DIV_INIT(_shift, _width) { .shift = _shift, .width = _width }
+
+#define CCU_GATE_DEFINE(_id, _var, _name, _parent, _reg_ctrl, \
+ _mask_gate, _flags) \
+static struct ccu_mix _var = { \
+ .gate = CCU_GATE_INIT(_mask_gate), \
+ .common = { \
+ .reg_ctrl = _reg_ctrl, \
+ CCU_COMMON(_id, _name, _parent, spacemit_gate_init, \
+ _flags) \
+ } \
+}
+
+#define CCU_FACTOR_DEFINE(_id, _var, _name, _parent, _div, _mul) \
+static struct ccu_mix _var = { \
+ .factor = CCU_FACTOR_INIT(_div, _mul), \
+ .common = { \
+ CCU_COMMON(_id, _name, _parent, spacemit_factor_init, \
+ 0) \
+ } \
+}
+
+#define CCU_MUX_DEFINE(_id, _var, _name, _parents, _num_p, _reg_ctrl, \
+ _shift, _width, _flags) \
+static struct ccu_mix _var = { \
+ .mux = CCU_MUX_INIT(_shift, _width), \
+ .common = { \
+ .reg_ctrl = _reg_ctrl, \
+ CCU_COMMON_PARENTS(_id, _name, _parents, _num_p, \
+ spacemit_mux_init, _flags) \
+ } \
+}
+
+#define CCU_DIV_DEFINE(_id, _var, _name, _parent, _reg_ctrl, _shift, \
+ _width, _flags) \
+static struct ccu_mix _var = { \
+ .div = CCU_DIV_INIT(_shift, _width), \
+ .common = { \
+ .reg_ctrl = _reg_ctrl, \
+ CCU_COMMON(_id, _name, _parent, spacemit_div_init, \
+ _flags) \
+ } \
+}
+
+#define CCU_FACTOR_GATE_FLAGS_DEFINE(_id, _var, _name, _parent, \
+ _reg_ctrl, _mask_gate, _div, _mul, \
+ _flags) \
+static struct ccu_mix _var = { \
+ .gate = CCU_GATE_INIT(_mask_gate), \
+ .factor = CCU_FACTOR_INIT(_div, _mul), \
+ .common = { \
+ .reg_ctrl = _reg_ctrl, \
+ CCU_COMMON(_id, _name, _parent, \
+ spacemit_factor_gate_init, _flags) \
+ } \
+}
+
+#define CCU_FACTOR_GATE_DEFINE(_id, _var, _name, _parent, _reg_ctrl, \
+ _mask_gate, _div, _mul) \
+ CCU_FACTOR_GATE_FLAGS_DEFINE(_id, _var, _name, _parent, \
+ _reg_ctrl, _mask_gate, _div, _mul, \
+ 0)
+
+#define CCU_MUX_GATE_DEFINE(_id, _var, _name, _parents, _num_p, \
+ _reg_ctrl, _shift, _width, _mask_gate, \
+ _flags) \
+static struct ccu_mix _var = { \
+ .gate = CCU_GATE_INIT(_mask_gate), \
+ .mux = CCU_MUX_INIT(_shift, _width), \
+ .common = { \
+ .reg_ctrl = _reg_ctrl, \
+ CCU_COMMON_PARENTS(_id, _name, _parents, _num_p, \
+ spacemit_mux_gate_init, _flags) \
+ } \
+}
+
+#define CCU_DIV_GATE_DEFINE(_id, _var, _name, _parent, _reg_ctrl, \
+ _shift, _width, _mask_gate, _flags) \
+static struct ccu_mix _var = { \
+ .gate = CCU_GATE_INIT(_mask_gate), \
+ .div = CCU_DIV_INIT(_shift, _width), \
+ .common = { \
+ .reg_ctrl = _reg_ctrl, \
+ CCU_COMMON(_id, _name, _parent, \
+ spacemit_div_gate_init, _flags) \
+ } \
+}
+
+#define CCU_MUX_DIV_GATE_DEFINE(_id, _var, _name, _parents, _num_p, \
+ _reg_ctrl, _mshift, _mwidth, _muxshift, \
+ _muxwidth, _mask_gate, _flags) \
+static struct ccu_mix _var = { \
+ .gate = CCU_GATE_INIT(_mask_gate), \
+ .div = CCU_DIV_INIT(_mshift, _mwidth), \
+ .mux = CCU_MUX_INIT(_muxshift, _muxwidth), \
+ .common = { \
+ .reg_ctrl = _reg_ctrl, \
+ CCU_COMMON_PARENTS(_id, _name, _parents, _num_p, \
+ spacemit_mux_div_gate_init, _flags) \
+ }, \
+}
+
+#define CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(_id, _var, _name, _parents, \
+ _num_p, _reg_ctrl, _reg_fc, \
+ _mshift, _mwidth, _mask_fc, \
+ _muxshift, _muxwidth, \
+ _mask_gate, _flags) \
+static struct ccu_mix _var = { \
+ .gate = CCU_GATE_INIT(_mask_gate), \
+ .div = CCU_DIV_INIT(_mshift, _mwidth), \
+ .mux = CCU_MUX_INIT(_muxshift, _muxwidth), \
+ .common = { \
+ .reg_ctrl = _reg_ctrl, \
+ .reg_fc = _reg_fc, \
+ .mask_fc = _mask_fc, \
+ CCU_COMMON_PARENTS(_id, _name, _parents, _num_p, \
+ spacemit_mux_div_gate_init, _flags) \
+ }, \
+}
+
+#define CCU_MUX_DIV_FC_DEFINE(_id, _var, _name, _parents, _num_p, \
+ _reg_ctrl, _reg_fc, _mshift, _mwidth, \
+ _mask_fc, _muxshift, _muxwidth, _flags) \
+static struct ccu_mix _var = { \
+ .div = CCU_DIV_INIT(_mshift, _mwidth), \
+ .mux = CCU_MUX_INIT(_muxshift, _muxwidth), \
+ .common = { \
+ .reg_ctrl = _reg_ctrl, \
+ .reg_fc = _reg_fc, \
+ .mask_fc = _mask_fc, \
+ CCU_COMMON_PARENTS(_id, _name, _parents, _num_p, \
+ spacemit_mux_div_init, _flags) \
+ }, \
+}
+
+#define CCU_MUX_FC_DEFINE(_id, _var, _name, _parents, _num_p, \
+ _reg_ctrl, _reg_fc, _mask_fc, _muxshift, \
+ _muxwidth, _flags) \
+static struct ccu_mix _var = { \
+ .mux = CCU_MUX_INIT(_muxshift, _muxwidth), \
+ .common = { \
+ .reg_ctrl = _reg_ctrl, \
+ .reg_fc = _reg_fc, \
+ .mask_fc = _mask_fc, \
+ CCU_COMMON_PARENTS(_id, _name, _parents, _num_p, \
+ spacemit_mux_init, \
+ _flags) \
+ }, \
+}
+
+static inline struct ccu_mix *clk_to_ccu_mix(struct clk *clk)
+{
+ struct ccu_common *common = clk_to_ccu_common(clk);
+
+ return container_of(common, struct ccu_mix, common);
+}
+
+int ccu_gate_enable(struct clk *clk);
+int ccu_gate_disable(struct clk *clk);
+
+int spacemit_gate_init(struct ccu_common *common);
+int spacemit_factor_init(struct ccu_common *common);
+int spacemit_mux_init(struct ccu_common *common);
+int spacemit_div_init(struct ccu_common *common);
+int spacemit_factor_gate_init(struct ccu_common *common);
+int spacemit_div_gate_init(struct ccu_common *common);
+int spacemit_mux_gate_init(struct ccu_common *common);
+int spacemit_mux_div_init(struct ccu_common *common);
+int spacemit_mux_div_gate_init(struct ccu_common *common);
+
+#endif /* _CLK_MIX_H_ */
diff --git a/drivers/clk/spacemit/clk_pll.c b/drivers/clk/spacemit/clk_pll.c
new file mode 100644
index 00000000000..56da70af58a
--- /dev/null
+++ b/drivers/clk/spacemit/clk_pll.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024 SpacemiT Technology Co. Ltd
+ * Copyright (c) 2024-2025 Haylen Chu <[email protected]>
+ * Copyright (c) 2025 Junhui Liu <[email protected]>
+ * Authors: Haylen Chu <[email protected]>
+ */
+
+#include <dm/device.h>
+#include <regmap.h>
+#include <linux/bug.h>
+#include <linux/clk-provider.h>
+
+#include "clk_pll.h"
+
+#define UBOOT_DM_SPACEMIT_CLK_PLL "spacemit_clk_pll"
+
+#define PLL_TIMEOUT_US 3000
+#define PLL_DELAY_US 5
+
+#define PLL_SWCR3_EN ((u32)BIT(31))
+#define PLL_SWCR3_MASK GENMASK(30, 0)
+
+static const struct ccu_pll_rate_tbl *ccu_pll_lookup_best_rate(struct ccu_pll *pll,
+ unsigned long rate)
+{
+ struct ccu_pll_config *config = &pll->config;
+ const struct ccu_pll_rate_tbl *best_entry;
+ unsigned long best_delta = ULONG_MAX;
+ int i;
+
+ for (i = 0; i < config->tbl_num; i++) {
+ const struct ccu_pll_rate_tbl *entry = &config->rate_tbl[i];
+ unsigned long delta = abs(entry->rate - rate);
+
+ if (delta < best_delta) {
+ best_delta = delta;
+ best_entry = entry;
+ }
+ }
+
+ return best_entry;
+}
+
+static const struct ccu_pll_rate_tbl *ccu_pll_lookup_matched_entry(struct ccu_pll *pll)
+{
+ struct ccu_pll_config *config = &pll->config;
+ u32 swcr1, swcr3;
+ int i;
+
+ swcr1 = ccu_read(&pll->common, swcr1);
+ swcr3 = ccu_read(&pll->common, swcr3);
+ swcr3 &= PLL_SWCR3_MASK;
+
+ for (i = 0; i < config->tbl_num; i++) {
+ const struct ccu_pll_rate_tbl *entry = &config->rate_tbl[i];
+
+ if (swcr1 == entry->swcr1 && swcr3 == entry->swcr3)
+ return entry;
+ }
+
+ return NULL;
+}
+
+static void ccu_pll_update_param(struct ccu_pll *pll, const struct ccu_pll_rate_tbl *entry)
+{
+ struct ccu_common *common = &pll->common;
+
+ regmap_write(common->regmap, common->reg_swcr1, entry->swcr1);
+ ccu_update(common, swcr3, PLL_SWCR3_MASK, entry->swcr3);
+}
+
+static int ccu_pll_enable(struct clk *clk)
+{
+ struct ccu_pll *pll = clk_to_ccu_pll(clk);
+ struct ccu_common *common = &pll->common;
+ unsigned int tmp;
+
+ ccu_update(common, swcr3, PLL_SWCR3_EN, PLL_SWCR3_EN);
+
+ /* check lock status */
+ return regmap_read_poll_timeout(common->lock_regmap,
+ pll->config.reg_lock,
+ tmp,
+ tmp & pll->config.mask_lock,
+ PLL_DELAY_US, PLL_TIMEOUT_US);
+}
+
+static int ccu_pll_disable(struct clk *clk)
+{
+ struct ccu_common *common = clk_to_ccu_common(clk);
+
+ ccu_update(common, swcr3, PLL_SWCR3_EN, 0);
+
+ return 0;
+}
+
+/*
+ * PLLs must be gated before changing rate, which is ensured by
+ * flag CLK_SET_RATE_GATE.
+ */
+static unsigned long ccu_pll_set_rate(struct clk *clk, unsigned long rate)
+{
+ struct ccu_pll *pll = clk_to_ccu_pll(clk);
+ const struct ccu_pll_rate_tbl *entry;
+
+ entry = ccu_pll_lookup_best_rate(pll, rate);
+ ccu_pll_update_param(pll, entry);
+
+ return 0;
+}
+
+static unsigned long ccu_pll_recalc_rate(struct clk *clk)
+{
+ struct ccu_pll *pll = clk_to_ccu_pll(clk);
+ const struct ccu_pll_rate_tbl *entry;
+
+ entry = ccu_pll_lookup_matched_entry(pll);
+
+ WARN_ON_ONCE(!entry);
+
+ return entry ? entry->rate : 0;
+}
+
+static const struct clk_ops spacemit_clk_pll_ops = {
+ .enable = ccu_pll_enable,
+ .disable = ccu_pll_disable,
+ .set_rate = ccu_pll_set_rate,
+ .get_rate = ccu_pll_recalc_rate,
+};
+
+int spacemit_pll_init(struct ccu_common *common)
+{
+ struct clk *clk = &common->clk;
+ struct ccu_pll *pll = clk_to_ccu_pll(clk);
+ int ret;
+
+ ret = clk_register(clk, UBOOT_DM_SPACEMIT_CLK_PLL,
+ common->name, common->parents[0]);
+ if (ret)
+ return ret;
+
+ if (ccu_pll_lookup_matched_entry(pll))
+ return 0;
+
+ ccu_pll_disable(clk);
+ ccu_pll_update_param(pll, &pll->config.rate_tbl[0]);
+
+ return 0;
+}
+
+U_BOOT_DRIVER(spacemit_clk_pll) = {
+ .name = UBOOT_DM_SPACEMIT_CLK_PLL,
+ .id = UCLASS_CLK,
+ .ops = &spacemit_clk_pll_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/spacemit/clk_pll.h b/drivers/clk/spacemit/clk_pll.h
new file mode 100644
index 00000000000..3987cc1141b
--- /dev/null
+++ b/drivers/clk/spacemit/clk_pll.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024 SpacemiT Technology Co. Ltd
+ * Copyright (c) 2024-2025 Haylen Chu <[email protected]>
+ * Copyright (c) 2025 Junhui Liu <[email protected]>
+ * Copyright (c) 2025-2026 RISCstar Ltd.
+ *
+ * Authors: Haylen Chu <[email protected]>
+ */
+
+#ifndef _CLK_PLL_H_
+#define _CLK_PLL_H_
+
+#include <linux/clk-provider.h>
+
+#include "clk_common.h"
+
+/**
+ * struct ccu_pll_rate_tbl - Structure mapping between PLL rate and register
+ * configuration.
+ *
+ * @rate: PLL rate
+ * @swcr1: Register value of PLLX_SW1_CTRL (PLLx_SWCR1).
+ * @swcr3: Register value of the PLLx_SW3_CTRL's lowest 31 bits of
+ * PLLx_SW3_CTRL (PLLx_SWCR3). This highest bit is for enabling
+ * the PLL and not contained in this field.
+ */
+struct ccu_pll_rate_tbl {
+ unsigned long rate;
+ u32 swcr1;
+ u32 swcr3;
+};
+
+#define CCU_PLL_RATE(_rate, _swcr1, _swcr3) \
+ { \
+ .rate = _rate, \
+ .swcr1 = _swcr1, \
+ .swcr3 = _swcr3, \
+ }
+
+struct ccu_pll_config {
+ const struct ccu_pll_rate_tbl *rate_tbl;
+ u32 tbl_num;
+ u32 reg_lock;
+ u32 mask_lock;
+};
+
+struct ccu_pll {
+ struct ccu_common common;
+ struct ccu_pll_config config;
+};
+
+#define CCU_PLL_CONFIG(_table, _reg_lock, _mask_lock) \
+ { \
+ .rate_tbl = (_table), \
+ .tbl_num = sizeof(_table) / sizeof((_table)[0]), \
+ .reg_lock = (_reg_lock), \
+ .mask_lock = (_mask_lock), \
+ }
+
+#define CCU_PLL_DEFINE(_id, _var, _name, _parent, _table, _reg_swcr1, \
+ _reg_swcr3, _reg_lock, _mask_lock, _flags) \
+static struct ccu_pll _var = { \
+ .config = CCU_PLL_CONFIG(_table, _reg_lock, _mask_lock), \
+ .common = { \
+ .reg_swcr1 = _reg_swcr1, \
+ .reg_swcr3 = _reg_swcr3, \
+ CCU_COMMON(_id, _name, _parent, spacemit_pll_init, _flags) \
+ } \
+}
+
+static inline struct ccu_pll *clk_to_ccu_pll(struct clk *clk)
+{
+ struct ccu_common *common = clk_to_ccu_common(clk);
+
+ return container_of(common, struct ccu_pll, common);
+}
+
+int spacemit_pll_init(struct ccu_common *common);
+
+#endif /* _CLK_PLL_H_ */
diff --git a/drivers/clk/ti/clk-ctrl.c b/drivers/clk/ti/clk-ctrl.c
index 08f7410edce..e3564277982 100644
--- a/drivers/clk/ti/clk-ctrl.c
+++ b/drivers/clk/ti/clk-ctrl.c
@@ -12,7 +12,7 @@
#include <asm/io.h>
#include <linux/iopoll.h>
-#define TRANSITION_TIMEOUT_US 10000
+#define LDELAY 1000000
struct clk_ti_ctrl_offs {
fdt_addr_t start;
@@ -39,30 +39,59 @@ static int clk_ti_ctrl_check_offs(struct clk *clk, fdt_addr_t offs)
#define IDLEST_DISABLED (MODULE_CLKCTRL_IDLEST_DISABLED << MODULE_CLKCTRL_IDLEST_SHIFT)
#define IDLEST_TRANSITION (MODULE_CLKCTRL_IDLEST_TRANSITIONING << MODULE_CLKCTRL_IDLEST_SHIFT)
-static int clk_ti_ctrl_disable_clock_module(u32 addr)
+static inline void wait_for_clk_disable(u32 addr)
{
- int val;
+ u32 bound = LDELAY;
+ int val = 0;
+
+ while ((val & MODULE_CLKCTRL_IDLEST_MASK) != IDLEST_DISABLED) {
+ val = readl(addr);
+
+ if (--bound == 0) {
+ printf("Clock disable failed for 0x%x idlest 0x%x\n",
+ addr, val);
+ return;
+ }
+ }
+}
+static int clk_ti_ctrl_disable_clock_module(u32 addr)
+{
clrsetbits_le32(addr, MODULE_CLKCTRL_MODULEMODE_MASK,
MODULE_CLKCTRL_MODULEMODE_SW_DISABLE <<
MODULE_CLKCTRL_MODULEMODE_SHIFT);
- return readl_relaxed_poll_timeout(addr, val,
- (val & MODULE_CLKCTRL_IDLEST_MASK) == IDLEST_DISABLED,
- TRANSITION_TIMEOUT_US);
+ wait_for_clk_disable(addr);
+
+ return 0;
}
-static int clk_ti_ctrl_enable_clock_module(u32 addr)
+static inline void wait_for_clk_enable(u32 addr)
{
- int val;
+ int val = IDLEST_DISABLED;
+ u32 bound = LDELAY;
+ while (((val & MODULE_CLKCTRL_IDLEST_MASK) == IDLEST_DISABLED) ||
+ ((val & MODULE_CLKCTRL_IDLEST_MASK) == IDLEST_TRANSITION)) {
+ val = readl(addr);
+
+ if (--bound == 0) {
+ printf("Clock enable failed for 0x%x idlest 0x%x\n",
+ addr, val);
+ return;
+ }
+ }
+}
+
+static int clk_ti_ctrl_enable_clock_module(u32 addr)
+{
clrsetbits_le32(addr, MODULE_CLKCTRL_MODULEMODE_MASK,
MODULE_CLKCTRL_MODULEMODE_SW_EXPLICIT_EN <<
MODULE_CLKCTRL_MODULEMODE_SHIFT);
- return readl_relaxed_poll_timeout(addr, val,
- ((val & MODULE_CLKCTRL_IDLEST_MASK) != IDLEST_DISABLED) &&
- ((val & MODULE_CLKCTRL_IDLEST_MASK) != IDLEST_TRANSITION),
- TRANSITION_TIMEOUT_US);
+
+ wait_for_clk_enable(addr);
+
+ return 0;
}
static int clk_ti_ctrl_disable(struct clk *clk)
diff --git a/drivers/core/device.c b/drivers/core/device.c
index d365204ba11..6024534ff93 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -1147,6 +1147,8 @@ int device_set_name(struct udevice *dev, const char *name)
name = strdup(name);
if (!name)
return -ENOMEM;
+ if (dev_get_flags(dev) & DM_FLAG_NAME_ALLOCED)
+ free((char *)dev->name);
dev->name = name;
device_set_name_alloced(dev);
diff --git a/drivers/core/of_access.c b/drivers/core/of_access.c
index 969492aae37..f910cc9a31b 100644
--- a/drivers/core/of_access.c
+++ b/drivers/core/of_access.c
@@ -1071,7 +1071,7 @@ int of_add_subnode(struct device_node *parent, const char *name, int len,
* make sure we don't use a child called "trevor" when we are
* searching for "trev".
*/
- if (!strncmp(child->name, name, len) && strlen(name) == len) {
+ if (!strncmp(child->name, name, len) && strlen(child->name) == len) {
*childp = child;
return -EEXIST;
}
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
index 1388b481031..b5d13c43db1 100644
--- a/drivers/core/ofnode.c
+++ b/drivers/core/ofnode.c
@@ -130,8 +130,10 @@ int oftree_new(oftree *treep)
if (!fdt)
return log_msg_ret("fla", -ENOMEM);
ret = fdt_create_empty_tree(fdt, size);
- if (ret)
+ if (ret) {
+ free(fdt);
return log_msg_ret("fla", -EINVAL);
+ }
oftree_list[oftree_count++] = fdt;
tree.fdt = fdt;
}
diff --git a/drivers/core/tag.c b/drivers/core/tag.c
index a3c5cb7e57c..78b5ee6dc07 100644
--- a/drivers/core/tag.c
+++ b/drivers/core/tag.c
@@ -46,7 +46,7 @@ int dev_tag_set_ptr(struct udevice *dev, enum dm_tag_t tag, void *ptr)
return -EEXIST;
}
- node = calloc(sizeof(*node), 1);
+ node = calloc(1, sizeof(*node));
if (!node)
return -ENOMEM;
@@ -70,7 +70,7 @@ int dev_tag_set_val(struct udevice *dev, enum dm_tag_t tag, ulong val)
return -EEXIST;
}
- node = calloc(sizeof(*node), 1);
+ node = calloc(1, sizeof(*node));
if (!node)
return -ENOMEM;
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 75b35fbc5be..a11f3043670 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -449,6 +449,14 @@ config SANDBOX_GPIO_COUNT
of 'anonymous' GPIOs that do not belong to any device or bank.
Select a suitable value depending on your needs.
+config SPACEMIT_GPIO
+ bool "Spacemit K1 GPIO driver"
+ depends on DM_GPIO && TARGET_SPACEMIT_K1
+ help
+ Support the GPIO device in Spacemit SoCs. The GPIOs are arranged
+ into a number of banks (different for each SoC type) each with 32
+ GPIOs.
+
config SUNXI_GPIO
bool "Allwinner GPIO driver"
depends on ARCH_SUNXI
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 863557e45ce..020e23c7252 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -66,6 +66,7 @@ obj-$(CONFIG_MVEBU_GPIO) += mvebu_gpio.o
obj-$(CONFIG_MSM_GPIO) += msm_gpio.o
obj-$(CONFIG_$(PHASE_)PCF8575_GPIO) += pcf8575_gpio.o
obj-$(CONFIG_$(PHASE_)QCOM_PMIC_GPIO) += qcom_pmic_gpio.o qcom_spmi_gpio.o
+obj-$(CONFIG_$(PHASE_)SPACEMIT_GPIO) += spacemit_gpio.o
obj-$(CONFIG_MT7620_GPIO) += mt7620_gpio.o
obj-$(CONFIG_MT7621_GPIO) += mt7621_gpio.o
obj-$(CONFIG_MSCC_SGPIO) += mscc_sgpio.o
diff --git a/drivers/gpio/spacemit_gpio.c b/drivers/gpio/spacemit_gpio.c
new file mode 100644
index 00000000000..de71880df15
--- /dev/null
+++ b/drivers/gpio/spacemit_gpio.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025-2026 RISCstar Ltd.
+ */
+
+#include <asm/gpio.h>
+#include <clk.h>
+#include <dm/device.h>
+#include <dm/device_compat.h>
+#include <dm/pinctrl.h>
+#include <dm/read.h>
+#include <dm/uclass.h>
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <log.h>
+
+#define GPIO_BANK_SIZE 32
+#define GPIO_TO_BANK(pin) ((pin) / GPIO_BANK_SIZE)
+#define GPIO_TO_BIT(pin) ((pin) % GPIO_BANK_SIZE)
+
+static inline int gpio_to_reg_offset(unsigned int pin)
+{
+ unsigned int bank = GPIO_TO_BANK(pin);
+
+ if (bank == 0)
+ return 0;
+ else if (bank == 1)
+ return 4;
+ else if (bank == 2)
+ return 8;
+ else if (bank == 3)
+ return 0x100;
+ log_warning("Use default GPIO bank for an invalid GPIO[%d].\n", pin);
+ return 0;
+}
+
+#define REG_PLR(pin) (0x00 + gpio_to_reg_offset(pin))
+#define REG_PDR(pin) (0x0c + gpio_to_reg_offset(pin))
+#define REG_PSR(pin) (0x18 + gpio_to_reg_offset(pin))
+#define REG_PCR(pin) (0x24 + gpio_to_reg_offset(pin))
+#define REG_SDR(pin) (0x54 + gpio_to_reg_offset(pin))
+#define REG_CDR(pin) (0x60 + gpio_to_reg_offset(pin))
+
+struct spacemit_gpio_data {
+ u16 gpio_base;
+ u16 gpio_count;
+ u8 num_banks;
+};
+
+struct spacemit_gpio_priv {
+ void __iomem *regs;
+};
+
+static int spacemit_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ struct spacemit_gpio_data *data;
+ u32 bank, offset, flags;
+
+ data = (struct spacemit_gpio_data *)dev_get_driver_data(dev);
+ if (args->args_count < 3) {
+ dev_err(dev, "Invalid args count: %d, expected 3\n",
+ args->args_count);
+ return -EINVAL;
+ }
+ bank = args->args[0];
+ offset = args->args[1];
+ flags = args->args[2];
+
+ if (bank >= data->num_banks) {
+ dev_err(dev, "Invalid gpio bank: %u (max %u)\n",
+ bank, data->num_banks - 1);
+ return -EINVAL;
+ }
+ if (offset >= GPIO_BANK_SIZE) {
+ dev_err(dev, "Invalid offset: %u (max 31)\n", offset);
+ return -EINVAL;
+ }
+ desc->offset = bank * GPIO_BANK_SIZE + offset;
+ desc->flags = gpio_flags_xlate(flags);
+ return 0;
+}
+
+static int spacemit_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+ struct spacemit_gpio_priv *priv = dev_get_priv(dev);
+ void __iomem *addr;
+ u32 value, mask;
+
+ addr = priv->regs + REG_PLR(offset);
+ value = readl(addr);
+ mask = 1 << GPIO_TO_BIT(offset);
+ return !!(value & mask);
+}
+
+static int spacemit_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+ struct spacemit_gpio_priv *priv = dev_get_priv(dev);
+ void __iomem *addr;
+ u32 value, mask;
+
+ addr = priv->regs + REG_PDR(offset);
+ value = readl(addr);
+ mask = 1 << GPIO_TO_BIT(offset);
+ if (value & mask)
+ return GPIOF_OUTPUT;
+ return GPIOF_INPUT;
+}
+
+static int spacemit_gpio_get_flags(struct udevice *dev, unsigned int offset,
+ ulong *flagsp)
+{
+ ulong flags = 0;
+ u32 dir;
+
+ dir = spacemit_gpio_get_function(dev, offset);
+ if (dir) {
+ flags |= GPIOD_IS_OUT;
+ if (spacemit_gpio_get_value(dev, offset))
+ flags |= GPIOD_IS_OUT_ACTIVE;
+ } else {
+ flags |= GPIOD_IS_IN;
+ }
+ *flagsp = flags;
+ return 0;
+}
+
+static int spacemit_gpio_set_flags(struct udevice *dev, unsigned int offset,
+ ulong flags)
+{
+ struct spacemit_gpio_priv *priv = dev_get_priv(dev);
+ void __iomem *addr;
+ int value;
+
+ value = (flags & GPIOD_IS_OUT_ACTIVE) ? 1 : 0;
+ if (flags & GPIOD_IS_IN) {
+ addr = priv->regs + REG_CDR(offset);
+ writel(1 << GPIO_TO_BIT(offset), addr);
+ }
+ if (flags & GPIOD_IS_OUT) {
+ if (value) {
+ addr = priv->regs + REG_PSR(offset);
+ writel(1 << GPIO_TO_BIT(offset), addr);
+ } else {
+ addr = priv->regs + REG_PCR(offset);
+ writel(1 << GPIO_TO_BIT(offset), addr);
+ }
+ addr = priv->regs + REG_SDR(offset);
+ writel(1 << GPIO_TO_BIT(offset), addr);
+ }
+ return 0;
+}
+
+static int spacemit_gpio_request(struct udevice *dev, unsigned int offset,
+ const char *label)
+{
+ const struct pinctrl_ops *ops;
+ struct udevice *pctldev;
+ int ret;
+
+ ret = uclass_first_device_err(UCLASS_PINCTRL, &pctldev);
+ if (ret)
+ return ret;
+
+ ops = pinctrl_get_ops(pctldev);
+ if (!ops->gpio_request_enable)
+ return -ENOSYS;
+
+ return ops->gpio_request_enable(pctldev, offset);
+}
+
+static int spacemit_gpio_rfree(struct udevice *dev, unsigned int offset)
+{
+ const struct pinctrl_ops *ops;
+ struct udevice *pctldev;
+ int ret;
+
+ ret = uclass_first_device_err(UCLASS_PINCTRL, &pctldev);
+ if (ret)
+ return ret;
+
+ ops = pinctrl_get_ops(pctldev);
+ if (!ops->gpio_disable_free)
+ return -ENOSYS;
+
+ return ops->gpio_disable_free(pctldev, offset);
+}
+
+static const struct dm_gpio_ops spacemit_gpio_ops = {
+ .request = spacemit_gpio_request,
+ .rfree = spacemit_gpio_rfree,
+ .xlate = spacemit_gpio_xlate,
+ .get_value = spacemit_gpio_get_value,
+ .get_function = spacemit_gpio_get_function,
+ .get_flags = spacemit_gpio_get_flags,
+ .set_flags = spacemit_gpio_set_flags,
+};
+
+static int spacemit_gpio_probe(struct udevice *dev)
+{
+ struct spacemit_gpio_priv *priv;
+ struct spacemit_gpio_data *data;
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct clk_bulk clks;
+ int ret;
+
+ data = (struct spacemit_gpio_data *)dev_get_driver_data(dev);
+ priv = dev_get_priv(dev);
+ priv->regs = dev_read_addr_ptr(dev);
+ if (!priv->regs) {
+ dev_err(dev, "Fail to get base address\n");
+ return -EINVAL;
+ }
+ uc_priv->bank_name = "GPIO";
+ uc_priv->gpio_count = data->gpio_count;
+ uc_priv->gpio_base = data->gpio_base;
+
+ ret = clk_get_bulk(dev, &clks);
+ if (ret) {
+ dev_err(dev, "Fail to get bulk clks\n");
+ return ret;
+ }
+ ret = clk_enable_bulk(&clks);
+ if (ret) {
+ dev_err(dev, "Fail to enable bulk clks\n");
+ goto out;
+ }
+ return 0;
+out:
+ clk_release_bulk(&clks);
+ return ret;
+}
+
+static const struct spacemit_gpio_data k1_gpio_data = {
+ .num_banks = 4,
+ .gpio_count = 128,
+ .gpio_base = 0,
+};
+
+static const struct udevice_id spacemit_gpio_ids[] = {
+ { .compatible = "spacemit,k1-gpio", .data = (uintptr_t)&k1_gpio_data, },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(k1_gpio) = {
+ .name = "spacemit_k1_gpio",
+ .id = UCLASS_GPIO,
+ .of_match = spacemit_gpio_ids,
+ .ops = &spacemit_gpio_ops,
+ .priv_auto = sizeof(struct spacemit_gpio_priv),
+ .probe = spacemit_gpio_probe,
+};
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 5f6586c78ba..0e8b2408376 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -797,6 +797,13 @@ config SYS_I2C_IHS
help
Support for gdsys IHS I2C driver on FPGA bus.
+config SYS_I2C_SPACEMIT_K1
+ bool "Spacemit K1 I2C driver"
+ depends on DM_I2C
+ help
+ Support for Spacemit I2C controller. It's based on
+ Driver Model.
+
source "drivers/i2c/muxes/Kconfig"
endif
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 2da649e97d3..0e3a0f9fb5f 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_SYS_I2C_RZ_RIIC) += rz_riic.o
obj-$(CONFIG_SYS_I2C_S3C24X0) += s3c24x0_i2c.o exynos_hs_i2c.o
obj-$(CONFIG_SYS_I2C_SANDBOX) += sandbox_i2c.o i2c-emul-uclass.o
obj-$(CONFIG_SYS_I2C_SH) += sh_i2c.o
+obj-$(CONFIG_SYS_I2C_SPACEMIT_K1) += k1_i2c.o
obj-$(CONFIG_SYS_I2C_STM32F7) += stm32f7_i2c.o
obj-$(CONFIG_SYS_I2C_SUN6I_P2WI) += sun6i_p2wi.o
obj-$(CONFIG_SYS_I2C_SUN8I_RSB) += sun8i_rsb.o
diff --git a/drivers/i2c/k1_i2c.c b/drivers/i2c/k1_i2c.c
new file mode 100644
index 00000000000..a08ff15803d
--- /dev/null
+++ b/drivers/i2c/k1_i2c.c
@@ -0,0 +1,523 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023-2026 Spacemit, Inc
+ * Copyright (C) 2025-2026 RISCstar Ltd.
+ */
+
+#include <asm/io.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <i2c.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <reset.h>
+#include "k1_i2c.h"
+
+#define ICR_OFFSET 0x00
+#define ISR_OFFSET 0x04
+#define ISAR_OFFSET 0x08
+#define IDBR_OFFSET 0x0c
+#define ILCR_OFFSET 0x10
+#define IWCR_OFFSET 0x14
+#define IRCR_OFFSET 0x18
+#define IBMR_OFFSET 0x1c
+#define WFIFO_OFFSET 0x20
+#define WFIFO_WPTR_OFFSET 0x24
+#define WFIFO_RPTR_OFFSET 0x28
+#define RFIFO_OFFSET 0x2c
+#define RFIFO_WPTR_OFFSET 0x30
+#define RFIFO_RPTR_OFFSET 0x34
+
+/* All transfers are described by this data structure */
+struct k1_i2c_msg {
+ u8 condition;
+ u8 acknack;
+ u8 direction;
+ u8 data;
+};
+
+struct k1_i2c {
+ u32 icr;
+ u32 isr;
+ u32 isar;
+ u32 idbr;
+ u32 ilcr;
+ u32 iwcr;
+ u32 irst_cyc;
+ u32 ibmr;
+};
+
+struct k1_i2c_priv {
+ int id;
+ void __iomem *base;
+ struct clk clk;
+ u32 clk_rate;
+};
+
+/*
+ * i2c_reset: - reset the host controller
+ *
+ */
+static void i2c_reset(void __iomem *base)
+{
+ u32 icr_mode;
+ u32 val;
+
+ /* Save bus mode (standard or fast speed) for later use */
+ icr_mode = readl(base + ICR_OFFSET) & ICR_MODE_MASK;
+ /* disable unit */
+ val = readl(base + ICR_OFFSET);
+ writel(val & ~ICR_IUE, base + ICR_OFFSET);
+ udelay(10);
+ /* reset the unit */
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_UR;
+ writel(val, base + ICR_OFFSET);
+ udelay(100);
+ /* disable unit */
+ val = readl(base + ICR_OFFSET);
+ writel(val & ~ICR_IUE, base + ICR_OFFSET);
+
+ /* set slave address */
+ writel(0x00, base + ISR_OFFSET);
+ /* set control reg values */
+ writel(I2C_ICR_INIT | icr_mode, base + ICR_OFFSET);
+ writel(I2C_ISR_INIT, base + ISR_OFFSET); /* set clear interrupt bits */
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_IUE;
+ writel(val, base + ICR_OFFSET); /* enable unit */
+ udelay(100);
+}
+
+static inline bool is_isr_set_or_clr(unsigned long isr, unsigned long set_mask,
+ unsigned long clr_mask)
+{
+ return ((isr & set_mask) == set_mask) && ((isr & clr_mask) == 0);
+}
+
+/*
+ * i2c_isr_set_cleared: - wait until certain bits of the I2C status register
+ * are set and cleared
+ *
+ * @return: 0 on success or -ETIMEDOUT.
+ */
+static int i2c_isr_set_cleared(void __iomem *base, unsigned long set_mask,
+ unsigned long clr_mask)
+{
+ int cnt = 1000, delay = 10, isr, ret;
+
+ ret = read_poll_timeout(readl, isr,
+ is_isr_set_or_clr(isr, set_mask, clr_mask),
+ delay, delay * cnt, base + ISR_OFFSET);
+ return ret;
+}
+
+/*
+ * i2c_transfer: - Transfer one byte over the i2c bus
+ *
+ * This function can transfer a byte over the i2c bus in both directions.
+ * It is used by the public API functions.
+ *
+ * @return: 0: transfer successful or error code
+ */
+static int i2c_transfer(void __iomem *base, struct k1_i2c_msg *msg)
+{
+ int ret;
+ u32 val;
+
+ if (!msg)
+ goto transfer_error_msg_empty;
+
+ switch (msg->direction) {
+ case I2C_WRITE:
+ /* check if bus is not busy */
+ if (i2c_isr_set_cleared(base, 0, ISR_IBB))
+ goto transfer_error_bus_busy;
+
+ /* start transmission */
+ val = readl(base + ICR_OFFSET);
+ val &= ~ICR_START;
+ writel(val, base + ICR_OFFSET);
+ val = readl(base + ICR_OFFSET);
+ val &= ~ICR_STOP;
+ writel(val, base + ICR_OFFSET);
+ writel(msg->data, base + IDBR_OFFSET);
+ if (msg->condition == I2C_COND_START) {
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_START;
+ writel(val, base + ICR_OFFSET);
+ }
+ if (msg->condition == I2C_COND_STOP) {
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_STOP;
+ writel(val, base + ICR_OFFSET);
+ }
+ if (msg->acknack == I2C_ACKNAK_SENDNAK) {
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_ACKNAK;
+ writel(val, base + ICR_OFFSET);
+ }
+ if (msg->acknack == I2C_ACKNAK_SENDACK) {
+ val = readl(base + ICR_OFFSET);
+ val &= ~ICR_ACKNAK;
+ writel(val, base + ICR_OFFSET);
+ }
+ val = readl(base + ICR_OFFSET);
+ val &= ~ICR_ALDIE;
+ writel(val, base + ICR_OFFSET);
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_TB;
+ writel(val, base + ICR_OFFSET);
+
+ /* transmit register empty? */
+ if (i2c_isr_set_cleared(base, ISR_ITE, 0))
+ goto transfer_error_transmit_timeout;
+
+ /* clear 'transmit empty' state */
+ val = readl(base + ISR_OFFSET);
+ val |= ISR_ITE;
+ writel(val, base + ISR_OFFSET);
+
+ /* wait for ACK from slave */
+ if (msg->acknack == I2C_ACKNAK_WAITACK)
+ if (i2c_isr_set_cleared(base, 0, ISR_ACKNAK))
+ goto transfer_error_ack_missing;
+ break;
+
+ case I2C_READ:
+ /* check if bus is not busy */
+ if (i2c_isr_set_cleared(base, 0, ISR_IBB))
+ goto transfer_error_bus_busy;
+
+ /* start receive */
+ val = readl(base + ICR_OFFSET);
+ val &= ~ICR_START;
+ writel(val, base + ICR_OFFSET);
+ val = readl(base + ICR_OFFSET);
+ val &= ~ICR_STOP;
+ writel(val, base + ICR_OFFSET);
+ if (msg->condition == I2C_COND_START) {
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_START;
+ writel(val, base + ICR_OFFSET);
+ }
+ if (msg->condition == I2C_COND_STOP) {
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_STOP;
+ writel(val, base + ICR_OFFSET);
+ }
+ if (msg->acknack == I2C_ACKNAK_SENDNAK) {
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_ACKNAK;
+ writel(val, base + ICR_OFFSET);
+ }
+ if (msg->acknack == I2C_ACKNAK_SENDACK) {
+ val = readl(base + ICR_OFFSET);
+ val &= ~ICR_ACKNAK;
+ writel(val, base + ICR_OFFSET);
+ }
+ val = readl(base + ICR_OFFSET);
+ val &= ~ICR_ALDIE;
+ writel(val, base + ICR_OFFSET);
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_TB;
+ writel(val, base + ICR_OFFSET);
+
+ /* receive register full? */
+ if (i2c_isr_set_cleared(base, ISR_IRF, 0))
+ goto transfer_error_receive_timeout;
+
+ msg->data = readl(base + IDBR_OFFSET);
+
+ /* clear 'receive empty' state */
+ val = readl(base + ISR_OFFSET);
+ val |= ISR_IRF;
+ writel(val, base + ISR_OFFSET);
+ break;
+ default:
+ goto transfer_error_illegal_param;
+ }
+
+ return 0;
+
+transfer_error_msg_empty:
+ debug("%s: error: 'msg' is empty\n", __func__);
+ ret = -EINVAL;
+ goto i2c_transfer_finish;
+
+transfer_error_transmit_timeout:
+ debug("%s: error: transmit timeout\n", __func__);
+ ret = -ETIMEDOUT;
+ goto i2c_transfer_finish;
+
+transfer_error_ack_missing:
+ debug("%s: error: ACK missing\n", __func__);
+ ret = -EREMOTEIO;
+ goto i2c_transfer_finish;
+
+transfer_error_receive_timeout:
+ debug("%s: error: receive timeout\n", __func__);
+ ret = -ETIMEDOUT;
+ goto i2c_transfer_finish;
+
+transfer_error_illegal_param:
+ debug("%s: error: illegal parameters\n", __func__);
+ ret = -EINVAL;
+ goto i2c_transfer_finish;
+
+transfer_error_bus_busy:
+ debug("%s: error: bus is busy\n", __func__);
+ ret = -EIO;
+ goto i2c_transfer_finish;
+
+i2c_transfer_finish:
+ debug("%s: ISR: 0x%04x\n", __func__, readl(base + ISR_OFFSET));
+ i2c_reset(base);
+ return ret;
+}
+
+static int __i2c_read(void __iomem *base, uchar chip, u8 *addr, int alen,
+ uchar *buffer, int len)
+{
+ struct k1_i2c_msg msg;
+ int ret;
+
+ debug("%s(chip=0x%02x, addr=0x%02x, alen=0x%02x, len=0x%02x)\n",
+ __func__, chip, *addr, alen, len);
+
+ if (len == 0) {
+ pr_err("reading zero byte is invalid\n");
+ return -EINVAL;
+ }
+
+ i2c_reset(base);
+
+ /* dummy chip address write */
+ debug("%s: dummy chip address write\n", __func__);
+ msg.condition = I2C_COND_START;
+ msg.acknack = I2C_ACKNAK_WAITACK;
+ msg.direction = I2C_WRITE;
+ msg.data = (chip << 1);
+ msg.data &= 0xFE;
+ ret = i2c_transfer(base, &msg);
+ if (ret)
+ return ret;
+
+ /*
+ * send memory address bytes;
+ * alen defines how much bytes we have to send.
+ */
+ while (--alen >= 0) {
+ debug("%s: send address byte %02x (alen=%d)\n",
+ __func__, *addr, alen);
+ msg.condition = I2C_COND_NORMAL;
+ msg.acknack = I2C_ACKNAK_WAITACK;
+ msg.direction = I2C_WRITE;
+ msg.data = addr[alen];
+ ret = i2c_transfer(base, &msg);
+ if (ret)
+ return ret;
+ }
+
+ /* start read sequence */
+ debug("%s: start read sequence\n", __func__);
+ msg.condition = I2C_COND_START;
+ msg.acknack = I2C_ACKNAK_WAITACK;
+ msg.direction = I2C_WRITE;
+ msg.data = (chip << 1);
+ msg.data |= 0x01;
+ ret = i2c_transfer(base, &msg);
+ if (ret)
+ return ret;
+
+ /* read bytes; send NACK at last byte */
+ while (len--) {
+ if (len == 0) {
+ msg.condition = I2C_COND_STOP;
+ msg.acknack = I2C_ACKNAK_SENDNAK;
+ } else {
+ msg.condition = I2C_COND_NORMAL;
+ msg.acknack = I2C_ACKNAK_SENDACK;
+ }
+
+ msg.direction = I2C_READ;
+ msg.data = 0x00;
+ ret = i2c_transfer(base, &msg);
+ if (ret)
+ return ret;
+
+ *buffer = msg.data;
+ debug("%s: reading byte (%p)=0x%02x\n",
+ __func__, buffer, *buffer);
+ buffer++;
+ }
+
+ i2c_reset(base);
+
+ return 0;
+}
+
+static int __i2c_write(struct k1_i2c *base, uchar chip, u8 *addr, int alen,
+ uchar *buffer, int len)
+{
+ struct k1_i2c_msg msg;
+ int ret;
+
+ debug("%s(chip=0x%02x, addr=0x%02x, alen=0x%02x, len=0x%02x)\n",
+ __func__, chip, *addr, alen, len);
+
+ i2c_reset(base);
+
+ /* chip address write */
+ debug("%s: chip address write\n", __func__);
+ msg.condition = I2C_COND_START;
+ msg.acknack = I2C_ACKNAK_WAITACK;
+ msg.direction = I2C_WRITE;
+ msg.data = (chip << 1);
+ msg.data &= 0xFE;
+ ret = i2c_transfer(base, &msg);
+ if (ret)
+ return ret;
+
+ /*
+ * send memory address bytes;
+ * alen defines how much bytes we have to send.
+ */
+ while (--alen >= 0) {
+ debug("%s: send address byte %02x (alen=%d)\n",
+ __func__, *addr, alen);
+ msg.condition = I2C_COND_NORMAL;
+ msg.acknack = I2C_ACKNAK_WAITACK;
+ msg.direction = I2C_WRITE;
+ msg.data = addr[alen];
+ ret = i2c_transfer(base, &msg);
+ if (ret)
+ return ret;
+ }
+
+ /* write bytes; send NACK at last byte */
+ while (len--) {
+ debug("%s: writing byte (%p)=0x%02x\n",
+ __func__, buffer, *buffer);
+
+ if (len == 0)
+ msg.condition = I2C_COND_STOP;
+ else
+ msg.condition = I2C_COND_NORMAL;
+
+ msg.acknack = I2C_ACKNAK_WAITACK;
+ msg.direction = I2C_WRITE;
+ msg.data = *(buffer++);
+
+ ret = i2c_transfer(base, &msg);
+ if (ret)
+ return ret;
+ }
+
+ i2c_reset(base);
+
+ return 0;
+}
+
+static int k1_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
+{
+ struct k1_i2c_priv *i2c = dev_get_priv(bus);
+ struct i2c_msg *dmsg, *omsg, dummy;
+
+ memset(&dummy, 0, sizeof(struct i2c_msg));
+
+ /*
+ * We expect either two messages (one with an offset and one with the
+ * actual data) or one message (just data or offset/data combined)
+ */
+ if (nmsgs > 2 || nmsgs == 0) {
+ debug("%s: Only one or two messages are supported.", __func__);
+ return -EINVAL;
+ }
+
+ omsg = nmsgs == 1 ? &dummy : msg;
+ dmsg = nmsgs == 1 ? msg : msg + 1;
+
+ if (dmsg->flags & I2C_M_RD)
+ return __i2c_read(i2c->base, dmsg->addr, omsg->buf,
+ omsg->len, dmsg->buf, dmsg->len);
+ else
+ return __i2c_write(i2c->base, dmsg->addr, omsg->buf,
+ omsg->len, dmsg->buf, dmsg->len);
+}
+
+static int k1_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
+{
+ struct k1_i2c_priv *priv = dev_get_priv(bus);
+ void __iomem *base = priv->base;
+ u32 val;
+
+ if (speed > I2C_SPEED_STANDARD_RATE)
+ val = ICR_FM;
+ else
+ val = ICR_SM;
+ clrsetbits_le32(base + ICR_OFFSET, ICR_MODE_MASK, val);
+
+ return 0;
+}
+
+static int k1_i2c_probe(struct udevice *bus)
+{
+ struct k1_i2c_priv *priv = dev_get_priv(bus);
+ struct reset_ctl reset;
+ int ret;
+
+ priv->id = dev_seq(bus);
+
+ /*
+ * The upstream K1 dts intentionally omits the 'resets' property
+ * on i2c nodes — the apbc clock-enable path performs the
+ * controller reset internally as part of clock gating. Treat the
+ * reset lookup as optional so we work on the kernel-mainline DT.
+ */
+ ret = reset_get_by_index(bus, 0, &reset);
+ if (!ret) {
+ reset_assert(&reset);
+ udelay(10);
+ reset_deassert(&reset);
+ udelay(10);
+ } else if (ret != -ENOENT && ret != -ENODATA) {
+ dev_err(bus, "%s: reset lookup failed (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ ret = clk_get_by_index(bus, 0, &priv->clk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(&priv->clk);
+ if (ret && ret != -ENOSYS && ret != -EOPNOTSUPP) {
+ debug("%s: failed to enable clock\n", __func__);
+ return ret;
+ }
+ priv->clk_rate = clk_get_rate(&priv->clk);
+
+ priv->base = (void *)devfdt_get_addr_ptr(bus);
+ k1_i2c_set_bus_speed(bus, priv->clk_rate);
+ return 0;
+}
+
+static const struct dm_i2c_ops k1_i2c_ops = {
+ .xfer = k1_i2c_xfer,
+ .set_bus_speed = k1_i2c_set_bus_speed,
+};
+
+static const struct udevice_id k1_i2c_ids[] = {
+ { .compatible = "spacemit,k1-i2c" },
+ { }
+};
+
+U_BOOT_DRIVER(i2c_spacemit) = {
+ .name = "i2c_spacemit",
+ .id = UCLASS_I2C,
+ .of_match = k1_i2c_ids,
+ .probe = k1_i2c_probe,
+ .priv_auto = sizeof(struct k1_i2c_priv),
+ .ops = &k1_i2c_ops,
+};
diff --git a/drivers/i2c/k1_i2c.h b/drivers/i2c/k1_i2c.h
new file mode 100644
index 00000000000..856947aa43b
--- /dev/null
+++ b/drivers/i2c/k1_i2c.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2023-2026 Spacemit Ltd.
+ * Copyright (C) 2025-2026 RISCstar Ltd.
+ */
+
+#ifndef __SPACEMIT_I2C_H
+#define __SPACEMIT_I2C_H
+
+/* Shall the current transfer have a start/stop condition? */
+#define I2C_COND_NORMAL 0
+#define I2C_COND_START 1
+#define I2C_COND_STOP 2
+
+/* Shall the current transfer be ack/nacked or being waited for it? */
+#define I2C_ACKNAK_WAITACK 1
+#define I2C_ACKNAK_SENDACK 2
+#define I2C_ACKNAK_SENDNAK 4
+
+/* Specify who shall transfer the data (master or slave) */
+#define I2C_READ 0
+#define I2C_WRITE 1
+
+#if (CONFIG_SYS_I2C_SPEED == 400000)
+#define I2C_ICR_INIT (ICR_FM | ICR_BEIE | ICR_IRFIE | ICR_ITEIE | \
+ ICR_GCD | ICR_SCLE)
+#else
+#define I2C_ICR_INIT (ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | \
+ ICR_SCLE)
+#endif
+
+/* ----- Control register bits ---------------------------------------- */
+
+#define ICR_START 0x1 /* start bit */
+#define ICR_STOP 0x2 /* stop bit */
+#define ICR_ACKNAK 0x4 /* send ACK(0) or NAK(1) */
+#define ICR_TB 0x8 /* transfer byte bit */
+#define ICR_MA BIT(12) /* master abort */
+#define ICR_SCLE BIT(13) /* master clock enable, mona SCLEA */
+#define ICR_IUE BIT(14) /* unit enable */
+#define ICR_GCD BIT(21) /* general call disable */
+#define ICR_ITEIE BIT(19) /* enable tx interrupts */
+#define ICR_IRFIE BIT(20) /* enable rx interrupts, mona: DRFIE */
+#define ICR_BEIE BIT(22) /* enable bus error ints */
+#define ICR_SSDIE BIT(24) /* slave STOP detected int enable */
+#define ICR_ALDIE BIT(18) /* enable arbitration interrupt */
+#define ICR_SADIE BIT(23) /* slave address detected int enable */
+#define ICR_UR BIT(10) /* unit reset */
+#define ICR_SM (0x0) /* Standard Mode */
+#define ICR_FM BIT(8) /* Fast Mode */
+#define ICR_MODE_MASK (0x300) /* Mode mask */
+
+/* ----- Status register bits ----------------------------------------- */
+
+#define ISR_RWM BIT(13) /* read/write mode */
+#define ISR_ACKNAK BIT(14) /* ack/nak status */
+#define ISR_UB BIT(15) /* unit busy */
+#define ISR_IBB BIT(16) /* bus busy */
+#define ISR_SSD BIT(24) /* slave stop detected */
+#define ISR_ALD BIT(18) /* arbitration loss detected */
+#define ISR_ITE BIT(19) /* tx buffer empty */
+#define ISR_IRF BIT(20) /* rx buffer full */
+#define ISR_GCAD BIT(21) /* general call address detected */
+#define ISR_SAD BIT(23) /* slave address detected */
+#define ISR_BED BIT(22) /* bus error no ACK/NAK */
+
+#define I2C_ISR_INIT 0x1FDE000
+
+#endif
diff --git a/drivers/misc/qfw_acpi.c b/drivers/misc/qfw_acpi.c
index 721f42ecbc3..afa3b820395 100644
--- a/drivers/misc/qfw_acpi.c
+++ b/drivers/misc/qfw_acpi.c
@@ -265,6 +265,7 @@ out:
struct acpi_rsdp *rsdp = ctx->rsdp;
rsdp->length = sizeof(*rsdp);
+ rsdp->ext_checksum = 0;
rsdp->ext_checksum = table_compute_checksum((u8 *)rsdp, sizeof(*rsdp));
gd_set_acpi_start(acpi_get_rsdp_addr());
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c
index d9c05b223d5..c2424d7875c 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -645,9 +645,11 @@ static int dwmci_set_ios(struct mmc *mmc)
int ret;
if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
- ret = regulator_set_value(mmc->vqmmc_supply, 1800000);
+ ret = regulator_set_value_clamp(mmc->vqmmc_supply,
+ 1700000, 1800000, 1950000);
else
- ret = regulator_set_value(mmc->vqmmc_supply, 3300000);
+ ret = regulator_set_value_clamp(mmc->vqmmc_supply,
+ 2700000, 3300000, 3600000);
if (ret && ret != -ENOSYS)
return ret;
}
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 08594e10266..71a6d8ea2f5 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -551,7 +551,7 @@ void sdhci_set_uhs_timing(struct sdhci_host *host)
void sdhci_set_voltage(struct sdhci_host *host)
{
- if (IS_ENABLED(CONFIG_MMC_IO_VOLTAGE)) {
+ if (CONFIG_IS_ENABLED(MMC_IO_VOLTAGE)) {
struct mmc *mmc = (struct mmc *)host->mmc;
u32 ctrl;
diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile
index 44e67cd913a..9a96be11e6d 100644
--- a/drivers/mtd/spi/Makefile
+++ b/drivers/mtd/spi/Makefile
@@ -8,14 +8,13 @@ spi-nor-y := sf_probe.o spi-nor-ids.o
ifdef CONFIG_XPL_BUILD
obj-$(CONFIG_SPL_SPI_BOOT) += fsl_espi_spl.o
+endif
+
ifeq ($(CONFIG_$(PHASE_)SPI_FLASH_TINY),y)
spi-nor-y += spi-nor-tiny.o
else
spi-nor-y += spi-nor-core.o
endif
-else
-spi-nor-y += spi-nor-core.o
-endif
obj-$(CONFIG_SPI_FLASH) += spi-nor.o
obj-$(CONFIG_SPI_FLASH_DATAFLASH) += sf_dataflash.o
diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c
index 7100b64bf22..dee03662d45 100644
--- a/drivers/mtd/spi/sf_probe.c
+++ b/drivers/mtd/spi/sf_probe.c
@@ -229,9 +229,11 @@ static int spi_flash_std_remove(struct udevice *dev)
spi_mem_dirmap_destroy(flash->dirmap.rdesc);
}
- ret = spi_nor_remove(flash);
- if (ret)
- return ret;
+ if (CONFIG_IS_ENABLED(SPI_FLASH_SOFT_RESET)) {
+ ret = spi_nor_remove(flash);
+ if (ret)
+ return ret;
+ }
if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
spi_flash_mtd_unregister(flash);
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 46a95a1ab6b..fd30aaeeaa8 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -378,6 +378,26 @@ config ASPEED_AST2600_PINCTRL
uses Generic Pinctrl framework and is compatible with the Linux
driver, i.e. it uses the same device tree configuration.
+config ASPEED_AST2700_SOC0_PINCTRL
+ bool "Aspeed AST2700 SoC0 pin control driver"
+ depends on DM && PINCTRL_GENERIC && ASPEED_AST2700
+ select PINCONF
+ default y
+ help
+ Support pin multiplexing control on Aspeed ast2700 SoC0. The driver
+ uses Generic Pinctrl framework and is compatible with the Linux
+ driver, i.e. it uses the same device tree configuration.
+
+config ASPEED_AST2700_SOC1_PINCTRL
+ bool "Aspeed AST2700 SoC1 pin control driver"
+ depends on DM && PINCTRL_GENERIC && ASPEED_AST2700
+ select PINCONF
+ default y
+ help
+ Support pin multiplexing control on Aspeed ast2700 SoC1. The driver
+ uses Generic Pinctrl framework and is compatible with the Linux
+ driver, i.e. it uses the same device tree configuration.
+
config PINCTRL_K210
bool "Kendryte K210 Fully-Programmable Input/Output Array driver"
depends on DM && PINCTRL_GENERIC
@@ -420,6 +440,7 @@ source "drivers/pinctrl/nxp/Kconfig"
source "drivers/pinctrl/qcom/Kconfig"
source "drivers/pinctrl/renesas/Kconfig"
source "drivers/pinctrl/rockchip/Kconfig"
+source "drivers/pinctrl/spacemit/Kconfig"
source "drivers/pinctrl/sunxi/Kconfig"
source "drivers/pinctrl/tegra/Kconfig"
source "drivers/pinctrl/uniphier/Kconfig"
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 6c6e8b59122..91149796bb5 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_$(PHASE_)PINCTRL_ROCKCHIP) += rockchip/
obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o
obj-$(CONFIG_PINCTRL_SCMI) += pinctrl-scmi.o
obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
+obj-$(CONFIG_$(PHASE_)PINCTRL_SPACEMIT_K1) += spacemit/
obj-$(CONFIG_PINCTRL_STARFIVE) += starfive/
obj-$(CONFIG_PINCTRL_STI) += pinctrl-sti.o
obj-$(CONFIG_PINCTRL_STM32) += pinctrl_stm32.o
diff --git a/drivers/pinctrl/aspeed/Makefile b/drivers/pinctrl/aspeed/Makefile
index a3e01ed1ca9..33b23fce629 100644
--- a/drivers/pinctrl/aspeed/Makefile
+++ b/drivers/pinctrl/aspeed/Makefile
@@ -1,2 +1,4 @@
obj-$(CONFIG_ASPEED_AST2500_PINCTRL) += pinctrl_ast2500.o
obj-$(CONFIG_ASPEED_AST2600_PINCTRL) += pinctrl_ast2600.o
+obj-$(CONFIG_ASPEED_AST2700_SOC0_PINCTRL) += pinctrl_ast2700_soc0.o
+obj-$(CONFIG_ASPEED_AST2700_SOC1_PINCTRL) += pinctrl_ast2700_soc1.o
diff --git a/drivers/pinctrl/aspeed/pinctrl_ast2700_soc0.c b/drivers/pinctrl/aspeed/pinctrl_ast2700_soc0.c
new file mode 100644
index 00000000000..b5111d7ee15
--- /dev/null
+++ b/drivers/pinctrl/aspeed/pinctrl_ast2700_soc0.c
@@ -0,0 +1,623 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 ASPEED Technology Inc.
+ */
+
+#include <asm/io.h>
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <errno.h>
+#include <vsprintf.h>
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#define SCU010 0x010
+#define SCU200 0x200
+#define SCU400 0x400
+#define SCU404 0x404
+#define SCU408 0x408
+#define SCU410 0x410
+/* One IO control register per ball, GPIO18A0..GPIO18B3 */
+#define SCU480 0x480
+
+/* Indexes into ast2700_soc0_pins[], in GPIO18A0..GPIO18B3 order */
+enum {
+ AC14,
+ AE15,
+ AD14,
+ AE14,
+ AF14,
+ AB13,
+ AB14,
+ AF15,
+ AF13,
+ AC13,
+ AD13,
+ AE13,
+};
+
+#define USB_PORTA_ROUTE BIT(9)
+#define USB_PORTB_ROUTE BIT(10)
+
+#define PORTA_U3_MODE GENMASK(1, 0)
+#define PORTA_U2_MODE GENMASK(3, 2)
+#define PORTB_U3_MODE GENMASK(5, 4)
+#define PORTB_U2_MODE GENMASK(7, 6)
+#define PORTA_MODE GENMASK(25, 24)
+#define PORTB_MODE GENMASK(29, 28)
+
+#define PINMUX(_function, _group, _offset, _mask, _val) \
+ { \
+ .function = _function, \
+ .group = _group, \
+ .offset = _offset, \
+ .mask = _mask, \
+ .val = _val, \
+ }
+
+struct ast2700_soc0_pinmux {
+ const char *function;
+ const char *group;
+ u32 offset;
+ u32 mask;
+ u32 val;
+};
+
+struct ast2700_soc0_sig {
+ u32 offset;
+ u32 mask;
+ const char *name;
+};
+
+struct ast2700_soc0_pin {
+ const char *name;
+ const struct ast2700_soc0_sig *sigs;
+ unsigned int nsigs;
+};
+
+struct ast2700_soc0_pinctrl_priv {
+ void __iomem *base;
+};
+
+struct ast2700_soc0_group {
+ const char *name;
+ const u8 *pins;
+ unsigned int npins;
+};
+
+static const u8 ast2700_soc0_emmcg1_pins[] = { AC14, AE15, AD14 };
+static const u8 ast2700_soc0_emmcg4_pins[] = {
+ AC14, AE15, AD14, AE14, AF14, AB13,
+};
+
+static const u8 ast2700_soc0_emmcg8_pins[] = {
+ AC14, AE15, AD14, AE14, AF14, AB13, AF13, AC13, AD13, AE13,
+};
+
+static const u8 ast2700_soc0_emmcwpn_pins[] = { AF15 };
+static const u8 ast2700_soc0_emmccdn_pins[] = { AB14 };
+static const u8 ast2700_soc0_vgaddc_pins[] = { AD13, AE13 };
+static const u8 ast2700_soc0_vb1_pins[] = { AC14, AE15, AD14, AE14 };
+static const u8 ast2700_soc0_vb0_pins[] = { AF15, AB14, AF13, AC13 };
+static const u8 ast2700_soc0_tsprstn_pins[] = { AF13 };
+static const u8 ast2700_soc0_ufsclki_pins[] = { AC13 };
+
+#define AST2700_SOC0_GROUP(_name, _pins) \
+ { \
+ .name = _name, \
+ .pins = _pins, \
+ .npins = ARRAY_SIZE(_pins), \
+ }
+
+/*
+ * The USB, JTAG and PCIe RC groups control internal routing only; they
+ * have no package balls, hence no group pins to configure.
+ */
+static const struct ast2700_soc0_group ast2700_soc0_groups[] = {
+ AST2700_SOC0_GROUP("EMMCG1", ast2700_soc0_emmcg1_pins),
+ AST2700_SOC0_GROUP("EMMCG4", ast2700_soc0_emmcg4_pins),
+ AST2700_SOC0_GROUP("EMMCG8", ast2700_soc0_emmcg8_pins),
+ AST2700_SOC0_GROUP("EMMCWPN", ast2700_soc0_emmcwpn_pins),
+ AST2700_SOC0_GROUP("EMMCCDN", ast2700_soc0_emmccdn_pins),
+ AST2700_SOC0_GROUP("VGADDC", ast2700_soc0_vgaddc_pins),
+ AST2700_SOC0_GROUP("VB1", ast2700_soc0_vb1_pins),
+ AST2700_SOC0_GROUP("VB0", ast2700_soc0_vb0_pins),
+ AST2700_SOC0_GROUP("TSPRSTN", ast2700_soc0_tsprstn_pins),
+ AST2700_SOC0_GROUP("UFSCLKI", ast2700_soc0_ufsclki_pins),
+ { "USB3A" },
+ { "USB3AAP" },
+ { "USB3ABP" },
+ { "USB3B" },
+ { "USB3BAP" },
+ { "USB3BBP" },
+ { "USB2A" },
+ { "USB2AAP" },
+ { "USB2ABP" },
+ { "USB2ADAP" },
+ { "USB2AH" },
+ { "USB2AHAP" },
+ { "USB2B" },
+ { "USB2BBP" },
+ { "USB2BAP" },
+ { "USB2BDBP" },
+ { "USB2BH" },
+ { "USB2BHBP" },
+ { "JTAG0" },
+ { "PCIERC0PERST" },
+ { "PCIERC1PERST" },
+};
+
+static const char * const ast2700_soc0_functions[] = {
+ "EMMC",
+ "VB",
+ "TSPRSTN",
+ "UFSCLKI",
+ "VGADDC",
+ "USB3AXHD",
+ "USB3AXHPD",
+ "USB3AXH",
+ "USB3AXHP",
+ "USB3AXH2B",
+ "USB3AXHP2B",
+ "USB3BXHD",
+ "USB3BXHPD",
+ "USB3BXH",
+ "USB3BXHP",
+ "USB3BXH2A",
+ "USB3BXHP2A",
+ "USB2AXHD1",
+ "USB2AXHPD1",
+ "USB2AXH",
+ "USB2AXHP",
+ "USB2AXH2B",
+ "USB2AXHP2B",
+ "USB2AD1",
+ "USB2AHPD0",
+ "USB2AH",
+ "USB2AHP",
+ "USB2AD0",
+ "USB2BXHD1",
+ "USB2BXHPD1",
+ "USB2BXH",
+ "USB2BXHP",
+ "USB2BXH2A",
+ "USB2BXHP2A",
+ "USB2BD1",
+ "USB2BHPD0",
+ "USB2BH",
+ "USB2BHP",
+ "USB2BD0",
+ "JTAGPSP",
+ "JTAGSSP",
+ "JTAGTSP",
+ "JTAGDDR",
+ "JTAGUSB3A",
+ "JTAGUSB3B",
+ "JTAGPCIEA",
+ "JTAGPCIEB",
+ "JTAGM0",
+ "PCIERC0PERST",
+ "PCIERC1PERST",
+};
+
+static const struct ast2700_soc0_pinmux ast2700_soc0_pinmuxes[] = {
+ PINMUX("EMMC", "EMMCG1", SCU400, GENMASK(2, 0), GENMASK(2, 0)),
+ PINMUX("EMMC", "EMMCG4", SCU400, GENMASK(5, 0), GENMASK(5, 0)),
+ PINMUX("EMMC", "EMMCG8", SCU400,
+ GENMASK(5, 0) | GENMASK(11, 8),
+ GENMASK(5, 0) | GENMASK(11, 8)),
+ PINMUX("EMMC", "EMMCWPN", SCU400, BIT(7), BIT(7)),
+ PINMUX("EMMC", "EMMCCDN", SCU400, BIT(6), BIT(6)),
+ PINMUX("VB", "VB1", SCU404, GENMASK(3, 0), GENMASK(3, 0)),
+ PINMUX("VB", "VB0", SCU010, BIT(17), BIT(17)),
+ PINMUX("TSPRSTN", "TSPRSTN", SCU010, BIT(9), BIT(9)),
+ PINMUX("UFSCLKI", "UFSCLKI", SCU010, BIT(19), BIT(19)),
+ PINMUX("VGADDC", "VGADDC", SCU404, GENMASK(11, 10),
+ GENMASK(11, 10)),
+
+ /*
+ * The Linux driver models the USB2/USB3 routing with additional
+ * virtual PHY pins (PORTA/PORTB_U2_PHY, PORTA/PORTB_U3_PHY) purely
+ * to reject conflicting cross-coupled selections; they carry no
+ * register writes. U-Boot applies the states given by the device
+ * tree without strict conflict checking, so only the real selector
+ * fields are programmed here while the group names stay identical.
+ */
+ PINMUX("USB3AXHD", "USB3A", SCU410,
+ PORTA_U3_MODE | USB_PORTA_ROUTE, USB_PORTA_ROUTE),
+ PINMUX("USB3AXHPD", "USB3A", SCU410,
+ PORTA_U3_MODE | USB_PORTA_ROUTE, 0),
+ PINMUX("USB3AXH", "USB3AAP", SCU410,
+ PORTA_U3_MODE | USB_PORTA_ROUTE, (2 << 0) | USB_PORTA_ROUTE),
+ PINMUX("USB3AXHP", "USB3AAP", SCU410,
+ PORTA_U3_MODE | USB_PORTA_ROUTE, 2 << 0),
+ PINMUX("USB3AXH2B", "USB3ABP", SCU410,
+ PORTA_U3_MODE | USB_PORTA_ROUTE, (3 << 0) | USB_PORTA_ROUTE),
+ PINMUX("USB3AXHP2B", "USB3ABP", SCU410,
+ PORTA_U3_MODE | USB_PORTA_ROUTE, 3 << 0),
+
+ PINMUX("USB3BXHD", "USB3B", SCU410,
+ PORTB_U3_MODE | USB_PORTB_ROUTE, USB_PORTB_ROUTE),
+ PINMUX("USB3BXHPD", "USB3B", SCU410,
+ PORTB_U3_MODE | USB_PORTB_ROUTE, 0),
+ PINMUX("USB3BXH", "USB3BBP", SCU410,
+ PORTB_U3_MODE | USB_PORTB_ROUTE, (2 << 4) | USB_PORTB_ROUTE),
+ PINMUX("USB3BXHP", "USB3BBP", SCU410,
+ PORTB_U3_MODE | USB_PORTB_ROUTE, 2 << 4),
+ PINMUX("USB3BXH2A", "USB3BAP", SCU410,
+ PORTB_U3_MODE | USB_PORTB_ROUTE, (3 << 4) | USB_PORTB_ROUTE),
+ PINMUX("USB3BXHP2A", "USB3BAP", SCU410,
+ PORTB_U3_MODE | USB_PORTB_ROUTE, 3 << 4),
+
+ PINMUX("USB2AXHD1", "USB2A", SCU410,
+ PORTA_U2_MODE | USB_PORTA_ROUTE, USB_PORTA_ROUTE),
+ PINMUX("USB2AXHPD1", "USB2A", SCU410,
+ PORTA_U2_MODE | USB_PORTA_ROUTE, 0),
+ PINMUX("USB2AXH", "USB2AAP", SCU410,
+ PORTA_U2_MODE | USB_PORTA_ROUTE, (2 << 2) | USB_PORTA_ROUTE),
+ PINMUX("USB2AXHP", "USB2AAP", SCU410,
+ PORTA_U2_MODE | USB_PORTA_ROUTE, 2 << 2),
+ PINMUX("USB2AXH2B", "USB2ABP", SCU410,
+ PORTA_U2_MODE | USB_PORTA_ROUTE, (3 << 2) | USB_PORTA_ROUTE),
+ PINMUX("USB2AXHP2B", "USB2ABP", SCU410,
+ PORTA_U2_MODE | USB_PORTA_ROUTE, 3 << 2),
+ PINMUX("USB2AD1", "USB2ADAP", SCU410, PORTA_U2_MODE, 1 << 2),
+ PINMUX("USB2AHPD0", "USB2AH", SCU410, PORTA_MODE, 0),
+ PINMUX("USB2AH", "USB2AHAP", SCU410, PORTA_MODE, 2 << 24),
+ PINMUX("USB2AHP", "USB2AHAP", SCU410, PORTA_MODE, 3 << 24),
+ PINMUX("USB2AD0", "USB2AHAP", SCU410, PORTA_MODE, 1 << 24),
+
+ PINMUX("USB2BXHD1", "USB2B", SCU410,
+ PORTB_U2_MODE | USB_PORTB_ROUTE, USB_PORTB_ROUTE),
+ PINMUX("USB2BXHPD1", "USB2B", SCU410,
+ PORTB_U2_MODE | USB_PORTB_ROUTE, 0),
+ PINMUX("USB2BXH", "USB2BBP", SCU410,
+ PORTB_U2_MODE | USB_PORTB_ROUTE, (2 << 6) | USB_PORTB_ROUTE),
+ PINMUX("USB2BXHP", "USB2BBP", SCU410,
+ PORTB_U2_MODE | USB_PORTB_ROUTE, 2 << 6),
+ PINMUX("USB2BXH2A", "USB2BAP", SCU410,
+ PORTB_U2_MODE | USB_PORTB_ROUTE, (3 << 6) | USB_PORTB_ROUTE),
+ PINMUX("USB2BXHP2A", "USB2BAP", SCU410,
+ PORTB_U2_MODE | USB_PORTB_ROUTE, 3 << 6),
+ PINMUX("USB2BD1", "USB2BDBP", SCU410, PORTB_U2_MODE, 1 << 6),
+ PINMUX("USB2BHPD0", "USB2BH", SCU410, PORTB_MODE, 0),
+ PINMUX("USB2BH", "USB2BHBP", SCU410, PORTB_MODE, 2 << 28),
+ PINMUX("USB2BHP", "USB2BHBP", SCU410, PORTB_MODE, 3 << 28),
+ PINMUX("USB2BD0", "USB2BHBP", SCU410, PORTB_MODE, 1 << 28),
+
+ PINMUX("JTAGPSP", "JTAG0", SCU408, GENMASK(12, 5), 0x00 << 5),
+ PINMUX("JTAGSSP", "JTAG0", SCU408, GENMASK(12, 5), 0x41 << 5),
+ PINMUX("JTAGTSP", "JTAG0", SCU408, GENMASK(12, 5), 0x42 << 5),
+ PINMUX("JTAGDDR", "JTAG0", SCU408, GENMASK(12, 5), 0x43 << 5),
+ PINMUX("JTAGUSB3A", "JTAG0", SCU408, GENMASK(12, 5), 0x44 << 5),
+ PINMUX("JTAGUSB3B", "JTAG0", SCU408, GENMASK(12, 5), 0x45 << 5),
+ PINMUX("JTAGPCIEA", "JTAG0", SCU408, GENMASK(12, 5), 0x46 << 5),
+ PINMUX("JTAGPCIEB", "JTAG0", SCU408, GENMASK(12, 5), 0x47 << 5),
+ PINMUX("JTAGM0", "JTAG0", SCU408, GENMASK(12, 5), 0x08 << 5),
+ PINMUX("PCIERC0PERST", "PCIERC0PERST", SCU200, BIT(21), BIT(21)),
+ PINMUX("PCIERC1PERST", "PCIERC1PERST", SCU200, BIT(19), BIT(19)),
+};
+
+/*
+ * Signals muxable on each package ball, strongest first. A pin acts as
+ * GPIO when none of its signal enable bits are set; gpio_request_enable
+ * clears them all. The pin order follows the GPIO18A0..GPIO18B3 numbering
+ * used by the gpio-ranges of the SoC0 GPIO controller.
+ */
+static const struct ast2700_soc0_sig ast2700_soc0_ac14_sigs[] = {
+ { SCU400, BIT(0), "EMMCCLK" },
+ { SCU404, BIT(0), "VB1CS" },
+};
+
+static const struct ast2700_soc0_sig ast2700_soc0_ae15_sigs[] = {
+ { SCU400, BIT(1), "EMMCCMD" },
+ { SCU404, BIT(1), "VB1CK" },
+};
+
+static const struct ast2700_soc0_sig ast2700_soc0_ad14_sigs[] = {
+ { SCU400, BIT(2), "EMMCDAT0" },
+ { SCU404, BIT(2), "VB1MOSI" },
+};
+
+static const struct ast2700_soc0_sig ast2700_soc0_ae14_sigs[] = {
+ { SCU400, BIT(3), "EMMCDAT1" },
+ { SCU404, BIT(3), "VB1MISO" },
+};
+
+static const struct ast2700_soc0_sig ast2700_soc0_af14_sigs[] = {
+ { SCU400, BIT(4), "EMMCDAT2" },
+};
+
+static const struct ast2700_soc0_sig ast2700_soc0_ab13_sigs[] = {
+ { SCU400, BIT(5), "EMMCDAT3" },
+};
+
+static const struct ast2700_soc0_sig ast2700_soc0_ab14_sigs[] = {
+ { SCU400, BIT(6), "EMMCCDN" },
+ { SCU010, BIT(17), "VB0CS" },
+};
+
+static const struct ast2700_soc0_sig ast2700_soc0_af15_sigs[] = {
+ { SCU400, BIT(7), "EMMCWPN" },
+ { SCU010, BIT(17), "VB0CK" },
+};
+
+static const struct ast2700_soc0_sig ast2700_soc0_af13_sigs[] = {
+ { SCU010, BIT(9), "TSPRSTN" },
+ { SCU400, BIT(8), "EMMCDAT4" },
+ { SCU010, BIT(17), "VB0MOSI" },
+};
+
+static const struct ast2700_soc0_sig ast2700_soc0_ac13_sigs[] = {
+ { SCU010, BIT(19), "UFSCLKI" },
+ { SCU400, BIT(9), "EMMCDAT5" },
+ { SCU010, BIT(17), "VB0MISO" },
+};
+
+static const struct ast2700_soc0_sig ast2700_soc0_ad13_sigs[] = {
+ { SCU400, BIT(10), "EMMCDAT6" },
+ { SCU404, BIT(10), "DDCCLK" },
+};
+
+static const struct ast2700_soc0_sig ast2700_soc0_ae13_sigs[] = {
+ { SCU400, BIT(11), "EMMCDAT7" },
+ { SCU404, BIT(11), "DDCDAT" },
+};
+
+#define AST2700_SOC0_PIN(_name, _sigs) \
+ { \
+ .name = _name, \
+ .sigs = _sigs, \
+ .nsigs = ARRAY_SIZE(_sigs), \
+ }
+
+static const struct ast2700_soc0_pin ast2700_soc0_pins[] = {
+ AST2700_SOC0_PIN("AC14", ast2700_soc0_ac14_sigs),
+ AST2700_SOC0_PIN("AE15", ast2700_soc0_ae15_sigs),
+ AST2700_SOC0_PIN("AD14", ast2700_soc0_ad14_sigs),
+ AST2700_SOC0_PIN("AE14", ast2700_soc0_ae14_sigs),
+ AST2700_SOC0_PIN("AF14", ast2700_soc0_af14_sigs),
+ AST2700_SOC0_PIN("AB13", ast2700_soc0_ab13_sigs),
+ AST2700_SOC0_PIN("AB14", ast2700_soc0_ab14_sigs),
+ AST2700_SOC0_PIN("AF15", ast2700_soc0_af15_sigs),
+ AST2700_SOC0_PIN("AF13", ast2700_soc0_af13_sigs),
+ AST2700_SOC0_PIN("AC13", ast2700_soc0_ac13_sigs),
+ AST2700_SOC0_PIN("AD13", ast2700_soc0_ad13_sigs),
+ AST2700_SOC0_PIN("AE13", ast2700_soc0_ae13_sigs),
+};
+
+static int ast2700_soc0_pinctrl_probe(struct udevice *dev)
+{
+ struct ast2700_soc0_pinctrl_priv *priv = dev_get_priv(dev);
+
+ priv->base = dev_remap_addr(dev->parent);
+ if (!priv->base)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int ast2700_soc0_pinctrl_get_groups_count(struct udevice *dev)
+{
+ return ARRAY_SIZE(ast2700_soc0_groups);
+}
+
+static const char *ast2700_soc0_pinctrl_get_group_name(struct udevice *dev,
+ unsigned int selector)
+{
+ return ast2700_soc0_groups[selector].name;
+}
+
+static int ast2700_soc0_pinctrl_get_functions_count(struct udevice *dev)
+{
+ return ARRAY_SIZE(ast2700_soc0_functions);
+}
+
+static const char *ast2700_soc0_pinctrl_get_function_name(struct udevice *dev,
+ unsigned int selector)
+{
+ return ast2700_soc0_functions[selector];
+}
+
+static int ast2700_soc0_pinctrl_group_set(struct udevice *dev,
+ unsigned int group_selector,
+ unsigned int func_selector)
+{
+ struct ast2700_soc0_pinctrl_priv *priv = dev_get_priv(dev);
+ const struct ast2700_soc0_pinmux *pinmux;
+ const char *function;
+ const char *group;
+ u32 i;
+
+ if (group_selector >= ARRAY_SIZE(ast2700_soc0_groups) ||
+ func_selector >= ARRAY_SIZE(ast2700_soc0_functions))
+ return -EINVAL;
+
+ function = ast2700_soc0_functions[func_selector];
+ group = ast2700_soc0_groups[group_selector].name;
+
+ for (i = 0; i < ARRAY_SIZE(ast2700_soc0_pinmuxes); i++) {
+ pinmux = &ast2700_soc0_pinmuxes[i];
+ if (strcmp(pinmux->function, function) ||
+ strcmp(pinmux->group, group))
+ continue;
+
+ clrsetbits_le32(priv->base + pinmux->offset, pinmux->mask,
+ pinmux->val);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int ast2700_soc0_pinctrl_gpio_request_enable(struct udevice *dev,
+ unsigned int selector)
+{
+ struct ast2700_soc0_pinctrl_priv *priv = dev_get_priv(dev);
+ const struct ast2700_soc0_pin *pin;
+ u32 i;
+
+ if (selector >= ARRAY_SIZE(ast2700_soc0_pins))
+ return -EINVAL;
+
+ pin = &ast2700_soc0_pins[selector];
+ for (i = 0; i < pin->nsigs; i++)
+ clrbits_le32(priv->base + pin->sigs[i].offset,
+ pin->sigs[i].mask);
+
+ return 0;
+}
+
+static int ast2700_soc0_pinctrl_get_pins_count(struct udevice *dev)
+{
+ return ARRAY_SIZE(ast2700_soc0_pins);
+}
+
+static const char *ast2700_soc0_pinctrl_get_pin_name(struct udevice *dev,
+ unsigned int selector)
+{
+ if (selector >= ARRAY_SIZE(ast2700_soc0_pins))
+ return NULL;
+
+ return ast2700_soc0_pins[selector].name;
+}
+
+static int ast2700_soc0_pinctrl_get_pin_muxing(struct udevice *dev,
+ unsigned int selector,
+ char *buf, int size)
+{
+ struct ast2700_soc0_pinctrl_priv *priv = dev_get_priv(dev);
+ const struct ast2700_soc0_pin *pin;
+ const struct ast2700_soc0_sig *sig;
+ u32 i;
+
+ if (selector >= ARRAY_SIZE(ast2700_soc0_pins))
+ return -EINVAL;
+
+ pin = &ast2700_soc0_pins[selector];
+ for (i = 0; i < pin->nsigs; i++) {
+ sig = &pin->sigs[i];
+ if ((readl(priv->base + sig->offset) & sig->mask) ==
+ sig->mask) {
+ snprintf(buf, size, "%s", sig->name);
+ return 0;
+ }
+ }
+
+ snprintf(buf, size, "GPIO");
+
+ return 0;
+}
+
+/* Drive strength in mA selectable by the IO control register [3:0] */
+static const u8 ast2700_soc0_drv_ma[] = {
+ 3, 6, 8, 11, 16, 18, 20, 23, 30, 32, 33, 35, 37, 38, 39, 41,
+};
+
+static const struct pinconf_param ast2700_soc0_pinconf_params[] = {
+ { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
+ { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 0 },
+ { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 0 },
+ { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 },
+};
+
+static int ast2700_soc0_pinctrl_pinconf_set(struct udevice *dev,
+ unsigned int selector,
+ unsigned int param,
+ unsigned int arg)
+{
+ struct ast2700_soc0_pinctrl_priv *priv = dev_get_priv(dev);
+ u32 mask, val, i;
+
+ if (selector >= ARRAY_SIZE(ast2700_soc0_pins))
+ return -EINVAL;
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ mask = BIT(5);
+ val = 0;
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ mask = GENMASK(5, 4);
+ val = 2 << 4;
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ mask = GENMASK(5, 4);
+ val = 3 << 4;
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ for (i = 0; i < ARRAY_SIZE(ast2700_soc0_drv_ma); i++) {
+ if (ast2700_soc0_drv_ma[i] == arg)
+ break;
+ }
+ if (i == ARRAY_SIZE(ast2700_soc0_drv_ma))
+ return -EINVAL;
+ mask = GENMASK(3, 0);
+ val = i;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ clrsetbits_le32(priv->base + SCU480 + selector * 4, mask, val);
+
+ return 0;
+}
+
+static int ast2700_soc0_pinctrl_pinconf_group_set(struct udevice *dev,
+ unsigned int selector,
+ unsigned int param,
+ unsigned int arg)
+{
+ const struct ast2700_soc0_group *group;
+ u32 i;
+ int ret;
+
+ if (selector >= ARRAY_SIZE(ast2700_soc0_groups))
+ return -EINVAL;
+
+ group = &ast2700_soc0_groups[selector];
+ if (!group->npins)
+ return -ENOTSUPP;
+
+ for (i = 0; i < group->npins; i++) {
+ ret = ast2700_soc0_pinctrl_pinconf_set(dev, group->pins[i],
+ param, arg);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct pinctrl_ops ast2700_soc0_pinctrl_ops = {
+ .set_state = pinctrl_generic_set_state,
+ .get_pins_count = ast2700_soc0_pinctrl_get_pins_count,
+ .get_pin_name = ast2700_soc0_pinctrl_get_pin_name,
+ .get_pin_muxing = ast2700_soc0_pinctrl_get_pin_muxing,
+ .get_groups_count = ast2700_soc0_pinctrl_get_groups_count,
+ .get_group_name = ast2700_soc0_pinctrl_get_group_name,
+ .get_functions_count = ast2700_soc0_pinctrl_get_functions_count,
+ .get_function_name = ast2700_soc0_pinctrl_get_function_name,
+ .pinmux_group_set = ast2700_soc0_pinctrl_group_set,
+ .gpio_request_enable = ast2700_soc0_pinctrl_gpio_request_enable,
+ .pinconf_num_params = ARRAY_SIZE(ast2700_soc0_pinconf_params),
+ .pinconf_params = ast2700_soc0_pinconf_params,
+ .pinconf_set = ast2700_soc0_pinctrl_pinconf_set,
+ .pinconf_group_set = ast2700_soc0_pinctrl_pinconf_group_set,
+};
+
+static const struct udevice_id ast2700_soc0_pinctrl_ids[] = {
+ { .compatible = "aspeed,ast2700-soc0-pinctrl" },
+ { }
+};
+
+U_BOOT_DRIVER(pinctrl_ast2700_soc0) = {
+ .name = "aspeed_ast2700_soc0_pinctrl",
+ .id = UCLASS_PINCTRL,
+ .of_match = ast2700_soc0_pinctrl_ids,
+ .priv_auto = sizeof(struct ast2700_soc0_pinctrl_priv),
+ .ops = &ast2700_soc0_pinctrl_ops,
+ .probe = ast2700_soc0_pinctrl_probe,
+};
diff --git a/drivers/pinctrl/aspeed/pinctrl_ast2700_soc1.c b/drivers/pinctrl/aspeed/pinctrl_ast2700_soc1.c
new file mode 100644
index 00000000000..05656c38ad2
--- /dev/null
+++ b/drivers/pinctrl/aspeed/pinctrl_ast2700_soc1.c
@@ -0,0 +1,1642 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 ASPEED Technology Inc.
+ */
+
+#include <asm/io.h>
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <errno.h>
+#include <vsprintf.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#define AST2700_SOC1_MUX_BASE 0x400
+#define AST2700_SOC1_PCIE_REG 0x908
+#define AST2700_SOC1_USB_MODE_REG 0x3b0
+#define AST2700_SOC1_SGMII_REG 0x47c
+#define AST2700_SOC1_MUX_MASK 0x7
+#define AST2700_SOC1_MUX_BITS_PER_PIN 4
+#define AST2700_SOC1_MUX_PINS_PER_REG 8
+
+#define AST2700_SOC1_BIAS_BASE 0x480
+#define AST2700_SOC1_BIAS_PINS_PER_REG 32
+
+#define AST2700_SOC1_DRV_BASE 0x4c0
+#define AST2700_SOC1_DRV_MASK 0x3
+#define AST2700_SOC1_DRV_BITS_PER_PIN 2
+#define AST2700_SOC1_DRV_PINS_PER_REG 16
+#define AST2700_SOC1_DRV_STEP_MA 4
+#define AST2700_SOC1_DRV_MIN_MA 4
+#define AST2700_SOC1_DRV_MAX_MA 16
+
+enum {
+ C16,
+ C14,
+ C11,
+ D9,
+ F14,
+ D10,
+ C12,
+ C13,
+ AC26,
+ AA25,
+ AB23,
+ U22,
+ V21,
+ N26,
+ P25,
+ N25,
+ V23,
+ W22,
+ AB26,
+ AD26,
+ P26,
+ AE26,
+ AF26,
+ AF25,
+ AE25,
+ AD25,
+ AF23,
+ AF20,
+ AF21,
+ AE21,
+ AE23,
+ AD22,
+ AF17,
+ AA16,
+ Y16,
+ V17,
+ J13,
+ AB16,
+ AC16,
+ AF16,
+ AA15,
+ AB15,
+ AC15,
+ AD15,
+ Y15,
+ AA14,
+ W16,
+ V16,
+ AB18,
+ AC18,
+ K13,
+ AA17,
+ AB17,
+ AD16,
+ AC17,
+ AD17,
+ AE16,
+ AE17,
+ AB24,
+ W26,
+ HOLE0,
+ HOLE1,
+ HOLE2,
+ HOLE3,
+ W25,
+ Y23,
+ Y24,
+ W21,
+ AA23,
+ AC22,
+ AB22,
+ Y21,
+ AE20,
+ AF19,
+ Y22,
+ AA20,
+ AA22,
+ AB20,
+ AF18,
+ AE19,
+ AD20,
+ AC20,
+ AA21,
+ AB21,
+ AC19,
+ AE18,
+ AD19,
+ AD18,
+ U25,
+ U26,
+ Y26,
+ AA24,
+ R25,
+ AA26,
+ R26,
+ Y25,
+ B16,
+ D14,
+ B15,
+ B14,
+ C17,
+ B13,
+ E14,
+ C15,
+ D24,
+ B23,
+ B22,
+ C23,
+ B18,
+ B21,
+ M15,
+ B19,
+ B26,
+ A25,
+ A24,
+ B24,
+ E26,
+ A21,
+ A19,
+ A18,
+ D26,
+ C26,
+ A23,
+ A22,
+ B25,
+ F26,
+ A26,
+ A14,
+ E10,
+ E13,
+ D12,
+ F10,
+ E11,
+ F11,
+ F13,
+ N15,
+ C20,
+ C19,
+ A8,
+ R14,
+ A7,
+ P14,
+ D20,
+ A6,
+ B6,
+ N14,
+ B7,
+ B8,
+ B9,
+ M14,
+ J11,
+ E7,
+ D19,
+ B11,
+ D15,
+ B12,
+ B10,
+ P13,
+ C18,
+ C6,
+ C7,
+ D7,
+ N13,
+ C8,
+ C9,
+ C10,
+ M16,
+ A15,
+ G11,
+ H7,
+ H8,
+ H9,
+ H10,
+ H11,
+ J9,
+ J10,
+ E9,
+ F9,
+ F8,
+ M13,
+ F7,
+ D8,
+ E8,
+ L12,
+ F12,
+ E12,
+ J12,
+ G7,
+ G8,
+ G9,
+ G10,
+ K12,
+ W17,
+ V18,
+ W18,
+ Y17,
+ AA18,
+ AA13,
+ Y18,
+ AA12,
+ W20,
+ V20,
+ Y11,
+ V14,
+ V19,
+ W14,
+ Y20,
+ AB19,
+ U21,
+ T24,
+ V24,
+ V22,
+ T23,
+ AC25,
+ AB25,
+ AC24,
+ PCIERC2_PERST,
+ PORTC_MODE,
+ PORTD_MODE,
+ SGMII0,
+};
+
+struct ast2700_soc1_field {
+ u32 reg;
+ u32 shift;
+ u32 mask;
+};
+
+struct ast2700_soc1_group {
+ const char *name;
+ const unsigned int *pins;
+ unsigned int npins;
+};
+
+struct ast2700_soc1_function {
+ const char *name;
+ const char *const *groups;
+ const u8 *muxvals;
+ unsigned int ngroups;
+};
+
+struct ast2700_soc1_pinctrl_priv {
+ void __iomem *scu;
+};
+
+#define PIN(n) \
+ [n] = #n
+
+static const char *const ast2700_soc1_pin_names[] = {
+ PIN(C16),
+ PIN(C14),
+ PIN(C11),
+ PIN(D9),
+ PIN(F14),
+ PIN(D10),
+ PIN(C12),
+ PIN(C13),
+ PIN(AC26),
+ PIN(AA25),
+ PIN(AB23),
+ PIN(U22),
+ PIN(V21),
+ PIN(N26),
+ PIN(P25),
+ PIN(N25),
+ PIN(V23),
+ PIN(W22),
+ PIN(AB26),
+ PIN(AD26),
+ PIN(P26),
+ PIN(AE26),
+ PIN(AF26),
+ PIN(AF25),
+ PIN(AE25),
+ PIN(AD25),
+ PIN(AF23),
+ PIN(AF20),
+ PIN(AF21),
+ PIN(AE21),
+ PIN(AE23),
+ PIN(AD22),
+ PIN(AF17),
+ PIN(AA16),
+ PIN(Y16),
+ PIN(V17),
+ PIN(J13),
+ PIN(AB16),
+ PIN(AC16),
+ PIN(AF16),
+ PIN(AA15),
+ PIN(AB15),
+ PIN(AC15),
+ PIN(AD15),
+ PIN(Y15),
+ PIN(AA14),
+ PIN(W16),
+ PIN(V16),
+ PIN(AB18),
+ PIN(AC18),
+ PIN(K13),
+ PIN(AA17),
+ PIN(AB17),
+ PIN(AD16),
+ PIN(AC17),
+ PIN(AD17),
+ PIN(AE16),
+ PIN(AE17),
+ PIN(AB24),
+ PIN(W26),
+ PIN(HOLE0),
+ PIN(HOLE1),
+ PIN(HOLE2),
+ PIN(HOLE3),
+ PIN(W25),
+ PIN(Y23),
+ PIN(Y24),
+ PIN(W21),
+ PIN(AA23),
+ PIN(AC22),
+ PIN(AB22),
+ PIN(Y21),
+ PIN(AE20),
+ PIN(AF19),
+ PIN(Y22),
+ PIN(AA20),
+ PIN(AA22),
+ PIN(AB20),
+ PIN(AF18),
+ PIN(AE19),
+ PIN(AD20),
+ PIN(AC20),
+ PIN(AA21),
+ PIN(AB21),
+ PIN(AC19),
+ PIN(AE18),
+ PIN(AD19),
+ PIN(AD18),
+ PIN(U25),
+ PIN(U26),
+ PIN(Y26),
+ PIN(AA24),
+ PIN(R25),
+ PIN(AA26),
+ PIN(R26),
+ PIN(Y25),
+ PIN(B16),
+ PIN(D14),
+ PIN(B15),
+ PIN(B14),
+ PIN(C17),
+ PIN(B13),
+ PIN(E14),
+ PIN(C15),
+ PIN(D24),
+ PIN(B23),
+ PIN(B22),
+ PIN(C23),
+ PIN(B18),
+ PIN(B21),
+ PIN(M15),
+ PIN(B19),
+ PIN(B26),
+ PIN(A25),
+ PIN(A24),
+ PIN(B24),
+ PIN(E26),
+ PIN(A21),
+ PIN(A19),
+ PIN(A18),
+ PIN(D26),
+ PIN(C26),
+ PIN(A23),
+ PIN(A22),
+ PIN(B25),
+ PIN(F26),
+ PIN(A26),
+ PIN(A14),
+ PIN(E10),
+ PIN(E13),
+ PIN(D12),
+ PIN(F10),
+ PIN(E11),
+ PIN(F11),
+ PIN(F13),
+ PIN(N15),
+ PIN(C20),
+ PIN(C19),
+ PIN(A8),
+ PIN(R14),
+ PIN(A7),
+ PIN(P14),
+ PIN(D20),
+ PIN(A6),
+ PIN(B6),
+ PIN(N14),
+ PIN(B7),
+ PIN(B8),
+ PIN(B9),
+ PIN(M14),
+ PIN(J11),
+ PIN(E7),
+ PIN(D19),
+ PIN(B11),
+ PIN(D15),
+ PIN(B12),
+ PIN(B10),
+ PIN(P13),
+ PIN(C18),
+ PIN(C6),
+ PIN(C7),
+ PIN(D7),
+ PIN(N13),
+ PIN(C8),
+ PIN(C9),
+ PIN(C10),
+ PIN(M16),
+ PIN(A15),
+ PIN(G11),
+ PIN(H7),
+ PIN(H8),
+ PIN(H9),
+ PIN(H10),
+ PIN(H11),
+ PIN(J9),
+ PIN(J10),
+ PIN(E9),
+ PIN(F9),
+ PIN(F8),
+ PIN(M13),
+ PIN(F7),
+ PIN(D8),
+ PIN(E8),
+ PIN(L12),
+ PIN(F12),
+ PIN(E12),
+ PIN(J12),
+ PIN(G7),
+ PIN(G8),
+ PIN(G9),
+ PIN(G10),
+ PIN(K12),
+ PIN(W17),
+ PIN(V18),
+ PIN(W18),
+ PIN(Y17),
+ PIN(AA18),
+ PIN(AA13),
+ PIN(Y18),
+ PIN(AA12),
+ PIN(W20),
+ PIN(V20),
+ PIN(Y11),
+ PIN(V14),
+ PIN(V19),
+ PIN(W14),
+ PIN(Y20),
+ PIN(AB19),
+ PIN(U21),
+ PIN(T24),
+ PIN(V24),
+ PIN(V22),
+ PIN(T23),
+ PIN(AC25),
+ PIN(AB25),
+ PIN(AC24),
+ PIN(PCIERC2_PERST),
+ PIN(PORTC_MODE),
+ PIN(PORTD_MODE),
+ PIN(SGMII0),
+};
+
+#define PIN_GROUP(name, ...) \
+ static const unsigned int name##_pins[] = { __VA_ARGS__ }
+
+/* Pin groups and functions */
+PIN_GROUP(ADC0, W17);
+PIN_GROUP(ADC1, V18);
+PIN_GROUP(ADC10, Y11);
+PIN_GROUP(ADC11, V14);
+PIN_GROUP(ADC12, V19);
+PIN_GROUP(ADC13, W14);
+PIN_GROUP(ADC14, Y20);
+PIN_GROUP(ADC15, AB19);
+PIN_GROUP(ADC2, W18);
+PIN_GROUP(ADC3, Y17);
+PIN_GROUP(ADC4, AA18);
+PIN_GROUP(ADC5, AA13);
+PIN_GROUP(ADC6, Y18);
+PIN_GROUP(ADC7, AA12);
+PIN_GROUP(ADC8, W20);
+PIN_GROUP(ADC9, V20);
+PIN_GROUP(AUXPWRGOOD0, W14);
+PIN_GROUP(AUXPWRGOOD1, Y20);
+PIN_GROUP(CANBUS, G7, G8, G9);
+PIN_GROUP(DI2C0, C16, D9);
+PIN_GROUP(DI2C1, C14, F14);
+PIN_GROUP(DI2C10, R25, AA26);
+PIN_GROUP(DI2C11, R26, Y25);
+PIN_GROUP(DI2C12, W25, Y23);
+PIN_GROUP(DI2C13, Y24, W21);
+PIN_GROUP(DI2C14, AA23, AC22);
+PIN_GROUP(DI2C15, AB22, Y21);
+PIN_GROUP(DI2C2, D10, C12);
+PIN_GROUP(DI2C3, C11, C13);
+PIN_GROUP(DI2C8, U25, U26);
+PIN_GROUP(DI2C9, Y26, AA24);
+PIN_GROUP(DSGPM0, D19, B10, C7, D7);
+PIN_GROUP(ESPI0, B16, D14, B15, B14, C17, B13, E14, C15);
+PIN_GROUP(ESPI1, C16, C14, C11, D9, F14, D10, C12, C13);
+PIN_GROUP(FSI0, AD20, AC20);
+PIN_GROUP(FSI1, AA21, AB21);
+PIN_GROUP(FSI2, AC19, AE18);
+PIN_GROUP(FSI3, AD19, AD18);
+PIN_GROUP(FWQSPI, M16, A15);
+PIN_GROUP(FWSPIABR, A14);
+PIN_GROUP(FWWPN, N15);
+PIN_GROUP(HBLED, V24);
+PIN_GROUP(HVI3C0, U25, U26);
+PIN_GROUP(HVI3C1, Y26, AA24);
+PIN_GROUP(HVI3C12, W25, Y23);
+PIN_GROUP(HVI3C13, Y24, W21);
+PIN_GROUP(HVI3C14, AA23, AC22);
+PIN_GROUP(HVI3C15, AB22, Y21);
+PIN_GROUP(HVI3C2, R25, AA26);
+PIN_GROUP(HVI3C3, R26, Y25);
+PIN_GROUP(I2C0, G11, H7);
+PIN_GROUP(I2C1, H8, H9);
+PIN_GROUP(I2C10, G8, G9);
+PIN_GROUP(I2C11, G10, K12);
+PIN_GROUP(I2C12, AC18, AA17);
+PIN_GROUP(I2C13, AB17, AD16);
+PIN_GROUP(I2C14, AC17, AD17);
+PIN_GROUP(I2C15, AE16, AE17);
+PIN_GROUP(I2C2, H10, H11);
+PIN_GROUP(I2C3, J9, J10);
+PIN_GROUP(I2C4, E9, F9);
+PIN_GROUP(I2C5, F8, M13);
+PIN_GROUP(I2C6, F7, D8);
+PIN_GROUP(I2C7, E8, L12);
+PIN_GROUP(I2C8, F12, E12);
+PIN_GROUP(I2C9, J12, G7);
+PIN_GROUP(I2CF0, F12, E12, J12, G7);
+PIN_GROUP(I2CF1, E9, F9, F8, M13);
+PIN_GROUP(I2CF2, F7, D8, E8, L12);
+PIN_GROUP(I3C10, AC19, AE18);
+PIN_GROUP(I3C11, AD19, AD18);
+PIN_GROUP(I3C4, AE20, AF19);
+PIN_GROUP(I3C5, Y22, AA20);
+PIN_GROUP(I3C6, AA22, AB20);
+PIN_GROUP(I3C7, AF18, AE19);
+PIN_GROUP(I3C8, AD20, AC20);
+PIN_GROUP(I3C9, AA21, AB21);
+PIN_GROUP(JTAGM1, D12, F10, E11, F11, F13);
+PIN_GROUP(LPC0, AF26, AF25, B16, D14, B15, B14, C17, B13, E14, C15);
+PIN_GROUP(LPC1, C16, C14, C11, D9, F14, D10, C12, C13, AE16, AE17);
+PIN_GROUP(LTPI, U25, U26, Y26, AA24);
+PIN_GROUP(LTPI_PS_I2C0, G11, H7);
+PIN_GROUP(LTPI_PS_I2C1, H8, H9);
+PIN_GROUP(LTPI_PS_I2C2, H10, H11);
+PIN_GROUP(LTPI_PS_I2C3, J9, J10);
+PIN_GROUP(MACLINK0, U21);
+PIN_GROUP(MACLINK1, AC24);
+PIN_GROUP(MACLINK2, T24);
+PIN_GROUP(MDIO0, B9, M14);
+PIN_GROUP(MDIO1, C9, C10);
+PIN_GROUP(MDIO2, E10, E13);
+PIN_GROUP(NCTS0, AF17);
+PIN_GROUP(NCTS1, AA15);
+PIN_GROUP(NCTS5, V21);
+PIN_GROUP(NCTS6, AB26);
+PIN_GROUP(NDCD0, AA16);
+PIN_GROUP(NDCD1, AB15);
+PIN_GROUP(NDCD5, N26);
+PIN_GROUP(NDCD6, AD26);
+PIN_GROUP(NDSR0, Y16);
+PIN_GROUP(NDSR1, AC15);
+PIN_GROUP(NDSR5, P25);
+PIN_GROUP(NDSR6, P26);
+PIN_GROUP(NDTR0, J13);
+PIN_GROUP(NDTR1, Y15);
+PIN_GROUP(NDTR5, V23);
+PIN_GROUP(NDTR6, AF26);
+PIN_GROUP(NRI0, V17);
+PIN_GROUP(NRI1, AD15);
+PIN_GROUP(NRI5, N25);
+PIN_GROUP(NRI6, AE26);
+PIN_GROUP(NRTS0, AB16);
+PIN_GROUP(NRTS1, AA14);
+PIN_GROUP(NRTS5, W22);
+PIN_GROUP(NRTS6, AF25);
+PIN_GROUP(OSCCLK, C17);
+PIN_GROUP(PE2SGRSTN, E10, PCIERC2_PERST);
+PIN_GROUP(PWM0, AE25);
+PIN_GROUP(PWM1, AD25);
+PIN_GROUP(PWM10, AB17);
+PIN_GROUP(PWM11, AD16);
+PIN_GROUP(PWM12, AC17);
+PIN_GROUP(PWM13, AD17);
+PIN_GROUP(PWM14, AE16);
+PIN_GROUP(PWM15, AE17);
+PIN_GROUP(PWM2, AF23);
+PIN_GROUP(PWM3, AF20);
+PIN_GROUP(PWM4, AF21);
+PIN_GROUP(PWM5, AE21);
+PIN_GROUP(PWM6, AE23);
+PIN_GROUP(PWM7, AD22);
+PIN_GROUP(PWM8, K13);
+PIN_GROUP(PWM9, AA17);
+PIN_GROUP(QSPI0, C23, B18);
+PIN_GROUP(QSPI1, B24, E26);
+PIN_GROUP(QSPI2, B25, F26);
+PIN_GROUP(RGMII0, C20, C19, A8, R14, A7, P14, D20, A6, B6, N14, B7, B8);
+PIN_GROUP(RGMII1, D19, B11, D15, B12, B10, P13, C18, C6, C7, D7, N13, C8);
+PIN_GROUP(RMII0, C20, A8, R14, A7, P14, A6, B6, N14);
+PIN_GROUP(RMII0RCLKO, D20);
+PIN_GROUP(RMII1, D19, D15, B12, B10, P13, C6, C7, D7);
+PIN_GROUP(RMII1RCLKO, C18);
+PIN_GROUP(SALT0, AC17);
+PIN_GROUP(SALT1, AD17);
+PIN_GROUP(SALT10, Y18);
+PIN_GROUP(SALT11, AA12);
+PIN_GROUP(SALT12, AB26);
+PIN_GROUP(SALT13, AD26);
+PIN_GROUP(SALT14, P26);
+PIN_GROUP(SALT15, AE26);
+PIN_GROUP(SALT2, AC15);
+PIN_GROUP(SALT3, AD15);
+PIN_GROUP(SALT4, W17);
+PIN_GROUP(SALT5, V18);
+PIN_GROUP(SALT6, W18);
+PIN_GROUP(SALT7, Y17);
+PIN_GROUP(SALT8, AA18);
+PIN_GROUP(SALT9, AA13);
+PIN_GROUP(SD, C16, C14, C11, D9, F14, D10, C12, C13);
+PIN_GROUP(SGMII, SGMII0);
+PIN_GROUP(SGPM0, U21, T24, V22, T23);
+PIN_GROUP(SGPM1, AC25, AB25, AB24, W26);
+PIN_GROUP(SGPS, B11, C18, N13, C8);
+PIN_GROUP(SIOONCTRLN0, AE23);
+PIN_GROUP(SIOONCTRLN1, AA15);
+PIN_GROUP(SIOPBIN0, AD25);
+PIN_GROUP(SIOPBIN1, AA16);
+PIN_GROUP(SIOPBON0, AE25);
+PIN_GROUP(SIOPBON1, AF17);
+PIN_GROUP(SIOPWREQN0, AE21);
+PIN_GROUP(SIOPWREQN1, AB16);
+PIN_GROUP(SIOPWRGD1, AB15);
+PIN_GROUP(SIOS3N0, AF20);
+PIN_GROUP(SIOS3N1, V17);
+PIN_GROUP(SIOS5N0, AF21);
+PIN_GROUP(SIOS5N1, J13);
+PIN_GROUP(SIOSCIN0, AF23);
+PIN_GROUP(SIOSCIN1, Y16);
+PIN_GROUP(SMON0, U21, T24, V22, T23);
+PIN_GROUP(SMON1, W26, AC25, AB25);
+PIN_GROUP(SPI0, D24, B23, B22);
+PIN_GROUP(SPI0ABR, M15);
+PIN_GROUP(SPI0CS1, B21);
+PIN_GROUP(SPI0WPN, B19);
+PIN_GROUP(SPI1, B26, A25, A24);
+PIN_GROUP(SPI1ABR, A19);
+PIN_GROUP(SPI1CS1, A21);
+PIN_GROUP(SPI1WPN, A18);
+PIN_GROUP(SPI2, D26, C26, A23, A22);
+PIN_GROUP(SPI2CS1, A26);
+PIN_GROUP(TACH0, AC26);
+PIN_GROUP(TACH1, AA25);
+PIN_GROUP(TACH10, AB26);
+PIN_GROUP(TACH11, AD26);
+PIN_GROUP(TACH12, P26);
+PIN_GROUP(TACH13, AE26);
+PIN_GROUP(TACH14, AF26);
+PIN_GROUP(TACH15, AF25);
+PIN_GROUP(TACH2, AB23);
+PIN_GROUP(TACH3, U22);
+PIN_GROUP(TACH4, V21);
+PIN_GROUP(TACH5, N26);
+PIN_GROUP(TACH6, P25);
+PIN_GROUP(TACH7, N25);
+PIN_GROUP(TACH8, V23);
+PIN_GROUP(TACH9, W22);
+PIN_GROUP(THRU0, AC26, AA25);
+PIN_GROUP(THRU1, AB23, U22);
+PIN_GROUP(THRU2, A19, A18);
+PIN_GROUP(THRU3, B25, F26);
+PIN_GROUP(UART0, AC16, AF16);
+PIN_GROUP(UART1, W16, V16);
+PIN_GROUP(UART2, AB18, AC18);
+PIN_GROUP(UART3, K13, AA17);
+PIN_GROUP(UART5, AB17, AD16);
+PIN_GROUP(UART6, AC17, AD17);
+PIN_GROUP(UART7, AE16, AE17);
+PIN_GROUP(UART8, M15, B19);
+PIN_GROUP(UART9, B26, A25);
+PIN_GROUP(UART10, A24, B24);
+PIN_GROUP(UART11, E26, A21);
+PIN_GROUP(USB2CD, PORTC_MODE);
+PIN_GROUP(USB2CH, PORTC_MODE);
+PIN_GROUP(USB2CU, PORTC_MODE);
+PIN_GROUP(USB2CUD, PORTC_MODE);
+PIN_GROUP(USB2DD, PORTD_MODE);
+PIN_GROUP(USB2DH, PORTD_MODE);
+PIN_GROUP(USBUART, G10, K12);
+PIN_GROUP(VGA, J11, E7);
+PIN_GROUP(VPI, C16, C14, C11, D9, F14, D10, AC26, AA25, AB23, U22, V21, N26,
+ P25, N25, V23, W22, AB26, AD26, P26, AE26, AF26, AF25, AE25, AD25,
+ AF23, AF20, AF21, AE21);
+PIN_GROUP(WDTRST0N, K13);
+PIN_GROUP(WDTRST1N, AA17);
+PIN_GROUP(WDTRST2N, AB17);
+PIN_GROUP(WDTRST3N, AD16);
+PIN_GROUP(WDTRST4N, AC25);
+PIN_GROUP(WDTRST5N, AB25);
+PIN_GROUP(WDTRST6N, AC24);
+PIN_GROUP(WDTRST7N, AB24);
+
+#define GROUP(n) \
+ { .name = #n, .pins = n##_pins, .npins = ARRAY_SIZE(n##_pins) }
+
+static const struct ast2700_soc1_group ast2700_soc1_groups[] = {
+ GROUP(ADC0),
+ GROUP(ADC1),
+ GROUP(ADC10),
+ GROUP(ADC11),
+ GROUP(ADC12),
+ GROUP(ADC13),
+ GROUP(ADC14),
+ GROUP(ADC15),
+ GROUP(ADC2),
+ GROUP(ADC3),
+ GROUP(ADC4),
+ GROUP(ADC5),
+ GROUP(ADC6),
+ GROUP(ADC7),
+ GROUP(ADC8),
+ GROUP(ADC9),
+ GROUP(AUXPWRGOOD0),
+ GROUP(AUXPWRGOOD1),
+ GROUP(CANBUS),
+ GROUP(DI2C0),
+ GROUP(DI2C1),
+ GROUP(DI2C10),
+ GROUP(DI2C11),
+ GROUP(DI2C12),
+ GROUP(DI2C13),
+ GROUP(DI2C14),
+ GROUP(DI2C15),
+ GROUP(DI2C2),
+ GROUP(DI2C3),
+ GROUP(DI2C8),
+ GROUP(DI2C9),
+ GROUP(DSGPM0),
+ GROUP(ESPI0),
+ GROUP(ESPI1),
+ GROUP(FSI0),
+ GROUP(FSI1),
+ GROUP(FSI2),
+ GROUP(FSI3),
+ GROUP(FWQSPI),
+ GROUP(FWSPIABR),
+ GROUP(FWWPN),
+ GROUP(HBLED),
+ GROUP(HVI3C0),
+ GROUP(HVI3C1),
+ GROUP(HVI3C12),
+ GROUP(HVI3C13),
+ GROUP(HVI3C14),
+ GROUP(HVI3C15),
+ GROUP(HVI3C2),
+ GROUP(HVI3C3),
+ GROUP(I2C0),
+ GROUP(I2C1),
+ GROUP(I2C10),
+ GROUP(I2C11),
+ GROUP(I2C12),
+ GROUP(I2C13),
+ GROUP(I2C14),
+ GROUP(I2C15),
+ GROUP(I2C2),
+ GROUP(I2C3),
+ GROUP(I2C4),
+ GROUP(I2C5),
+ GROUP(I2C6),
+ GROUP(I2C7),
+ GROUP(I2C8),
+ GROUP(I2C9),
+ GROUP(I2CF0),
+ GROUP(I2CF1),
+ GROUP(I2CF2),
+ GROUP(I3C10),
+ GROUP(I3C11),
+ GROUP(I3C4),
+ GROUP(I3C5),
+ GROUP(I3C6),
+ GROUP(I3C7),
+ GROUP(I3C8),
+ GROUP(I3C9),
+ GROUP(JTAGM1),
+ GROUP(LPC0),
+ GROUP(LPC1),
+ GROUP(LTPI),
+ GROUP(LTPI_PS_I2C0),
+ GROUP(LTPI_PS_I2C1),
+ GROUP(LTPI_PS_I2C2),
+ GROUP(LTPI_PS_I2C3),
+ GROUP(MACLINK0),
+ GROUP(MACLINK1),
+ GROUP(MACLINK2),
+ GROUP(MDIO0),
+ GROUP(MDIO1),
+ GROUP(MDIO2),
+ GROUP(NCTS0),
+ GROUP(NCTS1),
+ GROUP(NCTS5),
+ GROUP(NCTS6),
+ GROUP(NDCD0),
+ GROUP(NDCD1),
+ GROUP(NDCD5),
+ GROUP(NDCD6),
+ GROUP(NDSR0),
+ GROUP(NDSR1),
+ GROUP(NDSR5),
+ GROUP(NDSR6),
+ GROUP(NDTR0),
+ GROUP(NDTR1),
+ GROUP(NDTR5),
+ GROUP(NDTR6),
+ GROUP(NRI0),
+ GROUP(NRI1),
+ GROUP(NRI5),
+ GROUP(NRI6),
+ GROUP(NRTS0),
+ GROUP(NRTS1),
+ GROUP(NRTS5),
+ GROUP(NRTS6),
+ GROUP(OSCCLK),
+ GROUP(PE2SGRSTN),
+ GROUP(PWM0),
+ GROUP(PWM1),
+ GROUP(PWM10),
+ GROUP(PWM11),
+ GROUP(PWM12),
+ GROUP(PWM13),
+ GROUP(PWM14),
+ GROUP(PWM15),
+ GROUP(PWM2),
+ GROUP(PWM3),
+ GROUP(PWM4),
+ GROUP(PWM5),
+ GROUP(PWM6),
+ GROUP(PWM7),
+ GROUP(PWM8),
+ GROUP(PWM9),
+ GROUP(QSPI0),
+ GROUP(QSPI1),
+ GROUP(QSPI2),
+ GROUP(RGMII0),
+ GROUP(RGMII1),
+ GROUP(RMII0),
+ GROUP(RMII0RCLKO),
+ GROUP(RMII1),
+ GROUP(RMII1RCLKO),
+ GROUP(SALT0),
+ GROUP(SALT1),
+ GROUP(SALT10),
+ GROUP(SALT11),
+ GROUP(SALT12),
+ GROUP(SALT13),
+ GROUP(SALT14),
+ GROUP(SALT15),
+ GROUP(SALT2),
+ GROUP(SALT3),
+ GROUP(SALT4),
+ GROUP(SALT5),
+ GROUP(SALT6),
+ GROUP(SALT7),
+ GROUP(SALT8),
+ GROUP(SALT9),
+ GROUP(SD),
+ GROUP(SGMII),
+ GROUP(SGPM0),
+ GROUP(SGPM1),
+ GROUP(SGPS),
+ GROUP(SIOONCTRLN0),
+ GROUP(SIOONCTRLN1),
+ GROUP(SIOPBIN0),
+ GROUP(SIOPBIN1),
+ GROUP(SIOPBON0),
+ GROUP(SIOPBON1),
+ GROUP(SIOPWREQN0),
+ GROUP(SIOPWREQN1),
+ GROUP(SIOPWRGD1),
+ GROUP(SIOS3N0),
+ GROUP(SIOS3N1),
+ GROUP(SIOS5N0),
+ GROUP(SIOS5N1),
+ GROUP(SIOSCIN0),
+ GROUP(SIOSCIN1),
+ GROUP(SMON0),
+ GROUP(SMON1),
+ GROUP(SPI0),
+ GROUP(SPI0ABR),
+ GROUP(SPI0CS1),
+ GROUP(SPI0WPN),
+ GROUP(SPI1),
+ GROUP(SPI1ABR),
+ GROUP(SPI1CS1),
+ GROUP(SPI1WPN),
+ GROUP(SPI2),
+ GROUP(SPI2CS1),
+ GROUP(TACH0),
+ GROUP(TACH1),
+ GROUP(TACH10),
+ GROUP(TACH11),
+ GROUP(TACH12),
+ GROUP(TACH13),
+ GROUP(TACH14),
+ GROUP(TACH15),
+ GROUP(TACH2),
+ GROUP(TACH3),
+ GROUP(TACH4),
+ GROUP(TACH5),
+ GROUP(TACH6),
+ GROUP(TACH7),
+ GROUP(TACH8),
+ GROUP(TACH9),
+ GROUP(THRU0),
+ GROUP(THRU1),
+ GROUP(THRU2),
+ GROUP(THRU3),
+ GROUP(UART0),
+ GROUP(UART1),
+ GROUP(UART10),
+ GROUP(UART11),
+ GROUP(UART2),
+ GROUP(UART3),
+ GROUP(UART5),
+ GROUP(UART6),
+ GROUP(UART7),
+ GROUP(UART8),
+ GROUP(UART9),
+ GROUP(USB2CD),
+ GROUP(USB2CH),
+ GROUP(USB2CU),
+ GROUP(USB2CUD),
+ GROUP(USB2DD),
+ GROUP(USB2DH),
+ GROUP(USBUART),
+ GROUP(VGA),
+ GROUP(VPI),
+ GROUP(WDTRST0N),
+ GROUP(WDTRST1N),
+ GROUP(WDTRST2N),
+ GROUP(WDTRST3N),
+ GROUP(WDTRST4N),
+ GROUP(WDTRST5N),
+ GROUP(WDTRST6N),
+ GROUP(WDTRST7N),
+};
+
+/**
+ * VM() - Helper macro to unwrap a parenthesized list of arguments.
+ * @...: The parenthesized list to be unwrapped.
+ *
+ * Since the C preprocessor treats commas inside braces {} as argument
+ * separators for macros, wrap mux value lists in parentheses and strip
+ * them here when initializing the compound literal.
+ */
+#define VM(...) __VA_ARGS__
+
+/**
+ * FUNC() - Initialize an ast2700_soc1_function entry.
+ * @n: Name of the pin function.
+ * @m: Parenthesized list of mux values mapped 1:1 to the groups list.
+ * @...: Variable list of pin group names associated with this function.
+ */
+#define FUNC(n, m, ...) \
+ { \
+ .name = #n, \
+ .groups = (const char *const[]){ __VA_ARGS__ }, \
+ .muxvals = (const u8[]){ VM m }, \
+ .ngroups = sizeof((const char *const[]){ __VA_ARGS__ }) / \
+ sizeof(char *), \
+ }
+
+static const struct ast2700_soc1_function ast2700_soc1_functions[] = {
+ FUNC(ADC0, (0), "ADC0"),
+ FUNC(ADC1, (0), "ADC1"),
+ FUNC(ADC10, (0), "ADC10"),
+ FUNC(ADC11, (0), "ADC11"),
+ FUNC(ADC12, (0), "ADC12"),
+ FUNC(ADC13, (0), "ADC13"),
+ FUNC(ADC14, (0), "ADC14"),
+ FUNC(ADC15, (0), "ADC15"),
+ FUNC(ADC2, (0), "ADC2"),
+ FUNC(ADC3, (0), "ADC3"),
+ FUNC(ADC4, (0), "ADC4"),
+ FUNC(ADC5, (0), "ADC5"),
+ FUNC(ADC6, (0), "ADC6"),
+ FUNC(ADC7, (0), "ADC7"),
+ FUNC(ADC8, (0), "ADC8"),
+ FUNC(ADC9, (0), "ADC9"),
+ FUNC(AUXPWRGOOD0, (2), "AUXPWRGOOD0"),
+ FUNC(AUXPWRGOOD1, (2), "AUXPWRGOOD1"),
+ FUNC(CANBUS, (2), "CANBUS"),
+ FUNC(ESPI0, (1), "ESPI0"),
+ FUNC(ESPI1, (1), "ESPI1"),
+ FUNC(FSI0, (2), "FSI0"),
+ FUNC(FSI1, (2), "FSI1"),
+ FUNC(FSI2, (2), "FSI2"),
+ FUNC(FSI3, (2), "FSI3"),
+ FUNC(FWQSPI, (1), "FWQSPI"),
+ FUNC(FWSPIABR, (1), "FWSPIABR"),
+ FUNC(FWWPN, (1), "FWWPN"),
+ FUNC(HBLED, (2), "HBLED"),
+ FUNC(I2C0, (1, 2, 4), "I2C0", "LTPI_PS_I2C0", "DI2C0"),
+ FUNC(I2C1, (1, 2, 4), "I2C1", "LTPI_PS_I2C1", "DI2C1"),
+ FUNC(I2C10, (1, 2), "I2C10", "DI2C10"),
+ FUNC(I2C11, (1, 2), "I2C11", "DI2C11"),
+ FUNC(I2C12, (4, 2), "I2C12", "DI2C12"),
+ FUNC(I2C13, (4, 2), "I2C13", "DI2C13"),
+ FUNC(I2C14, (4, 2), "I2C14", "DI2C14"),
+ FUNC(I2C15, (2, 2), "I2C15", "DI2C15"),
+ FUNC(I2C2, (1, 2, 4), "I2C2", "LTPI_PS_I2C2", "DI2C2"),
+ FUNC(I2C3, (1, 2, 4), "I2C3", "LTPI_PS_I2C3", "DI2C3"),
+ FUNC(I2C4, (1), "I2C4"),
+ FUNC(I2C5, (1), "I2C5"),
+ FUNC(I2C6, (1), "I2C6"),
+ FUNC(I2C7, (1), "I2C7"),
+ FUNC(I2C8, (1, 2), "I2C8", "DI2C8"),
+ FUNC(I2C9, (1, 2), "I2C9", "DI2C9"),
+ FUNC(I2CF0, (5), "I2CF0"),
+ FUNC(I2CF1, (5), "I2CF1"),
+ FUNC(I2CF2, (5), "I2CF2"),
+ FUNC(I3C0, (1), "HVI3C0"),
+ FUNC(I3C1, (1), "HVI3C1"),
+ FUNC(I3C10, (1), "I3C10"),
+ FUNC(I3C11, (1), "I3C11"),
+ FUNC(I3C12, (1), "HVI3C12"),
+ FUNC(I3C13, (1), "HVI3C13"),
+ FUNC(I3C14, (1), "HVI3C14"),
+ FUNC(I3C15, (1), "HVI3C15"),
+ FUNC(I3C2, (1), "HVI3C2"),
+ FUNC(I3C3, (1), "HVI3C3"),
+ FUNC(I3C4, (1), "I3C4"),
+ FUNC(I3C5, (1), "I3C5"),
+ FUNC(I3C6, (1), "I3C6"),
+ FUNC(I3C7, (1), "I3C7"),
+ FUNC(I3C8, (1), "I3C8"),
+ FUNC(I3C9, (1), "I3C9"),
+ FUNC(JTAGM1, (1), "JTAGM1"),
+ FUNC(LPC0, (2), "LPC0"),
+ FUNC(LPC1, (2), "LPC1"),
+ FUNC(LTPI, (2), "LTPI"),
+ FUNC(MACLINK0, (4), "MACLINK0"),
+ FUNC(MACLINK1, (3), "MACLINK1"),
+ FUNC(MACLINK2, (4), "MACLINK2"),
+ FUNC(MDIO0, (1), "MDIO0"),
+ FUNC(MDIO1, (1), "MDIO1"),
+ FUNC(MDIO2, (1), "MDIO2"),
+ FUNC(NCTS0, (1), "NCTS0"),
+ FUNC(NCTS1, (1), "NCTS1"),
+ FUNC(NCTS5, (4), "NCTS5"),
+ FUNC(NCTS6, (4), "NCTS6"),
+ FUNC(NDCD0, (1), "NDCD0"),
+ FUNC(NDCD1, (1), "NDCD1"),
+ FUNC(NDCD5, (4), "NDCD5"),
+ FUNC(NDCD6, (4), "NDCD6"),
+ FUNC(NDSR0, (1), "NDSR0"),
+ FUNC(NDSR1, (1), "NDSR1"),
+ FUNC(NDSR5, (4), "NDSR5"),
+ FUNC(NDSR6, (4), "NDSR6"),
+ FUNC(NDTR0, (1), "NDTR0"),
+ FUNC(NDTR1, (1), "NDTR1"),
+ FUNC(NDTR5, (4), "NDTR5"),
+ FUNC(NDTR6, (4), "NDTR6"),
+ FUNC(NRI0, (1), "NRI0"),
+ FUNC(NRI1, (1), "NRI1"),
+ FUNC(NRI5, (4), "NRI5"),
+ FUNC(NRI6, (4), "NRI6"),
+ FUNC(NRTS0, (1), "NRTS0"),
+ FUNC(NRTS1, (1), "NRTS1"),
+ FUNC(NRTS5, (4), "NRTS5"),
+ FUNC(NRTS6, (4), "NRTS6"),
+ FUNC(OSCCLK, (3), "OSCCLK"),
+ FUNC(PCIERC, (2), "PE2SGRSTN"),
+ FUNC(PWM0, (1), "PWM0"),
+ FUNC(PWM1, (1), "PWM1"),
+ FUNC(PWM10, (3), "PWM10"),
+ FUNC(PWM11, (3), "PWM11"),
+ FUNC(PWM12, (3), "PWM12"),
+ FUNC(PWM13, (3), "PWM13"),
+ FUNC(PWM14, (3), "PWM14"),
+ FUNC(PWM15, (3), "PWM15"),
+ FUNC(PWM2, (1), "PWM2"),
+ FUNC(PWM3, (1), "PWM3"),
+ FUNC(PWM4, (1), "PWM4"),
+ FUNC(PWM5, (1), "PWM5"),
+ FUNC(PWM6, (1), "PWM6"),
+ FUNC(PWM7, (1), "PWM7"),
+ FUNC(PWM8, (3), "PWM8"),
+ FUNC(PWM9, (3), "PWM9"),
+ FUNC(QSPI0, (1), "QSPI0"),
+ FUNC(QSPI1, (1), "QSPI1"),
+ FUNC(QSPI2, (1), "QSPI2"),
+ FUNC(RGMII0, (1), "RGMII0"),
+ FUNC(RGMII1, (1), "RGMII1"),
+ FUNC(RMII0, (2), "RMII0"),
+ FUNC(RMII0RCLKO, (2), "RMII0RCLKO"),
+ FUNC(RMII1, (2), "RMII1"),
+ FUNC(RMII1RCLKO, (2), "RMII1RCLKO"),
+ FUNC(SALT0, (2), "SALT0"),
+ FUNC(SALT1, (2), "SALT1"),
+ FUNC(SALT10, (2), "SALT10"),
+ FUNC(SALT11, (2), "SALT11"),
+ FUNC(SALT12, (2), "SALT12"),
+ FUNC(SALT13, (2), "SALT13"),
+ FUNC(SALT14, (2), "SALT14"),
+ FUNC(SALT15, (2), "SALT15"),
+ FUNC(SALT2, (2), "SALT2"),
+ FUNC(SALT3, (2), "SALT3"),
+ FUNC(SALT4, (2), "SALT4"),
+ FUNC(SALT5, (2), "SALT5"),
+ FUNC(SALT6, (2), "SALT6"),
+ FUNC(SALT7, (2), "SALT7"),
+ FUNC(SALT8, (2), "SALT8"),
+ FUNC(SALT9, (2), "SALT9"),
+ FUNC(SD, (3), "SD"),
+ FUNC(SGMII, (1), "SGMII"),
+ FUNC(SGPM0, (1, 4), "SGPM0", "DSGPM0"),
+ FUNC(SGPM1, (1), "SGPM1"),
+ FUNC(SGPS, (5), "SGPS"),
+ FUNC(SIOONCTRLN0, (2), "SIOONCTRLN0"),
+ FUNC(SIOONCTRLN1, (2), "SIOONCTRLN1"),
+ FUNC(SIOPBIN0, (2), "SIOPBIN0"),
+ FUNC(SIOPBIN1, (2), "SIOPBIN1"),
+ FUNC(SIOPBON0, (2), "SIOPBON0"),
+ FUNC(SIOPBON1, (2), "SIOPBON1"),
+ FUNC(SIOPWREQN0, (2), "SIOPWREQN0"),
+ FUNC(SIOPWREQN1, (2), "SIOPWREQN1"),
+ FUNC(SIOPWRGD1, (2), "SIOPWRGD1"),
+ FUNC(SIOS3N0, (2), "SIOS3N0"),
+ FUNC(SIOS3N1, (2), "SIOS3N1"),
+ FUNC(SIOS5N0, (2), "SIOS5N0"),
+ FUNC(SIOS5N1, (2), "SIOS5N1"),
+ FUNC(SIOSCIN0, (2), "SIOSCIN0"),
+ FUNC(SIOSCIN1, (2), "SIOSCIN1"),
+ FUNC(SMON0, (2), "SMON0"),
+ FUNC(SMON1, (4), "SMON1"),
+ FUNC(SPI0, (1), "SPI0"),
+ FUNC(SPI0ABR, (1), "SPI0ABR"),
+ FUNC(SPI0CS1, (1), "SPI0CS1"),
+ FUNC(SPI0WPN, (1), "SPI0WPN"),
+ FUNC(SPI1, (1), "SPI1"),
+ FUNC(SPI1ABR, (1), "SPI1ABR"),
+ FUNC(SPI1CS1, (1), "SPI1CS1"),
+ FUNC(SPI1WPN, (1), "SPI1WPN"),
+ FUNC(SPI2, (1), "SPI2"),
+ FUNC(SPI2CS1, (1), "SPI2CS1"),
+ FUNC(TACH0, (1), "TACH0"),
+ FUNC(TACH1, (1), "TACH1"),
+ FUNC(TACH10, (1), "TACH10"),
+ FUNC(TACH11, (1), "TACH11"),
+ FUNC(TACH12, (1), "TACH12"),
+ FUNC(TACH13, (1), "TACH13"),
+ FUNC(TACH14, (1), "TACH14"),
+ FUNC(TACH15, (1), "TACH15"),
+ FUNC(TACH2, (1), "TACH2"),
+ FUNC(TACH3, (1), "TACH3"),
+ FUNC(TACH4, (1), "TACH4"),
+ FUNC(TACH5, (1), "TACH5"),
+ FUNC(TACH6, (1), "TACH6"),
+ FUNC(TACH7, (1), "TACH7"),
+ FUNC(TACH8, (1), "TACH8"),
+ FUNC(TACH9, (1), "TACH9"),
+ FUNC(THRU0, (2), "THRU0"),
+ FUNC(THRU1, (2), "THRU1"),
+ FUNC(THRU2, (4), "THRU2"),
+ FUNC(THRU3, (4), "THRU3"),
+ FUNC(UART0, (1), "UART0"),
+ FUNC(UART1, (1), "UART1"),
+ FUNC(UART10, (3), "UART10"),
+ FUNC(UART11, (3), "UART11"),
+ FUNC(UART2, (1), "UART2"),
+ FUNC(UART3, (1), "UART3"),
+ FUNC(UART5, (1), "UART5"),
+ FUNC(UART6, (1), "UART6"),
+ FUNC(UART7, (1), "UART7"),
+ FUNC(UART8, (3), "UART8"),
+ FUNC(UART9, (3), "UART9"),
+ FUNC(USB2C, (0, 1, 2, 3), "USB2CUD", "USB2CD", "USB2CH", "USB2CU"),
+ FUNC(USB2D, (1, 2), "USB2DD", "USB2DH"),
+ FUNC(USBUART, (2), "USBUART"),
+ FUNC(VGA, (1), "VGA"),
+ FUNC(VPI, (5), "VPI"),
+ FUNC(WDTRST0N, (2), "WDTRST0N"),
+ FUNC(WDTRST1N, (2), "WDTRST1N"),
+ FUNC(WDTRST2N, (2), "WDTRST2N"),
+ FUNC(WDTRST3N, (2), "WDTRST3N"),
+ FUNC(WDTRST4N, (2), "WDTRST4N"),
+ FUNC(WDTRST5N, (2), "WDTRST5N"),
+ FUNC(WDTRST6N, (2), "WDTRST6N"),
+ FUNC(WDTRST7N, (2), "WDTRST7N"),
+};
+
+static struct ast2700_soc1_field
+ast2700_soc1_pinmux_field_from_pin(unsigned int pin)
+{
+ return (struct ast2700_soc1_field){
+ .reg = AST2700_SOC1_MUX_BASE +
+ (pin / AST2700_SOC1_MUX_PINS_PER_REG) * sizeof(u32),
+ .shift = (pin % AST2700_SOC1_MUX_PINS_PER_REG) *
+ AST2700_SOC1_MUX_BITS_PER_PIN,
+ .mask = AST2700_SOC1_MUX_MASK,
+ };
+}
+
+static void __iomem *ast2700_soc1_reg(struct ast2700_soc1_pinctrl_priv *priv,
+ unsigned int offset)
+{
+ return (u8 __iomem *)priv->scu + offset;
+}
+
+static struct ast2700_soc1_field
+ast2700_soc1_field_from_pin(unsigned int pin)
+{
+ switch (pin) {
+ case PCIERC2_PERST:
+ return (struct ast2700_soc1_field){
+ .reg = AST2700_SOC1_PCIE_REG,
+ .shift = 0,
+ .mask = 0x1,
+ };
+ case PORTC_MODE:
+ return (struct ast2700_soc1_field){
+ .reg = AST2700_SOC1_USB_MODE_REG,
+ .shift = 0,
+ .mask = 0x3,
+ };
+ case PORTD_MODE:
+ return (struct ast2700_soc1_field){
+ .reg = AST2700_SOC1_USB_MODE_REG,
+ .shift = 2,
+ .mask = 0x3,
+ };
+ case SGMII0:
+ return (struct ast2700_soc1_field){
+ .reg = AST2700_SOC1_SGMII_REG,
+ .shift = 0,
+ .mask = 0x1,
+ };
+ default:
+ return ast2700_soc1_pinmux_field_from_pin(pin);
+ }
+}
+
+static void
+ast2700_soc1_set_muxval(struct ast2700_soc1_pinctrl_priv *priv,
+ unsigned int pin, unsigned int muxval)
+{
+ struct ast2700_soc1_field field = ast2700_soc1_field_from_pin(pin);
+
+ /*
+ * PCIERC2_PERST is controlled by a 1-bit enable instead of the
+ * function mux value.
+ */
+ if (pin == PCIERC2_PERST)
+ muxval = 1;
+
+ clrsetbits_le32(ast2700_soc1_reg(priv, field.reg),
+ field.mask << field.shift,
+ (muxval & field.mask) << field.shift);
+}
+
+static int ast2700_soc1_pinctrl_get_pins_count(struct udevice *dev)
+{
+ return ARRAY_SIZE(ast2700_soc1_pin_names);
+}
+
+static const char *
+ast2700_soc1_pinctrl_get_pin_name(struct udevice *dev, unsigned int selector)
+{
+ if (selector >= ARRAY_SIZE(ast2700_soc1_pin_names))
+ return NULL;
+
+ return ast2700_soc1_pin_names[selector];
+}
+
+static const struct ast2700_soc1_group *
+ast2700_soc1_find_group(const char *name)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(ast2700_soc1_groups); i++) {
+ if (!strcmp(ast2700_soc1_groups[i].name, name))
+ return &ast2700_soc1_groups[i];
+ }
+
+ return NULL;
+}
+
+static int ast2700_soc1_pinctrl_get_pin_muxing(struct udevice *dev,
+ unsigned int selector,
+ char *buf, int size)
+{
+ struct ast2700_soc1_pinctrl_priv *priv = dev_get_priv(dev);
+ const struct ast2700_soc1_function *func;
+ const struct ast2700_soc1_group *group;
+ struct ast2700_soc1_field field;
+ unsigned int muxval, want, f, g, i;
+ bool adc_pin;
+
+ if (selector >= ARRAY_SIZE(ast2700_soc1_pin_names))
+ return -EINVAL;
+
+ field = ast2700_soc1_field_from_pin(selector);
+ muxval = (readl(ast2700_soc1_reg(priv, field.reg)) >> field.shift) &
+ field.mask;
+
+ /* Mux value 0 selects GPIO, except on the ADC balls where it is 1 */
+ adc_pin = selector >= W17 && selector <= AB19;
+ if (selector <= AC24 && muxval == (adc_pin ? 1 : 0)) {
+ snprintf(buf, size, "GPIO");
+ return 0;
+ }
+
+ for (f = 0; f < ARRAY_SIZE(ast2700_soc1_functions); f++) {
+ func = &ast2700_soc1_functions[f];
+ for (g = 0; g < func->ngroups; g++) {
+ want = func->muxvals[g];
+ /* PCIERC2_PERST is a 1-bit enable, not a mux value */
+ if (selector == PCIERC2_PERST)
+ want = 1;
+ if (want != muxval)
+ continue;
+ group = ast2700_soc1_find_group(func->groups[g]);
+ if (!group)
+ continue;
+ for (i = 0; i < group->npins; i++) {
+ if (group->pins[i] != selector)
+ continue;
+ snprintf(buf, size, "%s (%s)", func->name,
+ group->name);
+ return 0;
+ }
+ }
+ }
+
+ snprintf(buf, size, "unknown (mux 0x%x)", muxval);
+
+ return 0;
+}
+
+static int ast2700_soc1_pinctrl_get_groups_count(struct udevice *dev)
+{
+ return ARRAY_SIZE(ast2700_soc1_groups);
+}
+
+static const char *
+ast2700_soc1_pinctrl_get_group_name(struct udevice *dev,
+ unsigned int selector)
+{
+ if (selector >= ARRAY_SIZE(ast2700_soc1_groups))
+ return NULL;
+
+ return ast2700_soc1_groups[selector].name;
+}
+
+static int ast2700_soc1_pinctrl_get_functions_count(struct udevice *dev)
+{
+ return ARRAY_SIZE(ast2700_soc1_functions);
+}
+
+static const char *
+ast2700_soc1_pinctrl_get_function_name(struct udevice *dev,
+ unsigned int selector)
+{
+ if (selector >= ARRAY_SIZE(ast2700_soc1_functions))
+ return NULL;
+
+ return ast2700_soc1_functions[selector].name;
+}
+
+static int
+ast2700_soc1_pinctrl_group_set(struct udevice *dev,
+ unsigned int group_selector,
+ unsigned int func_selector)
+{
+ struct ast2700_soc1_pinctrl_priv *priv = dev_get_priv(dev);
+ const struct ast2700_soc1_function *func;
+ const struct ast2700_soc1_group *group;
+ unsigned int muxval = 0;
+ unsigned int i;
+
+ if (group_selector >= ARRAY_SIZE(ast2700_soc1_groups) ||
+ func_selector >= ARRAY_SIZE(ast2700_soc1_functions))
+ return -EINVAL;
+
+ group = &ast2700_soc1_groups[group_selector];
+ func = &ast2700_soc1_functions[func_selector];
+
+ for (i = 0; i < func->ngroups; i++) {
+ if (!strcmp(group->name, func->groups[i])) {
+ muxval = func->muxvals[i];
+ break;
+ }
+ }
+
+ if (i == func->ngroups)
+ return -EINVAL;
+
+ for (i = 0; i < group->npins; i++)
+ ast2700_soc1_set_muxval(priv, group->pins[i], muxval);
+
+ return 0;
+}
+
+static int
+ast2700_soc1_pinctrl_gpio_request_enable(struct udevice *dev,
+ unsigned int selector)
+{
+ struct ast2700_soc1_pinctrl_priv *priv = dev_get_priv(dev);
+ unsigned int muxval = 0;
+
+ if (selector > AC24)
+ return -ENOTSUPP;
+
+ if (selector >= W17 && selector <= AB19)
+ muxval = 1;
+
+ ast2700_soc1_set_muxval(priv, selector, muxval);
+
+ return 0;
+}
+
+/*
+ * Drive strength is controlled by 2-bit fields in the registers at
+ * DRV_BASE, but the mapping from pins to fields is sparse and
+ * non-linear: only a subset of the pins supports drive strength
+ * selection. The table stores (field index + 1) so that the implicit
+ * zero initialization marks the unsupported pins.
+ */
+static const u8 ast2700_soc1_drv_map[] = {
+ [C16] = 1, [C14] = 2, [C11] = 3, [D9] = 4, [F14] = 5,
+ [D10] = 6, [C12] = 7, [C13] = 8, [W25] = 9, [Y23] = 10,
+ [Y24] = 11, [W21] = 12, [AA23] = 13, [AC22] = 14, [AB22] = 15,
+ [Y21] = 16, [AE20] = 17, [AF19] = 18, [Y22] = 19, [AA20] = 20,
+ [AA22] = 21, [AB20] = 22, [AF18] = 23, [AE19] = 24, [AD20] = 25,
+ [AC20] = 26, [AA21] = 27, [AB21] = 28, [AC19] = 29, [AE18] = 30,
+ [AD19] = 31, [AD18] = 32, [U25] = 33, [U26] = 34, [Y26] = 35,
+ [AA24] = 36, [R25] = 37, [AA26] = 38, [R26] = 39, [Y25] = 40,
+ [B16] = 41, [D14] = 42, [B15] = 43, [B14] = 44, [C17] = 45,
+ [B13] = 46, [E14] = 47, [C15] = 48, [D24] = 49, [B23] = 50,
+ [B22] = 51, [C23] = 52, [B18] = 53, [B21] = 54, [M15] = 55,
+ [B19] = 56, [B26] = 57, [A25] = 58, [A24] = 59, [B24] = 60,
+ [E26] = 61, [A21] = 62, [A19] = 63, [A18] = 64, [D26] = 65,
+ [C26] = 66, [A23] = 67, [A22] = 68, [B25] = 69, [F26] = 70,
+ [A26] = 71, [A14] = 72, [E10] = 73, [E13] = 74, [D12] = 75,
+ [F10] = 76, [E11] = 77, [F11] = 78, [F13] = 79, [N15] = 80,
+ [C20] = 81, [C19] = 82, [A8] = 83, [R14] = 84, [A7] = 85,
+ [P14] = 86, [D20] = 87, [A6] = 88, [B6] = 89, [N14] = 90,
+ [B7] = 91, [B8] = 92, [B9] = 93, [M14] = 94, [J11] = 95,
+ [E7] = 96, [D19] = 97, [B11] = 98, [D15] = 99, [B12] = 100,
+ [B10] = 101, [P13] = 102, [C18] = 103, [C6] = 104, [C7] = 105,
+ [D7] = 106, [N13] = 107, [C8] = 108, [C9] = 109, [C10] = 110,
+ [M16] = 111, [A15] = 112, [E9] = 113, [F9] = 114, [F8] = 115,
+ [M13] = 116, [F7] = 117, [D8] = 118, [E8] = 119, [L12] = 120,
+};
+
+static struct ast2700_soc1_field
+ast2700_soc1_bias_field_from_pin(unsigned int pin)
+{
+ return (struct ast2700_soc1_field){
+ .reg = AST2700_SOC1_BIAS_BASE +
+ (pin / AST2700_SOC1_BIAS_PINS_PER_REG) * sizeof(u32),
+ .shift = pin % AST2700_SOC1_BIAS_PINS_PER_REG,
+ .mask = 0x1,
+ };
+}
+
+static struct ast2700_soc1_field
+ast2700_soc1_drv_field_from_idx(unsigned int idx)
+{
+ return (struct ast2700_soc1_field){
+ .reg = AST2700_SOC1_DRV_BASE +
+ (idx / AST2700_SOC1_DRV_PINS_PER_REG) * sizeof(u32),
+ .shift = (idx % AST2700_SOC1_DRV_PINS_PER_REG) *
+ AST2700_SOC1_DRV_BITS_PER_PIN,
+ .mask = AST2700_SOC1_DRV_MASK,
+ };
+}
+
+static const struct pinconf_param ast2700_soc1_pinconf_params[] = {
+ { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
+ { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 0 },
+ { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 0 },
+ { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 },
+};
+
+static int ast2700_soc1_pinctrl_pinconf_set(struct udevice *dev,
+ unsigned int selector,
+ unsigned int param,
+ unsigned int arg)
+{
+ struct ast2700_soc1_pinctrl_priv *priv = dev_get_priv(dev);
+ struct ast2700_soc1_field field;
+ unsigned int val, idx;
+
+ if (selector > AC24)
+ return -EINVAL;
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ case PIN_CONFIG_BIAS_PULL_UP:
+ /*
+ * One bias enable bit per pin; the pull direction is fixed
+ * in silicon. Setting the bit disables the bias.
+ */
+ field = ast2700_soc1_bias_field_from_pin(selector);
+ val = param == PIN_CONFIG_BIAS_DISABLE ? 1 : 0;
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ if (selector >= ARRAY_SIZE(ast2700_soc1_drv_map) ||
+ !ast2700_soc1_drv_map[selector])
+ return -ENOTSUPP;
+ if (arg < AST2700_SOC1_DRV_MIN_MA ||
+ arg > AST2700_SOC1_DRV_MAX_MA ||
+ arg % AST2700_SOC1_DRV_STEP_MA)
+ return -EINVAL;
+ idx = ast2700_soc1_drv_map[selector] - 1;
+ field = ast2700_soc1_drv_field_from_idx(idx);
+ val = arg / AST2700_SOC1_DRV_STEP_MA - 1;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ clrsetbits_le32(ast2700_soc1_reg(priv, field.reg),
+ field.mask << field.shift, val << field.shift);
+
+ return 0;
+}
+
+static int ast2700_soc1_pinctrl_pinconf_group_set(struct udevice *dev,
+ unsigned int selector,
+ unsigned int param,
+ unsigned int arg)
+{
+ const struct ast2700_soc1_group *group;
+ unsigned int i;
+ int ret;
+
+ if (selector >= ARRAY_SIZE(ast2700_soc1_groups))
+ return -EINVAL;
+
+ group = &ast2700_soc1_groups[selector];
+ for (i = 0; i < group->npins; i++) {
+ ret = ast2700_soc1_pinctrl_pinconf_set(dev, group->pins[i],
+ param, arg);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ast2700_soc1_pinctrl_probe(struct udevice *dev)
+{
+ struct ast2700_soc1_pinctrl_priv *priv = dev_get_priv(dev);
+
+ priv->scu = dev_remap_addr(dev->parent);
+ if (!priv->scu)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static const struct pinctrl_ops ast2700_soc1_pinctrl_ops = {
+ .set_state = pinctrl_generic_set_state,
+ .get_pins_count = ast2700_soc1_pinctrl_get_pins_count,
+ .get_pin_name = ast2700_soc1_pinctrl_get_pin_name,
+ .get_pin_muxing = ast2700_soc1_pinctrl_get_pin_muxing,
+ .get_groups_count = ast2700_soc1_pinctrl_get_groups_count,
+ .get_group_name = ast2700_soc1_pinctrl_get_group_name,
+ .get_functions_count = ast2700_soc1_pinctrl_get_functions_count,
+ .get_function_name = ast2700_soc1_pinctrl_get_function_name,
+ .pinmux_group_set = ast2700_soc1_pinctrl_group_set,
+ .gpio_request_enable = ast2700_soc1_pinctrl_gpio_request_enable,
+ .pinconf_num_params = ARRAY_SIZE(ast2700_soc1_pinconf_params),
+ .pinconf_params = ast2700_soc1_pinconf_params,
+ .pinconf_set = ast2700_soc1_pinctrl_pinconf_set,
+ .pinconf_group_set = ast2700_soc1_pinctrl_pinconf_group_set,
+};
+
+static const struct udevice_id ast2700_soc1_pinctrl_ids[] = {
+ { .compatible = "aspeed,ast2700-soc1-pinctrl" },
+ { }
+};
+
+U_BOOT_DRIVER(pinctrl_ast2700_soc1) = {
+ .name = "aspeed_ast2700_soc1_pinctrl",
+ .id = UCLASS_PINCTRL,
+ .of_match = ast2700_soc1_pinctrl_ids,
+ .priv_auto = sizeof(struct ast2700_soc1_pinctrl_priv),
+ .ops = &ast2700_soc1_pinctrl_ops,
+ .probe = ast2700_soc1_pinctrl_probe,
+};
diff --git a/drivers/pinctrl/spacemit/Kconfig b/drivers/pinctrl/spacemit/Kconfig
new file mode 100644
index 00000000000..6aab89e160c
--- /dev/null
+++ b/drivers/pinctrl/spacemit/Kconfig
@@ -0,0 +1,9 @@
+config PINCTRL_SPACEMIT_K1
+ bool "Spacemit K1 SoC pinctrl driver"
+ depends on PINCTRL_GENERIC && DM
+ help
+ Supports pin multiplexing control on Spacemit K1 SoCs.
+
+ The driver is controlled by a device tree node which contains both
+ the GPIO definitions and pin control functions for each available
+ multiplex function.
diff --git a/drivers/pinctrl/spacemit/Makefile b/drivers/pinctrl/spacemit/Makefile
new file mode 100644
index 00000000000..0dc43c72cb1
--- /dev/null
+++ b/drivers/pinctrl/spacemit/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-y += pinctrl-k1.o
diff --git a/drivers/pinctrl/spacemit/pinctrl-k1.c b/drivers/pinctrl/spacemit/pinctrl-k1.c
new file mode 100644
index 00000000000..a6a22eacac7
--- /dev/null
+++ b/drivers/pinctrl/spacemit/pinctrl-k1.c
@@ -0,0 +1,550 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 Yixun Lan <[email protected]>
+ * Copyright (c) 2025-2026 RISCstar Ltd.
+ */
+
+#include <clk.h>
+#include <dm/device.h>
+#include <dm/device_compat.h>
+#include <dm/pinctrl.h>
+#include <dm/read.h>
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+
+/*
+ * +---------+----------+-----------+--------+--------+----------+--------+
+ * | pull | drive | schmitter | slew | edge | strong | mux |
+ * | up/down | strength | trigger | rate | detect | pull | mode |
+ * +---------+----------+-----------+--------+--------+----------+--------+
+ * 3 bits 3 bits 2 bits 1 bit 3 bits 1 bit 3 bits
+ */
+
+#define PAD_MUX GENMASK(2, 0)
+#define PAD_STRONG_PULL BIT(3)
+#define PAD_EDGE_RISE BIT(4)
+#define PAD_EDGE_FALL BIT(5)
+#define PAD_EDGE_CLEAR BIT(6)
+#define PAD_SLEW_RATE GENMASK(12, 11)
+#define PAD_SLEW_RATE_EN BIT(7)
+#define PAD_SCHMITT GENMASK(9, 8)
+#define PAD_DRIVE GENMASK(12, 10)
+#define PAD_PULLDOWN BIT(13)
+#define PAD_PULLUP BIT(14)
+#define PAD_PULL_EN BIT(15)
+
+#define PIN_POWER_STATE_1V8 1800
+#define PIN_POWER_STATE_3V3 3300
+
+enum spacemit_pin_io_type {
+ IO_TYPE_NONE = 0,
+ IO_TYPE_1V8,
+ IO_TYPE_3V3,
+ IO_TYPE_EXTERNAL,
+};
+
+struct spacemit_pin_io {
+ unsigned int pin : 12; // 0~4095
+ unsigned int io_type : 4; // 0~15
+ unsigned int ds : 8; // 0~255
+ unsigned int reserved : 8;
+};
+
+struct spacemit_pinctrl_data {
+ struct spacemit_pin_io *io_pins;
+ int nr_io_pins;
+
+ void __iomem * (*pin_to_reg)(struct udevice *dev, unsigned int pin);
+ int (*get_gpio_mux)(struct udevice *dev, unsigned int pin);
+ int (*get_pins)(struct udevice *dev);
+ int (*get_functions)(struct udevice *dev);
+ int (*get_io_type)(struct udevice *dev, unsigned int pin);
+};
+
+struct spacemit_pinctrl_priv {
+ void __iomem *regs;
+ struct spacemit_pin_io *io_pins;
+ int nr_io_pins;
+};
+
+struct spacemit_pin_mux_config {
+ const struct spacemit_pin *pin;
+ u32 config;
+};
+
+struct spacemit_pin_drv_strength {
+ unsigned int val : 8;
+ unsigned int ma : 16;
+ unsigned int reserved : 8;
+};
+
+static char pin_name[PINNAME_SIZE];
+
+/* External: IO voltage via external source, can be 1.8V or 3.3V */
+static struct spacemit_pin_io k1_io_pins[] = {
+ { 47, IO_TYPE_EXTERNAL, 0, },
+ { 48, IO_TYPE_EXTERNAL, 0, },
+ { 49, IO_TYPE_EXTERNAL, 0, },
+ { 50, IO_TYPE_EXTERNAL, 0, },
+ { 51, IO_TYPE_EXTERNAL, 0, },
+ { 52, IO_TYPE_EXTERNAL, 0, },
+ { 75, IO_TYPE_EXTERNAL, 0, },
+ { 76, IO_TYPE_EXTERNAL, 0, },
+ { 77, IO_TYPE_EXTERNAL, 0, },
+ { 78, IO_TYPE_EXTERNAL, 0, },
+ { 79, IO_TYPE_EXTERNAL, 0, },
+ { 80, IO_TYPE_EXTERNAL, 0, },
+ { 98, IO_TYPE_EXTERNAL, 0, },
+ { 99, IO_TYPE_EXTERNAL, 0, },
+ { 100, IO_TYPE_EXTERNAL, 0, },
+ { 101, IO_TYPE_EXTERNAL, 0, },
+ { 102, IO_TYPE_EXTERNAL, 0, },
+ { 103, IO_TYPE_EXTERNAL, 0, },
+ { 104, IO_TYPE_EXTERNAL, 0, },
+ { 105, IO_TYPE_EXTERNAL, 0, },
+ { 106, IO_TYPE_EXTERNAL, 0, },
+ { 107, IO_TYPE_EXTERNAL, 0, },
+ { 108, IO_TYPE_EXTERNAL, 0, },
+ { 109, IO_TYPE_EXTERNAL, 0, },
+};
+
+static inline int k1_get_pins(struct udevice *dev)
+{
+ return 128;
+}
+
+static inline int k1_get_functions(struct udevice *dev)
+{
+ return 7;
+}
+
+// The pin number equals to the gpio number.
+static void __iomem *k1_pin_to_reg(struct udevice *dev, unsigned int pin)
+{
+ struct spacemit_pinctrl_priv *priv = dev_get_priv(dev);
+ unsigned int offset = 1;
+
+ if (pin < 86) {
+ offset += pin;
+ } else if (pin < 93) {
+ offset += pin + 36;
+ } else if (pin < 98) {
+ offset += pin + 23;
+ } else if (pin == 98) { // QSPI_DAT3
+ offset += 92;
+ } else if (pin == 99) { // QSPI_DAT2
+ offset += 91;
+ } else if (pin == 100) { // QSPI_DAT1
+ offset += 90;
+ } else if (pin == 101) { // QSPI_DAT0
+ offset += 89;
+ } else if (pin == 102) { // QSPI_CLK
+ offset += 94;
+ } else if (pin == 103) { // QSPI_CS1
+ offset += 93;
+ } else if (pin < 111) {
+ offset += pin + 5;
+ } else if (pin < 128) {
+ offset += pin + 19;
+ } else {
+ dev_err(dev, "Invalid pin (%u)\n", pin);
+ return NULL;
+ }
+ return priv->regs + (offset << 2);
+}
+
+static int k1_get_gpio_mux(struct udevice *dev, unsigned int selector)
+{
+ u32 mux = 0;
+
+ if (selector < 70) {
+ mux = 0;
+ } else if (selector < 74) {
+ mux = 1;
+ } else if (selector < 93) {
+ mux = 0;
+ } else if (selector < 104) {
+ mux = 1;
+ } else if (selector < 110) {
+ mux = 4;
+ } else if (selector < 128) {
+ mux = 0;
+ } else {
+ dev_err(dev, "Invalid pin (%u)\n", selector);
+ return -EINVAL;
+ }
+ return mux;
+}
+
+static int k1_get_io_type(struct udevice *dev, unsigned int selector)
+{
+ if (selector < 47)
+ return IO_TYPE_1V8;
+ else if (selector < 53)
+ return IO_TYPE_EXTERNAL;
+ else if (selector < 75)
+ return IO_TYPE_1V8;
+ else if (selector < 81)
+ return IO_TYPE_EXTERNAL;
+ else if (selector < 98)
+ return IO_TYPE_1V8;
+ else if (selector < 110)
+ return IO_TYPE_EXTERNAL;
+ else if (selector < 128)
+ return IO_TYPE_1V8;
+ return -EINVAL;
+}
+
+/* use IO high level output current as the table */
+static struct spacemit_pin_drv_strength spacemit_ds_1v8_tbl[4] = {
+ { 0, 11 },
+ { 2, 21 },
+ { 4, 32 },
+ { 6, 42 },
+};
+
+static struct spacemit_pin_drv_strength spacemit_ds_3v3_tbl[8] = {
+ { 0, 7 },
+ { 2, 10 },
+ { 4, 13 },
+ { 6, 16 },
+ { 1, 19 },
+ { 3, 23 },
+ { 5, 26 },
+ { 7, 29 },
+};
+
+static inline u8 spacemit_get_ds_value(struct spacemit_pin_drv_strength *tbl,
+ u32 num, u32 ma)
+{
+ int i;
+
+ for (i = 0; i < num; i++)
+ if (ma <= tbl[i].ma)
+ return tbl[i].val;
+
+ return tbl[num - 1].val;
+}
+
+static inline u32 spacemit_get_ds_ma(struct spacemit_pin_drv_strength *tbl,
+ u32 num, u32 val)
+{
+ int i;
+
+ for (i = 0; i < num; i++)
+ if (val == tbl[i].val)
+ return tbl[i].ma;
+
+ return 0;
+}
+
+static inline u8 spacemit_get_drive_strength(enum spacemit_pin_io_type type,
+ u32 ma)
+{
+ switch (type) {
+ case IO_TYPE_1V8:
+ return spacemit_get_ds_value(spacemit_ds_1v8_tbl,
+ ARRAY_SIZE(spacemit_ds_1v8_tbl),
+ ma);
+ case IO_TYPE_3V3:
+ return spacemit_get_ds_value(spacemit_ds_3v3_tbl,
+ ARRAY_SIZE(spacemit_ds_3v3_tbl),
+ ma);
+ default:
+ return 0;
+ }
+}
+
+static inline u32 spacemit_get_drive_strength_ma(enum spacemit_pin_io_type type,
+ u32 value)
+{
+ switch (type) {
+ case IO_TYPE_1V8:
+ return spacemit_get_ds_ma(spacemit_ds_1v8_tbl,
+ ARRAY_SIZE(spacemit_ds_1v8_tbl),
+ value & 0x6);
+ case IO_TYPE_3V3:
+ return spacemit_get_ds_ma(spacemit_ds_3v3_tbl,
+ ARRAY_SIZE(spacemit_ds_3v3_tbl),
+ value);
+ default:
+ return 0;
+ }
+}
+
+static inline u16 spacemit_dt_get_pin(u32 value)
+{
+ return value >> 16;
+}
+
+static inline u16 spacemit_dt_get_pin_mux(u32 value)
+{
+ return value & GENMASK(15, 0);
+}
+
+static int spacemit_get_pins_count(struct udevice *dev)
+{
+ struct spacemit_pinctrl_data *data;
+
+ data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
+ if (!data || !data->get_pins)
+ return -EINVAL;
+ return data->get_pins(dev);
+}
+
+static const char *spacemit_get_pin_name(struct udevice *dev,
+ unsigned int selector)
+{
+ struct spacemit_pinctrl_data *data;
+ unsigned int npins;
+
+ data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
+ if (!data || !data->get_pins)
+ return NULL;
+ npins = data->get_pins(dev);
+
+ if (selector >= npins)
+ snprintf(pin_name, PINNAME_SIZE, "Error");
+ else
+ snprintf(pin_name, PINNAME_SIZE, "PIN%u", selector);
+
+ return pin_name;
+}
+
+static int spacemit_get_functions_count(struct udevice *dev)
+{
+ struct spacemit_pinctrl_data *data;
+
+ data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
+ if (!data || !data->get_functions)
+ return -EINVAL;
+ return data->get_functions(dev);
+}
+
+static int spacemit_get_pin_muxing(struct udevice *dev, unsigned int pin,
+ char *buf, int size)
+{
+ struct spacemit_pinctrl_data *data;
+ void __iomem *addr;
+ u32 mux, val;
+
+ data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
+ if (!data || !data->pin_to_reg)
+ return -EINVAL;
+
+ addr = data->pin_to_reg(dev, pin);
+ if (!addr)
+ return -EINVAL;
+
+ val = readl(addr);
+ mux = val & PAD_MUX;
+ snprintf(buf, size, "[%p] 0x%08x MUX%d", addr, val, mux);
+ return 0;
+}
+
+static int spacemit_pinctrl_request_gpio(struct udevice *dev,
+ unsigned int selector)
+{
+ struct spacemit_pinctrl_data *data;
+ void __iomem *addr;
+ int mux;
+
+ data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
+ if (!data || !data->pin_to_reg || !data->get_gpio_mux)
+ return -EINVAL;
+ addr = data->pin_to_reg(dev, selector);
+ mux = data->get_gpio_mux(dev, selector);
+ if (mux < 0) {
+ dev_err(dev, "Invalid pin (%d)\n", selector);
+ return -EINVAL;
+ }
+ clrsetbits_le32(addr, PAD_MUX, mux & PAD_MUX);
+ return 0;
+}
+
+static int spacemit_pinctrl_free_gpio(struct udevice *dev,
+ unsigned int selector)
+{
+ return 0;
+}
+
+static int spacemit_pinmux_set(struct udevice *dev, unsigned int pin,
+ unsigned int mux)
+{
+ struct spacemit_pinctrl_data *data;
+ void __iomem *addr;
+
+ data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
+ if (!data || !data->pin_to_reg)
+ return -EINVAL;
+ addr = data->pin_to_reg(dev, pin);
+ clrsetbits_le32(addr, PAD_MUX, mux & PAD_MUX);
+ return 0;
+}
+
+static int spacemit_pinmux_property_set(struct udevice *dev, u32 pinmux_group)
+{
+ u32 pin, mux;
+
+ pin = spacemit_dt_get_pin(pinmux_group);
+ mux = spacemit_dt_get_pin_mux(pinmux_group);
+ return spacemit_pinmux_set(dev, pin, mux);
+}
+
+static const struct pinconf_param spacemit_pinconf_params[] = {
+ { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
+ { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
+ { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
+ { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, U32_MAX },
+ { "power-source", PIN_CONFIG_POWER_SOURCE, U32_MAX },
+};
+
+static int spacemit_pinconf_set(struct udevice *dev, unsigned int pin_selector,
+ unsigned int param, unsigned int argument)
+{
+ struct spacemit_pinctrl_data *data;
+ struct spacemit_pinctrl_priv *priv = dev_get_priv(dev);
+ void __iomem *addr;
+ u32 mask = 0;
+ unsigned int io_type;
+ u8 ds;
+ bool found;
+ int i;
+
+ data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
+ if (!data || !data->pin_to_reg)
+ return -EINVAL;
+ addr = data->pin_to_reg(dev, pin_selector);
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ clrbits_le32(addr, PAD_PULLUP | PAD_PULLDOWN | PAD_PULL_EN);
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ mask = PAD_PULLDOWN | PAD_PULLUP | PAD_PULL_EN;
+ clrsetbits_le32(addr, mask, PAD_PULLDOWN | PAD_PULL_EN);
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ mask = PAD_PULLDOWN | PAD_PULLUP | PAD_PULL_EN;
+ clrsetbits_le32(addr, mask, PAD_PULLUP | PAD_PULL_EN);
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ io_type = IO_TYPE_1V8;
+ for (i = 0; i < priv->nr_io_pins; i++) {
+ if (priv->io_pins[i].pin != pin_selector)
+ continue;
+ io_type = priv->io_pins[i].io_type;
+ break;
+ }
+ if (io_type != IO_TYPE_3V3 && io_type != IO_TYPE_1V8) {
+ dev_err(dev, "Invalid IO type (%d)\n", io_type);
+ return -EINVAL;
+ }
+ ds = spacemit_get_drive_strength(io_type, argument);
+ clrsetbits_le32(addr, PAD_DRIVE, ds);
+ break;
+ case PIN_CONFIG_POWER_SOURCE:
+ for (i = 0, found = false; i < priv->nr_io_pins; i++) {
+ if (priv->io_pins[i].pin != pin_selector)
+ continue;
+ if (argument == PIN_POWER_STATE_3V3) {
+ priv->io_pins[i].io_type = IO_TYPE_3V3;
+ found = true;
+ } else if (argument == PIN_POWER_STATE_1V8) {
+ priv->io_pins[i].io_type = IO_TYPE_1V8;
+ found = true;
+ }
+ break;
+ }
+ if (!found && argument != PIN_POWER_STATE_1V8) {
+ dev_err(dev, "Invalid power source (%d)\n", argument);
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static int spacemit_pinctrl_probe(struct udevice *dev)
+{
+ struct spacemit_pinctrl_data *data;
+ struct spacemit_pinctrl_priv *priv;
+ struct clk_bulk clks;
+ size_t size;
+ int ret;
+
+ data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
+ priv = dev_get_priv(dev);
+ priv->regs = dev_read_addr_ptr(dev);
+ if (!priv->regs) {
+ dev_err(dev, "Fail to get base address\n");
+ return -EINVAL;
+ }
+ priv->nr_io_pins = data->nr_io_pins;
+ size = priv->nr_io_pins * sizeof(struct spacemit_pin_io);
+ priv->io_pins = (struct spacemit_pin_io *)memdup(data->io_pins, size);
+ if (!priv->io_pins) {
+ dev_err(dev, "Fail to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ ret = clk_get_bulk(dev, &clks);
+ if (ret) {
+ dev_err(dev, "Fail to get bulk clks\n");
+ goto out_get_clk;
+ }
+ ret = clk_enable_bulk(&clks);
+ if (ret) {
+ dev_err(dev, "Fail to enable bulk clks\n");
+ goto out_clks;
+ }
+ return 0;
+out_clks:
+ clk_release_bulk(&clks);
+out_get_clk:
+ free(priv->io_pins);
+ return ret;
+}
+
+static const struct spacemit_pinctrl_data k1_pinctrl_data = {
+ .io_pins = k1_io_pins,
+ .nr_io_pins = ARRAY_SIZE(k1_io_pins),
+ .pin_to_reg = k1_pin_to_reg,
+ .get_gpio_mux = k1_get_gpio_mux,
+ .get_pins = k1_get_pins,
+ .get_functions = k1_get_functions,
+ .get_io_type = k1_get_io_type,
+};
+
+static const struct udevice_id spacemit_pinctrl_ids[] = {
+ {
+ .compatible = "spacemit,k1-pinctrl",
+ .data = (uintptr_t)&k1_pinctrl_data,
+ }, { /* sentinel */ }
+};
+
+static const struct pinctrl_ops spacemit_pinctrl_ops = {
+ .get_pins_count = spacemit_get_pins_count,
+ .get_pin_name = spacemit_get_pin_name,
+ .get_functions_count = spacemit_get_functions_count,
+ .get_pin_muxing = spacemit_get_pin_muxing,
+ .set_state = pinctrl_generic_set_state,
+ .gpio_request_enable = spacemit_pinctrl_request_gpio,
+ .gpio_disable_free = spacemit_pinctrl_free_gpio,
+ .pinmux_set = spacemit_pinmux_set,
+ .pinmux_property_set = spacemit_pinmux_property_set,
+ .pinconf_num_params = ARRAY_SIZE(spacemit_pinconf_params),
+ .pinconf_params = spacemit_pinconf_params,
+ .pinconf_set = spacemit_pinconf_set,
+};
+
+U_BOOT_DRIVER(spacemit_pinctrl) = {
+ .name = "spacemit_pinctrl",
+ .id = UCLASS_PINCTRL,
+ .of_match = spacemit_pinctrl_ids,
+ .ops = &spacemit_pinctrl_ops,
+ .priv_auto = sizeof(struct spacemit_pinctrl_priv),
+ .probe = spacemit_pinctrl_probe,
+};
diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig
index 4bd9b4e1940..d45869cb30f 100644
--- a/drivers/power/pmic/Kconfig
+++ b/drivers/power/pmic/Kconfig
@@ -434,6 +434,14 @@ config PMIC_RAA215300
allows register access and will bind the sysreset driver
(CONFIG_SYSRESET_RAA215300) if it is enabled.
+config PMIC_SPACEMIT_P1
+ bool "Enable driver for Spacemit P1 power management chip"
+ depends on DM_PMIC
+ help
+ The P1 PMIC integrates multiple functions including
+ voltage regulators, a watchdog timer, GPIO interfaces, and a
+ real-time clock.
+
config DM_PMIC_MTK_PWRAP
bool "Enable driver for MediaTek PMIC Wrapper Support"
help
diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
index 2cda5a892fd..0ac92084221 100644
--- a/drivers/power/pmic/Makefile
+++ b/drivers/power/pmic/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_$(PHASE_)DM_PMIC_TPS80031) += tps80031.o
obj-$(CONFIG_$(PHASE_)PMIC_PALMAS) += palmas.o
obj-$(CONFIG_$(PHASE_)PMIC_LP873X) += lp873x.o
obj-$(CONFIG_$(PHASE_)PMIC_LP87565) += lp87565.o
+obj-$(CONFIG_PMIC_SPACEMIT_P1) += pmic_spacemit_p1.o
obj-$(CONFIG_PMIC_STPMIC1) += stpmic1.o
obj-$(CONFIG_PMIC_TPS65217) += pmic_tps65217.o
obj-$(CONFIG_PMIC_TPS65219) += tps65219.o
diff --git a/drivers/power/pmic/pmic_spacemit_p1.c b/drivers/power/pmic/pmic_spacemit_p1.c
new file mode 100644
index 00000000000..46c5926874c
--- /dev/null
+++ b/drivers/power/pmic/pmic_spacemit_p1.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025-2026 RISCstar Ltd.
+ */
+
+#include <dm.h>
+#include <power/pmic.h>
+#include <power/spacemit_p1.h>
+
+static int pmic_p1_reg_count(struct udevice *dev)
+{
+ return P1_MAX_REGS;
+}
+
+static int pmic_p1_write(struct udevice *dev, uint reg, const u8 *buffer,
+ int len)
+{
+ int ret;
+
+ ret = dm_i2c_write(dev, reg, buffer, len);
+ if (ret)
+ pr_err("%s write error on register %02x\n", dev->name, reg);
+
+ return ret;
+}
+
+static int pmic_p1_read(struct udevice *dev, uint reg, u8 *buffer,
+ int len)
+{
+ int ret;
+
+ ret = dm_i2c_read(dev, reg, buffer, len);
+ if (ret)
+ pr_err("%s read error on register %02x\n", dev->name, reg);
+
+ return ret;
+}
+
+static const struct pmic_child_info p1_children_info[] = {
+ { .prefix = "buck", .driver = P1_BUCK_DRIVER },
+ { .prefix = "aldo", .driver = P1_ALDO_DRIVER },
+ { .prefix = "dldo", .driver = P1_DLDO_DRIVER },
+ { },
+};
+
+static int pmic_p1_bind(struct udevice *dev)
+{
+ const struct pmic_child_info *p1_children_info =
+ (struct pmic_child_info *)dev_get_driver_data(dev);
+ ofnode regulators_node;
+ int children;
+
+ regulators_node = dev_read_subnode(dev, "regulators");
+ if (!ofnode_valid(regulators_node)) {
+ debug("%s regulators subnode not found\n", dev->name);
+ return -EINVAL;
+ }
+
+ children = pmic_bind_children(dev, regulators_node,
+ p1_children_info);
+ if (!children)
+ debug("%s has no children (regulators)\n", dev->name);
+
+ return 0;
+}
+
+static int pmic_p1_probe(struct udevice *dev)
+{
+ return 0;
+}
+
+static struct dm_pmic_ops pmic_p1_ops = {
+ .reg_count = pmic_p1_reg_count,
+ .read = pmic_p1_read,
+ .write = pmic_p1_write,
+};
+
+static const struct udevice_id pmic_p1_match[] = {
+ {
+ .compatible = "spacemit,p1",
+ .data = (ulong)&p1_children_info,
+ }, {
+ /* sentinel */
+ }
+};
+
+U_BOOT_DRIVER(pmic_p1) = {
+ .name = "pmic_p1",
+ .id = UCLASS_PMIC,
+ .of_match = pmic_p1_match,
+ .bind = pmic_p1_bind,
+ .probe = pmic_p1_probe,
+ .ops = &pmic_p1_ops,
+};
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
index 3b3ed97eb9f..c3dc5d6f97b 100644
--- a/drivers/power/regulator/Kconfig
+++ b/drivers/power/regulator/Kconfig
@@ -557,3 +557,53 @@ config DM_REGULATOR_MT6359
MediaTek MT6359 PMIC.
This driver supports the control of different power rails of device
through regulator interface.
+
+config DM_REGULATOR_PMBUS_HELPER
+ bool "Shared regulator helpers for PMBus chip drivers"
+ depends on DM_REGULATOR && PMBUS && DM_I2C
+ help
+ Provide shared get_value / set_value / get_enable / set_enable
+ operations for UCLASS_REGULATOR drivers that bind PMBus 1.x
+ compliant voltage regulators. Per chip drivers
+ (mps,mpq8785, lltc,ltc3882, ...) consume this helper to avoid
+ duplicating the LINEAR16 / DIRECT decoder dispatch and the
+ VOUT_MODE / VOUT_COMMAND / OPERATION transport sequences.
+
+config DM_REGULATOR_PMBUS_GENERIC
+ bool "Generic PMBus 1.x regulator driver (compatible=\"pmbus\")"
+ depends on DM_REGULATOR_PMBUS_HELPER
+ help
+ Catch all UCLASS_REGULATOR driver bound to compatible = "pmbus".
+ Auto detects the VOUT numeric format from the chip's VOUT_MODE
+ register and exposes telemetry plus voltage set / get against
+ the standard PMBus 1.x command codes. Use this for PMBus
+ compliant chips that have no per chip driver yet; promote to a
+ per chip driver only when chip specific quirks (vendor
+ registers, VID coercion, ADDR pin auto promotion, non standard
+ m / b / R coefficients) need handling.
+
+config DM_REGULATOR_MPQ8785
+ bool "MPS MPQ8785 / MPM3695 / MPM82504 PMBus voltage regulator"
+ depends on DM_REGULATOR_PMBUS_HELPER
+ help
+ Driver for the Monolithic Power Systems MPQ8785 family of
+ digital multiphase voltage regulators with PMBus. Supports
+ MPM3695, MPM3695-25, MPM82504, and MPQ8785. Adapted from the
+ Linux drivers/hwmon/pmbus/mpq8785.c reference.
+
+config SANDBOX_PMBUS
+ bool "Sandbox PMBus 1.x chip emulator"
+ depends on SANDBOX && PMBUS && DM_I2C
+ help
+ Emulate a PMBus 1.x compliant chip behind a sandbox I2C bus so
+ the PMBus framework (lib/pmbus.c), the generic regulator
+ (DM_REGULATOR_PMBUS_GENERIC) and the pmbus CLI command can be
+ exercised by the dm unit tests with no real hardware. Only
+ useful for testing; say N on real boards.
+
+config DM_REGULATOR_SPACEMIT_P1
+ bool "Enable driver for Spacemit P1 PMIC regulators"
+ depends on DM_REGULATOR && PMIC_SPACEMIT_P1
+ help
+ Enable implementation of driver-model regulator uclass features
+ for regulator P1. The driver supports BUCKs, LDOs and SWITCHes.
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
index 36a84e7cd71..3a148545387 100644
--- a/drivers/power/regulator/Makefile
+++ b/drivers/power/regulator/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_$(PHASE_)REGULATOR_RK8XX) += rk8xx.o
obj-$(CONFIG_DM_REGULATOR_S2MPS11) += s2mps11_regulator.o
obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
obj-$(CONFIG_DM_REGULATOR_SANDBOX) += sandbox.o
+obj-$(CONFIG_DM_REGULATOR_SPACEMIT_P1) += spacemit_p1_regulator.o
obj-$(CONFIG_REGULATOR_TPS65090) += tps65090_regulator.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PALMAS) += palmas_regulator.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PBIAS) += pbias_regulator.o
@@ -49,3 +50,7 @@ obj-$(CONFIG_REGULATOR_RZG2L_USBPHY) += rzg2l-usbphy-regulator.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_CPCAP) += cpcap_regulator.o
obj-$(CONFIG_DM_REGULATOR_MT6357) += mt6357_regulator.o
obj-$(CONFIG_DM_REGULATOR_MT6359) += mt6359_regulator.o
+obj-$(CONFIG_DM_REGULATOR_PMBUS_HELPER) += pmbus_helper.o
+obj-$(CONFIG_DM_REGULATOR_PMBUS_GENERIC) += pmbus_generic.o
+obj-$(CONFIG_DM_REGULATOR_MPQ8785) += mpq8785.o
+obj-$(CONFIG_SANDBOX_PMBUS) += sandbox_pmbus.o
diff --git a/drivers/power/regulator/mpq8785.c b/drivers/power/regulator/mpq8785.c
new file mode 100644
index 00000000000..dc321f0e410
--- /dev/null
+++ b/drivers/power/regulator/mpq8785.c
@@ -0,0 +1,494 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2026 Free Mobile, Vincent Jardin
+ *
+ * MPS MPQ8785 / MPM3695 / MPM3695-25 / MPM82504 PMBus voltage
+ * regulator driver. Adapted from
+ * linux/drivers/hwmon/pmbus/mpq8785.c
+ * (Charles Hsu, GPL-2.0-or-later) with the kernel hwmon publication
+ * and caching layers stripped.
+ *
+ * Hooks the shared pmbus_helper UCLASS_REGULATOR ops + adds the MPS
+ * specific identify (VOUT_MODE switch between LINEAR16 and DIRECT
+ * m=64 R=1 for the chip's "VID" mode), the MPS vendor extension
+ * (pmbus mps last|clear last|clear force), and ADDR_VBOOT auto
+ * promotion when the DT declared address fails the MFR_ID probe.
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <i2c.h>
+#include <log.h>
+#include <pmbus.h>
+#include <vsprintf.h>
+#include <linux/bitops.h>
+#include <power/regulator.h>
+
+#include "pmbus_helper.h"
+
+/* Chip family identifiers (driver_data). */
+enum mpq_chip_id {
+ MPQ_MPM3695 = 0,
+ MPQ_MPM3695_25 = 1,
+ MPQ_MPM82504 = 2,
+ MPQ_MPQ8785 = 3,
+};
+
+/*
+ * MPS vendor extended command codes (NOT in PMBus 1.3 Part II).
+ *
+ * CLEAR_LAST_FAULT (08h) clears the NVM backed PROTECTION_LAST
+ * register. Gated by MFR_CFG_EXT (F5h)
+ * bit[6] = 1; chip silently no ops if the
+ * gate is cleared.
+ * PROTECTION_LAST (FBh) single event, NVM backed log of the last
+ * protection event. Survives the chip's own
+ * power cycle. The boot time post mortem the
+ * SoC has no other way to obtain.
+ *
+ * NEVER issue CLEAR_LAST_FAULT (08h) implicitly; it would erase the
+ * post mortem trail. Only the explicit pmbus mps clear last and
+ * pmbus mps clear force subcommands write it.
+ */
+#define PMBUS_CLEAR_LAST_FAULT 0x08
+#define MPS_PROTECTION_LAST 0xfb
+
+/*
+ * MFR_CFG_EXT (F5h) is an MPS extended config WORD (16 bits, not a
+ * byte). Bit[6] (MFR_CLR_FAULT_CFG) gates CLEAR_LAST_FAULT (08h)
+ * clearing PROTECTION_LAST. Several other bits are fixed and MUST be
+ * preserved on writeback; always read modify write the full 16 bits,
+ * only flip bit[6], restore on the way out.
+ */
+#define MPS_MFR_CFG_EXT 0xf5
+#define MPS_MFR_CFG_EXT_CLR_LAST_EN BIT(6)
+
+/*
+ * MPQ8785 driver_info (transcribed from Linux's
+ * drivers/hwmon/pmbus/mpq8785.c::mpq8785_info). DIRECT format with
+ * chip specific m / b / R coefficients on VIN, IOUT, TEMPERATURE.
+ * VOUT format is selected at probe time from VOUT_MODE: bits[7:5] == 0
+ * selects LINEAR16, bits[7:5] == 1 or 2 selects DIRECT m=64 R=1.
+ */
+
+/*
+ * MPS-extended STATUS_* bit names. The MPQ8785 family reuses several
+ * bit positions documented as RESERVED / UNKNOWN / NONE_ABOVE /
+ * MFR_SPECIFIC by PMBus 1.3 for chip specific signals. The override
+ * table below substitutes the chip name for the standard one when
+ * the bit is set, leaving every other PMBus 1.3 standard bit
+ * (VOUT_OV, IOUT_OC, TEMP, CML, ...) unchanged.
+ *
+ * STATUS_WORD bit[12] spec MFR_SPECIFIC chip NVM_SUMMARY (NVM
+ * backed PROTECTION_LAST
+ * register is non zero)
+ * STATUS_WORD bit[8] spec UNKNOWN chip WATCH_DOG (internal
+ * calculation FSM watchdog
+ * overflow)
+ * STATUS_WORD bit[0] spec NONE_ABOVE chip DRMOS_FAULT (DrMOS
+ * stage fault)
+ * STATUS_CML bit[4] spec MEMORY chip MTP_CRC_FAULT (NVM
+ * CRC mismatch on restore)
+ * STATUS_CML bit[0] spec OTHER_MEM_LOGIC chip MTP_FAULT (NVM
+ * signature fault)
+ * STATUS_TEMPERATURE bit[0] (PMBus leaves bits[3:0] reserved on
+ * this register) chip
+ * OT_SELF (controller die
+ * OT condition)
+ */
+static const struct pmbus_status_override mpq8785_status_overrides[] = {
+ { PMBUS_STATUS_WORD, BIT(12), "NVM_SUMMARY" },
+ { PMBUS_STATUS_WORD, BIT(8), "WATCH_DOG" },
+ { PMBUS_STATUS_WORD, BIT(0), "DRMOS_FAULT" },
+ { PMBUS_STATUS_CML, BIT(4), "MTP_CRC_FAULT" },
+ { PMBUS_STATUS_CML, BIT(0), "MTP_FAULT" },
+ { PMBUS_STATUS_TEMPERATURE, BIT(0), "OT_SELF" },
+ { /* sentinel */ }
+};
+
+static struct pmbus_driver_info mpq8785_info = {
+ .pages = 1,
+ .format[PSC_VOLTAGE_IN] = pmbus_fmt_direct,
+ .format[PSC_VOLTAGE_OUT] = pmbus_fmt_linear, /* refined per VOUT_MODE */
+ .format[PSC_CURRENT_OUT] = pmbus_fmt_direct,
+ .format[PSC_TEMPERATURE] = pmbus_fmt_direct,
+ .m[PSC_VOLTAGE_IN] = 4, .b[PSC_VOLTAGE_IN] = 0, .R[PSC_VOLTAGE_IN] = 1,
+ .m[PSC_CURRENT_OUT] = 16, .b[PSC_CURRENT_OUT] = 0, .R[PSC_CURRENT_OUT] = 0,
+ .m[PSC_TEMPERATURE] = 1, .b[PSC_TEMPERATURE] = 0, .R[PSC_TEMPERATURE] = 0,
+ /*
+ * Sensor set this family actually implements with calibrated
+ * coefficients: VIN, VOUT, IOUT, TEMP. READ_IIN / READ_POUT are
+ * ACKed by the silicon but uncalibrated here (the kernel's mpq8646
+ * / mpq8785 drivers expose neither), so declaring the set keeps
+ * pmbus_print_telemetry from printing a bogus POUT / IIN -- matching
+ * the kernel's per-chip sensor list.
+ */
+ .classes_present = BIT(PSC_VOLTAGE_IN) | BIT(PSC_VOLTAGE_OUT) |
+ BIT(PSC_CURRENT_OUT) | BIT(PSC_TEMPERATURE),
+ .status_overrides = mpq8785_status_overrides,
+};
+
+/*
+ * MPM3695 / MPM3695-25 / MPM82504 driver_info: VOUT in DIRECT format
+ * with chip family default m=8 R=2. Other sensor classes default to
+ * LINEAR (the chip family does not document non standard formats for
+ * VIN / IOUT / TEMPERATURE; the helper falls back to LINEAR11 when
+ * the active info is non NULL but format[c] is linear).
+ */
+static struct pmbus_driver_info mpm82504_info = {
+ .pages = 1,
+ .format[PSC_VOLTAGE_OUT] = pmbus_fmt_direct,
+ .m[PSC_VOLTAGE_OUT] = 8, .b[PSC_VOLTAGE_OUT] = 0, .R[PSC_VOLTAGE_OUT] = 2,
+ .format[PSC_VOLTAGE_IN] = pmbus_fmt_linear,
+ .format[PSC_CURRENT_OUT] = pmbus_fmt_linear,
+ .format[PSC_TEMPERATURE] = pmbus_fmt_linear,
+};
+
+/*
+ * Chip match for the framework's pmbus dev <bus>:<addr> raw I2C
+ * path. Used when the operator selects the chip directly by address
+ * instead of by regulator-name; the framework probes MFR_ID, sees
+ * "MPS" (after the byte reverse helper), and caches mpq8785_info.
+ */
+static const struct pmbus_chip_match mpq8785_match = {
+ .mfr_id = "MPS",
+ .mfr_id_reverse = true,
+ .vendor = "mps",
+ .info = &mpq8785_info,
+};
+
+static const struct pmbus_bit mpq_protection_last_bits[] = {
+ { 1u << 15, "INIT_FAULT" },
+ { 1u << 14, "NVM_CRC_ERROR" },
+ { 1u << 13, "NVM_FAULT" },
+ { 1u << 12, "OC_PHASE_FAULT" },
+ { 1u << 11, "OTP_SELF_FAULT" },
+ { 1u << 9, "SWITCH_PRD_FAULT" },
+ { 1u << 8, "VIN_OV_FAULT" },
+ { 1u << 7, "VOUT_OV_FAULT" },
+ { 1u << 6, "VOUT_UV_FAULT" },
+ { 1u << 5, "OC_TOT_FAULT" },
+ { 1u << 4, "VIN_UVLO_FAULT" },
+ { 1u << 3, "DRMOS_OTP" },
+ { /* sentinel */ }
+};
+
+static int mps_require_active(struct udevice **chip)
+{
+ const struct pmbus_active_dev *act = pmbus_active();
+
+ if (!act) {
+ printf("pmbus mps: no active device. Use 'pmbus dev <bus>:<addr>' first.\n");
+ return CMD_RET_FAILURE;
+ }
+ if (strcmp(act->vendor, "mps") != 0) {
+ printf("pmbus mps: active device is not from vendor 'mps' (got '%s')\n",
+ act->vendor[0] ? act->vendor : "(generic)");
+ return CMD_RET_FAILURE;
+ }
+ if (pmbus_active_get_i2c(chip)) {
+ printf("pmbus mps: cannot reach i2c%d:0x%02x\n",
+ act->bus_seq, act->addr);
+ return CMD_RET_FAILURE;
+ }
+ return CMD_RET_SUCCESS;
+}
+
+static int mps_do_last(struct udevice *chip)
+{
+ u16 prot_last = 0;
+
+ if (pmbus_read_word(chip, MPS_PROTECTION_LAST, &prot_last)) {
+ printf("pmbus mps: PROTECTION_LAST (FBh) read failed\n");
+ return CMD_RET_FAILURE;
+ }
+ printf("PROTECTION_LAST (FBh) = 0x%04x [", prot_last);
+ pmbus_print_bits(prot_last, mpq_protection_last_bits);
+ printf("] (NVM, survives MPQ power cycle)\n");
+ return CMD_RET_SUCCESS;
+}
+
+static int mps_do_clear_last(struct udevice *chip)
+{
+ int ret;
+
+ printf("pmbus mps: WARNING, erasing NVM PROTECTION_LAST (FBh) post mortem\n");
+ ret = dm_i2c_write(chip, PMBUS_CLEAR_LAST_FAULT, NULL, 0);
+ if (ret) {
+ printf("pmbus mps: CLEAR_LAST_FAULT (08h) write failed (%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ printf("pmbus mps: CLEAR_LAST_FAULT (08h) issued; gated by MFR_CFG_EXT bit[6]\n");
+ printf(" (chip silently no ops if F5h bit[6] = 0; verify by re reading FBh)\n");
+ return CMD_RET_SUCCESS;
+}
+
+static int mps_do_clear_force(struct udevice *chip)
+{
+ u8 wp_orig = 0;
+ u16 cfg_orig = 0, cfg_unlocked;
+ int ret, last_rc = 0, rc;
+
+ printf("pmbus mps: FORCE; temporarily lowering WRITE_PROTECT and MFR_CFG_EXT.CLEAR_LAST_EN\n");
+ printf("pmbus mps: WARNING, erasing NVM PROTECTION_LAST (FBh) post mortem\n");
+
+ ret = pmbus_read_byte(chip, PMBUS_WRITE_PROTECT, &wp_orig);
+ if (ret) {
+ printf("pmbus mps: WRITE_PROTECT (10h) read failed (%d), aborting force\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ ret = pmbus_read_word(chip, MPS_MFR_CFG_EXT, &cfg_orig);
+ if (ret) {
+ printf("pmbus mps: MFR_CFG_EXT (F5h) read failed (%d), aborting force\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ printf("pmbus mps: saved WRITE_PROTECT=0x%02x MFR_CFG_EXT=0x%04x\n",
+ wp_orig, cfg_orig);
+
+ if (wp_orig != 0) {
+ u8 wp_open = 0x00;
+
+ ret = dm_i2c_write(chip, PMBUS_WRITE_PROTECT, &wp_open, 1);
+ if (ret) {
+ printf("pmbus mps: WRITE_PROTECT clear failed (%d), chip refuses unlock\n",
+ ret);
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ cfg_unlocked = cfg_orig | MPS_MFR_CFG_EXT_CLR_LAST_EN;
+ ret = pmbus_write_word(chip, MPS_MFR_CFG_EXT, cfg_unlocked);
+ if (ret) {
+ printf("pmbus mps: MFR_CFG_EXT <- 0x%04x failed (%d)\n",
+ cfg_unlocked, ret);
+ goto restore_wp;
+ }
+
+ last_rc = dm_i2c_write(chip, PMBUS_CLEAR_LAST_FAULT, NULL, 0);
+ if (last_rc)
+ printf("pmbus mps: CLEAR_LAST_FAULT (08h) write failed (%d) even with gate open\n",
+ last_rc);
+ else
+ printf("pmbus mps: CLEAR_LAST_FAULT (08h) issued with MFR_CFG_EXT bit[6]=1, PROTECTION_LAST should now read 0x0000\n");
+
+ rc = pmbus_write_word(chip, MPS_MFR_CFG_EXT, cfg_orig);
+ if (rc)
+ printf("pmbus mps: MFR_CFG_EXT restore failed (%d), gate may stay open until POR\n",
+ rc);
+
+restore_wp:
+ if (wp_orig != 0) {
+ rc = dm_i2c_write(chip, PMBUS_WRITE_PROTECT, &wp_orig, 1);
+ if (rc)
+ printf("pmbus mps: WRITE_PROTECT restore failed (%d), chip stays unlocked until POR\n",
+ rc);
+ }
+
+ return (ret || last_rc) ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
+static int mps_vendor_handler(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *chip;
+ int rc;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ rc = mps_require_active(&chip);
+ if (rc)
+ return rc;
+
+ if (!strcmp(argv[1], "last") && argc == 2)
+ return mps_do_last(chip);
+ if (!strcmp(argv[1], "clear") && argc >= 3) {
+ if (!strcmp(argv[2], "last"))
+ return mps_do_clear_last(chip);
+ if (!strcmp(argv[2], "force"))
+ return mps_do_clear_force(chip);
+ }
+ return CMD_RET_USAGE;
+}
+
+static const struct pmbus_vendor_op mps_vendor_op = {
+ .vendor = "mps",
+ .handler = mps_vendor_handler,
+ .help = "pmbus mps last : read MPS PROTECTION_LAST (FBh)\n"
+ "pmbus mps clear last : issue MPS CLEAR_LAST_FAULT (08h) (DESTRUCTIVE)\n"
+ "pmbus mps clear force : force clear via MFR_CFG_EXT bit[6] (DESTRUCTIVE)\n",
+};
+
+static void mpq8785_identify_vout(struct udevice *i2c_dev)
+{
+ enum pmbus_data_format fmt;
+
+ /*
+ * Let the shared helper read VOUT_MODE and pick the base format
+ * (the single source of truth for the bit layout). The MPS quirk:
+ * this family encodes VOUT in DIRECT with m=64 R=1 whenever
+ * VOUT_MODE reports VID *or* DIRECT -- override the helper's
+ * generic DIRECT m=1 / VID-unwired result in those two modes.
+ * LINEAR and IEEE754 keep the helper's selection unchanged.
+ */
+ fmt = pmbus_regulator_identify_vout(i2c_dev, &mpq8785_info);
+ if (fmt == pmbus_fmt_vid || fmt == pmbus_fmt_direct) {
+ mpq8785_info.format[PSC_VOLTAGE_OUT] = pmbus_fmt_direct;
+ mpq8785_info.m[PSC_VOLTAGE_OUT] = 64;
+ mpq8785_info.b[PSC_VOLTAGE_OUT] = 0;
+ mpq8785_info.R[PSC_VOLTAGE_OUT] = 1;
+ }
+}
+
+/*
+ * The MPQ8785 datasheet revision letter changes which window the
+ * analog ADDR_VBOOT level resolves to. Boards have been observed at
+ * 0x10 (later die rev) versus the 0x20 the original driver assumed.
+ * If the DT declared address fails the MFR_ID probe at probe time,
+ * walk the three documented windows looking for an MPS responder.
+ *
+ * Each window covers 16 consecutive 7 bit I2C addresses; the low
+ * nibble selects the chip's MFR_ADDR_PMBUS slot within the window.
+ */
+#define MPS_ADDR_VBOOT_WINDOW_SIZE 16
+static const u8 mps_addr_window_starts[] = { 0x10, 0x20, 0x60 };
+
+static int mpq8785_probe_addr(struct udevice *bus, u8 addr,
+ struct udevice **chip_out)
+{
+ char id[PMBUS_MFR_STRING_MAX] = "";
+ struct udevice *chip;
+ int ret;
+
+ ret = i2c_get_chip(bus, addr, 1, &chip);
+ if (ret)
+ return ret;
+ ret = pmbus_read_string(chip, PMBUS_MFR_ID, id, sizeof(id), true);
+ if (ret < 0)
+ return ret;
+ if (strncmp(id, "MPS", 3) != 0)
+ return -ENODEV;
+ *chip_out = chip;
+ return 0;
+}
+
+static int mpq8785_scan_windows(struct udevice *bus, u8 *found_addr,
+ struct udevice **chip_out)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(mps_addr_window_starts); i++) {
+ for (j = 0; j < MPS_ADDR_VBOOT_WINDOW_SIZE; j++) {
+ u8 addr = mps_addr_window_starts[i] + j;
+
+ if (mpq8785_probe_addr(bus, addr, chip_out) == 0) {
+ *found_addr = addr;
+ return 0;
+ }
+ }
+ }
+ return -ENODEV;
+}
+
+static struct pmbus_driver_info *mpq8785_pick_info(enum mpq_chip_id chip_id)
+{
+ switch (chip_id) {
+ case MPQ_MPM3695:
+ case MPQ_MPM3695_25:
+ case MPQ_MPM82504:
+ return &mpm82504_info;
+ case MPQ_MPQ8785:
+ default:
+ return &mpq8785_info;
+ }
+}
+
+static int mpq8785_probe(struct udevice *dev)
+{
+ enum mpq_chip_id chip_id = (enum mpq_chip_id)dev_get_driver_data(dev);
+ struct pmbus_regulator_priv *priv = dev_get_priv(dev);
+ struct pmbus_driver_info *info = mpq8785_pick_info(chip_id);
+ static bool match_registered;
+ static bool vendor_registered;
+ u32 fb_div;
+ int ret;
+
+ ret = pmbus_regulator_probe_common(dev, info, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * Verify the chip answers MFR_ID="MPS" at the DT declared
+ * address. If it doesn't, walk the documented ADDR_VBOOT windows
+ * looking for it (a die rev address shift). On a hit, replace
+ * priv->i2c_dev with the discovered chip handle and continue.
+ */
+ {
+ char id[PMBUS_MFR_STRING_MAX] = "";
+
+ ret = pmbus_read_string(priv->i2c_dev, PMBUS_MFR_ID, id,
+ sizeof(id), true);
+ if (ret < 0 || strncmp(id, "MPS", 3) != 0) {
+ struct udevice *bus = dev_get_parent(dev);
+ struct udevice *promoted;
+ u8 found = 0;
+
+ if (mpq8785_scan_windows(bus, &found, &promoted) == 0) {
+ printf("MPQ8785: DT addr 0x%02x silent, auto promoted to 0x%02x\n",
+ (unsigned int)dev_read_addr(dev), found);
+ priv->i2c_dev = promoted;
+ } else {
+ printf("MPQ8785: no MPS responder found in 0x10..0x1f / 0x20..0x2f / 0x60..0x6f\n");
+ return -ENODEV;
+ }
+ }
+ }
+
+ /* MPQ8785 specific: refine VOUT format from VOUT_MODE. */
+ if (chip_id == MPQ_MPQ8785)
+ mpq8785_identify_vout(priv->i2c_dev);
+
+ /* Apply mps,vout-fb-divider-ratio-permille if present in DT. */
+ fb_div = dev_read_u32_default(dev, "mps,vout-fb-divider-ratio-permille", 0);
+ if (fb_div) {
+ ret = pmbus_regulator_apply_voltage_scale(dev, fb_div);
+ if (ret) {
+ printf("MPQ8785: VOUT_SCALE_LOOP write failed (%d)\n", ret);
+ return ret;
+ }
+ }
+
+ /*
+ * Register the chip match and the MPS vendor handler exactly
+ * once across all bound MPS regulators (a board could legally
+ * carry several). Both registries are global and idempotent
+ * matches return -ENOSPC, so the static guards keep things
+ * tidy.
+ */
+ if (!match_registered) {
+ if (pmbus_register_chip(&mpq8785_match) == 0)
+ match_registered = true;
+ }
+ if (!vendor_registered) {
+ if (pmbus_register_vendor_handler(&mps_vendor_op) == 0)
+ vendor_registered = true;
+ }
+ return 0;
+}
+
+static const struct udevice_id mpq8785_ids[] = {
+ { .compatible = "mps,mpm3695", .data = MPQ_MPM3695 },
+ { .compatible = "mps,mpm3695-25", .data = MPQ_MPM3695_25 },
+ { .compatible = "mps,mpm82504", .data = MPQ_MPM82504 },
+ { .compatible = "mps,mpq8785", .data = MPQ_MPQ8785 },
+ { }
+};
+
+U_BOOT_DRIVER(mpq8785_regulator) = {
+ .name = "mpq8785_regulator",
+ .id = UCLASS_REGULATOR,
+ .of_match = mpq8785_ids,
+ .probe = mpq8785_probe,
+ .ops = &pmbus_regulator_ops,
+ .priv_auto = sizeof(struct pmbus_regulator_priv),
+};
diff --git a/drivers/power/regulator/pmbus_generic.c b/drivers/power/regulator/pmbus_generic.c
new file mode 100644
index 00000000000..6ba50ce08f8
--- /dev/null
+++ b/drivers/power/regulator/pmbus_generic.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2026 Free Mobile, Vincent Jardin
+ *
+ * Generic PMBus 1.x compatible voltage regulator driver.
+ *
+ * Catch all driver bound to compatible = "pmbus" for chips that have
+ * no per chip driver under drivers/power/regulator/. The probe path
+ * detects the VOUT numeric format from VOUT_MODE bits[7:5]:
+ *
+ * - 0 LINEAR16 with the exponent supplied via VOUT_MODE bits[4:0]
+ * - 1 VID; mapped to pmbus_fmt_vid (decoder returns 0 today; per
+ * chip driver still required to plug a VID table)
+ * - 2 DIRECT; default coefficients m=1, b=0, R=0 (per chip
+ * coefficients arrive via PMBUS_QUERY / PMBUS_COEFFICIENTS,
+ * not yet consumed by U-Boot; values may need a per chip
+ * driver if telemetry numbers are wrong)
+ * - 3 IEEE754; mapped to pmbus_fmt_ieee754 (decoder returns 0
+ * today; per chip driver required)
+ *
+ * Other sensor classes (VIN, IIN, IOUT, TEMPERATURE) default to
+ * LINEAR which is the spec baseline for compliant chips. If an
+ * operator sees wrong telemetry numbers on this driver, the answer
+ * is to write a per chip driver with the correct format[] / m / b / R.
+ *
+ * Adapted in spirit from linux/drivers/hwmon/pmbus/pmbus.c (the
+ * kernel's generic probe driver). The U-Boot version drops the
+ * page count auto detection (most generic compliant parts are
+ * single rail; multi rail chips are quirky enough to need a per
+ * chip driver) and the kernel hwmon publication layers.
+ */
+
+#include <dm.h>
+#include <i2c.h>
+#include <log.h>
+#include <pmbus.h>
+#include <power/regulator.h>
+
+#include "pmbus_helper.h"
+
+struct pmbus_generic_priv {
+ struct pmbus_regulator_priv base; /* must be first */
+ struct pmbus_driver_info info;
+};
+
+static int pmbus_generic_probe(struct udevice *dev)
+{
+ struct pmbus_generic_priv *gpriv = dev_get_priv(dev);
+ struct pmbus_driver_info *info = &gpriv->info;
+ enum pmbus_sensor_classes c;
+ int ret;
+
+ info->pages = 1;
+ for (c = 0; c < PSC_NUM_CLASSES; c++) {
+ info->format[c] = pmbus_fmt_linear;
+ info->m[c] = 0;
+ info->b[c] = 0;
+ info->R[c] = 0;
+ }
+
+ ret = pmbus_regulator_probe_common(dev, info, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * Avoid reading non supported pages to avoid device's sticky
+ * status.
+ */
+ info->pages = dev_read_u32_default(dev, "pmbus,num-pages", 1);
+ if (info->pages < 1)
+ info->pages = 1;
+
+ pmbus_regulator_identify_vout(gpriv->base.i2c_dev, info);
+
+ return 0;
+}
+
+static const struct udevice_id pmbus_generic_ids[] = {
+ { .compatible = "pmbus" },
+ { }
+};
+
+U_BOOT_DRIVER(pmbus_generic_regulator) = {
+ .name = "pmbus_generic_regulator",
+ .id = UCLASS_REGULATOR,
+ .of_match = pmbus_generic_ids,
+ .probe = pmbus_generic_probe,
+ .ops = &pmbus_regulator_ops,
+ .priv_auto = sizeof(struct pmbus_generic_priv),
+};
diff --git a/drivers/power/regulator/pmbus_helper.c b/drivers/power/regulator/pmbus_helper.c
new file mode 100644
index 00000000000..4763470442d
--- /dev/null
+++ b/drivers/power/regulator/pmbus_helper.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2026 Free Mobile, Vincent Jardin
+ *
+ * Shared UCLASS_REGULATOR operations over the PMBus 1.x framework.
+ * See pmbus_helper.h for the API surface and doc/develop/pmbus.rst
+ * for the porting guide.
+ *
+ * No code in this file may reference a specific chip family or
+ * board. Chip specific quirks (vendor registers, VID coercion,
+ * ADDR pin auto promotion, byte reversed MFR strings, etc.) belong
+ * in the per chip driver under drivers/power/regulator/<chip>.c.
+ */
+
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <i2c.h>
+#include <log.h>
+#include <pmbus.h>
+#include <vsprintf.h>
+#include <linux/types.h>
+#include <power/regulator.h>
+
+#include "pmbus_helper.h"
+
+static int pmbus_regulator_select_page(struct pmbus_regulator_priv *priv)
+{
+ u8 p;
+
+ if (priv->page <= 0)
+ return 0;
+ p = (u8)priv->page;
+ return dm_i2c_write(priv->i2c_dev, PMBUS_PAGE, &p, 1);
+}
+
+static int pmbus_regulator_get_value(struct udevice *dev)
+{
+ struct pmbus_regulator_priv *priv = dev_get_priv(dev);
+ u8 vout_mode = 0;
+ u16 raw = 0;
+ s64 uv;
+ int ret;
+
+ ret = pmbus_regulator_select_page(priv);
+ if (ret)
+ return ret;
+ pmbus_read_byte(priv->i2c_dev, PMBUS_VOUT_MODE, &vout_mode);
+ if (pmbus_read_word(priv->i2c_dev, PMBUS_READ_VOUT, &raw))
+ return -EIO;
+
+ if (priv->info)
+ uv = pmbus_reg2data(priv->info, PSC_VOLTAGE_OUT, raw, vout_mode);
+ else
+ uv = pmbus_reg2data_linear16(raw, vout_mode);
+
+ if (uv > INT_MAX)
+ uv = INT_MAX;
+ if (uv < INT_MIN)
+ uv = INT_MIN;
+ return (int)uv;
+}
+
+static int pmbus_regulator_set_value(struct udevice *dev, int uV)
+{
+ struct pmbus_regulator_priv *priv = dev_get_priv(dev);
+ u8 vout_mode = 0;
+ u8 buf[2];
+ u16 raw;
+ int ret;
+
+ ret = pmbus_regulator_select_page(priv);
+ if (ret)
+ return ret;
+ if (pmbus_read_byte(priv->i2c_dev, PMBUS_VOUT_MODE, &vout_mode))
+ return -EIO;
+
+ /*
+ * Dispatch on the chip's VOUT_MODE selector. LINEAR16 and DIRECT
+ * are wired today; VID and IEEE754 return -ENOSYS until their
+ * encoders land. For DIRECT, the m / b / R triple comes from the
+ * chip's pmbus_driver_info[PSC_VOLTAGE_OUT]; if the per chip
+ * driver did not populate them, the encoder cannot run.
+ */
+ switch (vout_mode & PB_VOUT_MODE_MODE_MASK) {
+ case PB_VOUT_MODE_LINEAR:
+ raw = pmbus_data2reg_linear16((s64)uV, vout_mode);
+ break;
+ case PB_VOUT_MODE_DIRECT:
+ if (!priv->info ||
+ priv->info->format[PSC_VOLTAGE_OUT] != pmbus_fmt_direct)
+ return -ENODATA;
+ raw = pmbus_data2reg_direct((s64)uV,
+ priv->info->m[PSC_VOLTAGE_OUT],
+ priv->info->b[PSC_VOLTAGE_OUT],
+ priv->info->R[PSC_VOLTAGE_OUT]);
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ buf[0] = (u8)(raw & 0xff);
+ buf[1] = (u8)((raw >> 8) & 0xff);
+ return dm_i2c_write(priv->i2c_dev, PMBUS_VOUT_COMMAND, buf, 2);
+}
+
+static int pmbus_regulator_get_enable(struct udevice *dev)
+{
+ struct pmbus_regulator_priv *priv = dev_get_priv(dev);
+ u8 op = 0;
+ int ret;
+
+ ret = pmbus_regulator_select_page(priv);
+ if (ret)
+ return ret;
+ if (pmbus_read_byte(priv->i2c_dev, PMBUS_OPERATION, &op))
+ return -EIO;
+ return (op & PB_OPERATION_ON) ? 1 : 0;
+}
+
+static int pmbus_regulator_set_enable(struct udevice *dev, bool enable)
+{
+ struct pmbus_regulator_priv *priv = dev_get_priv(dev);
+ u8 op = 0;
+ int ret;
+
+ ret = pmbus_regulator_select_page(priv);
+ if (ret)
+ return ret;
+ if (pmbus_read_byte(priv->i2c_dev, PMBUS_OPERATION, &op))
+ return -EIO;
+
+ if (enable)
+ op |= PB_OPERATION_ON;
+ else
+ op &= (u8)~PB_OPERATION_ON;
+ return dm_i2c_write(priv->i2c_dev, PMBUS_OPERATION, &op, 1);
+}
+
+const struct dm_regulator_ops pmbus_regulator_ops = {
+ .get_value = pmbus_regulator_get_value,
+ .set_value = pmbus_regulator_set_value,
+ .get_enable = pmbus_regulator_get_enable,
+ .set_enable = pmbus_regulator_set_enable,
+};
+
+int pmbus_regulator_read_temp(struct udevice *reg_dev, int *temp_mc)
+{
+ struct pmbus_regulator_priv *priv;
+ u16 raw = 0;
+ s64 udeg;
+ int ret;
+
+ if (!reg_dev || !temp_mc)
+ return -EINVAL;
+ priv = dev_get_priv(reg_dev);
+ if (!priv || !priv->i2c_dev)
+ return -ENODEV;
+
+ ret = pmbus_regulator_select_page(priv);
+ if (ret)
+ return ret;
+ if (pmbus_read_word(priv->i2c_dev, PMBUS_READ_TEMPERATURE_1, &raw))
+ return -EIO;
+
+ /*
+ * vout_mode is meaningless for the temperature class. With a
+ * chip info record the dispatcher honours its per-class format
+ * (DIRECT m/b/R for MPS, LINEAR11 for spec-compliant parts);
+ * without one, fall back to the PMBus 1.x standard LINEAR11.
+ */
+ if (priv->info)
+ udeg = pmbus_reg2data(priv->info, PSC_TEMPERATURE, raw, 0);
+ else
+ udeg = pmbus_reg2data_linear11(raw);
+
+ *temp_mc = (int)(udeg / 1000);
+ return 0;
+}
+
+enum pmbus_data_format
+pmbus_regulator_identify_vout(struct udevice *i2c_dev,
+ struct pmbus_driver_info *info)
+{
+ u8 vout_mode = 0;
+
+ if (pmbus_read_byte(i2c_dev, PMBUS_VOUT_MODE, &vout_mode))
+ return info->format[PSC_VOLTAGE_OUT];
+
+ switch (vout_mode & PB_VOUT_MODE_MODE_MASK) {
+ case PB_VOUT_MODE_LINEAR:
+ info->format[PSC_VOLTAGE_OUT] = pmbus_fmt_linear;
+ break;
+ case PB_VOUT_MODE_VID:
+ info->format[PSC_VOLTAGE_OUT] = pmbus_fmt_vid;
+ break;
+ case PB_VOUT_MODE_DIRECT:
+ info->format[PSC_VOLTAGE_OUT] = pmbus_fmt_direct;
+ info->m[PSC_VOLTAGE_OUT] = 1;
+ info->b[PSC_VOLTAGE_OUT] = 0;
+ info->R[PSC_VOLTAGE_OUT] = 0;
+ break;
+ case PB_VOUT_MODE_IEEE754:
+ info->format[PSC_VOLTAGE_OUT] = pmbus_fmt_ieee754;
+ break;
+ default:
+ break;
+ }
+ return info->format[PSC_VOLTAGE_OUT];
+}
+
+const struct pmbus_driver_info *pmbus_regulator_info_by_addr(int bus_seq,
+ u8 addr)
+{
+ struct uclass *uc;
+ struct udevice *r;
+
+ if (uclass_get(UCLASS_REGULATOR, &uc))
+ return NULL;
+
+ uclass_foreach_dev(r, uc) {
+ struct udevice *parent = dev_get_parent(r);
+ struct pmbus_regulator_priv *priv;
+ int ra;
+
+ if (!parent || device_get_uclass_id(parent) != UCLASS_I2C)
+ continue;
+ if (dev_seq(parent) != bus_seq)
+ continue;
+ ra = dev_read_addr(r);
+ if (ra < 0 || (u8)ra != addr)
+ continue;
+
+ /*
+ * Address matches. Only chips driven through this helper
+ * carry a pmbus_regulator_priv at the head of their priv;
+ * identify them by their shared ops vector so we never
+ * misread a foreign regulator's private layout.
+ */
+ if (!r->driver || r->driver->ops != &pmbus_regulator_ops)
+ return NULL;
+ if (device_probe(r))
+ return NULL;
+ priv = dev_get_priv(r);
+ return priv ? priv->info : NULL;
+ }
+ return NULL;
+}
+
+/*
+ * Spawn the generic UCLASS_THERMAL companion (drivers/thermal/
+ * pmbus_thermal.c) as a child of this regulator so READ_TEMPERATURE_1
+ * is reachable through the standard `temperature list` / `temperature
+ * get` interface. Named "<regulator-name>-temp" so several PMBus rails
+ * on one board produce distinct, descriptive device names. Failure is
+ * non-fatal: the chip still works as a UCLASS_REGULATOR.
+ */
+static void pmbus_regulator_bind_thermal(struct udevice *dev)
+{
+ struct udevice *therm;
+ const char *rname;
+ char name[48];
+
+ if (!IS_ENABLED(CONFIG_PMBUS_THERMAL))
+ return;
+ if (device_bind_driver(dev, "pmbus_thermal", "pmbus-temp", &therm))
+ return;
+ rname = dev_read_string(dev, "regulator-name");
+ snprintf(name, sizeof(name), "%s-temp", rname ? rname : dev->name);
+ device_set_name(therm, name);
+}
+
+int pmbus_regulator_probe_common(struct udevice *dev,
+ const struct pmbus_driver_info *info,
+ int page)
+{
+ struct pmbus_regulator_priv *priv = dev_get_priv(dev);
+ int chip_addr;
+ int ret;
+
+ chip_addr = dev_read_addr(dev);
+ if (chip_addr < 0)
+ return -EINVAL;
+
+ ret = i2c_get_chip(dev_get_parent(dev), (u32)chip_addr, 1, &priv->i2c_dev);
+ if (ret)
+ return ret;
+
+ priv->info = info;
+ priv->page = page;
+
+ if (page > 0) {
+ u8 p = (u8)page;
+
+ ret = dm_i2c_write(priv->i2c_dev, PMBUS_PAGE, &p, 1);
+ if (ret)
+ return ret;
+ }
+
+ pmbus_regulator_bind_thermal(dev);
+ return 0;
+}
+
+int pmbus_regulator_apply_voltage_scale(struct udevice *dev,
+ u32 fb_divider_permille)
+{
+ struct pmbus_regulator_priv *priv = dev_get_priv(dev);
+ u8 buf[2];
+
+ if (fb_divider_permille == 0)
+ return 0;
+ buf[0] = (u8)(fb_divider_permille & 0xff);
+ buf[1] = (u8)((fb_divider_permille >> 8) & 0xff);
+ return dm_i2c_write(priv->i2c_dev, PMBUS_VOUT_SCALE_LOOP, buf, 2);
+}
diff --git a/drivers/power/regulator/pmbus_helper.h b/drivers/power/regulator/pmbus_helper.h
new file mode 100644
index 00000000000..82f263cd513
--- /dev/null
+++ b/drivers/power/regulator/pmbus_helper.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2026 Free Mobile, Vincent Jardin
+ *
+ * Shared UCLASS_REGULATOR ops for PMBus 1.x voltage regulator chips.
+ *
+ * Per chip drivers under drivers/power/regulator/<chip>.c bind a
+ * vendor,chip compatible from DT and call pmbus_regulator_probe_common()
+ * in their .probe. They install pmbus_regulator_ops as the .ops vector;
+ * the helper handles VOUT_MODE / READ_VOUT / VOUT_COMMAND / OPERATION
+ * via the tree level <pmbus.h> framework.
+ *
+ * Per chip drivers retain control of identify hooks (VOUT_MODE based
+ * format selection), chip specific quirks (vendor registers, ADDR pin
+ * auto promotion), and DT property handling (e.g. MPS
+ * mps,vout-fb-divider-ratio-permille).
+ */
+
+#ifndef _DRIVERS_POWER_REGULATOR_PMBUS_HELPER_H_
+#define _DRIVERS_POWER_REGULATOR_PMBUS_HELPER_H_
+
+#include <linux/types.h>
+#include <pmbus.h>
+
+struct udevice;
+struct dm_regulator_ops;
+
+/*
+ * Per chip private state. The first field of every per chip driver's
+ * priv_auto must be (or contain at offset 0) a struct
+ * pmbus_regulator_priv so the shared ops vector can recover it via
+ * dev_get_priv(dev).
+ *
+ * i2c_dev chip handle obtained from dev->parent at probe time
+ * (the parent must be a UCLASS_I2C bus).
+ * page PMBUS_PAGE selector for multi rail chips. Single rail
+ * chips set page = 0; the helper writes PMBUS_PAGE only
+ * when page > 0 to avoid wasted bus traffic on single
+ * rail parts.
+ * info pointer to the chip's pmbus_driver_info; consumed by
+ * pmbus_reg2data() / pmbus_data2reg_linear16() to pick
+ * the right format[] / m / b / R coefficients.
+ */
+struct pmbus_regulator_priv {
+ struct udevice *i2c_dev;
+ int page;
+ const struct pmbus_driver_info *info;
+};
+
+extern const struct dm_regulator_ops pmbus_regulator_ops;
+
+/*
+ * Per chip probe glue. Reads `reg` from DT, gets the I2C chip handle
+ * from dev->parent, populates priv->i2c_dev / page / info, and writes
+ * PMBUS_PAGE if page > 0. Per chip drivers call this in their .probe
+ * before any chip specific identification.
+ */
+int pmbus_regulator_probe_common(struct udevice *dev,
+ const struct pmbus_driver_info *info,
+ int page);
+
+/*
+ * Optional helper for per chip drivers that honour an external
+ * feedback divider DT property (e.g. MPS mps,vout-fb-divider-ratio-
+ * permille). Writes the supplied ratio to PMBUS_VOUT_SCALE_LOOP at
+ * probe time. fb_divider_permille == 0 leaves the chip default.
+ */
+int pmbus_regulator_apply_voltage_scale(struct udevice *dev,
+ u32 fb_divider_permille);
+
+/*
+ * Read PMBUS_VOUT_MODE and set info->format[PSC_VOLTAGE_OUT] from its
+ * mode selector bits[7:5] per PMBus 1.3 Part II sec 8.3:
+ * LINEAR -> pmbus_fmt_linear
+ * VID -> pmbus_fmt_vid
+ * DIRECT -> pmbus_fmt_direct (default coefficients m=1, b=0, R=0)
+ * IEEE754 -> pmbus_fmt_ieee754
+ *
+ * The single place that knows the VOUT_MODE bit layout; both the
+ * generic regulator and per chip drivers call it so they never
+ * re-implement the switch. Returns the selected format so a chip
+ * driver can post-adjust a quirk (e.g. MPS encodes VOUT in DIRECT
+ * with m=64 R=1 even when VOUT_MODE reports VID). On a VOUT_MODE read
+ * failure the format is left unchanged and the prior value is returned.
+ */
+enum pmbus_data_format
+pmbus_regulator_identify_vout(struct udevice *i2c_dev,
+ struct pmbus_driver_info *info);
+
+#endif /* _DRIVERS_POWER_REGULATOR_PMBUS_HELPER_H_ */
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c
index 1c7f75a9338..0f5ba51ad6c 100644
--- a/drivers/power/regulator/regulator-uclass.c
+++ b/drivers/power/regulator/regulator-uclass.c
@@ -111,6 +111,33 @@ int regulator_get_suspend_value(struct udevice *dev)
return ops->get_suspend_value(dev);
}
+int regulator_set_value_clamp(struct udevice *dev,
+ int min_uV, int target_uV, int max_uV)
+{
+ const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+ struct dm_regulator_uclass_plat *uc_pdata;
+ int uV;
+
+ if (!ops || !ops->set_value)
+ return -ENOSYS;
+
+ uc_pdata = dev_get_uclass_plat(dev);
+ if (uc_pdata->min_uV != -ENODATA && max_uV < uc_pdata->min_uV)
+ return -EINVAL;
+ if (uc_pdata->max_uV != -ENODATA && min_uV > uc_pdata->max_uV)
+ return -EINVAL;
+ if (min_uV > max_uV)
+ return -EINVAL;
+
+ if (uc_pdata->min_uV != -ENODATA)
+ min_uV = max(min_uV, uc_pdata->min_uV);
+ if (uc_pdata->max_uV != -ENODATA)
+ max_uV = min(max_uV, uc_pdata->max_uV);
+ uV = clamp(target_uV, min_uV, max_uV);
+
+ return regulator_set_value(dev, uV);
+}
+
/*
* To be called with at most caution as there is no check
* before setting the actual voltage value.
diff --git a/drivers/power/regulator/sandbox.c b/drivers/power/regulator/sandbox.c
index 80a68f5a30d..813ee301e73 100644
--- a/drivers/power/regulator/sandbox.c
+++ b/drivers/power/regulator/sandbox.c
@@ -56,10 +56,11 @@ static struct dm_regulator_mode sandbox_buck_modes[] = {
MODE(BUCK_OM_PWM, OM2REG(BUCK_OM_PWM), "PWM"),
};
-/* LDO: 1,2 - voltage range */
+/* LDO: 1,2,3 - voltage range */
static struct output_range ldo_voltage_range[] = {
RANGE(OUT_LDO1_UV_MIN, OUT_LDO1_UV_MAX, OUT_LDO1_UV_STEP),
RANGE(OUT_LDO2_UV_MIN, OUT_LDO2_UV_MAX, OUT_LDO2_UV_STEP),
+ RANGE(OUT_LDO3_UV_MIN, OUT_LDO3_UV_MAX, OUT_LDO3_UV_STEP),
};
/* LDO: 1 - current range */
@@ -288,8 +289,8 @@ static int ldo_set_voltage(struct udevice *dev, int uV)
static int ldo_get_current(struct udevice *dev)
{
- /* LDO2 - unsupported */
- if (dev->driver_data == 2)
+ /* LDO: 2,3 - unsupported */
+ if (dev->driver_data >= 2)
return -ENOSYS;
return out_get_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UA,
@@ -298,8 +299,8 @@ static int ldo_get_current(struct udevice *dev)
static int ldo_set_current(struct udevice *dev, int uA)
{
- /* LDO2 - unsupported */
- if (dev->driver_data == 2)
+ /* LDO: 2,3 - unsupported */
+ if (dev->driver_data >= 2)
return -ENOSYS;
return out_set_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UA,
diff --git a/drivers/power/regulator/sandbox_pmbus.c b/drivers/power/regulator/sandbox_pmbus.c
new file mode 100644
index 00000000000..7b52d2ca293
--- /dev/null
+++ b/drivers/power/regulator/sandbox_pmbus.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2026 Free Mobile - Vincent Jardin
+ *
+ * Sandbox PMBus 1.x chip stub (UCLASS_I2C_EMUL).
+ *
+ * Stub a DT i2c node so the PMBus framework, the generic pmbus
+ * regulator and the pmbus CLI command can be tested.
+ * The model is a flat per-command 16 bit
+ * register file combined with some fixed identification strings.
+ */
+
+#include <dm.h>
+#include <i2c.h>
+#include <pmbus.h>
+#include <linux/ctype.h>
+
+#define PMBUS_EMUL_NREG 256
+
+struct sandbox_pmbus_priv {
+ u16 reg[PMBUS_EMUL_NREG];
+ bool supported[PMBUS_EMUL_NREG];
+};
+
+/* Identification strings reported in the natural (forward) byte order. */
+static const char *pmbus_emul_string(u8 cmd)
+{
+ switch (cmd) {
+ case PMBUS_MFR_ID:
+ return "SANDBOX";
+ case PMBUS_MFR_MODEL:
+ return "PMBUS-EMUL";
+ case PMBUS_MFR_REVISION:
+ return "1.0";
+ default:
+ return NULL;
+ }
+}
+
+static int sandbox_pmbus_read(struct sandbox_pmbus_priv *priv, u8 cmd,
+ u8 *buf, int len)
+{
+ const char *str = pmbus_emul_string(cmd);
+ int i;
+
+ if (len < 1)
+ return -EINVAL;
+
+ if (str) {
+ int slen = strlen(str);
+
+ /*
+ * Block payload: [length][bytes...]. A one-byte read is
+ * the SMBus length probe and reports the true length (the
+ * master just NAKs early). Any longer read must have room
+ * for length + payload, otherwise the length byte would
+ * lie about the data actually returned.
+ */
+ if (len > 1 && len < slen + 1)
+ return -EREMOTEIO;
+ buf[0] = (u8)slen;
+ for (i = 1; i < len; i++)
+ buf[i] = (i - 1 < slen) ? (u8)str[i - 1] : 0;
+ return 0;
+ }
+
+ if (!priv->supported[cmd])
+ return -EREMOTEIO; /* chip NAKs an unimplemented command */
+
+ for (i = 0; i < len; i++)
+ buf[i] = (u8)(priv->reg[cmd] >> (8 * i));
+ return 0;
+}
+
+static int sandbox_pmbus_write(struct sandbox_pmbus_priv *priv, u8 cmd,
+ const u8 *buf, int len)
+{
+ if (len == 0)
+ return 0; /* send-byte (eg CLEAR_FAULTS): just ACK */
+ if (!priv->supported[cmd])
+ return -EREMOTEIO;
+ if (len == 1)
+ priv->reg[cmd] = buf[0];
+ else
+ priv->reg[cmd] = (u16)buf[0] | ((u16)buf[1] << 8);
+ return 0;
+}
+
+static int sandbox_pmbus_xfer(struct udevice *emul, struct i2c_msg *msg,
+ int nmsgs)
+{
+ struct sandbox_pmbus_priv *priv = dev_get_priv(emul);
+ u8 cmd;
+
+ if (nmsgs == 0)
+ return 0;
+ /* A PMBus transaction always opens with the command-code write. */
+ if (msg[0].flags & I2C_M_RD)
+ return -EIO;
+ if (msg[0].len == 0)
+ return 0; /* address-only probe */
+ cmd = msg[0].buf[0];
+
+ if (nmsgs >= 2 && (msg[1].flags & I2C_M_RD))
+ return sandbox_pmbus_read(priv, cmd, msg[1].buf, msg[1].len);
+
+ return sandbox_pmbus_write(priv, cmd, msg[0].buf + 1, msg[0].len - 1);
+}
+
+static void sandbox_pmbus_support(struct sandbox_pmbus_priv *priv, u8 cmd,
+ u16 val)
+{
+ priv->supported[cmd] = true;
+ priv->reg[cmd] = val;
+}
+
+static int sandbox_pmbus_probe(struct udevice *emul)
+{
+ struct sandbox_pmbus_priv *priv = dev_get_priv(emul);
+
+ /* Configuration / identification. */
+ sandbox_pmbus_support(priv, PMBUS_PAGE, 0);
+ sandbox_pmbus_support(priv, PMBUS_OPERATION, PB_OPERATION_ON);
+ sandbox_pmbus_support(priv, PMBUS_ON_OFF_CONFIG, 0);
+ sandbox_pmbus_support(priv, PMBUS_WRITE_PROTECT, 0);
+ sandbox_pmbus_support(priv, PMBUS_CAPABILITY, 0x30);
+ sandbox_pmbus_support(priv, PMBUS_VOUT_MODE, 0x18); /* LINEAR 2^-8 */
+ sandbox_pmbus_support(priv, PMBUS_VOUT_COMMAND, 0x0200);
+ sandbox_pmbus_support(priv, PMBUS_VOUT_TRIM, 0);
+ sandbox_pmbus_support(priv, PMBUS_VOUT_MAX, 0x0400);
+ sandbox_pmbus_support(priv, PMBUS_VOUT_SCALE_LOOP, 0);
+ sandbox_pmbus_support(priv, PMBUS_REVISION, PMBUS_REV_13);
+
+ /* Status registers, all clean. */
+ sandbox_pmbus_support(priv, PMBUS_STATUS_BYTE, 0);
+ sandbox_pmbus_support(priv, PMBUS_STATUS_WORD, 0);
+ sandbox_pmbus_support(priv, PMBUS_STATUS_VOUT, 0);
+ sandbox_pmbus_support(priv, PMBUS_STATUS_IOUT, 0);
+ sandbox_pmbus_support(priv, PMBUS_STATUS_INPUT, 0);
+ sandbox_pmbus_support(priv, PMBUS_STATUS_TEMPERATURE, 0);
+ sandbox_pmbus_support(priv, PMBUS_STATUS_CML, 0);
+
+ /*
+ * Telemetry the emulated chip implements. READ_IIN and READ_POUT
+ * are intentionally absent so callers see the unsupported path.
+ */
+ sandbox_pmbus_support(priv, PMBUS_READ_VIN, 0x0abc);
+ sandbox_pmbus_support(priv, PMBUS_READ_VOUT, 0x0200);
+ sandbox_pmbus_support(priv, PMBUS_READ_IOUT, 0x0123);
+ sandbox_pmbus_support(priv, PMBUS_READ_TEMPERATURE_1, 0x0019);
+
+ return 0;
+}
+
+static struct dm_i2c_ops sandbox_pmbus_emul_ops = {
+ .xfer = sandbox_pmbus_xfer,
+};
+
+static const struct udevice_id sandbox_pmbus_ids[] = {
+ { .compatible = "sandbox,i2c-pmbus" },
+ { }
+};
+
+U_BOOT_DRIVER(sandbox_pmbus_emul) = {
+ .name = "sandbox_pmbus_emul",
+ .id = UCLASS_I2C_EMUL,
+ .of_match = sandbox_pmbus_ids,
+ .probe = sandbox_pmbus_probe,
+ .priv_auto = sizeof(struct sandbox_pmbus_priv),
+ .ops = &sandbox_pmbus_emul_ops,
+};
diff --git a/drivers/power/regulator/spacemit_p1_regulator.c b/drivers/power/regulator/spacemit_p1_regulator.c
new file mode 100644
index 00000000000..7b5eb710edf
--- /dev/null
+++ b/drivers/power/regulator/spacemit_p1_regulator.c
@@ -0,0 +1,464 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025-2026 RISCstar Ltd.
+ */
+
+#include <dm.h>
+#include <dm/lists.h>
+#include <errno.h>
+#include <log.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <power/spacemit_p1.h>
+
+struct p1_reg_info {
+ uint min_uv;
+ uint step_uv;
+ u8 vsel_reg;
+ u8 vsel_sleep_reg;
+ u8 config_reg;
+ u8 vsel_mask;
+ u8 min_sel;
+ u8 max_sel;
+};
+
+static const struct p1_reg_info p1_bucks[] = {
+ /* BUCK 1 */
+ { 500000, 5000, P1_BUCK_VSEL(1), P1_BUCK_SVSEL(1), P1_BUCK_CTRL(1),
+ BUCK_VSEL_MASK, 0x00, 0xaa },
+ { 1375000, 25000, P1_BUCK_VSEL(1), P1_BUCK_SVSEL(1), P1_BUCK_CTRL(1),
+ BUCK_VSEL_MASK, 0xab, 0xfe },
+ /* BUCK 2 */
+ { 500000, 5000, P1_BUCK_VSEL(2), P1_BUCK_SVSEL(2), P1_BUCK_CTRL(2),
+ BUCK_VSEL_MASK, 0x00, 0xaa },
+ { 1375000, 25000, P1_BUCK_VSEL(2), P1_BUCK_SVSEL(2), P1_BUCK_CTRL(2),
+ BUCK_VSEL_MASK, 0xab, 0xfe },
+ /* BUCK 3 */
+ { 500000, 5000, P1_BUCK_VSEL(3), P1_BUCK_SVSEL(3), P1_BUCK_CTRL(3),
+ BUCK_VSEL_MASK, 0x00, 0xaa },
+ { 1375000, 25000, P1_BUCK_VSEL(3), P1_BUCK_SVSEL(3), P1_BUCK_CTRL(3),
+ BUCK_VSEL_MASK, 0xab, 0xfe },
+ /* BUCK 4 */
+ { 500000, 5000, P1_BUCK_VSEL(4), P1_BUCK_SVSEL(4), P1_BUCK_CTRL(4),
+ BUCK_VSEL_MASK, 0x00, 0xaa },
+ { 1375000, 25000, P1_BUCK_VSEL(4), P1_BUCK_SVSEL(4), P1_BUCK_CTRL(4),
+ BUCK_VSEL_MASK, 0xab, 0xfe },
+ /* BUCK 5 */
+ { 500000, 5000, P1_BUCK_VSEL(5), P1_BUCK_SVSEL(5), P1_BUCK_CTRL(5),
+ BUCK_VSEL_MASK, 0x00, 0xaa },
+ { 1375000, 25000, P1_BUCK_VSEL(5), P1_BUCK_SVSEL(5), P1_BUCK_CTRL(5),
+ BUCK_VSEL_MASK, 0xab, 0xfe },
+ /* BUCK 6 */
+ { 500000, 5000, P1_BUCK_VSEL(6), P1_BUCK_SVSEL(6), P1_BUCK_CTRL(6),
+ BUCK_VSEL_MASK, 0x00, 0xaa },
+ { 1375000, 25000, P1_BUCK_VSEL(6), P1_BUCK_SVSEL(6), P1_BUCK_CTRL(6),
+ BUCK_VSEL_MASK, 0xab, 0xfe },
+};
+
+static const struct p1_reg_info p1_aldos[] = {
+ /* ALDO 1 */
+ { 500000, 25000, P1_ALDO_VOLT(1), P1_ALDO_SVOLT(1), P1_ALDO_CTRL(1),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+ /* ALDO 2 */
+ { 500000, 25000, P1_ALDO_VOLT(2), P1_ALDO_SVOLT(2), P1_ALDO_CTRL(2),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+ /* ALDO 3 */
+ { 500000, 25000, P1_ALDO_VOLT(3), P1_ALDO_SVOLT(3), P1_ALDO_CTRL(3),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+ /* ALDO 4 */
+ { 500000, 25000, P1_ALDO_VOLT(4), P1_ALDO_SVOLT(4), P1_ALDO_CTRL(4),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+};
+
+static const struct p1_reg_info p1_dldos[] = {
+ /* DLDO 1 */
+ { 500000, 25000, P1_DLDO_VOLT(1), P1_DLDO_SVOLT(1), P1_DLDO_CTRL(1),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+ /* DLDO 2 */
+ { 500000, 25000, P1_DLDO_VOLT(2), P1_DLDO_SVOLT(2), P1_DLDO_CTRL(2),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+ /* DLDO 3 */
+ { 500000, 25000, P1_DLDO_VOLT(3), P1_DLDO_SVOLT(3), P1_DLDO_CTRL(3),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+ /* DLDO 4 */
+ { 500000, 25000, P1_DLDO_VOLT(4), P1_DLDO_SVOLT(4), P1_DLDO_CTRL(4),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+ /* DLDO 5 */
+ { 500000, 25000, P1_DLDO_VOLT(5), P1_DLDO_SVOLT(5), P1_DLDO_CTRL(5),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+ /* DLDO 6 */
+ { 500000, 25000, P1_DLDO_VOLT(6), P1_DLDO_SVOLT(6), P1_DLDO_CTRL(6),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+ /* DLDO 7 */
+ { 500000, 25000, P1_DLDO_VOLT(7), P1_DLDO_SVOLT(7), P1_DLDO_CTRL(7),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+};
+
+static const struct p1_reg_info *get_buck_reg(struct udevice *pmic,
+ int idx, int uvolt)
+{
+ if (idx < 0)
+ return NULL;
+ if (uvolt < 1375000)
+ return &p1_bucks[(idx - 1) * 2 + 0];
+ return &p1_bucks[(idx - 1) * 2 + 1];
+}
+
+static const struct p1_reg_info *get_aldo_reg(struct udevice *pmic,
+ int idx, int uvolt)
+{
+ if (idx < 1 || idx > ARRAY_SIZE(p1_aldos))
+ return NULL;
+ return &p1_aldos[idx - 1];
+}
+
+static const struct p1_reg_info *get_dldo_reg(struct udevice *pmic,
+ int idx, int uvolt)
+{
+ if (idx < 1 || idx > ARRAY_SIZE(p1_dldos))
+ return NULL;
+ return &p1_dldos[idx - 1];
+}
+
+static int buck_get_value(struct udevice *dev)
+{
+ const struct dm_pmic_ops *ops = device_get_ops(dev->parent);
+ const struct p1_reg_info *info;
+ uint val;
+ int ret;
+
+ if (!ops || !ops->read)
+ return -ENOSYS;
+
+ info = get_buck_reg(dev->parent, dev->driver_data, 0);
+ if (!info)
+ return -ENOENT;
+ ret = pmic_reg_read(dev->parent, info->vsel_reg);
+ if (ret < 0)
+ return ret;
+ val = ret & info->vsel_mask;
+ while (val > info->max_sel)
+ info++;
+
+ return info->min_uv + (val - info->min_sel) * info->step_uv;
+}
+
+static int buck_set_value(struct udevice *dev, int uvolt)
+{
+ const struct dm_pmic_ops *ops = device_get_ops(dev->parent);
+ const struct p1_reg_info *info;
+ uint val;
+ int ret;
+
+ if (!ops || !ops->write)
+ return -ENOSYS;
+
+ info = get_buck_reg(dev->parent, dev->driver_data, uvolt);
+ if (!info)
+ return -ENOENT;
+ val = (uvolt - info->min_uv);
+ val = val / info->step_uv;
+ val += info->min_sel;
+ ret = pmic_reg_write(dev->parent, info->vsel_reg, val);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int buck_get_enable(struct udevice *dev)
+{
+ const struct p1_reg_info *info;
+ int ret;
+
+ info = get_buck_reg(dev->parent, dev->driver_data, 0);
+ if (!info)
+ return -ENOENT;
+
+ ret = pmic_reg_read(dev->parent, info->config_reg);
+ if (ret < 0)
+ return ret;
+ return ret & BUCK_EN_MASK;
+}
+
+static int buck_set_enable(struct udevice *dev, bool enable)
+{
+ const struct p1_reg_info *info;
+ uint val;
+ int ret;
+
+ info = get_buck_reg(dev->parent, dev->driver_data, 0);
+ if (!info)
+ return -ENOENT;
+
+ ret = pmic_reg_read(dev->parent, info->config_reg);
+ if (ret < 0)
+ return ret;
+ val = (unsigned int)ret;
+ val &= BUCK_EN_MASK;
+
+ if (enable == val)
+ return 0;
+
+ val = enable;
+ ret = pmic_clrsetbits(dev->parent, info->config_reg, BUCK_EN_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct dm_regulator_ops p1_buck_ops = {
+ .get_value = buck_get_value,
+ .set_value = buck_set_value,
+ .get_enable = buck_get_enable,
+ .set_enable = buck_set_enable,
+};
+
+static int p1_buck_probe(struct udevice *dev)
+{
+ struct dm_regulator_uclass_plat *uc_pdata;
+
+ uc_pdata = dev_get_uclass_plat(dev);
+
+ uc_pdata->type = REGULATOR_TYPE_BUCK;
+ uc_pdata->mode_count = 0;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(p1_buck) = {
+ .name = P1_BUCK_DRIVER,
+ .id = UCLASS_REGULATOR,
+ .ops = &p1_buck_ops,
+ .probe = p1_buck_probe,
+};
+
+static int aldo_get_value(struct udevice *dev)
+{
+ const struct dm_pmic_ops *ops = device_get_ops(dev->parent);
+ const struct p1_reg_info *info;
+ uint val;
+ int ret;
+
+ if (!ops || !ops->read)
+ return -ENOSYS;
+
+ info = get_aldo_reg(dev->parent, dev->driver_data, 0);
+ if (!info)
+ return -ENOENT;
+
+ ret = pmic_reg_read(dev->parent, info->vsel_reg);
+ if (ret < 0)
+ return ret;
+
+ val = ret & info->vsel_mask;
+ while (val > info->max_sel)
+ info++;
+
+ return info->min_uv + (val - info->min_sel) * info->step_uv;
+}
+
+static int aldo_set_value(struct udevice *dev, int uvolt)
+{
+ const struct dm_pmic_ops *ops = device_get_ops(dev->parent);
+ const struct p1_reg_info *info;
+ uint val;
+ int ret;
+
+ if (!ops || !ops->write)
+ return -ENOSYS;
+
+ info = get_aldo_reg(dev->parent, dev->driver_data, uvolt);
+ if (!info)
+ return -ENOENT;
+ val = (uvolt - info->min_uv);
+ val = val / info->step_uv;
+ val += info->min_sel;
+ ret = pmic_reg_write(dev->parent, info->vsel_reg, val);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int aldo_get_enable(struct udevice *dev)
+{
+ const struct p1_reg_info *info;
+ int ret;
+
+ info = get_aldo_reg(dev->parent, dev->driver_data, 0);
+ if (!info)
+ return -ENOENT;
+
+ ret = pmic_reg_read(dev->parent, info->config_reg);
+ if (ret < 0)
+ return ret;
+ return ret & ALDO_EN_MASK;
+}
+
+static int aldo_set_enable(struct udevice *dev, bool enable)
+{
+ const struct p1_reg_info *info;
+ uint val;
+ int ret;
+
+ info = get_aldo_reg(dev->parent, dev->driver_data, 0);
+ if (!info)
+ return -ENOENT;
+
+ ret = pmic_reg_read(dev->parent, info->config_reg);
+ if (ret < 0)
+ return ret;
+ val = (unsigned int)ret;
+ val &= ALDO_EN_MASK;
+
+ if (enable == val)
+ return 0;
+
+ val = enable;
+ ret = pmic_clrsetbits(dev->parent, info->config_reg, ALDO_EN_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct dm_regulator_ops p1_aldo_ops = {
+ .get_value = aldo_get_value,
+ .set_value = aldo_set_value,
+ .get_enable = aldo_get_enable,
+ .set_enable = aldo_set_enable,
+};
+
+static int p1_aldo_probe(struct udevice *dev)
+{
+ struct dm_regulator_uclass_plat *uc_pdata;
+
+ uc_pdata = dev_get_uclass_plat(dev);
+
+ uc_pdata->type = REGULATOR_TYPE_LDO;
+ uc_pdata->mode_count = 0;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(p1_aldo) = {
+ .name = P1_ALDO_DRIVER,
+ .id = UCLASS_REGULATOR,
+ .ops = &p1_aldo_ops,
+ .probe = p1_aldo_probe,
+};
+
+static int dldo_get_value(struct udevice *dev)
+{
+ const struct dm_pmic_ops *ops = device_get_ops(dev->parent);
+ const struct p1_reg_info *info;
+ uint val;
+ int ret;
+
+ if (!ops || !ops->read)
+ return -ENOSYS;
+
+ info = get_dldo_reg(dev->parent, dev->driver_data, 0);
+ if (!info)
+ return -ENOENT;
+
+ ret = pmic_reg_read(dev->parent, info->vsel_reg);
+ if (ret < 0)
+ return ret;
+
+ val = ret & info->vsel_mask;
+ while (val > info->max_sel)
+ info++;
+
+ return info->min_uv + (val - info->min_sel) * info->step_uv;
+}
+
+static int dldo_set_value(struct udevice *dev, int uvolt)
+{
+ const struct dm_pmic_ops *ops = device_get_ops(dev->parent);
+ const struct p1_reg_info *info;
+ uint val;
+ int ret;
+
+ if (!ops || !ops->write)
+ return -ENOSYS;
+
+ info = get_dldo_reg(dev->parent, dev->driver_data, uvolt);
+ if (!info)
+ return -ENOENT;
+ val = (uvolt - info->min_uv);
+ val = val / info->step_uv;
+ val += info->min_sel;
+ ret = pmic_reg_write(dev->parent, info->vsel_reg, val);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int dldo_get_enable(struct udevice *dev)
+{
+ const struct p1_reg_info *info;
+ int ret;
+
+ info = get_dldo_reg(dev->parent, dev->driver_data, 0);
+ if (!info)
+ return -ENOENT;
+
+ ret = pmic_reg_read(dev->parent, info->config_reg);
+ if (ret < 0)
+ return ret;
+ return ret & DLDO_EN_MASK;
+}
+
+static int dldo_set_enable(struct udevice *dev, bool enable)
+{
+ const struct p1_reg_info *info;
+ uint val;
+ int ret;
+
+ info = get_dldo_reg(dev->parent, dev->driver_data, 0);
+ if (!info)
+ return -ENOENT;
+
+ ret = pmic_reg_read(dev->parent, info->config_reg);
+ if (ret < 0)
+ return ret;
+ val = (unsigned int)ret;
+ val &= DLDO_EN_MASK;
+
+ if (enable == val)
+ return 0;
+
+ val = enable;
+ ret = pmic_clrsetbits(dev->parent, info->config_reg, DLDO_EN_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct dm_regulator_ops p1_dldo_ops = {
+ .get_value = dldo_get_value,
+ .set_value = dldo_set_value,
+ .get_enable = dldo_get_enable,
+ .set_enable = dldo_set_enable,
+};
+
+static int p1_dldo_probe(struct udevice *dev)
+{
+ struct dm_regulator_uclass_plat *uc_pdata;
+
+ uc_pdata = dev_get_uclass_plat(dev);
+
+ uc_pdata->type = REGULATOR_TYPE_LDO;
+ uc_pdata->mode_count = 0;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(p1_dldo) = {
+ .name = P1_DLDO_DRIVER,
+ .id = UCLASS_REGULATOR,
+ .ops = &p1_dldo_ops,
+ .probe = p1_dldo_probe,
+};
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index c851354c7a5..db764ed5ace 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -279,12 +279,5 @@ config RESET_RZG2L_USBPHY_CTRL
RZ/G2L SoC. This is required for USB 2.0 functionality to work on this
SoC.
-config RESET_SPACEMIT_K1
- bool "Support for SPACEMIT's K1 Reset driver"
- depends on DM_RESET
- help
- Support for SPACEMIT's K1 Reset system. Basic Assert/Deassert
- is supported.
-
source "drivers/reset/stm32/Kconfig"
endmenu
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 3fce96509cd..384f4336331 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -37,7 +37,7 @@ obj-$(CONFIG_RESET_DRA7) += reset-dra7.o
obj-$(CONFIG_RESET_AT91) += reset-at91.o
obj-$(CONFIG_$(PHASE_)RESET_JH7110) += reset-jh7110.o
obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
-obj-$(CONFIG_RESET_SPACEMIT_K1) += reset-spacemit-k1.o
+obj-$(CONFIG_CLK_SPACEMIT_K1) += spacemit/
obj-$(CONFIG_ARCH_STM32) += stm32/
obj-$(CONFIG_ARCH_STM32MP) += stm32/
diff --git a/drivers/reset/reset-spacemit-k1.c b/drivers/reset/reset-spacemit-k1.c
deleted file mode 100644
index 613e002fc4f..00000000000
--- a/drivers/reset/reset-spacemit-k1.c
+++ /dev/null
@@ -1,548 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2022 Spacemit Inc.
- * Copyright (C) 2025 Huan Zhou <[email protected]>
- */
-
-#include <asm/io.h>
-#include <config.h>
-#include <dm.h>
-#include <dm/device_compat.h>
-#include <dm/device-internal.h>
-#include <dm/lists.h>
-#include <dt-bindings/reset/spacemit-k1-reset.h>
-#include <linux/bitops.h>
-#include <reset-uclass.h>
-
-/* APBC register offset */
-#define APBC_UART1_CLK_RST 0x0
-#define APBC_UART2_CLK_RST 0x4
-#define APBC_GPIO_CLK_RST 0x8
-#define APBC_PWM0_CLK_RST 0xc
-#define APBC_PWM1_CLK_RST 0x10
-#define APBC_PWM2_CLK_RST 0x14
-#define APBC_PWM3_CLK_RST 0x18
-#define APBC_TWSI8_CLK_RST 0x20
-#define APBC_UART3_CLK_RST 0x24
-#define APBC_RTC_CLK_RST 0x28
-#define APBC_TWSI0_CLK_RST 0x2c
-#define APBC_TWSI1_CLK_RST 0x30
-#define APBC_TIMERS1_CLK_RST 0x34
-#define APBC_TWSI2_CLK_RST 0x38
-#define APBC_AIB_CLK_RST 0x3c
-#define APBC_TWSI4_CLK_RST 0x40
-#define APBC_TIMERS2_CLK_RST 0x44
-#define APBC_ONEWIRE_CLK_RST 0x48
-#define APBC_TWSI5_CLK_RST 0x4c
-#define APBC_DRO_CLK_RST 0x58
-#define APBC_IR_CLK_RST 0x5c
-#define APBC_TWSI6_CLK_RST 0x60
-#define APBC_TWSI7_CLK_RST 0x68
-#define APBC_TSEN_CLK_RST 0x6c
-
-#define APBC_UART4_CLK_RST 0x70
-#define APBC_UART5_CLK_RST 0x74
-#define APBC_UART6_CLK_RST 0x78
-#define APBC_SSP3_CLK_RST 0x7c
-
-#define APBC_SSPA0_CLK_RST 0x80
-#define APBC_SSPA1_CLK_RST 0x84
-
-#define APBC_IPC_AP2AUD_CLK_RST 0x90
-#define APBC_UART7_CLK_RST 0x94
-#define APBC_UART8_CLK_RST 0x98
-#define APBC_UART9_CLK_RST 0x9c
-
-#define APBC_CAN0_CLK_RST 0xa0
-#define APBC_PWM4_CLK_RST 0xa8
-#define APBC_PWM5_CLK_RST 0xac
-#define APBC_PWM6_CLK_RST 0xb0
-#define APBC_PWM7_CLK_RST 0xb4
-#define APBC_PWM8_CLK_RST 0xb8
-#define APBC_PWM9_CLK_RST 0xbc
-#define APBC_PWM10_CLK_RST 0xc0
-#define APBC_PWM11_CLK_RST 0xc4
-#define APBC_PWM12_CLK_RST 0xc8
-#define APBC_PWM13_CLK_RST 0xcc
-#define APBC_PWM14_CLK_RST 0xd0
-#define APBC_PWM15_CLK_RST 0xd4
-#define APBC_PWM16_CLK_RST 0xd8
-#define APBC_PWM17_CLK_RST 0xdc
-#define APBC_PWM18_CLK_RST 0xe0
-#define APBC_PWM19_CLK_RST 0xe4
-/* end of APBC register offset */
-
-/* MPMU register offset */
-#define MPMU_WDTPCR 0x200
-/* end of MPMU register offset */
-
-/* APMU register offset */
-#define APMU_JPG_CLK_RES_CTRL 0x20
-#define APMU_CSI_CCIC2_CLK_RES_CTRL 0x24
-#define APMU_ISP_CLK_RES_CTRL 0x38
-#define APMU_LCD_CLK_RES_CTRL1 0x44
-#define APMU_LCD_SPI_CLK_RES_CTRL 0x48
-#define APMU_LCD_CLK_RES_CTRL2 0x4c
-#define APMU_CCIC_CLK_RES_CTRL 0x50
-#define APMU_SDH0_CLK_RES_CTRL 0x54
-#define APMU_SDH1_CLK_RES_CTRL 0x58
-#define APMU_USB_CLK_RES_CTRL 0x5c
-#define APMU_QSPI_CLK_RES_CTRL 0x60
-#define APMU_USB_CLK_RES_CTRL 0x5c
-#define APMU_DMA_CLK_RES_CTRL 0x64
-#define APMU_AES_CLK_RES_CTRL 0x68
-#define APMU_VPU_CLK_RES_CTRL 0xa4
-#define APMU_GPU_CLK_RES_CTRL 0xcc
-#define APMU_SDH2_CLK_RES_CTRL 0xe0
-#define APMU_PMUA_MC_CTRL 0xe8
-#define APMU_PMU_CC2_AP 0x100
-#define APMU_PMUA_EM_CLK_RES_CTRL 0x104
-
-#define APMU_AUDIO_CLK_RES_CTRL 0x14c
-#define APMU_HDMI_CLK_RES_CTRL 0x1B8
-
-#define APMU_PCIE_CLK_RES_CTRL_0 0x3cc
-#define APMU_PCIE_CLK_RES_CTRL_1 0x3d4
-#define APMU_PCIE_CLK_RES_CTRL_2 0x3dc
-
-#define APMU_EMAC0_CLK_RES_CTRL 0x3e4
-#define APMU_EMAC1_CLK_RES_CTRL 0x3ec
-/* end of APMU register offset */
-
-/* APBC2 register offset */
-#define APBC2_UART1_CLK_RST 0x00
-#define APBC2_SSP2_CLK_RST 0x04
-#define APBC2_TWSI3_CLK_RST 0x08
-#define APBC2_RTC_CLK_RST 0x0c
-#define APBC2_TIMERS0_CLK_RST 0x10
-#define APBC2_KPC_CLK_RST 0x14
-#define APBC2_GPIO_CLK_RST 0x1c
-/* end of APBC2 register offset */
-
-enum spacemit_reset_base_type {
- RST_BASE_TYPE_MPMU = 0,
- RST_BASE_TYPE_APMU = 1,
- RST_BASE_TYPE_APBC = 2,
- RST_BASE_TYPE_APBS = 3,
- RST_BASE_TYPE_CIU = 4,
- RST_BASE_TYPE_DCIU = 5,
- RST_BASE_TYPE_DDRC = 6,
- RST_BASE_TYPE_AUDC = 7,
- RST_BASE_TYPE_APBC2 = 8,
-};
-
-struct spacemit_reset_signal {
- u32 offset;
- u32 mask;
- u32 deassert_val;
- u32 assert_val;
- enum spacemit_reset_base_type type;
-};
-
-struct spacemit_reset_base {
- void __iomem *mpmu_base;
- void __iomem *apmu_base;
- void __iomem *apbc_base;
- void __iomem *apbs_base;
- void __iomem *ciu_base;
- void __iomem *dciu_base;
- void __iomem *ddrc_base;
- void __iomem *audio_ctrl_base;
- void __iomem *apbc2_base;
-};
-
-struct spacemit_reset {
- struct spacemit_reset_base io_base;
- const struct spacemit_reset_signal *signals;
-};
-
-enum {
- RESET_TWSI6_SPL = 0,
- RESET_TWSI8_SPL,
- RESET_SDH_AXI_SPL,
- RESET_SDH0_SPL,
- RESET_USB_AXI_SPL,
- RESET_USBP1_AXI_SPL,
- RESET_USB3_0_SPL,
- RESET_QSPI_SPL,
- RESET_QSPI_BUS_SPL,
- RESET_AES_SPL,
- RESET_SDH2_SPL,
- RESET_NUMBER_SPL
-};
-
-static const struct spacemit_reset_signal
- k1_reset_signals[RESET_NUMBER] = {
- [RESET_UART1] = { APBC_UART1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_UART2] = { APBC_UART2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_GPIO] = { APBC_GPIO_CLK_RST, BIT(2), 0,
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM0] = { APBC_PWM0_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM1] = { APBC_PWM1_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM2] = { APBC_PWM2_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM3] = { APBC_PWM3_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM4] = { APBC_PWM4_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM5] = { APBC_PWM5_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM6] = { APBC_PWM6_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM7] = { APBC_PWM7_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM8] = { APBC_PWM8_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM9] = { APBC_PWM9_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM10] = { APBC_PWM10_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM11] = { APBC_PWM11_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM12] = { APBC_PWM12_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM13] = { APBC_PWM13_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM14] = { APBC_PWM14_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM15] = { APBC_PWM15_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM16] = { APBC_PWM16_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM17] = { APBC_PWM17_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM18] = { APBC_PWM18_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_PWM19] = { APBC_PWM19_CLK_RST,
- BIT(2) | BIT(0),
- BIT(0),
- BIT(2),
- RST_BASE_TYPE_APBC },
- [RESET_SSP3] = { APBC_SSP3_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_UART3] = { APBC_UART3_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_RTC] = { APBC_RTC_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_TWSI0] = { APBC_TWSI0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_TIMERS1] = { APBC_TIMERS1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_AIB] = { APBC_AIB_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_TIMERS2] = { APBC_TIMERS2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_ONEWIRE] = { APBC_ONEWIRE_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_SSPA0] = { APBC_SSPA0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_SSPA1] = { APBC_SSPA1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_DRO] = { APBC_DRO_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_IR] = { APBC_IR_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_TWSI1] = { APBC_TWSI1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_TSEN] = { APBC_TSEN_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_TWSI2] = { APBC_TWSI2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_TWSI4] = { APBC_TWSI4_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_TWSI5] = { APBC_TWSI5_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_TWSI6] = { APBC_TWSI6_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_TWSI7] = { APBC_TWSI7_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_TWSI8] = { APBC_TWSI8_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_IPC_AP2AUD] = { APBC_IPC_AP2AUD_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_UART4] = { APBC_UART4_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_UART5] = { APBC_UART5_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_UART6] = { APBC_UART6_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_UART7] = { APBC_UART7_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_UART8] = { APBC_UART8_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_UART9] = { APBC_UART9_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- [RESET_CAN0] = { APBC_CAN0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
- /* MPMU */
- [RESET_WDT] = { MPMU_WDTPCR, BIT(2), 0, BIT(2), RST_BASE_TYPE_MPMU },
- /* APMU */
- [RESET_JPG] = { APMU_JPG_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
- [RESET_CSI] = { APMU_CSI_CCIC2_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
- [RESET_CCIC2_PHY] = { APMU_CSI_CCIC2_CLK_RES_CTRL,
- BIT(2),
- BIT(2),
- 0,
- RST_BASE_TYPE_APMU },
- [RESET_CCIC3_PHY] = { APMU_CSI_CCIC2_CLK_RES_CTRL,
- BIT(29),
- BIT(29),
- 0,
- RST_BASE_TYPE_APMU },
- [RESET_ISP] = { APMU_ISP_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
- [RESET_ISP_AHB] = { APMU_ISP_CLK_RES_CTRL, BIT(3), BIT(3), 0, RST_BASE_TYPE_APMU },
- [RESET_ISP_CI] = { APMU_ISP_CLK_RES_CTRL, BIT(16), BIT(16), 0, RST_BASE_TYPE_APMU },
- [RESET_ISP_CPP] = { APMU_ISP_CLK_RES_CTRL, BIT(27), BIT(27), 0, RST_BASE_TYPE_APMU },
- [RESET_LCD] = { APMU_LCD_CLK_RES_CTRL1, BIT(4), BIT(4), 0, RST_BASE_TYPE_APMU },
- [RESET_DSI_ESC] = { APMU_LCD_CLK_RES_CTRL1, BIT(3), BIT(3), 0, RST_BASE_TYPE_APMU },
- [RESET_V2D] = { APMU_LCD_CLK_RES_CTRL1, BIT(27), BIT(27), 0, RST_BASE_TYPE_APMU },
- [RESET_MIPI] = { APMU_LCD_CLK_RES_CTRL1, BIT(15), BIT(15), 0, RST_BASE_TYPE_APMU },
- [RESET_LCD_SPI] = { APMU_LCD_SPI_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
- [RESET_LCD_SPI_BUS] = { APMU_LCD_SPI_CLK_RES_CTRL,
- BIT(4),
- BIT(4),
- 0,
- RST_BASE_TYPE_APMU },
- [RESET_LCD_SPI_HBUS] = { APMU_LCD_SPI_CLK_RES_CTRL,
- BIT(2),
- BIT(2),
- 0,
- RST_BASE_TYPE_APMU },
- [RESET_LCD_MCLK] = { APMU_LCD_CLK_RES_CTRL2, BIT(9), BIT(9), 0, RST_BASE_TYPE_APMU },
- [RESET_CCIC_4X] = { APMU_CCIC_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
- [RESET_CCIC1_PHY] = { APMU_CCIC_CLK_RES_CTRL, BIT(2), BIT(2), 0, RST_BASE_TYPE_APMU },
- [RESET_SDH_AXI] = { APMU_SDH0_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
- [RESET_SDH0] = { APMU_SDH0_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
- [RESET_SDH1] = { APMU_SDH1_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
- [RESET_USB_AXI] = { APMU_USB_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
- [RESET_USBP1_AXI] = { APMU_USB_CLK_RES_CTRL, BIT(4), BIT(4), 0, RST_BASE_TYPE_APMU },
- [RESET_USB3_0] = { APMU_USB_CLK_RES_CTRL,
- BIT(9) | BIT(10) | BIT(11),
- BIT(9) | BIT(10) | BIT(11),
- 0,
- RST_BASE_TYPE_APMU },
- [RESET_QSPI] = { APMU_QSPI_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
- [RESET_QSPI_BUS] = { APMU_QSPI_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
- [RESET_DMA] = { APMU_DMA_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
- [RESET_AES] = { APMU_AES_CLK_RES_CTRL, BIT(4), BIT(4), 0, RST_BASE_TYPE_APMU },
- [RESET_VPU] = { APMU_VPU_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
- [RESET_GPU] = { APMU_GPU_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
- [RESET_SDH2] = { APMU_SDH2_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
- [RESET_MC] = { APMU_PMUA_MC_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
- [RESET_EM_AXI] = { APMU_PMUA_EM_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
- [RESET_EM] = { APMU_PMUA_EM_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
- [RESET_AUDIO_SYS] = { APMU_AUDIO_CLK_RES_CTRL,
- BIT(0) | BIT(2) | BIT(3),
- BIT(0) | BIT(2) | BIT(3),
- 0,
- RST_BASE_TYPE_APMU },
- [RESET_HDMI] = { APMU_HDMI_CLK_RES_CTRL, BIT(9), BIT(9), 0, RST_BASE_TYPE_APMU },
- [RESET_PCIE0] = { APMU_PCIE_CLK_RES_CTRL_0,
- BIT(3) | BIT(4) | BIT(5) | BIT(8),
- BIT(3) | BIT(4) | BIT(5),
- BIT(8),
- RST_BASE_TYPE_APMU },
- [RESET_PCIE1] = { APMU_PCIE_CLK_RES_CTRL_1,
- BIT(3) | BIT(4) | BIT(5) | BIT(8),
- BIT(3) | BIT(4) | BIT(5),
- BIT(8),
- RST_BASE_TYPE_APMU },
- [RESET_PCIE2] = { APMU_PCIE_CLK_RES_CTRL_2, 0x138, 0x38, 0x100, RST_BASE_TYPE_APMU },
- [RESET_EMAC0] = { APMU_EMAC0_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
- [RESET_EMAC1] = { APMU_EMAC1_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
- [RESET_SEC_UART1] = { APBC2_UART1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
- [RESET_SEC_SSP2] = { APBC2_SSP2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
- [RESET_SEC_TWSI3] = { APBC2_TWSI3_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
- [RESET_SEC_RTC] = { APBC2_RTC_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
- [RESET_SEC_TIMERS0] = { APBC2_TIMERS0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
- [RESET_SEC_KPC] = { APBC2_KPC_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
- [RESET_SEC_GPIO] = { APBC2_GPIO_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
- };
-
-static u32 spacemit_reset_read(struct spacemit_reset *reset, u32 id)
-{
- void __iomem *base;
-
- switch (reset->signals[id].type) {
- case RST_BASE_TYPE_APMU:
- base = reset->io_base.apmu_base;
- break;
- case RST_BASE_TYPE_APBC:
- base = reset->io_base.apbc_base;
- break;
- default:
- base = reset->io_base.apbc_base;
- break;
- }
-
- return readl(base + reset->signals[id].offset);
-}
-
-static void spacemit_reset_write(struct spacemit_reset *reset, u32 value, u32 id)
-{
- void __iomem *base;
-
- switch (reset->signals[id].type) {
- case RST_BASE_TYPE_APMU:
- base = reset->io_base.apmu_base;
- break;
- case RST_BASE_TYPE_APBC:
- base = reset->io_base.apbc_base;
- break;
- default:
- base = reset->io_base.apbc_base;
- break;
- }
-
- writel(value, base + reset->signals[id].offset);
-}
-
-static void spacemit_reset_set(struct reset_ctl *rst, u32 id, bool assert)
-{
- u32 value;
- struct spacemit_reset *reset = dev_get_priv(rst->dev);
-
- value = spacemit_reset_read(reset, id);
-
- if (assert) {
- value &= ~reset->signals[id].mask;
- value |= reset->signals[id].assert_val;
- } else {
- value &= ~reset->signals[id].mask;
- value |= reset->signals[id].deassert_val;
- }
-
- spacemit_reset_write(reset, value, id);
-}
-
-static int spacemit_reset_update(struct reset_ctl *rst, bool assert)
-{
- if (rst->id < RESET_UART1 || rst->id >= RESET_NUMBER)
- return 0;
-
- /* can not write to twsi8 */
- if (rst->id == RESET_TWSI8)
- return 0;
-
- spacemit_reset_set(rst, rst->id, assert);
- return 0;
-}
-
-static int spacemit_reset_assert(struct reset_ctl *rst)
-{
- return spacemit_reset_update(rst, true);
-}
-
-static int spacemit_reset_deassert(struct reset_ctl *rst)
-{
- return spacemit_reset_update(rst, false);
-}
-
-static int spacemit_k1_reset_probe(struct udevice *dev)
-{
- struct spacemit_reset *reset = dev_get_priv(dev);
-
- reset->io_base.mpmu_base = (void *)dev_remap_addr_index(dev, 0);
- if (!reset->io_base.mpmu_base) {
- pr_err("failed to map mpmu registers\n");
- goto out;
- }
-
- reset->io_base.apmu_base = (void *)dev_remap_addr_index(dev, 1);
- if (!reset->io_base.apmu_base) {
- pr_err("failed to map apmu registers\n");
- goto out;
- }
-
- reset->io_base.apbc_base = (void *)dev_remap_addr_index(dev, 2);
- if (!reset->io_base.apbc_base) {
- pr_err("failed to map apbc registers\n");
- goto out;
- }
-
- reset->io_base.apbs_base = (void *)dev_remap_addr_index(dev, 3);
- if (!reset->io_base.apbs_base) {
- pr_err("failed to map apbs registers\n");
- goto out;
- }
-
- reset->io_base.ciu_base = (void *)dev_remap_addr_index(dev, 4);
- if (!reset->io_base.ciu_base) {
- pr_err("failed to map ciu registers\n");
- goto out;
- }
-
- reset->io_base.dciu_base = (void *)dev_remap_addr_index(dev, 5);
- if (!reset->io_base.dciu_base) {
- pr_err("failed to map dragon ciu registers\n");
- goto out;
- }
-
- reset->io_base.ddrc_base = (void *)dev_remap_addr_index(dev, 6);
- if (!reset->io_base.ddrc_base) {
- pr_err("failed to map ddrc registers\n");
- goto out;
- }
-
- reset->io_base.apbc2_base = (void *)dev_remap_addr_index(dev, 7);
- if (!reset->io_base.apbc2_base) {
- pr_err("failed to map apbc2 registers\n");
- goto out;
- }
-
- reset->signals = k1_reset_signals;
-
-out:
- return 0;
-}
-
-const struct reset_ops k1_reset_ops = {
- .rst_assert = spacemit_reset_assert,
- .rst_deassert = spacemit_reset_deassert,
-};
-
-static const struct udevice_id k1_reset_ids[] = {
- { .compatible = "spacemit,k1-reset", },
- {},
-};
-
-U_BOOT_DRIVER(k1_reset) = {
- .name = "spacemit,k1-reset",
- .id = UCLASS_RESET,
- .ops = &k1_reset_ops,
- .of_match = k1_reset_ids,
- .probe = spacemit_k1_reset_probe,
- .priv_auto = sizeof(struct spacemit_reset),
-};
diff --git a/drivers/reset/spacemit/Makefile b/drivers/reset/spacemit/Makefile
new file mode 100644
index 00000000000..16722c68954
--- /dev/null
+++ b/drivers/reset/spacemit/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# SpacemiT reset drivers
+
+obj-y += reset-spacemit-k1.o
diff --git a/drivers/reset/spacemit/reset-spacemit-k1.c b/drivers/reset/spacemit/reset-spacemit-k1.c
new file mode 100644
index 00000000000..129e1cc96f1
--- /dev/null
+++ b/drivers/reset/spacemit/reset-spacemit-k1.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * SpacemiT K1 reset driver (syscon-bound).
+ *
+ * Copyright (C) 2022 Spacemit Inc.
+ * Copyright (C) 2025 Huan Zhou <[email protected]>
+ * Copyright (C) 2026 RISCstar Ltd.
+ */
+
+#include <asm/io.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dt-bindings/clock/spacemit,k1-syscon.h>
+#include <linux/bitops.h>
+#include <malloc.h>
+#include <reset-uclass.h>
+#include <soc/spacemit/k1-reset.h>
+#include <soc/spacemit/k1-syscon.h>
+
+/* ===================================================================
+ * Per-syscon reset signal tables.
+ *
+ * Indexed by the kernel-side per-syscon-local IDs from
+ * <dt-bindings/clock/spacemit,k1-syscon.h>. Each entry is
+ * (offset, assert_mask, deassert_mask): bits in assert_mask are set
+ * when the reset line is asserted; bits in deassert_mask are set when
+ * deasserted; the union (assert_mask | deassert_mask) is the set of
+ * bits the controller will overwrite on each transition.
+ *
+ * Layout mirrors the kernel-side K1 reset driver.
+ * ===================================================================
+ */
+
+struct spacemit_k1_reset_data {
+ u32 offset;
+ u32 assert_mask;
+ u32 deassert_mask;
+};
+
+#define RESET_DATA(o, a, d) { \
+ .offset = (o), .assert_mask = (a), .deassert_mask = (d) \
+}
+
+static const struct spacemit_k1_reset_data k1_mpmu_resets[] = {
+ [RESET_WDT] = RESET_DATA(MPMU_WDTPCR, BIT(2), 0),
+};
+
+static const struct spacemit_k1_reset_data k1_apbc_resets[] = {
+ [RESET_UART0] = RESET_DATA(APBC_UART1_CLK_RST, BIT(2), 0),
+ [RESET_UART2] = RESET_DATA(APBC_UART2_CLK_RST, BIT(2), 0),
+ [RESET_UART3] = RESET_DATA(APBC_UART3_CLK_RST, BIT(2), 0),
+ [RESET_UART4] = RESET_DATA(APBC_UART4_CLK_RST, BIT(2), 0),
+ [RESET_UART5] = RESET_DATA(APBC_UART5_CLK_RST, BIT(2), 0),
+ [RESET_UART6] = RESET_DATA(APBC_UART6_CLK_RST, BIT(2), 0),
+ [RESET_UART7] = RESET_DATA(APBC_UART7_CLK_RST, BIT(2), 0),
+ [RESET_UART8] = RESET_DATA(APBC_UART8_CLK_RST, BIT(2), 0),
+ [RESET_UART9] = RESET_DATA(APBC_UART9_CLK_RST, BIT(2), 0),
+ [RESET_GPIO] = RESET_DATA(APBC_GPIO_CLK_RST, BIT(2), 0),
+ [RESET_PWM0] = RESET_DATA(APBC_PWM0_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM1] = RESET_DATA(APBC_PWM1_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM2] = RESET_DATA(APBC_PWM2_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM3] = RESET_DATA(APBC_PWM3_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM4] = RESET_DATA(APBC_PWM4_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM5] = RESET_DATA(APBC_PWM5_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM6] = RESET_DATA(APBC_PWM6_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM7] = RESET_DATA(APBC_PWM7_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM8] = RESET_DATA(APBC_PWM8_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM9] = RESET_DATA(APBC_PWM9_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM10] = RESET_DATA(APBC_PWM10_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM11] = RESET_DATA(APBC_PWM11_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM12] = RESET_DATA(APBC_PWM12_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM13] = RESET_DATA(APBC_PWM13_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM14] = RESET_DATA(APBC_PWM14_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM15] = RESET_DATA(APBC_PWM15_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM16] = RESET_DATA(APBC_PWM16_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM17] = RESET_DATA(APBC_PWM17_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM18] = RESET_DATA(APBC_PWM18_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM19] = RESET_DATA(APBC_PWM19_CLK_RST, BIT(2), BIT(0)),
+ [RESET_SSP3] = RESET_DATA(APBC_SSP3_CLK_RST, BIT(2), 0),
+ [RESET_RTC] = RESET_DATA(APBC_RTC_CLK_RST, BIT(2), 0),
+ [RESET_TWSI0] = RESET_DATA(APBC_TWSI0_CLK_RST, BIT(2), 0),
+ [RESET_TWSI1] = RESET_DATA(APBC_TWSI1_CLK_RST, BIT(2), 0),
+ [RESET_TWSI2] = RESET_DATA(APBC_TWSI2_CLK_RST, BIT(2), 0),
+ [RESET_TWSI4] = RESET_DATA(APBC_TWSI4_CLK_RST, BIT(2), 0),
+ [RESET_TWSI5] = RESET_DATA(APBC_TWSI5_CLK_RST, BIT(2), 0),
+ [RESET_TWSI6] = RESET_DATA(APBC_TWSI6_CLK_RST, BIT(2), 0),
+ [RESET_TWSI7] = RESET_DATA(APBC_TWSI7_CLK_RST, BIT(2), 0),
+ [RESET_TWSI8] = RESET_DATA(APBC_TWSI8_CLK_RST, BIT(2), 0),
+ [RESET_TIMERS1] = RESET_DATA(APBC_TIMERS1_CLK_RST, BIT(2), 0),
+ [RESET_TIMERS2] = RESET_DATA(APBC_TIMERS2_CLK_RST, BIT(2), 0),
+ [RESET_AIB] = RESET_DATA(APBC_AIB_CLK_RST, BIT(2), 0),
+ [RESET_ONEWIRE] = RESET_DATA(APBC_ONEWIRE_CLK_RST, BIT(2), 0),
+ [RESET_SSPA0] = RESET_DATA(APBC_SSPA0_CLK_RST, BIT(2), 0),
+ [RESET_SSPA1] = RESET_DATA(APBC_SSPA1_CLK_RST, BIT(2), 0),
+ [RESET_DRO] = RESET_DATA(APBC_DRO_CLK_RST, BIT(2), 0),
+ [RESET_IR] = RESET_DATA(APBC_IR_CLK_RST, BIT(2), 0),
+ [RESET_TSEN] = RESET_DATA(APBC_TSEN_CLK_RST, BIT(2), 0),
+ [RESET_IPC_AP2AUD] = RESET_DATA(APBC_IPC_AP2AUD_CLK_RST, BIT(2), 0),
+ [RESET_CAN0] = RESET_DATA(APBC_CAN0_CLK_RST, BIT(2), 0),
+};
+
+static const struct spacemit_k1_reset_data k1_apmu_resets[] = {
+ [RESET_CCIC_4X] = RESET_DATA(APMU_CCIC_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_CCIC1_PHY] = RESET_DATA(APMU_CCIC_CLK_RES_CTRL, 0, BIT(2)),
+ [RESET_SDH_AXI] = RESET_DATA(APMU_SDH0_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_SDH0] = RESET_DATA(APMU_SDH0_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_SDH1] = RESET_DATA(APMU_SDH1_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_SDH2] = RESET_DATA(APMU_SDH2_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_USBP1_AXI] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(4)),
+ [RESET_USB_AXI] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_USB30_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(9)),
+ [RESET_USB30_VCC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(10)),
+ [RESET_USB30_PHY] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(11)),
+ [RESET_QSPI] = RESET_DATA(APMU_QSPI_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_QSPI_BUS] = RESET_DATA(APMU_QSPI_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_DMA] = RESET_DATA(APMU_DMA_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_AES] = RESET_DATA(APMU_AES_CLK_RES_CTRL, 0, BIT(4)),
+ [RESET_VPU] = RESET_DATA(APMU_VPU_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_GPU] = RESET_DATA(APMU_GPU_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_EMMC] = RESET_DATA(APMU_PMUA_EM_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_EMMC_X] = RESET_DATA(APMU_PMUA_EM_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_AUDIO_SYS] = RESET_DATA(APMU_AUDIO_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_AUDIO_MCU] = RESET_DATA(APMU_AUDIO_CLK_RES_CTRL, 0, BIT(2)),
+ [RESET_AUDIO_APMU] = RESET_DATA(APMU_AUDIO_CLK_RES_CTRL, 0, BIT(3)),
+ [RESET_HDMI] = RESET_DATA(APMU_HDMI_CLK_RES_CTRL, 0, BIT(9)),
+ [RESET_PCIE0_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_0, 0, BIT(3)),
+ [RESET_PCIE0_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_0, 0, BIT(4)),
+ [RESET_PCIE0_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_0, 0, BIT(5)),
+ [RESET_PCIE0_GLOBAL] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_0, BIT(8), 0),
+ [RESET_PCIE1_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_1, 0, BIT(3)),
+ [RESET_PCIE1_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_1, 0, BIT(4)),
+ [RESET_PCIE1_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_1, 0, BIT(5)),
+ [RESET_PCIE1_GLOBAL] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_1, BIT(8), 0),
+ [RESET_PCIE2_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_2, 0, BIT(3)),
+ [RESET_PCIE2_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_2, 0, BIT(4)),
+ [RESET_PCIE2_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_2, 0, BIT(5)),
+ [RESET_PCIE2_GLOBAL] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_2, BIT(8), 0),
+ [RESET_EMAC0] = RESET_DATA(APMU_EMAC0_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_EMAC1] = RESET_DATA(APMU_EMAC1_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_JPG] = RESET_DATA(APMU_JPG_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_CCIC2PHY] = RESET_DATA(APMU_CSI_CCIC2_CLK_RES_CTRL, 0, BIT(2)),
+ [RESET_CCIC3PHY] = RESET_DATA(APMU_CSI_CCIC2_CLK_RES_CTRL, 0, BIT(29)),
+ [RESET_CSI] = RESET_DATA(APMU_CSI_CCIC2_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_ISP] = RESET_DATA(APMU_ISP_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_ISP_CPP] = RESET_DATA(APMU_ISP_CLK_RES_CTRL, 0, BIT(27)),
+ [RESET_ISP_BUS] = RESET_DATA(APMU_ISP_CLK_RES_CTRL, 0, BIT(3)),
+ [RESET_ISP_CI] = RESET_DATA(APMU_ISP_CLK_RES_CTRL, 0, BIT(16)),
+ [RESET_DPU_MCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL2, 0, BIT(9)),
+ [RESET_DPU_ESC] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(3)),
+ [RESET_DPU_HCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(4)),
+ [RESET_DPU_SPIBUS] = RESET_DATA(APMU_LCD_SPI_CLK_RES_CTRL, 0, BIT(4)),
+ [RESET_DPU_SPI_HBUS] = RESET_DATA(APMU_LCD_SPI_CLK_RES_CTRL, 0, BIT(2)),
+ [RESET_V2D] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(27)),
+ [RESET_MIPI] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(15)),
+ [RESET_MC] = RESET_DATA(APMU_PMUA_MC_CTRL, 0, BIT(0)),
+};
+
+static const struct spacemit_k1_reset_data k1_apbc2_resets[] = {
+ [RESET_APBC2_UART1] = RESET_DATA(APBC2_UART1_CLK_RST, BIT(2), 0),
+ [RESET_APBC2_SSP2] = RESET_DATA(APBC2_SSP2_CLK_RST, BIT(2), 0),
+ [RESET_APBC2_TWSI3] = RESET_DATA(APBC2_TWSI3_CLK_RST, BIT(2), 0),
+ [RESET_APBC2_RTC] = RESET_DATA(APBC2_RTC_CLK_RST, BIT(2), 0),
+ [RESET_APBC2_TIMERS0] = RESET_DATA(APBC2_TIMERS0_CLK_RST, BIT(2), 0),
+ [RESET_APBC2_KPC] = RESET_DATA(APBC2_KPC_CLK_RST, BIT(2), 0),
+ [RESET_APBC2_GPIO] = RESET_DATA(APBC2_GPIO_CLK_RST, BIT(2), 0),
+};
+
+/* ===================================================================
+ * Driver
+ * ===================================================================
+ */
+
+struct spacemit_k1_reset_priv {
+ void __iomem *base;
+ const struct spacemit_k1_reset_data *table;
+ size_t table_size;
+};
+
+static int spacemit_k1_reset_xfer(struct reset_ctl *rst, bool assert)
+{
+ struct spacemit_k1_reset_priv *priv = dev_get_priv(rst->dev);
+ const struct spacemit_k1_reset_data *e;
+ u32 v;
+
+ if (rst->id >= priv->table_size)
+ return -EINVAL;
+
+ e = &priv->table[rst->id];
+ if (e->assert_mask == 0 && e->deassert_mask == 0)
+ return -EINVAL; /* not owned by this syscon */
+
+ v = readl(priv->base + e->offset);
+ v &= ~(e->assert_mask | e->deassert_mask);
+ v |= assert ? e->assert_mask : e->deassert_mask;
+ writel(v, priv->base + e->offset);
+
+ return 0;
+}
+
+static int spacemit_k1_reset_assert(struct reset_ctl *rst)
+{
+ return spacemit_k1_reset_xfer(rst, true);
+}
+
+static int spacemit_k1_reset_deassert(struct reset_ctl *rst)
+{
+ return spacemit_k1_reset_xfer(rst, false);
+}
+
+static int spacemit_k1_reset_request(struct reset_ctl *rst)
+{
+ struct spacemit_k1_reset_priv *priv = dev_get_priv(rst->dev);
+
+ return rst->id < priv->table_size ? 0 : -EINVAL;
+}
+
+static const struct reset_ops spacemit_k1_reset_ops = {
+ .request = spacemit_k1_reset_request,
+ .rst_assert = spacemit_k1_reset_assert,
+ .rst_deassert = spacemit_k1_reset_deassert,
+};
+
+static int spacemit_k1_reset_probe(struct udevice *dev)
+{
+ struct spacemit_k1_reset_priv *priv = dev_get_priv(dev);
+
+ priv->base = (void __iomem *)dev_remap_addr(dev);
+ if (!priv->base)
+ return -ENODEV;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(spacemit_k1_reset) = {
+ .name = "spacemit_k1_reset",
+ .id = UCLASS_RESET,
+ .ops = &spacemit_k1_reset_ops,
+ .probe = spacemit_k1_reset_probe,
+ .priv_auto = sizeof(struct spacemit_k1_reset_priv),
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+int spacemit_k1_reset_bind(struct udevice *parent,
+ enum spacemit_k1_reset_syscon syscon)
+{
+ struct spacemit_k1_reset_priv *priv;
+ struct udevice *rst_dev;
+ const struct spacemit_k1_reset_data *table;
+ size_t table_size;
+ int ret;
+
+ switch (syscon) {
+ case SPACEMIT_K1_RESET_MPMU:
+ table = k1_mpmu_resets;
+ table_size = ARRAY_SIZE(k1_mpmu_resets);
+ break;
+ case SPACEMIT_K1_RESET_APMU:
+ table = k1_apmu_resets;
+ table_size = ARRAY_SIZE(k1_apmu_resets);
+ break;
+ case SPACEMIT_K1_RESET_APBC:
+ table = k1_apbc_resets;
+ table_size = ARRAY_SIZE(k1_apbc_resets);
+ break;
+ case SPACEMIT_K1_RESET_APBC2:
+ table = k1_apbc2_resets;
+ table_size = ARRAY_SIZE(k1_apbc2_resets);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = device_bind_driver_to_node(parent, "spacemit_k1_reset", "reset",
+ dev_ofnode(parent), &rst_dev);
+ if (ret)
+ return ret;
+
+ priv = malloc(sizeof(*priv));
+ if (!priv) {
+ device_unbind(rst_dev);
+ return -ENOMEM;
+ }
+ priv->table = table;
+ priv->table_size = table_size;
+ dev_set_priv(rst_dev, priv);
+
+ return 0;
+}
diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c
index 955f1c96407..917445e1ee2 100644
--- a/drivers/serial/serial_lpuart.c
+++ b/drivers/serial/serial_lpuart.c
@@ -362,10 +362,14 @@ static int _lpuart32_serial_putc(struct lpuart_serial_plat *plat,
static int _lpuart32_serial_tstc(struct lpuart_serial_plat *plat)
{
struct lpuart_fsl_reg32 *base = plat->reg;
- u32 water;
+ u32 stat, water;
lpuart_read32(plat->flags, &base->water, &water);
+ lpuart_read32(plat->flags, &base->stat, &stat);
+ if (stat & STAT_OR)
+ lpuart_write32(plat->flags, &base->stat, STAT_OR);
+
if ((water >> 24) == 0)
return 0;
diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index b520c727900..0df9a4f5abd 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -497,6 +497,7 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
{
struct udevice *bus = dev->parent;
struct dw_spi_priv *priv = dev_get_priv(bus);
+ struct spi_slave *slave = dev_get_parent_priv(dev);
const u8 *tx = dout;
u8 *rx = din;
int ret = 0;
@@ -504,6 +505,17 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
u32 val;
u32 cs;
+ /* Get bits_per_word from slave (set by device driver) */
+ if (slave->bits_per_word >= 4 && slave->bits_per_word <= priv->max_xfer) {
+ priv->bits_per_word = slave->bits_per_word;
+ } else if (slave->bits_per_word == 0) {
+ priv->bits_per_word = 8; /* Default if not set */
+ } else {
+ dev_warn(dev, "Invalid bits_per_word %u, using 8\n",
+ slave->bits_per_word);
+ priv->bits_per_word = 8;
+ }
+
/* spi core configured to do 8 bit transfers */
if (bitlen % 8) {
dev_err(dev, "Non byte aligned SPI transfer.\n");
diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c
index 65ab3e306d7..5692510a558 100644
--- a/drivers/spi/fsl_qspi.c
+++ b/drivers/spi/fsl_qspi.c
@@ -267,6 +267,14 @@ static const struct fsl_qspi_devtype_data ls2080a_data = {
.little_endian = true,
};
+static const struct fsl_qspi_devtype_data spacemit_k1_data = {
+ .rxfifo = SZ_128,
+ .txfifo = SZ_256,
+ .ahb_buf_size = SZ_128,
+ .quirks = QUADSPI_QUIRK_TKT253890,
+ .little_endian = true,
+};
+
struct fsl_qspi {
struct udevice *dev;
void __iomem *iobase;
@@ -870,6 +878,7 @@ static const struct udevice_id fsl_qspi_ids[] = {
{ .compatible = "fsl,ls1021a-qspi", .data = (ulong)&ls1021a_data, },
{ .compatible = "fsl,ls1088a-qspi", .data = (ulong)&ls2080a_data, },
{ .compatible = "fsl,ls2080a-qspi", .data = (ulong)&ls2080a_data, },
+ { .compatible = "spacemit,k1-qspi", .data = (ulong)&spacemit_k1_data, },
{ }
};
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 9ad0d699850..3b7e807a024 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -63,4 +63,12 @@ config DM_THERMAL_JC42
Enable support for the JEDEC JC-42.4 temperature sensor found
on the SPD bus of DDR3 and DDR4 DIMMs (TSE2004av and compatible).
+config PMBUS_THERMAL
+ bool "Generic PMBus temperature"
+ depends on DM_REGULATOR_PMBUS_HELPER
+ help
+ Expose the die-temperature reading of any PMBus voltage
+ regulator bound by a pmbus_helper based chip driver
+ as a UCLASS_THERMAL device.
+
endif # if DM_THERMAL
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index c9fa7561b45..475026a5ab2 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_SANDBOX) += thermal_sandbox.o
obj-$(CONFIG_TI_DRA7_THERMAL) += ti-bandgap.o
obj-$(CONFIG_TI_LM74_THERMAL) += ti-lm74.o
obj-$(CONFIG_DM_THERMAL_JC42) += jc42.o
+obj-$(CONFIG_PMBUS_THERMAL) += pmbus_thermal.o
diff --git a/drivers/thermal/pmbus_thermal.c b/drivers/thermal/pmbus_thermal.c
new file mode 100644
index 00000000000..c251cea8ddf
--- /dev/null
+++ b/drivers/thermal/pmbus_thermal.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2026 Free Mobile - Vincent Jardin
+ *
+ * Generic UCLASS_THERMAL companion for PMBus voltage regulators.
+ *
+ * Works with any chip bound by a pmbus_helper based regulator driver
+ * (drivers/power/regulator/<chip>.c calling
+ * pmbus_regulator_probe_common()). It auto-spawns one
+ * of these thermal devices per regulator.
+ *
+ * The reading is the chip's READ_TEMPERATURE_1 (PMBus 0x8D), decoded
+ * through the parent's pmbus_driver_info.
+ */
+
+#include <dm.h>
+#include <pmbus.h>
+#include <thermal.h>
+
+static int pmbus_thermal_get_temp(struct udevice *dev, int *temp)
+{
+ return pmbus_regulator_read_temp(dev_get_parent(dev), temp);
+}
+
+static const struct dm_thermal_ops pmbus_thermal_ops = {
+ .get_temp = pmbus_thermal_get_temp,
+};
+
+U_BOOT_DRIVER(pmbus_thermal) = {
+ .name = "pmbus_thermal",
+ .id = UCLASS_THERMAL,
+ .ops = &pmbus_thermal_ops,
+};