summaryrefslogtreecommitdiff
path: root/drivers/clk
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk')
-rw-r--r--drivers/clk/Kconfig11
-rw-r--r--drivers/clk/Makefile3
-rw-r--r--drivers/clk/airoha/clk-airoha.c6
-rw-r--r--drivers/clk/altera/clk-mem-n5x.c9
-rw-r--r--drivers/clk/altera/clk-n5x.c8
-rw-r--r--drivers/clk/aspeed/Makefile1
-rw-r--r--drivers/clk/aspeed/clk_ast2500.c8
-rw-r--r--drivers/clk/aspeed/clk_ast2600.c8
-rw-r--r--drivers/clk/aspeed/clk_ast2700.c952
-rw-r--r--drivers/clk/at91/sam9x60.c2
-rw-r--r--drivers/clk/at91/sam9x7.c2
-rw-r--r--drivers/clk/at91/sama7d65.c6
-rw-r--r--drivers/clk/at91/sama7g5.c6
-rw-r--r--drivers/clk/at91/sckc.c5
-rw-r--r--drivers/clk/clk-divider.c16
-rw-r--r--drivers/clk/clk-hsdk-cgu.c4
-rw-r--r--drivers/clk/clk-stub.c6
-rw-r--r--drivers/clk/clk_versal.c1
-rw-r--r--drivers/clk/imx/Makefile2
-rw-r--r--drivers/clk/imx/clk-frac-pll.c199
-rw-r--r--drivers/clk/imx/clk-imx6q.c255
-rw-r--r--drivers/clk/imx/clk-imx8mq.c55
-rw-r--r--drivers/clk/imx/clk.h3
-rw-r--r--drivers/clk/mediatek/clk-mt8189.c44
-rw-r--r--drivers/clk/mediatek/clk-mtk.c22
-rw-r--r--drivers/clk/meson/g12a.c5
-rw-r--r--drivers/clk/meson/gxbb.c5
-rw-r--r--drivers/clk/owl/Kconfig8
-rw-r--r--drivers/clk/qcom/Kconfig16
-rw-r--r--drivers/clk/qcom/Makefile2
-rw-r--r--drivers/clk/qcom/clock-milos.c269
-rw-r--r--drivers/clk/qcom/clock-qcm2290.c2
-rw-r--r--drivers/clk/qcom/clock-qcs615.c63
-rw-r--r--drivers/clk/qcom/clock-sa8775p.c63
-rw-r--r--drivers/clk/qcom/clock-sc7280.c55
-rw-r--r--drivers/clk/qcom/clock-sm6125.c260
-rw-r--r--drivers/clk/renesas/Kconfig14
-rw-r--r--drivers/clk/renesas/Makefile1
-rw-r--r--drivers/clk/renesas/r8a78000-cpg.c282
-rw-r--r--drivers/clk/renesas/rcar-cpg-lib.c4
-rw-r--r--drivers/clk/rockchip/clk_rk3528.c2
-rw-r--r--drivers/clk/rockchip/clk_rk3576.c54
-rw-r--r--drivers/clk/sophgo/clk-cv1800b.c5
-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/stm32/Kconfig2
-rw-r--r--drivers/clk/sunxi/clk_sunxi.c2
-rw-r--r--drivers/clk/ti/clk-ctrl.c77
56 files changed, 5703 insertions, 120 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index c2da7b3938b..145e4cfa2de 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -134,8 +134,8 @@ config CLK_CDCE9XX
bool "Enable CDCD9XX clock driver"
depends on CLK && ARCH_OMAP2PLUS
help
- Enable the clock synthesizer driver for CDCE913/925/937/949
- series of chips.
+ Enable the clock synthesizer driver for CDCE913/925/937/949
+ series of chips.
config CLK_ICS8N3QV01
bool "Enable ICS8N3QV01 VCXO driver"
@@ -216,7 +216,7 @@ config CLK_HSDK
Synopsys ARC HSDK-4xD boards
config CLK_VERSACLOCK
- tristate "Enable VersaClock 5/6 devices"
+ bool "Enable VersaClock 5/6 devices"
depends on CLK
depends on CLK_CCF
depends on OF_CONTROL
@@ -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 5f0c0d8a5c2..2bfa789e48a 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -16,7 +16,7 @@ obj-$(CONFIG_$(PHASE_)CLK_STUB) += clk-stub.o
obj-y += adi/
obj-y += airoha/
obj-y += analogbits/
-obj-y += imx/
+obj-$(CONFIG_MACH_IMX) += imx/
obj-$(CONFIG_CLK_JH7110) += starfive/
obj-y += tegra/
obj-y += ti/
@@ -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/altera/clk-mem-n5x.c b/drivers/clk/altera/clk-mem-n5x.c
index ac59571a853..149e3016dd3 100644
--- a/drivers/clk/altera/clk-mem-n5x.c
+++ b/drivers/clk/altera/clk-mem-n5x.c
@@ -103,12 +103,13 @@ static int socfpga_mem_clk_enable(struct clk *clk)
static int socfpga_mem_clk_of_to_plat(struct udevice *dev)
{
struct socfpga_mem_clk_plat *plat = dev_get_plat(dev);
- fdt_addr_t addr;
+ void __iomem *addr;
- addr = devfdt_get_addr(dev);
- if (addr == FDT_ADDR_T_NONE)
+ addr = dev_read_addr_ptr(dev);
+ if (!addr)
return -EINVAL;
- plat->regs = (void __iomem *)addr;
+
+ plat->regs = addr;
return 0;
}
diff --git a/drivers/clk/altera/clk-n5x.c b/drivers/clk/altera/clk-n5x.c
index 185c9028a78..0a3bae38589 100644
--- a/drivers/clk/altera/clk-n5x.c
+++ b/drivers/clk/altera/clk-n5x.c
@@ -454,12 +454,12 @@ static int socfpga_clk_probe(struct udevice *dev)
static int socfpga_clk_of_to_plat(struct udevice *dev)
{
struct socfpga_clk_plat *plat = dev_get_plat(dev);
- fdt_addr_t addr;
+ void __iomem *addr;
- addr = devfdt_get_addr(dev);
- if (addr == FDT_ADDR_T_NONE)
+ addr = dev_read_addr_ptr(dev);
+ if (!addr)
return -EINVAL;
- plat->regs = (void __iomem *)addr;
+ plat->regs = addr;
return 0;
}
diff --git a/drivers/clk/aspeed/Makefile b/drivers/clk/aspeed/Makefile
index 84776e5265e..285180b67cf 100644
--- a/drivers/clk/aspeed/Makefile
+++ b/drivers/clk/aspeed/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_ASPEED_AST2500) += clk_ast2500.o
obj-$(CONFIG_ASPEED_AST2600) += clk_ast2600.o
+obj-$(CONFIG_ASPEED_AST2700) += clk_ast2700.o
diff --git a/drivers/clk/aspeed/clk_ast2500.c b/drivers/clk/aspeed/clk_ast2500.c
index a330dcda4dc..94c7f662319 100644
--- a/drivers/clk/aspeed/clk_ast2500.c
+++ b/drivers/clk/aspeed/clk_ast2500.c
@@ -534,7 +534,7 @@ static int ast2500_clk_enable(struct clk *clk)
return 0;
}
-struct clk_ops ast2500_clk_ops = {
+static const struct clk_ops ast2500_clk_ops = {
.get_rate = ast2500_clk_get_rate,
.set_rate = ast2500_clk_set_rate,
.enable = ast2500_clk_enable,
@@ -544,9 +544,9 @@ static int ast2500_clk_of_to_plat(struct udevice *dev)
{
struct ast2500_clk_priv *priv = dev_get_priv(dev);
- priv->scu = devfdt_get_addr_ptr(dev);
- if (IS_ERR(priv->scu))
- return PTR_ERR(priv->scu);
+ priv->scu = dev_read_addr_ptr(dev);
+ if (!priv->scu)
+ return -EINVAL;
return 0;
}
diff --git a/drivers/clk/aspeed/clk_ast2600.c b/drivers/clk/aspeed/clk_ast2600.c
index 535010b7941..74209e947ed 100644
--- a/drivers/clk/aspeed/clk_ast2600.c
+++ b/drivers/clk/aspeed/clk_ast2600.c
@@ -1161,7 +1161,7 @@ static void ast2600_clk_dump(struct udevice *dev)
}
#endif
-struct clk_ops ast2600_clk_ops = {
+static const struct clk_ops ast2600_clk_ops = {
.get_rate = ast2600_clk_get_rate,
.set_rate = ast2600_clk_set_rate,
.enable = ast2600_clk_enable,
@@ -1174,9 +1174,9 @@ static int ast2600_clk_probe(struct udevice *dev)
{
struct ast2600_clk_priv *priv = dev_get_priv(dev);
- priv->scu = devfdt_get_addr_ptr(dev);
- if (IS_ERR(priv->scu))
- return PTR_ERR(priv->scu);
+ priv->scu = dev_read_addr_ptr(dev);
+ if (!priv->scu)
+ return -EINVAL;
ast2600_init_rgmii_clk(priv->scu, &rgmii_clk_defconfig);
ast2600_init_rmii_clk(priv->scu, &rmii_clk_defconfig);
diff --git a/drivers/clk/aspeed/clk_ast2700.c b/drivers/clk/aspeed/clk_ast2700.c
new file mode 100644
index 00000000000..ca76abef48f
--- /dev/null
+++ b/drivers/clk/aspeed/clk_ast2700.c
@@ -0,0 +1,952 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) ASPEED Technology Inc.
+ */
+
+#include <asm/io.h>
+#include <asm/arch/scu_ast2700.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dm/lists.h>
+#include <syscon.h>
+#include <linux/bitfield.h>
+
+#include <dt-bindings/clock/aspeed,ast2700-scu.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * RGMII clock source tree
+ * HPLL -->|\
+ * | |---->| divider |---->RGMII 125M for MAC#0 & MAC#1
+ * APLL -->|/
+ */
+#define RGMII_DEFAULT_CLK_SRC SCU1_CLK_HPLL
+
+struct mac_delay_config {
+ u32 tx_delay_1000;
+ u32 rx_delay_1000;
+ u32 tx_delay_100;
+ u32 rx_delay_100;
+ u32 tx_delay_10;
+ u32 rx_delay_10;
+};
+
+typedef int (*ast2700_clk_init_fn)(struct udevice *dev);
+
+struct ast2700_clk_priv {
+ void __iomem *reg;
+ ast2700_clk_init_fn init;
+};
+
+static u32 ast2700_soc1_get_pll_rate(struct ast2700_scu1 *scu, int pll_idx)
+{
+ union ast2700_pll_reg pll_reg;
+ u32 mul = 1, div = 1;
+
+ switch (pll_idx) {
+ case SCU1_CLK_HPLL:
+ pll_reg.w = readl(&scu->hpll);
+ break;
+ case SCU1_CLK_APLL:
+ pll_reg.w = readl(&scu->apll);
+ break;
+ case SCU1_CLK_DPLL:
+ pll_reg.w = readl(&scu->dpll);
+ break;
+ }
+
+ if (!pll_reg.b.bypass) {
+ mul = (pll_reg.b.m + 1) / (pll_reg.b.n + 1);
+ div = (pll_reg.b.p + 1);
+ }
+
+ return ((CLKIN_25M * mul) / div);
+}
+
+#define SCU_CLKSEL2_HCLK_DIV_MASK GENMASK(22, 20)
+#define SCU_CLKSEL2_HCLK_DIV_SHIFT 20
+
+static u32 ast2700_soc1_get_hclk_rate(struct ast2700_scu1 *scu)
+{
+ u32 rate = ast2700_soc1_get_pll_rate(scu, SCU1_CLK_HPLL);
+ u32 clk_sel2 = readl(&scu->clk_sel2);
+ u32 hclk_div = (clk_sel2 & SCU_CLKSEL2_HCLK_DIV_MASK) >>
+ SCU_CLKSEL2_HCLK_DIV_SHIFT;
+
+ if (!hclk_div)
+ hclk_div = 2;
+ else
+ hclk_div++;
+
+ return (rate / hclk_div);
+}
+
+#define SCU1_CLKSEL1_PCLK_DIV_MASK GENMASK(20, 18)
+#define SCU1_CLKSEL1_PCLK_DIV_SHIFT 18
+
+static u32 ast2700_soc1_get_pclk_rate(struct ast2700_scu1 *scu)
+{
+ u32 rate = ast2700_soc1_get_pll_rate(scu, SCU1_CLK_HPLL);
+
+ u32 clk_sel1 = readl(&scu->clk_sel1);
+ u32 pclk_div = (clk_sel1 & SCU1_CLKSEL1_PCLK_DIV_MASK) >>
+ SCU1_CLKSEL1_PCLK_DIV_SHIFT;
+
+ return (rate / ((pclk_div + 1) * 2));
+}
+
+#define SCU_UART_CLKGEN_N_MASK GENMASK(17, 8)
+#define SCU_UART_CLKGEN_N_SHIFT 8
+#define SCU_UART_CLKGEN_R_MASK GENMASK(7, 0)
+#define SCU_UART_CLKGEN_R_SHIFT 0
+
+static u32 ast2700_soc1_get_uart_uxclk_rate(struct ast2700_scu1 *scu)
+{
+ u32 uxclk_sel = readl(&scu->clk_sel2) & GENMASK(1, 0);
+ u32 uxclk_ctrl = readl(&scu->uxclk_ctrl);
+ u32 rate;
+
+ switch (uxclk_sel) {
+ case 0:
+ rate = ast2700_soc1_get_pll_rate(scu, SCU1_CLK_APLL) / 4;
+ break;
+ case 1:
+ rate = ast2700_soc1_get_pll_rate(scu, SCU1_CLK_APLL) / 2;
+ break;
+ case 2:
+ rate = ast2700_soc1_get_pll_rate(scu, SCU1_CLK_APLL);
+ break;
+ case 3:
+ rate = ast2700_soc1_get_pll_rate(scu, SCU1_CLK_HPLL);
+ break;
+ }
+
+ u32 n = (uxclk_ctrl & SCU_UART_CLKGEN_N_MASK) >>
+ SCU_UART_CLKGEN_N_SHIFT;
+ u32 r = (uxclk_ctrl & SCU_UART_CLKGEN_R_MASK) >>
+ SCU_UART_CLKGEN_R_SHIFT;
+
+ return ((rate * r) / (n * 2));
+}
+
+#define SCU_HUART_CLKGEN_N_MASK GENMASK(17, 8)
+#define SCU_HUART_CLKGEN_N_SHIFT 8
+#define SCU_HUART_CLKGEN_R_MASK GENMASK(7, 0)
+#define SCU_HUART_CLKGEN_R_SHIFT 0
+
+static u32 ast2700_soc1_get_uart_huxclk_rate(struct ast2700_scu1 *scu)
+{
+ u32 huxclk_sel = (readl(&scu->clk_sel2) & GENMASK(4, 3)) >> 3;
+ u32 huxclk_ctrl = readl(&scu->huxclk_ctrl);
+ u32 n = (huxclk_ctrl & SCU_HUART_CLKGEN_N_MASK) >>
+ SCU_HUART_CLKGEN_N_SHIFT;
+ u32 r = (huxclk_ctrl & SCU_HUART_CLKGEN_R_MASK) >>
+ SCU_HUART_CLKGEN_R_SHIFT;
+ u32 rate;
+
+ switch (huxclk_sel) {
+ case 0:
+ rate = ast2700_soc1_get_pll_rate(scu, SCU1_CLK_APLL) / 4;
+ break;
+ case 1:
+ rate = ast2700_soc1_get_pll_rate(scu, SCU1_CLK_APLL) / 2;
+ break;
+ case 2:
+ rate = ast2700_soc1_get_pll_rate(scu, SCU1_CLK_APLL);
+ break;
+ case 3:
+ rate = ast2700_soc1_get_pll_rate(scu, SCU1_CLK_HPLL);
+ break;
+ }
+
+ return ((rate * r) / (n * 2));
+}
+
+#define SCU_CLKSRC1_SDIO_DIV_MASK GENMASK(16, 14)
+#define SCU_CLKSRC1_SDIO_DIV_SHIFT 14
+#define SCU_CLKSRC1_SDIO_SEL BIT(13)
+const int ast2700_sd_div_tbl[] = {
+ 2, 2, 3, 4, 5, 6, 7, 8
+};
+
+static u32 ast2700_soc1_get_sdio_clk_rate(struct ast2700_scu1 *scu)
+{
+ u32 rate = 0;
+ u32 clk_sel1 = readl(&scu->clk_sel1);
+ u32 div = (clk_sel1 & SCU_CLKSRC1_SDIO_DIV_MASK) >>
+ SCU_CLKSRC1_SDIO_DIV_SHIFT;
+
+ if (clk_sel1 & SCU_CLKSRC1_SDIO_SEL)
+ rate = ast2700_soc1_get_pll_rate(scu, SCU1_CLK_APLL);
+ else
+ rate = ast2700_soc1_get_pll_rate(scu, SCU1_CLK_HPLL);
+
+ if (!div)
+ div = 1;
+
+ div++;
+
+ return (rate / div);
+}
+
+static void ast2700_init_sdclk(struct ast2700_scu1 *scu)
+{
+ u32 src_clk = ast2700_soc1_get_pll_rate(scu, SCU1_CLK_HPLL);
+ u32 reg_280;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ if (src_clk / ast2700_sd_div_tbl[i] <= 125000000)
+ break;
+ }
+
+ reg_280 = readl(&scu->clk_sel1);
+ reg_280 &= ~(SCU_CLKSRC1_SDIO_DIV_MASK | SCU_CLKSRC1_SDIO_SEL);
+ reg_280 |= i << SCU_CLKSRC1_SDIO_DIV_SHIFT;
+ writel(reg_280, &scu->clk_sel1);
+}
+
+static u32
+ast2700_soc1_get_uart_clk_rate(struct ast2700_scu1 *scu, int uart_idx)
+{
+ u32 rate = 0;
+
+ if (readl(&scu->clk_sel1) & BIT(uart_idx))
+ rate = ast2700_soc1_get_uart_huxclk_rate(scu);
+ else
+ rate = ast2700_soc1_get_uart_uxclk_rate(scu);
+
+ return rate;
+}
+
+static ulong ast2700_soc1_clk_get_rate(struct clk *clk)
+{
+ struct ast2700_clk_priv *priv = dev_get_priv(clk->dev);
+ struct ast2700_scu1 *scu = (struct ast2700_scu1 *)priv->reg;
+ ulong rate = 0;
+
+ switch (clk->id) {
+ case SCU1_CLK_HPLL:
+ case SCU1_CLK_APLL:
+ case SCU1_CLK_DPLL:
+ rate = ast2700_soc1_get_pll_rate(scu, clk->id);
+ break;
+ case SCU1_CLK_AHB:
+ rate = ast2700_soc1_get_hclk_rate(scu);
+ break;
+ case SCU1_CLK_APB:
+ rate = ast2700_soc1_get_pclk_rate(scu);
+ break;
+ case SCU1_CLK_GATE_UART0CLK:
+ rate = ast2700_soc1_get_uart_clk_rate(scu, 0);
+ break;
+ case SCU1_CLK_GATE_UART1CLK:
+ rate = ast2700_soc1_get_uart_clk_rate(scu, 1);
+ break;
+ case SCU1_CLK_GATE_UART2CLK:
+ rate = ast2700_soc1_get_uart_clk_rate(scu, 2);
+ break;
+ case SCU1_CLK_GATE_UART3CLK:
+ rate = ast2700_soc1_get_uart_clk_rate(scu, 3);
+ break;
+ case SCU1_CLK_GATE_UART5CLK:
+ rate = ast2700_soc1_get_uart_clk_rate(scu, 5);
+ break;
+ case SCU1_CLK_GATE_UART6CLK:
+ rate = ast2700_soc1_get_uart_clk_rate(scu, 6);
+ break;
+ case SCU1_CLK_GATE_UART7CLK:
+ rate = ast2700_soc1_get_uart_clk_rate(scu, 7);
+ break;
+ case SCU1_CLK_GATE_UART8CLK:
+ rate = ast2700_soc1_get_uart_clk_rate(scu, 8);
+ break;
+ case SCU1_CLK_GATE_UART9CLK:
+ rate = ast2700_soc1_get_uart_clk_rate(scu, 9);
+ break;
+ case SCU1_CLK_GATE_UART10CLK:
+ rate = ast2700_soc1_get_uart_clk_rate(scu, 10);
+ break;
+ case SCU1_CLK_GATE_UART11CLK:
+ rate = ast2700_soc1_get_uart_clk_rate(scu, 11);
+ break;
+ case SCU1_CLK_GATE_UART12CLK:
+ rate = ast2700_soc1_get_uart_clk_rate(scu, 12);
+ break;
+ case SCU1_CLK_GATE_SDCLK:
+ rate = ast2700_soc1_get_sdio_clk_rate(scu);
+ break;
+ case SCU1_CLK_UXCLK:
+ rate = ast2700_soc1_get_uart_uxclk_rate(scu);
+ break;
+ case SCU1_CLK_HUXCLK:
+ rate = ast2700_soc1_get_uart_huxclk_rate(scu);
+ break;
+ default:
+ debug("%s: unknown clk %ld\n", __func__, clk->id);
+ return -ENOENT;
+ }
+
+ return rate;
+}
+
+static int ast2700_soc1_clk_enable(struct clk *clk)
+{
+ struct ast2700_clk_priv *priv = dev_get_priv(clk->dev);
+ struct ast2700_scu1 *scu = (struct ast2700_scu1 *)priv->reg;
+ u32 clkgate_bit;
+
+ if (clk->id >= 32)
+ clkgate_bit = BIT(clk->id - 32);
+ else
+ clkgate_bit = BIT(clk->id);
+
+ writel(clkgate_bit, &scu->clkgate_clr1);
+
+ return 0;
+}
+
+static const struct clk_ops ast2700_soc1_clk_ops = {
+ .get_rate = ast2700_soc1_clk_get_rate,
+ .enable = ast2700_soc1_clk_enable,
+};
+
+#define SCU_HW_REVISION_ID GENMASK(23, 16)
+#define SCU_CPUCLK_MASK GENMASK(4, 2)
+#define SCU_CPUCLK_SHIFT 2
+static u32 ast2700_soc0_get_hpll_rate(struct ast2700_scu0 *scu)
+{
+ u32 chip_id1 = readl(&scu->chip_id1);
+ u32 hwstrap1 = readl(&scu->hwstrap1);
+ union ast2700_pll_reg pll_reg;
+ u32 mul = 1, div = 1;
+ u32 rate;
+
+ pll_reg.w = readl(&scu->hpll);
+
+ if ((chip_id1 & SCU_HW_REVISION_ID) && (hwstrap1 & BIT(3))) {
+ switch ((hwstrap1 & GENMASK(4, 2)) >> 2) {
+ case 2:
+ rate = 1800000000;
+ break;
+ case 3:
+ rate = 1700000000;
+ break;
+ case 6:
+ rate = 1200000000;
+ break;
+ case 7:
+ rate = 800000000;
+ break;
+ default:
+ rate = 1600000000;
+ }
+ } else if (hwstrap1 & GENMASK(3, 2)) {
+ switch ((hwstrap1 & GENMASK(3, 2)) >> 2) {
+ case 1U:
+ rate = 1900000000;
+ break;
+ case 2U:
+ rate = 1800000000;
+ break;
+ case 3U:
+ rate = 1700000000;
+ break;
+ default:
+ rate = 1600000000;
+ break;
+ }
+ } else {
+ if (pll_reg.b.bypass == 0U) {
+ /* F = 25Mhz * [(M + 2) / 2 * (n + 1)] / (p + 1) */
+ mul = (pll_reg.b.m + 1) / ((pll_reg.b.n + 1) * 2);
+ div = (pll_reg.b.p + 1);
+ }
+ rate = ((CLKIN_25M * mul) / div);
+ }
+
+ return rate;
+}
+
+static u32 ast2700_soc0_get_pll_rate(struct ast2700_scu0 *scu, int pll_idx)
+{
+ union ast2700_pll_reg pll_reg;
+ u32 mul = 1, div = 1;
+ u32 rate;
+
+ switch (pll_idx) {
+ case SCU0_CLK_DPLL:
+ pll_reg.w = readl(&scu->dpll);
+ break;
+ case SCU0_CLK_MPLL:
+ pll_reg.w = readl(&scu->mpll);
+ break;
+ default:
+ pr_err("%s: invalid PSP clock source (%d)\n", __func__, pll_idx);
+ return 0;
+ }
+
+ if (pll_reg.b.bypass == 0U) {
+ if (pll_idx == SCU0_CLK_MPLL) {
+ /* F = 25Mhz * [M / (n + 1)] / (p + 1) */
+ mul = (pll_reg.b.m) / ((pll_reg.b.n + 1));
+ div = (pll_reg.b.p + 1);
+ } else {
+ /* F = 25Mhz * [(M + 2) / 2 * (n + 1)] / (p + 1) */
+ mul = (pll_reg.b.m + 1) / ((pll_reg.b.n + 1) * 2);
+ div = (pll_reg.b.p + 1);
+ }
+ }
+
+ rate = ((CLKIN_25M * mul) / div);
+
+ return rate;
+}
+
+/*
+ * AST2700A1
+ * SCU010[4:2]:
+ * 000: CPUCLK=MPLL=1.6GHz (MPLL default setting with SCU310, SCU314)
+ * 001: CPUCLK=HPLL=2.0GHz (HPLL default setting with SCU300, SCU304)
+ * 010: CPUCLK=HPLL=1.8GHz (HPLL frequency is constance and is not controlled by SCU300, SCU304)
+ * 011: CPUCLK=HPLL=1.7GHz (HPLL frequency is constance and is not controlled by SCU300, SCU304)
+ * 100: CPUCLK=MPLL/2=800MHz (MPLL default setting with SCU310, SCU314)
+ * 101: CPUCLK=HPLL/2=1.0GHz (HPLL default setting with SCU300, SCU304)
+ * 110: CPUCLK=HPLL=1.2GHz (HPLL frequency is constance and is not controlled by SCU300, SCU304)
+ * 111: CPUCLK=HPLL=800MHz (HPLL frequency is constance and is not controlled by SCU300, SCU304)
+ */
+
+static u32 ast2700_soc0_get_pspclk_rate(struct ast2700_scu0 *scu)
+{
+ u32 chip_id1 = readl(&scu->chip_id1);
+ u32 hwstrap1 = readl(&scu->hwstrap1);
+ u32 rate;
+ int cpuclk_set;
+
+ if (chip_id1 & SCU_HW_REVISION_ID) {
+ cpuclk_set = (hwstrap1 & SCU_CPUCLK_MASK) >> SCU_CPUCLK_SHIFT;
+ switch (cpuclk_set) {
+ case 0:
+ rate = ast2700_soc0_get_pll_rate(scu, SCU0_CLK_MPLL);
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 6:
+ case 7:
+ rate = ast2700_soc0_get_hpll_rate(scu);
+ break;
+ case 4:
+ rate = ast2700_soc0_get_pll_rate(scu, SCU0_CLK_MPLL) / 2;
+ break;
+ case 5:
+ rate = ast2700_soc0_get_hpll_rate(scu) / 2;
+ break;
+ default:
+ rate = ast2700_soc0_get_hpll_rate(scu);
+ break;
+ }
+ } else {
+ if (hwstrap1 & BIT(4))
+ rate = ast2700_soc0_get_hpll_rate(scu);
+ else
+ rate = ast2700_soc0_get_pll_rate(scu, SCU0_CLK_MPLL);
+ }
+ return rate;
+}
+
+static u32 ast2700_soc0_get_axi0clk_rate(struct ast2700_scu0 *scu)
+{
+ return ast2700_soc0_get_pspclk_rate(scu) / 2;
+}
+
+#define SCU_AHB_DIV_MASK GENMASK(6, 5)
+#define SCU_AHB_DIV_SHIFT 5
+static u32 hclk_ast2700a1_div_table[] = {
+ 6, 5, 4, 7,
+};
+
+static u32 ast2700_soc0_get_hclk_rate(struct ast2700_scu0 *scu)
+{
+ u32 hwstrap1 = readl(&scu->hwstrap1);
+ u32 chip_id1 = readl(&scu->chip_id1);
+ u32 src_clk;
+ int div;
+
+ if (chip_id1 & SCU_HW_REVISION_ID) {
+ if (hwstrap1 & BIT(7))
+ src_clk = ast2700_soc0_get_pll_rate(scu, SCU0_CLK_MPLL);
+ else
+ src_clk = ast2700_soc0_get_hpll_rate(scu);
+
+ div = (hwstrap1 & SCU_AHB_DIV_MASK) >> SCU_AHB_DIV_SHIFT;
+ div = hclk_ast2700a1_div_table[div];
+ } else {
+ if (hwstrap1 & BIT(7))
+ src_clk = ast2700_soc0_get_hpll_rate(scu);
+ else
+ src_clk = ast2700_soc0_get_pll_rate(scu, SCU0_CLK_MPLL);
+
+ div = (hwstrap1 & SCU_AHB_DIV_MASK) >> SCU_AHB_DIV_SHIFT;
+
+ if (!div)
+ div = 4;
+ else
+ div = (div + 1) * 2;
+ }
+ return (src_clk / div);
+}
+
+static u32 ast2700_soc0_get_axi1clk_rate(struct ast2700_scu0 *scu)
+{
+ if (readl(&scu->chip_id1) & SCU_HW_REVISION_ID)
+ return ast2700_soc0_get_pll_rate(scu, SCU0_CLK_MPLL) / 4;
+ else
+ return ast2700_soc0_get_hclk_rate(scu);
+}
+
+#define SCU0_CLKSEL1_PCLK_DIV_MASK GENMASK(25, 23)
+#define SCU0_CLKSEL1_PCLK_DIV_SHIFT 23
+
+static u32 ast2700_soc0_get_pclk_rate(struct ast2700_scu0 *scu)
+{
+ u32 rate = ast2700_soc0_get_axi0clk_rate(scu);
+ u32 clksel1 = readl(&scu->clk_sel1);
+ int div;
+
+ div = (clksel1 & SCU0_CLKSEL1_PCLK_DIV_MASK) >>
+ SCU0_CLKSEL1_PCLK_DIV_SHIFT;
+
+ return (rate / ((div + 1) * 2));
+}
+
+#define SCU_CLKSEL1_MPHYCLK_SEL_MASK GENMASK(19, 18)
+#define SCU_CLKSEL1_MPHYCLK_SEL_SHIFT 18
+#define SCU_CLKSEL1_MPHYCLK_DIV_MASK GENMASK(7, 0)
+static u32 ast2700_soc0_get_mphyclk_rate(struct ast2700_scu0 *scu)
+{
+ int div = readl(&scu->mphyclk_para) & SCU_CLKSEL1_MPHYCLK_DIV_MASK;
+ u32 chip_id1 = readl(&scu->chip_id1);
+ u32 clk_sel2;
+ int clk_sel;
+ u32 rate = 0;
+
+ if (chip_id1 & SCU_HW_REVISION_ID) {
+ clk_sel2 = readl(&scu->clk_sel2);
+ clk_sel = (clk_sel2 & SCU_CLKSEL1_MPHYCLK_SEL_MASK)
+ >> SCU_CLKSEL1_MPHYCLK_SEL_SHIFT;
+ switch (clk_sel) {
+ case 0:
+ rate = ast2700_soc0_get_pll_rate(scu, SCU0_CLK_MPLL);
+ break;
+ case 1:
+ rate = ast2700_soc0_get_hpll_rate(scu);
+ break;
+ case 2:
+ rate = ast2700_soc0_get_pll_rate(scu, SCU0_CLK_DPLL);
+ break;
+ case 3:
+ rate = 26000000;
+ break;
+ }
+ } else {
+ rate = ast2700_soc0_get_hpll_rate(scu);
+ }
+
+ return (rate / (div + 1));
+}
+
+static void ast2700_mphy_clk_init(struct ast2700_scu0 *scu)
+{
+ u32 clksrc1, rate = 0;
+ int i;
+
+ /* set mphy clk */
+ if (readl(&scu->chip_id1) & SCU_HW_REVISION_ID) {
+ clksrc1 = (readl(&scu->clk_sel2) & SCU_CLKSEL1_MPHYCLK_SEL_MASK)
+ >> SCU_CLKSEL1_MPHYCLK_SEL_SHIFT;
+ switch (clksrc1) {
+ case 0:
+ rate = ast2700_soc0_get_pll_rate(scu, SCU0_CLK_MPLL);
+ break;
+ case 1:
+ rate = ast2700_soc0_get_hpll_rate(scu);
+ break;
+ case 2:
+ rate = ast2700_soc0_get_pll_rate(scu, SCU0_CLK_DPLL);
+ break;
+ case 3:
+ rate = 26000000;
+ break;
+ }
+ } else {
+ rate = ast2700_soc0_get_hpll_rate(scu);
+ }
+
+ for (i = 1; i < 256; i++) {
+ if ((rate / i) <= 26000000)
+ break;
+ }
+
+ /* register defined the value plus 1 is divider*/
+ i--;
+ writel(i, &scu->mphyclk_para);
+}
+
+#define SCU_CLKSRC1_EMMC_DIV_MASK GENMASK(14, 12)
+#define SCU_CLKSRC1_EMMC_DIV_SHIFT 12
+#define SCU_CLKSRC1_EMMC_SEL BIT(11)
+static u32 ast2700_soc0_get_emmcclk_rate(struct ast2700_scu0 *scu)
+{
+ u32 clksel1 = readl(&scu->clk_sel1);
+ u32 rate;
+ int div;
+
+ div = (clksel1 & SCU_CLKSRC1_EMMC_DIV_MASK) >> SCU_CLKSRC1_EMMC_DIV_SHIFT;
+
+ if (clksel1 & SCU_CLKSRC1_EMMC_SEL)
+ rate = ast2700_soc0_get_hpll_rate(scu) / 4;
+ else
+ rate = ast2700_soc0_get_pll_rate(scu, SCU0_CLK_MPLL) / 4;
+
+ return (rate / ((div + 1) * 2));
+}
+
+static void ast2700_emmc_init(struct ast2700_scu0 *scu)
+{
+ u32 clksrc1, rate, div;
+ int i;
+
+ /* set clk/cmd driving */
+ writel(2, &scu->gpio18d0_ioctrl); /* clk driving */
+ writel(1, &scu->gpio18d1_ioctrl); /* cmd driving */
+ writel(1, &scu->gpio18d2_ioctrl); /* data0 driving */
+ writel(1, &scu->gpio18d3_ioctrl); /* data1 driving */
+ writel(1, &scu->gpio18d4_ioctrl); /* data2 driving */
+ writel(1, &scu->gpio18d5_ioctrl); /* data2 driving */
+
+ /* emmc clk: set clk src mpll/4:400Mhz */
+ clksrc1 = readl(&scu->clk_sel1);
+ rate = ast2700_soc0_get_pll_rate(scu, SCU0_CLK_MPLL) / 4;
+ for (i = 0; i < 8; i++) {
+ div = (i + 1) * 2;
+ if ((rate / div) <= 200000000)
+ break;
+ }
+
+ clksrc1 &= ~(SCU_CLKSRC1_EMMC_DIV_MASK | SCU_CLKSRC1_EMMC_SEL);
+ clksrc1 |= (i << SCU_CLKSRC1_EMMC_DIV_SHIFT);
+ writel(clksrc1, &scu->clk_sel1);
+}
+
+static void ast2700_vga_clk_init(struct ast2700_scu0 *scu)
+{
+ if ((readl(&scu->chip_id1) & SCU_HW_REVISION_ID) == 0)
+ return;
+
+ // Use d0clk/d1clk which generated from hpll for vga0/1 after A0
+ // Use CRT1clk as soc display source
+ setbits_le32(&scu->clk_sel3, BIT(14) | BIT(13) | BIT(12));
+}
+
+static u32 ast2700_soc0_get_uartclk_rate(struct ast2700_scu0 *scu)
+{
+ u32 clksel2 = readl(&scu->clk_sel2);
+ u32 div = 1;
+ u32 rate;
+
+ if (clksel2 & BIT(15))
+ rate = 192000000;
+ else
+ rate = 24000000;
+
+ if (clksel2 & BIT(30))
+ div = 13;
+ return (rate / div);
+}
+
+static ulong ast2700_soc0_clk_get_rate(struct clk *clk)
+{
+ struct ast2700_clk_priv *priv = dev_get_priv(clk->dev);
+ ulong rate = 0;
+
+ switch (clk->id) {
+ case SCU0_CLK_PSP:
+ rate = ast2700_soc0_get_pspclk_rate((struct ast2700_scu0 *)priv->reg);
+ break;
+ case SCU0_CLK_HPLL:
+ rate = ast2700_soc0_get_hpll_rate((struct ast2700_scu0 *)priv->reg);
+ break;
+ case SCU0_CLK_DPLL:
+ case SCU0_CLK_MPLL:
+ rate = ast2700_soc0_get_pll_rate((struct ast2700_scu0 *)priv->reg, clk->id);
+ break;
+ case SCU0_CLK_AXI0:
+ rate = ast2700_soc0_get_axi0clk_rate((struct ast2700_scu0 *)priv->reg);
+ break;
+ case SCU0_CLK_AXI1:
+ rate = ast2700_soc0_get_axi1clk_rate((struct ast2700_scu0 *)priv->reg);
+ break;
+ case SCU0_CLK_AHB:
+ rate = ast2700_soc0_get_hclk_rate((struct ast2700_scu0 *)priv->reg);
+ break;
+ case SCU0_CLK_APB:
+ rate = ast2700_soc0_get_pclk_rate((struct ast2700_scu0 *)priv->reg);
+ break;
+ case SCU0_CLK_GATE_EMMCCLK:
+ rate = ast2700_soc0_get_emmcclk_rate((struct ast2700_scu0 *)priv->reg);
+ break;
+ case SCU0_CLK_GATE_UART4CLK:
+ rate = ast2700_soc0_get_uartclk_rate((struct ast2700_scu0 *)priv->reg);
+ break;
+ case SCU0_CLK_MPHY:
+ rate = ast2700_soc0_get_mphyclk_rate((struct ast2700_scu0 *)priv->reg);
+ break;
+ default:
+ debug("%s: unknown clk %ld\n", __func__, clk->id);
+ return -ENOENT;
+ }
+
+ return rate;
+}
+
+static int ast2700_soc0_clk_enable(struct clk *clk)
+{
+ struct ast2700_clk_priv *priv = dev_get_priv(clk->dev);
+ struct ast2700_scu0 *scu = (struct ast2700_scu0 *)priv->reg;
+ u32 clkgate_bit = BIT(clk->id);
+
+ writel(clkgate_bit, &scu->clkgate_clr);
+
+ return 0;
+}
+
+static const struct clk_ops ast2700_soc0_clk_ops = {
+ .get_rate = ast2700_soc0_clk_get_rate,
+ .enable = ast2700_soc0_clk_enable,
+};
+
+static void ast2700_init_mac_clk(struct ast2700_scu1 *scu)
+{
+ u32 src_clk = ast2700_soc1_get_pll_rate(scu, SCU1_CLK_HPLL);
+ u32 reg_280;
+ u8 div_idx;
+
+ /* The MAC source clock selects HPLL only, and the default clock
+ * setting is 200 Mhz.
+ * Calculate the corresponding divider:
+ * 1: div 2
+ * 2: div 3
+ * ...
+ * 7: div 8
+ */
+ for (div_idx = 1; div_idx <= 7; div_idx++)
+ if (DIV_ROUND_UP(src_clk, div_idx + 1) == 200000000)
+ break;
+
+ if (div_idx == 8) {
+ pr_err("MAC clock cannot divide to 200 MHz\n");
+ return;
+ }
+
+ /* set HPLL clock divider */
+ reg_280 = readl(&scu->clk_sel1);
+ reg_280 &= ~GENMASK(31, 29);
+ reg_280 |= div_idx << 29;
+ writel(reg_280, &scu->clk_sel1);
+}
+
+static void ast2700_init_rgmii_clk(struct ast2700_scu1 *scu)
+{
+ u32 reg_284 = readl(&scu->clk_sel2);
+ u32 src_clk = ast2700_soc1_get_pll_rate(scu, RGMII_DEFAULT_CLK_SRC);
+
+ if (RGMII_DEFAULT_CLK_SRC == SCU1_CLK_HPLL) {
+ u32 reg_280;
+ u8 div_idx;
+
+ /* Calculate the corresponding divider:
+ * 1: div 4
+ * 2: div 6
+ * ...
+ * 7: div 16
+ */
+ for (div_idx = 1; div_idx <= 7; div_idx++) {
+ u8 div = 4 + 2 * (div_idx - 1);
+
+ if (DIV_ROUND_UP(src_clk, div) == 125000000)
+ break;
+ }
+ if (div_idx == 8) {
+ pr_err("RGMII using HPLL cannot divide to 125 MHz\n");
+ return;
+ }
+
+ /* set HPLL clock divider */
+ reg_280 = readl(&scu->clk_sel1);
+ reg_280 &= ~GENMASK(27, 25);
+ reg_280 |= div_idx << 25;
+ writel(reg_280, &scu->clk_sel1);
+
+ /* select HPLL clock source */
+ reg_284 &= ~BIT(18);
+ } else {
+ /* APLL clock divider is fixed to 8 */
+ if (DIV_ROUND_UP(src_clk, 8) != 125000000) {
+ pr_err("RGMII using APLL cannot divide to 125 MHz\n");
+ return;
+ }
+
+ /* select APLL clock source */
+ reg_284 |= BIT(18);
+ }
+
+ writel(reg_284, &scu->clk_sel2);
+}
+
+static void ast2700_init_rmii_clk(struct ast2700_scu1 *scu)
+{
+ u32 src_clk = ast2700_soc1_get_pll_rate(scu, SCU1_CLK_HPLL);
+ u32 reg_280;
+ u8 div_idx;
+
+ /* The RMII source clock selects HPLL only.
+ * Calculate the corresponding divider:
+ * 1: div 8
+ * 2: div 12
+ * ...
+ * 7: div 32
+ */
+ for (div_idx = 1; div_idx <= 7; div_idx++) {
+ u8 div = 8 + 4 * (div_idx - 1);
+
+ if (DIV_ROUND_UP(src_clk, div) == 50000000)
+ break;
+ }
+ if (div_idx == 8) {
+ pr_err("RMII using HPLL cannot divide to 50 MHz\n");
+ return;
+ }
+
+ /* set RMII clock divider */
+ reg_280 = readl(&scu->clk_sel1);
+ reg_280 &= ~GENMASK(23, 21);
+ reg_280 |= div_idx << 21;
+ writel(reg_280, &scu->clk_sel1);
+}
+
+static void ast2700_init_spi(struct ast2700_scu1 *scu)
+{
+ writel(readl(&scu->io_driving8) | 0x0000aaaa, &scu->io_driving8); /* fwspi driving */
+ writel(readl(&scu->io_driving3) | 0x00000aaa, &scu->io_driving3); /* spi0 driving */
+ writel(readl(&scu->io_driving3) | 0x0aaa0000, &scu->io_driving3); /* spi1 driving */
+ writel(readl(&scu->io_driving4) | 0x00002aaa, &scu->io_driving4); /* spi2 driving */
+}
+
+#define SCU1_CLK_I3C_DIV_MASK GENMASK(25, 23)
+#define SCU1_CLK_I3C_DIV(n) ((n) - 1)
+static void ast2700_init_i3c_clk(struct ast2700_scu1 *scu)
+{
+ u32 reg_284;
+
+ /* I3C 250MHz = HPLL/4 */
+ reg_284 = readl(&scu->clk_sel2);
+ reg_284 &= ~SCU1_CLK_I3C_DIV_MASK;
+ reg_284 |= FIELD_PREP(SCU1_CLK_I3C_DIV_MASK, SCU1_CLK_I3C_DIV(4));
+ writel(reg_284, &scu->clk_sel2);
+}
+
+static int ast2700_clk1_init(struct udevice *dev)
+{
+ struct ast2700_clk_priv *priv = dev_get_priv(dev);
+ struct ast2700_scu1 *scu = (struct ast2700_scu1 *)priv->reg;
+
+ ast2700_init_spi(scu);
+ ast2700_init_mac_clk(scu);
+ ast2700_init_rgmii_clk(scu);
+ ast2700_init_rmii_clk(scu);
+ ast2700_init_sdclk(scu);
+ ast2700_init_i3c_clk(scu);
+
+ return 0;
+}
+
+static int ast2700_clk0_init(struct udevice *dev)
+{
+ struct ast2700_clk_priv *priv = dev_get_priv(dev);
+ struct ast2700_scu0 *scu = (struct ast2700_scu0 *)priv->reg;
+
+ ast2700_emmc_init(scu);
+ ast2700_mphy_clk_init(scu);
+ ast2700_vga_clk_init(scu);
+
+ return 0;
+}
+
+static int ast2700_clk_probe(struct udevice *dev)
+{
+ struct ast2700_clk_priv *priv = dev_get_priv(dev);
+
+ priv->init = (ast2700_clk_init_fn)dev_get_driver_data(dev);
+ priv->reg = (void __iomem *)dev_read_addr_ptr(dev);
+
+ if (priv->init)
+ return priv->init(dev);
+
+ return 0;
+}
+
+static int ast2700_clk_bind(struct udevice *dev)
+{
+ struct udevice *sysreset_dev, *rst_dev;
+ int ret;
+
+ /* The system reset driver does not have a device node, so bind it here */
+ ret = device_bind_driver(gd->dm_root, "ast_sysreset", "reset", &sysreset_dev);
+ if (ret)
+ debug("Warning: No sysreset driver: ret = %d\n", ret);
+
+ /* Bind the per-SCU reset controller to the same ofnode so that
+ * <&syscon0/1 RESET_X> phandle references resolve to a UCLASS_RESET
+ * device. This pairs with the airoha-style binding pattern.
+ */
+ if (CONFIG_IS_ENABLED(RESET_AST2700)) {
+ ret = device_bind_driver_to_node(dev, "ast2700_reset", "reset",
+ dev_ofnode(dev), &rst_dev);
+ if (ret)
+ debug("Warning: failed to bind reset controller: ret = %d\n", ret);
+ }
+
+ return 0;
+}
+
+static const struct udevice_id ast2700_soc1_clk_ids[] = {
+ { .compatible = "aspeed,ast2700-scu1", .data = (ulong)&ast2700_clk1_init },
+ { },
+};
+
+U_BOOT_DRIVER(aspeed_ast2700_soc1_clk) = {
+ .name = "aspeed_ast2700_scu1",
+ .id = UCLASS_CLK,
+ .of_match = ast2700_soc1_clk_ids,
+ .priv_auto = sizeof(struct ast2700_clk_priv),
+ .ops = &ast2700_soc1_clk_ops,
+ .probe = ast2700_clk_probe,
+ .bind = ast2700_clk_bind,
+};
+
+static const struct udevice_id ast2700_soc0_clk_ids[] = {
+ { .compatible = "aspeed,ast2700-scu0", .data = (ulong)&ast2700_clk0_init },
+ { },
+};
+
+U_BOOT_DRIVER(aspeed_ast2700_soc0_clk) = {
+ .name = "aspeed_ast2700_scu0",
+ .id = UCLASS_CLK,
+ .of_match = ast2700_soc0_clk_ids,
+ .priv_auto = sizeof(struct ast2700_clk_priv),
+ .ops = &ast2700_soc0_clk_ops,
+ .probe = ast2700_clk_probe,
+ .bind = ast2700_clk_bind,
+};
diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c
index 2251e2846fa..0d0e39db57e 100644
--- a/drivers/clk/at91/sam9x60.c
+++ b/drivers/clk/at91/sam9x60.c
@@ -426,7 +426,7 @@ static const struct pmc_clk_setup sam9x60_clk_setup[] = {
static int sam9x60_clk_probe(struct udevice *dev)
{
- void __iomem *base = (void *)devfdt_get_addr_ptr(dev);
+ void __iomem *base = dev_read_addr_ptr(dev);
unsigned int *clkmuxallocs[64], *muxallocs[64];
const char *p[10];
unsigned int cm[10], m[10], *tmpclkmux, *tmpmux;
diff --git a/drivers/clk/at91/sam9x7.c b/drivers/clk/at91/sam9x7.c
index 9ea253e6ff8..93f899b6617 100644
--- a/drivers/clk/at91/sam9x7.c
+++ b/drivers/clk/at91/sam9x7.c
@@ -817,7 +817,7 @@ static const struct {
static int sam9x7_clk_probe(struct udevice *dev)
{
- void __iomem *base = (void *)devfdt_get_addr_ptr(dev);
+ void __iomem *base = dev_read_addr_ptr(dev);
unsigned int *clkmuxallocs[64], *muxallocs[64];
const char *p[10];
unsigned int cm[10], m[10], *tmpclkmux, *tmpmux;
diff --git a/drivers/clk/at91/sama7d65.c b/drivers/clk/at91/sama7d65.c
index 9f0b394543b..0c17a8cf67b 100644
--- a/drivers/clk/at91/sama7d65.c
+++ b/drivers/clk/at91/sama7d65.c
@@ -1176,7 +1176,7 @@ static const struct pmc_clk_setup sama7d65_clk_setup[] = {
static int sama7d65_clk_probe(struct udevice *dev)
{
- void __iomem *base = (void *)devfdt_get_addr(dev);
+ void __iomem *base = dev_read_addr_ptr(dev);
unsigned int *clkmuxallocs[SAMA7D65_MAX_MUX_ALLOCS];
unsigned int *muxallocs[SAMA7D65_MAX_MUX_ALLOCS];
const char *p[12];
@@ -1185,8 +1185,8 @@ static int sama7d65_clk_probe(struct udevice *dev)
bool main_osc_bypass;
int ret, muxallocindex = 0, clkmuxallocindex = 0, i, j;
- if (IS_ERR(base))
- return PTR_ERR(base);
+ if (!base)
+ return -EINVAL;
memset(muxallocs, 0, ARRAY_SIZE(muxallocs));
memset(clkmuxallocs, 0, ARRAY_SIZE(clkmuxallocs));
diff --git a/drivers/clk/at91/sama7g5.c b/drivers/clk/at91/sama7g5.c
index f24d251857f..c436038aed2 100644
--- a/drivers/clk/at91/sama7g5.c
+++ b/drivers/clk/at91/sama7g5.c
@@ -1109,7 +1109,7 @@ static const struct pmc_clk_setup sama7g5_clk_setup[] = {
static int sama7g5_clk_probe(struct udevice *dev)
{
- void __iomem *base = devfdt_get_addr_ptr(dev);
+ void __iomem *base = dev_read_addr_ptr(dev);
unsigned int *clkmuxallocs[SAMA7G5_MAX_MUX_ALLOCS];
unsigned int *muxallocs[SAMA7G5_MAX_MUX_ALLOCS];
const char *p[10];
@@ -1118,8 +1118,8 @@ static int sama7g5_clk_probe(struct udevice *dev)
bool main_osc_bypass;
int ret, muxallocindex = 0, clkmuxallocindex = 0, i, j;
- if (IS_ERR(base))
- return PTR_ERR(base);
+ if (!base)
+ return -EINVAL;
memset(muxallocs, 0, sizeof(muxallocs));
memset(clkmuxallocs, 0, sizeof(clkmuxallocs));
diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c
index dcaffd360fd..410bc088248 100644
--- a/drivers/clk/at91/sckc.c
+++ b/drivers/clk/at91/sckc.c
@@ -124,12 +124,15 @@ U_BOOT_DRIVER(at91_sam9x60_td_slck) = {
static int at91_sam9x60_sckc_probe(struct udevice *dev)
{
struct sam9x60_sckc *sckc = dev_get_priv(dev);
- void __iomem *base = devfdt_get_addr_ptr(dev);
+ void __iomem *base = dev_read_addr_ptr(dev);
const char *slow_rc_osc, *slow_osc;
const char *parents[2];
struct clk *clk, c;
int ret;
+ if (!base)
+ return -EINVAL;
+
ret = clk_get_by_index(dev, 0, &c);
if (ret)
return ret;
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index e692b9c2167..d30786a9e6c 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -228,20 +228,30 @@ static struct clk *_register_divider(struct udevice *dev, const char *name,
return clk;
}
-struct clk *clk_register_divider(struct udevice *dev, const char *name,
+struct clk *clk_register_divider_table(struct udevice *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
- u8 clk_divider_flags)
+ u8 clk_divider_flags, const struct clk_div_table *table)
{
struct clk *clk;
clk = _register_divider(dev, name, parent_name, flags, reg, shift,
- width, clk_divider_flags, NULL);
+ width, clk_divider_flags, table);
if (IS_ERR(clk))
return ERR_CAST(clk);
return clk;
}
+struct clk *clk_register_divider(struct udevice *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_divider_flags)
+{
+ return clk_register_divider_table(dev, name, parent_name, flags, reg,
+ shift, width, clk_divider_flags,
+ NULL);
+}
+
U_BOOT_DRIVER(ccf_clk_divider) = {
.name = UBOOT_DM_CLK_CCF_DIVIDER,
.id = UCLASS_CLK,
diff --git a/drivers/clk/clk-hsdk-cgu.c b/drivers/clk/clk-hsdk-cgu.c
index 53655059279..dbc926a4391 100644
--- a/drivers/clk/clk-hsdk-cgu.c
+++ b/drivers/clk/clk-hsdk-cgu.c
@@ -753,11 +753,11 @@ static int hsdk_cgu_clk_probe(struct udevice *dev)
else
hsdk_clk->map = hsdk_4xd_clk_map;
- hsdk_clk->cgu_regs = devfdt_get_addr_index_ptr(dev, 0);
+ hsdk_clk->cgu_regs = dev_read_addr_index_ptr(dev, 0);
if (!hsdk_clk->cgu_regs)
return -EINVAL;
- hsdk_clk->creg_regs = devfdt_get_addr_index_ptr(dev, 1);
+ hsdk_clk->creg_regs = dev_read_addr_index_ptr(dev, 1);
if (!hsdk_clk->creg_regs)
return -EINVAL;
diff --git a/drivers/clk/clk-stub.c b/drivers/clk/clk-stub.c
index 117266ac778..4a6c71016da 100644
--- a/drivers/clk/clk-stub.c
+++ b/drivers/clk/clk-stub.c
@@ -49,11 +49,13 @@ static struct clk_ops stub_clk_ops = {
};
static const struct udevice_id stub_clk_ids[] = {
+ { .compatible = "qcom,qcs615-rpmh-clk" },
{ .compatible = "qcom,rpmcc" },
- { .compatible = "qcom,sdm670-rpmh-clk" },
- { .compatible = "qcom,sdm845-rpmh-clk" },
+ { .compatible = "qcom,sa8775p-rpmh-clk" },
{ .compatible = "qcom,sc7180-rpmh-clk" },
{ .compatible = "qcom,sc7280-rpmh-clk" },
+ { .compatible = "qcom,sdm670-rpmh-clk" },
+ { .compatible = "qcom,sdm845-rpmh-clk" },
{ .compatible = "qcom,sm6350-rpmh-clk" },
{ .compatible = "qcom,sm8150-rpmh-clk" },
{ .compatible = "qcom,sm8250-rpmh-clk" },
diff --git a/drivers/clk/clk_versal.c b/drivers/clk/clk_versal.c
index 78a2410ca21..2e0c382ef30 100644
--- a/drivers/clk/clk_versal.c
+++ b/drivers/clk/clk_versal.c
@@ -326,6 +326,7 @@ static int __versal_clock_get_parents(struct clock_parent *parents, u32 *data,
parent = &parents[i];
parent->id = data[i] & CLK_PARENTS_ID_MASK;
if (data[i] == DUMMY_PARENT) {
+ parent->id = 0;
strcpy(parent->name, "dummy_name");
parent->flag = 0;
} else {
diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index f2fd6ff8ca0..a825ed9988f 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -17,7 +17,7 @@ obj-$(CONFIG_$(PHASE_)CLK_IMX8MN) += clk-imx8mn.o clk-pll14xx.o \
clk-composite-8m.o
obj-$(CONFIG_$(PHASE_)CLK_IMX8MP) += clk-imx8mp.o clk-pll14xx.o \
clk-composite-8m.o
-obj-$(CONFIG_$(PHASE_)CLK_IMX8MQ) += clk-imx8mq.o clk-pll14xx.o \
+obj-$(CONFIG_$(PHASE_)CLK_IMX8MQ) += clk-imx8mq.o clk-frac-pll.o \
clk-composite-8m.o
obj-$(CONFIG_$(PHASE_)CLK_IMX93) += clk-imx93.o clk-fracn-gppll.o \
clk-gate-93.o clk-composite-93.o
diff --git a/drivers/clk/imx/clk-frac-pll.c b/drivers/clk/imx/clk-frac-pll.c
new file mode 100644
index 00000000000..057389d3069
--- /dev/null
+++ b/drivers/clk/imx/clk-frac-pll.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2026 NXP
+ *
+ */
+
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <dm/devres.h>
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <clk.h>
+#include <div64.h>
+#include <linux/printk.h>
+#include <linux/bitfield.h>
+
+#include "clk.h"
+
+#define PLL_CFG0 0x0
+#define PLL_CFG1 0x4
+
+#define PLL_LOCK_STATUS BIT(31)
+#define PLL_PD_MASK BIT(19)
+#define PLL_BYPASS_MASK BIT(14)
+#define PLL_NEWDIV_VAL BIT(12)
+#define PLL_NEWDIV_ACK BIT(11)
+#define PLL_FRAC_DIV_MASK GENMASK(30, 7)
+#define PLL_INT_DIV_MASK GENMASK(6, 0)
+#define PLL_OUTPUT_DIV_MASK GENMASK(4, 0)
+#define PLL_FRAC_DENOM 0x1000000
+
+#define PLL_FRAC_LOCK_TIMEOUT 10000
+#define PLL_FRAC_ACK_TIMEOUT 500000
+
+struct clk_frac_pll {
+ struct clk clk;
+ void __iomem *base;
+};
+
+#define to_clk_frac_pll(_clk) container_of(_clk, struct clk_frac_pll, clk)
+
+static int clk_wait_lock(struct clk_frac_pll *pll)
+{
+ u32 val;
+
+ return readl_poll_timeout(pll->base, val, val & PLL_LOCK_STATUS,
+ PLL_FRAC_LOCK_TIMEOUT);
+}
+
+static int clk_wait_ack(struct clk_frac_pll *pll)
+{
+ u32 val;
+
+ /* return directly if the pll is in powerdown or in bypass */
+ if (readl_relaxed(pll->base) & (PLL_PD_MASK | PLL_BYPASS_MASK))
+ return 0;
+
+ /* Wait for the pll's divfi and divff to be reloaded */
+ return readl_poll_timeout(pll->base, val, val & PLL_NEWDIV_ACK,
+ PLL_FRAC_ACK_TIMEOUT);
+}
+
+static int clk_pll_prepare(struct clk *clk)
+{
+ struct clk_frac_pll *pll = to_clk_frac_pll(clk);
+ u32 val;
+
+ val = readl_relaxed(pll->base + PLL_CFG0);
+ val &= ~PLL_PD_MASK;
+ writel_relaxed(val, pll->base + PLL_CFG0);
+
+ return clk_wait_lock(pll);
+}
+
+static int clk_pll_unprepare(struct clk *clk)
+{
+ struct clk_frac_pll *pll = to_clk_frac_pll(clk);
+ u32 val;
+
+ val = readl_relaxed(pll->base + PLL_CFG0);
+ val |= PLL_PD_MASK;
+ writel_relaxed(val, pll->base + PLL_CFG0);
+
+ return 0;
+}
+
+static unsigned long clk_pll_recalc_rate(struct clk *clk)
+{
+ struct clk_frac_pll *pll = to_clk_frac_pll(clk);
+ u32 val, divff, divfi, divq;
+ ulong parent_rate = clk_get_parent_rate(clk);
+ u64 temp64 = parent_rate;
+ u64 rate;
+
+ val = readl_relaxed(pll->base + PLL_CFG0);
+ divq = (FIELD_GET(PLL_OUTPUT_DIV_MASK, val) + 1) * 2;
+ val = readl_relaxed(pll->base + PLL_CFG1);
+ divff = FIELD_GET(PLL_FRAC_DIV_MASK, val);
+ divfi = FIELD_GET(PLL_INT_DIV_MASK, val);
+
+ temp64 *= 8;
+ temp64 *= divff;
+ do_div(temp64, PLL_FRAC_DENOM);
+ do_div(temp64, divq);
+
+ rate = parent_rate * 8 * (divfi + 1);
+ do_div(rate, divq);
+ rate += temp64;
+
+ return rate;
+}
+
+static ulong clk_pll_set_rate(struct clk *clk, unsigned long rate)
+{
+ struct clk_frac_pll *pll = to_clk_frac_pll(clk);
+ ulong parent_rate = clk_get_parent_rate(clk);
+ u32 val, divfi, divff;
+ u64 temp64;
+ int ret;
+
+ parent_rate *= 8;
+ rate *= 2;
+ divfi = rate / parent_rate;
+ temp64 = parent_rate * divfi;
+ temp64 = rate - temp64;
+ temp64 *= PLL_FRAC_DENOM;
+ do_div(temp64, parent_rate);
+ divff = temp64;
+
+ val = readl_relaxed(pll->base + PLL_CFG1);
+ val &= ~(PLL_FRAC_DIV_MASK | PLL_INT_DIV_MASK);
+ val |= (divff << 7) | (divfi - 1);
+ writel_relaxed(val, pll->base + PLL_CFG1);
+
+ val = readl_relaxed(pll->base + PLL_CFG0);
+ val &= ~0x1f;
+ writel_relaxed(val, pll->base + PLL_CFG0);
+
+ /* Set the NEV_DIV_VAL to reload the DIVFI and DIVFF */
+ val = readl_relaxed(pll->base + PLL_CFG0);
+ val |= PLL_NEWDIV_VAL;
+ writel_relaxed(val, pll->base + PLL_CFG0);
+
+ ret = clk_wait_ack(pll);
+
+ /* clear the NEV_DIV_VAL */
+ val = readl_relaxed(pll->base + PLL_CFG0);
+ val &= ~PLL_NEWDIV_VAL;
+ writel_relaxed(val, pll->base + PLL_CFG0);
+
+ if (ret)
+ return ret;
+
+ return clk_pll_recalc_rate(clk);
+}
+
+static const struct clk_ops clk_frac_pll_ops = {
+ .enable = clk_pll_prepare,
+ .disable = clk_pll_unprepare,
+ .set_rate = clk_pll_set_rate,
+ .get_rate = clk_pll_recalc_rate,
+};
+
+struct clk *imx_clk_frac_pll(const char *name, const char *parent_name,
+ void __iomem *base)
+{
+ struct clk_frac_pll *pll;
+ struct clk *clk;
+ int ret;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ pll->base = base;
+ clk = &pll->clk;
+
+ ret = clk_register(clk, "imx_clk_frac_pll", name, parent_name);
+ if (ret) {
+ pr_err("%s: failed to register pll %s %d\n",
+ __func__, name, ret);
+ kfree(pll);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+U_BOOT_DRIVER(clk_frac_pll) = {
+ .name = "imx_clk_frac_pll",
+ .id = UCLASS_CLK,
+ .ops = &clk_frac_pll_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c
index f57ac79f8ca..846b8011f5c 100644
--- a/drivers/clk/imx/clk-imx6q.c
+++ b/drivers/clk/imx/clk-imx6q.c
@@ -9,6 +9,7 @@
#include <log.h>
#include <asm/arch/clock.h>
#include <asm/arch/imx-regs.h>
+#include <dm/of_access.h>
#include <dt-bindings/clock/imx6qdl-clock.h>
#include "clk.h"
@@ -46,6 +47,33 @@ static struct clk_ops imx6q_clk_ops = {
.disable = ccf_clk_disable,
};
+static const char *const pll_bypass_src_sels[] = {
+ "osc",
+ "lvds1_in",
+ "lvds2_in",
+ "dummy",
+};
+
+static const char *const pll2_bypass_sels[] = {
+ "pll2",
+ "pll2_bypass_src",
+};
+
+static const char *const pll3_bypass_sels[] = {
+ "pll3",
+ "pll3_bypass_src",
+};
+
+static const char *const pll5_bypass_sels[] = {
+ "pll5",
+ "pll5_bypass_src",
+};
+
+static const char *const pll6_bypass_sels[] = {
+ "pll6",
+ "pll6_bypass_src",
+};
+
static const char *const usdhc_sels[] = {
"pll2_pfd2_396m",
"pll2_pfd0_352m",
@@ -72,6 +100,23 @@ static const char *const ecspi_sels[] = {
"pll3_60m",
"osc",
};
+
+static const struct clk_div_table post_div_table[] = {
+ { .val = 2, .div = 1, },
+ { .val = 1, .div = 2, },
+ { .val = 0, .div = 4, },
+ { /* sentinel */ }
+};
+
+static const struct clk_div_table video_div_table[] = {
+ { .val = 0, .div = 1, },
+ { .val = 1, .div = 2, },
+ { .val = 2, .div = 1, },
+ { .val = 3, .div = 4, },
+ { /* sentinel */ }
+};
+
+#if CONFIG_IS_ENABLED(VIDEO)
static const char *const ipu_sels[] = {
"mmdc_ch0_axi",
"pll2_pfd2_396m",
@@ -113,6 +158,122 @@ static const char *ipu2_di1_sels_2[] = {
static unsigned int share_count_mipi_core_cfg;
+static void of_assigned_ldb_sels(struct udevice *dev, int *ldb_di0_sel,
+ int *ldb_di1_sel)
+{
+ struct ofnode_phandle_args clk_args, parent_args;
+ ofnode node = dev_ofnode(dev);
+ int count, err;
+
+ count = dev_count_phandle_with_args(dev, "assigned-clocks",
+ "#clock-cells", 0);
+ if (count <= 0) {
+ if (count == 0)
+ debug("%s: no assigned_clocks found\n", dev->name);
+ else
+ pr_err("%s: failed to get phandle count (%d)\n",
+ dev->name, count);
+ return;
+ }
+
+ for (int i = 0; i < count; i++) {
+ err = dev_read_phandle_with_args(dev, "assigned-clocks",
+ "#clock-cells", 0, i,
+ &clk_args);
+ if (err == -ENOENT)
+ /* Skip empty handles */
+ continue;
+ else if (err < 0)
+ return;
+
+ if (!ofnode_equal(clk_args.node, node) ||
+ clk_args.args[0] >= IMX6QDL_CLK_END) {
+ pr_err("%s: clock %d not in ccm\n", dev->name, i);
+ return;
+ }
+
+ err = dev_read_phandle_with_args(dev, "assigned-clock-parents",
+ "#clock-cells", 0, i,
+ &parent_args);
+ if (err < 0)
+ return;
+
+ if (!ofnode_equal(parent_args.node, node) ||
+ parent_args.args[0] >= IMX6QDL_CLK_END) {
+ pr_err("%s: parent clock %d not in ccm\n", dev->name,
+ i);
+ return;
+ }
+
+ if (clk_args.args[0] == IMX6QDL_CLK_LDB_DI0_SEL)
+ *ldb_di0_sel = parent_args.args[0];
+ else if (clk_args.args[0] == IMX6QDL_CLK_LDB_DI1_SEL)
+ *ldb_di1_sel = parent_args.args[0];
+ }
+}
+
+static void imx6q_init_ldb_clks(struct udevice *dev)
+{
+ int ldb_di_sel[] = { IMX6QDL_CLK_END, IMX6QDL_CLK_END };
+ enum ldb_di_clock ldb_di_clk[] = { MXC_MMDC_CH1_CLK, MXC_MMDC_CH1_CLK };
+
+ of_assigned_ldb_sels(dev, &ldb_di_sel[0], &ldb_di_sel[1]);
+ for (int i = 0; i < 2; i++) {
+ switch (ldb_di_sel[i]) {
+ case IMX6QDL_CLK_PLL5_VIDEO_DIV:
+ ldb_di_clk[i] = MXC_PLL5_CLK;
+ break;
+ case IMX6QDL_CLK_PLL2_PFD0_352M:
+ ldb_di_clk[i] = MXC_PLL2_PFD0_CLK;
+ break;
+ case IMX6QDL_CLK_PLL2_PFD2_396M: {
+ struct clk *clk, *parent;
+
+ int err = clk_get_by_id(IMX6QDL_CLK_PERIPH_PRE, &clk);
+
+ if (err) {
+ pr_err("%s: failed to get periph_pre clock "
+ "(%d)\n",
+ dev->name, err);
+ return;
+ }
+
+ err = clk_get_by_id(IMX6QDL_CLK_PLL2_PFD2_396M,
+ &parent);
+ if (err) {
+ pr_err("%s: failed to get pll2_pfd2_396m clock"
+ " (%d)\n",
+ dev->name, err);
+ return;
+ }
+
+ if (parent == clk) {
+ pr_err("%s: ldb_di%d_sel: couldn't disable "
+ "pll2_pfd2_396m clock\n",
+ dev->name, i);
+ return;
+ }
+
+ ldb_di_clk[i] = MXC_PLL2_PFD2_CLK;
+ break;
+ }
+ case IMX6QDL_CLK_MMDC_CH1_AXI:
+ case IMX6QDL_CLK_END:
+ /* use the default clock */
+ break;
+ case IMX6QDL_CLK_PLL3_USB_OTG:
+ ldb_di_clk[i] = MXC_PLL3_SW_CLK;
+ break;
+ default:
+ pr_err("%s: invalid LDB clock parent\n", dev->name);
+ return;
+ }
+ }
+
+ select_ldb_di_clock_source(ldb_di_clk[0], ldb_di_clk[1]);
+}
+#endif /* CONFIG_IS_ENABLED(VIDEO) */
+
static int imx6q_clk_probe(struct udevice *dev)
{
void *base;
@@ -120,26 +281,70 @@ static int imx6q_clk_probe(struct udevice *dev)
/* Anatop clocks */
base = (void *)ANATOP_BASE_ADDR;
- clk_dm(IMX6QDL_CLK_PLL2,
- imx_clk_pllv3(dev, IMX_PLLV3_GENERIC, "pll2_bus", "osc",
- base + 0x30, 0x1));
+ clk_dm(IMX6QDL_PLL2_BYPASS_SRC,
+ imx_clk_mux(dev, "pll2_bypass_src", base + 0x30, 14, 2,
+ pll_bypass_src_sels,
+ ARRAY_SIZE(pll_bypass_src_sels)));
+ clk_dm(IMX6QDL_PLL3_BYPASS_SRC,
+ imx_clk_mux(dev, "pll3_bypass_src", base + 0x10, 14, 2,
+ pll_bypass_src_sels,
+ ARRAY_SIZE(pll_bypass_src_sels)));
+ clk_dm(IMX6QDL_PLL5_BYPASS_SRC,
+ imx_clk_mux(dev, "pll5_bypass_src", base + 0xa0, 14, 2,
+ pll_bypass_src_sels,
+ ARRAY_SIZE(pll_bypass_src_sels)));
+ clk_dm(IMX6QDL_PLL6_BYPASS_SRC,
+ imx_clk_mux(dev, "pll6_bypass_src", base + 0xe0, 14, 2,
+ pll_bypass_src_sels,
+ ARRAY_SIZE(pll_bypass_src_sels)));
+
+ clk_dm(IMX6QDL_CLK_PLL2, imx_clk_pllv3(dev, IMX_PLLV3_GENERIC, "pll2",
+ "osc", base + 0x30, 0x1));
+ clk_dm(IMX6QDL_CLK_PLL3, imx_clk_pllv3(dev, IMX_PLLV3_USB, "pll3",
+ "osc", base + 0x10, 0x3));
+ clk_dm(IMX6QDL_CLK_PLL5, imx_clk_pllv3(dev, IMX_PLLV3_AV, "pll5", "osc",
+ base + 0xa0, 0x7f));
+ clk_dm(IMX6QDL_CLK_PLL6, imx_clk_pllv3(dev, IMX_PLLV3_ENET, "pll6",
+ "osc", base + 0xe0, 0x3));
+
+ clk_dm(IMX6QDL_PLL2_BYPASS,
+ imx_clk_mux_flags(dev, "pll2_bypass", base + 0x30, 16, 1,
+ pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels),
+ CLK_SET_RATE_PARENT));
+ clk_dm(IMX6QDL_PLL3_BYPASS,
+ imx_clk_mux_flags(dev, "pll3_bypass", base + 0x10, 16, 1,
+ pll3_bypass_sels, ARRAY_SIZE(pll3_bypass_sels),
+ CLK_SET_RATE_PARENT));
+ clk_dm(IMX6QDL_PLL5_BYPASS,
+ imx_clk_mux_flags(dev, "pll5_bypass", base + 0xa0, 16, 1,
+ pll5_bypass_sels, ARRAY_SIZE(pll5_bypass_sels),
+ CLK_SET_RATE_PARENT));
+ clk_dm(IMX6QDL_PLL6_BYPASS,
+ imx_clk_mux_flags(dev, "pll6_bypass", base + 0xe0, 16, 1,
+ pll6_bypass_sels, ARRAY_SIZE(pll6_bypass_sels),
+ CLK_SET_RATE_PARENT));
+
+ SET_CLK_PARENT(IMX6QDL_PLL2_BYPASS, IMX6QDL_CLK_PLL2);
+ SET_CLK_PARENT(IMX6QDL_PLL3_BYPASS, IMX6QDL_CLK_PLL3);
+ SET_CLK_PARENT(IMX6QDL_PLL5_BYPASS, IMX6QDL_CLK_PLL5);
+ SET_CLK_PARENT(IMX6QDL_PLL6_BYPASS, IMX6QDL_CLK_PLL6);
+
+ clk_dm(IMX6QDL_CLK_PLL2_BUS,
+ imx_clk_gate(dev, "pll2_bus", "pll2_bypass", base + 0x30, 13));
clk_dm(IMX6QDL_CLK_PLL3_USB_OTG,
- imx_clk_pllv3(dev, IMX_PLLV3_USB, "pll3_usb_otg", "osc",
- base + 0x10, 0x3));
+ imx_clk_gate(dev, "pll3_usb_otg", "pll3_bypass", base + 0x10,
+ 13));
+ clk_dm(IMX6QDL_CLK_PLL5_VIDEO,
+ imx_clk_gate(dev, "pll5_video", "pll5_bypass", base + 0xa0, 13));
+ clk_dm(IMX6QDL_CLK_PLL6_ENET,
+ imx_clk_gate(dev, "pll6_enet", "pll6_bypass", base + 0xe0, 13));
+
clk_dm(IMX6QDL_CLK_PLL3_60M,
imx_clk_fixed_factor(dev, "pll3_60m", "pll3_usb_otg", 1, 8));
clk_dm(IMX6QDL_CLK_PLL3_80M,
imx_clk_fixed_factor(dev, "pll3_80m", "pll3_usb_otg", 1, 6));
clk_dm(IMX6QDL_CLK_PLL3_120M,
imx_clk_fixed_factor(dev, "pll3_120m", "pll3_usb_otg", 1, 4));
- clk_dm(IMX6QDL_CLK_PLL5, imx_clk_pllv3(dev, IMX_PLLV3_AV, "pll5", "osc",
- base + 0xa0, 0x7f));
- clk_dm(IMX6QDL_CLK_PLL5_VIDEO,
- imx_clk_gate(dev, "pll5_video", "pll5", base + 0xa0, 13));
- clk_dm(IMX6QDL_CLK_PLL6, imx_clk_pllv3(dev, IMX_PLLV3_ENET, "pll6",
- "osc", base + 0xe0, 0x3));
- clk_dm(IMX6QDL_CLK_PLL6_ENET,
- imx_clk_gate(dev, "pll6_enet", "pll6", base + 0xe0, 13));
clk_dm(IMX6QDL_CLK_PLL2_PFD0_352M,
imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0));
@@ -151,10 +356,14 @@ static int imx6q_clk_probe(struct udevice *dev)
clk_dm(IMX6QDL_CLK_PLL2_198M,
imx_clk_fixed_factor(dev, "pll2_198m", "pll2_pfd2_396m", 1, 2));
clk_dm(IMX6QDL_CLK_PLL5_POST_DIV,
- imx_clk_fixed_factor(dev, "pll5_post_div", "pll5_video", 1, 1));
+ clk_register_divider_table(dev, "pll5_post_div", "pll5_video",
+ CLK_SET_RATE_PARENT, base + 0xa0, 19,
+ 2, 0, post_div_table));
clk_dm(IMX6QDL_CLK_PLL5_VIDEO_DIV,
- imx_clk_fixed_factor(dev, "pll5_video_div", "pll5_post_div", 1,
- 1));
+ clk_register_divider_table(dev, "pll5_video_div",
+ "pll5_post_div", CLK_SET_RATE_PARENT,
+ base + 0x170, 30, 2, 0,
+ video_div_table));
clk_dm(IMX6QDL_CLK_VIDEO_27M,
imx_clk_fixed_factor(dev, "video_27m", "pll3_pfd1_540m", 1,
20));
@@ -263,6 +472,7 @@ static int imx6q_clk_probe(struct udevice *dev)
imx_clk_gate2(dev, "mmdc_ch1_axi", "mmdc_ch1_axi_podf",
base + 0x74, 22));
+#if CONFIG_IS_ENABLED(VIDEO)
clk_dm(IMX6QDL_CLK_IPU1_SEL,
imx_clk_mux(dev, "ipu1_sel", base + 0x3c, 9, 2, ipu_sels,
ARRAY_SIZE(ipu_sels)));
@@ -279,9 +489,12 @@ static int imx6q_clk_probe(struct udevice *dev)
ldb_di_sels, ARRAY_SIZE(ldb_di_sels)));
} else {
/*
- * Need to set these as read-only due to a hardware bug.
- * Keeping default mux values. Fixed on the i.MX6 QuadPlus
- */
+ * Need to set these as read-only due to a hardware bug.
+ * Keeping default mux values. Fixed on the i.MX6 QuadPlus
+ * Need to set the clocks now and make them read-only due to a
+ * hardware bug. Fixed on the i.MX6 QuadPlus
+ */
+ imx6q_init_ldb_clks(dev);
clk_dm(IMX6QDL_CLK_LDB_DI0_SEL,
imx_clk_mux_flags(dev, "ldb_di0_sel", base + 0x2c, 9, 3,
ldb_di_sels, ARRAY_SIZE(ldb_di_sels),
@@ -413,6 +626,7 @@ static int imx6q_clk_probe(struct udevice *dev)
ARRAY_SIZE(ipu2_di1_sels),
CLK_SET_RATE_PARENT));
}
+#endif /* CONFIG_IS_ENABLED(VIDEO) */
clk_dm(IMX6QDL_CLK_ECSPI1,
imx_clk_gate2(dev, "ecspi1", "ecspi_root", base + 0x6c, 0));
@@ -453,6 +667,8 @@ static int imx6q_clk_probe(struct udevice *dev)
imx_clk_gate2(dev, "enet", "ipg", base + 0x6c, 10));
clk_dm(IMX6QDL_CLK_ENET_REF,
imx_clk_fixed_factor(dev, "enet_ref", "pll6_enet", 1, 1));
+
+#if CONFIG_IS_ENABLED(VIDEO)
clk_dm(IMX6QDL_CLK_MIPI_CORE_CFG,
imx_clk_gate2_shared(dev, "mipi_core_cfg", "video_27m",
base + 0x74, 16,
@@ -480,6 +696,7 @@ static int imx6q_clk_probe(struct udevice *dev)
SET_CLK_PARENT(IMX6QDL_CLK_IPU1_SEL,
IMX6QDL_CLK_PLL3_PFD1_540M);
}
+#endif /* CONFIG_IS_ENABLED(VIDEO) */
return 0;
}
diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c
index fe6cba19758..78156003ad9 100644
--- a/drivers/clk/imx/clk-imx8mq.c
+++ b/drivers/clk/imx/clk-imx8mq.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright 2019 NXP
+ * Copyright 2019, 2026 NXP
* Copyright 2022 Purism
* Peng Fan <[email protected]>
*/
@@ -155,39 +155,52 @@ static int imx8mq_clk_probe(struct udevice *dev)
imx_clk_mux(dev, "dram_pll_ref_sel", base + 0x60, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MQ_ARM_PLL_REF_SEL,
- imx_clk_mux(dev, "arm_pll_ref_sel", base + 0x28, 0, 2,
+ imx_clk_mux(dev, "arm_pll_ref_sel", base + 0x28, 16, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MQ_GPU_PLL_REF_SEL,
- imx_clk_mux(dev, "gpu_pll_ref_sel", base + 0x18, 0, 2,
+ imx_clk_mux(dev, "gpu_pll_ref_sel", base + 0x18, 16, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MQ_VPU_PLL_REF_SEL,
- imx_clk_mux(dev, "vpu_pll_ref_sel", base + 0x20, 0, 2,
+ imx_clk_mux(dev, "vpu_pll_ref_sel", base + 0x20, 16, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MQ_SYS3_PLL1_REF_SEL,
imx_clk_mux(dev, "sys3_pll_ref_sel", base + 0x48, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MQ_AUDIO_PLL1_REF_SEL,
- imx_clk_mux(dev, "audio_pll1_ref_sel", base + 0x0, 0, 2,
+ imx_clk_mux(dev, "audio_pll1_ref_sel", base + 0x0, 16, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MQ_AUDIO_PLL2_REF_SEL,
- imx_clk_mux(dev, "audio_pll2_ref_sel", base + 0x8, 0, 2,
+ imx_clk_mux(dev, "audio_pll2_ref_sel", base + 0x8, 16, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MQ_VIDEO_PLL1_REF_SEL,
- imx_clk_mux(dev, "video_pll1_ref_sel", base + 0x10, 0, 2,
+ imx_clk_mux(dev, "video_pll1_ref_sel", base + 0x10, 16, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MQ_VIDEO2_PLL1_REF_SEL,
imx_clk_mux(dev, "video_pll2_ref_sel", base + 0x54, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
+ clk_dm(IMX8MQ_ARM_PLL_REF_DIV,
+ imx_clk_divider(dev, "arm_pll_ref_div", "arm_pll_ref_sel", base + 0x28, 5, 6));
+ clk_dm(IMX8MQ_GPU_PLL_REF_DIV,
+ imx_clk_divider(dev, "gpu_pll_ref_div", "gpu_pll_ref_sel", base + 0x18, 5, 6));
+ clk_dm(IMX8MQ_VPU_PLL_REF_DIV,
+ imx_clk_divider(dev, "vpu_pll_ref_div", "vpu_pll_ref_sel", base + 0x20, 5, 6));
+ clk_dm(IMX8MQ_AUDIO_PLL1_REF_DIV,
+ imx_clk_divider(dev, "audio_pll1_ref_div", "audio_pll1_ref_sel", base + 0x0, 5, 6));
+ clk_dm(IMX8MQ_AUDIO_PLL2_REF_DIV,
+ imx_clk_divider(dev, "audio_pll2_ref_div", "audio_pll2_ref_sel", base + 0x8, 5, 6));
+ clk_dm(IMX8MQ_VIDEO_PLL1_REF_DIV,
+ imx_clk_divider(dev, "video_pll1_ref_div", "video_pll1_ref_sel", base + 0x10, 5, 6));
+
clk_dm(IMX8MQ_ARM_PLL,
- imx_clk_pll14xx("arm_pll", "arm_pll_ref_sel",
- base + 0x28, &imx_1416x_pll));
+ imx_clk_frac_pll("arm_pll", "arm_pll_ref_div",
+ base + 0x28));
clk_dm(IMX8MQ_GPU_PLL,
- imx_clk_pll14xx("gpu_pll", "gpu_pll_ref_sel",
- base + 0x18, &imx_1416x_pll));
+ imx_clk_frac_pll("gpu_pll", "gpu_pll_ref_div",
+ base + 0x18));
clk_dm(IMX8MQ_VPU_PLL,
- imx_clk_pll14xx("vpu_pll", "vpu_pll_ref_sel",
- base + 0x20, &imx_1416x_pll));
+ imx_clk_frac_pll("vpu_pll", "vpu_pll_ref_div",
+ base + 0x20));
clk_dm(IMX8MQ_SYS1_PLL1,
clk_register_fixed_rate(NULL, "sys1_pll", 800000000));
@@ -196,14 +209,14 @@ static int imx8mq_clk_probe(struct udevice *dev)
clk_dm(IMX8MQ_SYS2_PLL1,
clk_register_fixed_rate(NULL, "sys3_pll", 1000000000));
clk_dm(IMX8MQ_AUDIO_PLL1,
- imx_clk_pll14xx("audio_pll1", "audio_pll1_ref_sel",
- base + 0x0, &imx_1443x_pll));
+ imx_clk_frac_pll("audio_pll1", "audio_pll1_ref_div",
+ base + 0x0));
clk_dm(IMX8MQ_AUDIO_PLL2,
- imx_clk_pll14xx("audio_pll2", "audio_pll2_ref_sel",
- base + 0x8, &imx_1443x_pll));
+ imx_clk_frac_pll("audio_pll2", "audio_pll2_ref_div",
+ base + 0x8));
clk_dm(IMX8MQ_VIDEO_PLL1,
- imx_clk_pll14xx("video_pll1", "video_pll1_ref_sel",
- base + 0x10, &imx_1443x_pll));
+ imx_clk_frac_pll("video_pll1", "video_pll1_ref_div",
+ base + 0x10));
/* PLL bypass out */
clk_dm(IMX8MQ_ARM_PLL_BYPASS,
@@ -356,8 +369,8 @@ static int imx8mq_clk_probe(struct udevice *dev)
clk_dm(IMX8MQ_CLK_A53_DIV,
imx_clk_divider2(dev, "arm_a53_div", "arm_a53_cg",
base + 0x8000, 0, 3));
- clk_dm(IMX8MQ_CLK_A53_CORE,
- imx_clk_mux2(dev, "arm_a53_src", base + 0x9880, 24, 1,
+ clk_dm(IMX8MQ_CLK_ARM,
+ imx_clk_mux2(dev, "arm_a53_core", base + 0x9880, 24, 1,
imx8mq_a53_core_sels, ARRAY_SIZE(imx8mq_a53_core_sels)));
clk_dm(IMX8MQ_CLK_AHB,
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index b53f35df84f..6aef72c0212 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -78,6 +78,9 @@ struct clk *imx_clk_pll14xx(const char *name, const char *parent_name,
void __iomem *base,
const struct imx_pll14xx_clk *pll_clk);
+struct clk *imx_clk_frac_pll(const char *name, const char *parent_name,
+ void __iomem *base);
+
struct clk *clk_register_gate2(struct udevice *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 bit_idx, u8 cgr_val,
diff --git a/drivers/clk/mediatek/clk-mt8189.c b/drivers/clk/mediatek/clk-mt8189.c
index 9e640059f11..d11947ee461 100644
--- a/drivers/clk/mediatek/clk-mt8189.c
+++ b/drivers/clk/mediatek/clk-mt8189.c
@@ -1641,6 +1641,46 @@ static const struct mtk_gate mminfra_config_clks[] = {
GATE_MMINFRA_CONFIG1(CLK_MMINFRA_GCE_26M, CLK_TOP_MMINFRA_SEL, 17),
};
+static const struct mtk_gate_regs ufscfg_ao_reg_cg_regs = {
+ .set_ofs = 0x8,
+ .clr_ofs = 0xc,
+ .sta_ofs = 0x4,
+};
+
+#define GATE_UFSCFG_AO_REG_EXT(_id, _parent, _shift) \
+ GATE_FLAGS(_id, _parent, &ufscfg_ao_reg_cg_regs, _shift, \
+ CLK_GATE_SETCLR | CLK_PARENT_EXT)
+
+#define GATE_UFSCFG_AO_REG_TOP(_id, _parent, _shift) \
+ GATE_FLAGS(_id, _parent, &ufscfg_ao_reg_cg_regs, _shift, \
+ CLK_GATE_SETCLR | CLK_PARENT_TOPCKGEN)
+
+static const struct mtk_gate ufs_config_ao_clks[] = {
+ GATE_UFSCFG_AO_REG_EXT(CLK_UFSCFG_AO_REG_UNIPRO_TX_SYM, CLK_PAD_CLK26M, 1),
+ GATE_UFSCFG_AO_REG_EXT(CLK_UFSCFG_AO_REG_UNIPRO_RX_SYM0, CLK_PAD_CLK26M, 2),
+ GATE_UFSCFG_AO_REG_EXT(CLK_UFSCFG_AO_REG_UNIPRO_RX_SYM1, CLK_PAD_CLK26M, 3),
+ GATE_UFSCFG_AO_REG_TOP(CLK_UFSCFG_AO_REG_UNIPRO_SYS, CLK_TOP_U_SEL, 4),
+ GATE_UFSCFG_AO_REG_EXT(CLK_UFSCFG_AO_REG_U_SAP_CFG, CLK_PAD_CLK26M, 5),
+ GATE_UFSCFG_AO_REG_TOP(CLK_UFSCFG_AO_REG_U_PHY_TOP_AHB_S_BUS, CLK_TOP_AXI_U_SEL, 6),
+};
+
+static const struct mtk_gate_regs ufscfg_pdn_reg_cg_regs = {
+ .set_ofs = 0x8,
+ .clr_ofs = 0xc,
+ .sta_ofs = 0x4,
+};
+
+#define GATE_UFSCFG_PDN_REG(_id, _parent, _shift) \
+ GATE_FLAGS(_id, _parent, &ufscfg_pdn_reg_cg_regs, _shift, \
+ CLK_GATE_SETCLR | CLK_PARENT_TOPCKGEN)
+
+static const struct mtk_gate ufs_config_pdn_clks[] = {
+ GATE_UFSCFG_PDN_REG(CLK_UFSCFG_REG_UFSHCI_UFS, CLK_TOP_U_SEL, 0),
+ GATE_UFSCFG_PDN_REG(CLK_UFSCFG_REG_UFSHCI_AES, CLK_TOP_AES_UFSFDE_SEL, 1),
+ GATE_UFSCFG_PDN_REG(CLK_UFSCFG_REG_UFSHCI_U_AHB, CLK_TOP_AXI_U_SEL, 3),
+ GATE_UFSCFG_PDN_REG(CLK_UFSCFG_REG_UFSHCI_U_AXI, CLK_TOP_MEM_SUB_U_SEL, 5),
+};
+
static const struct mtk_parent vlp_26m_oscd10_parents[] = {
EXT_PARENT(CLK_PAD_CLK26M),
TOP_PARENT(CLK_TOP_OSC_D10),
@@ -1955,6 +1995,8 @@ GATE_CLK_DATA(perao_clks);
GATE_CLK_DATA(imp_clks);
GATE_CLK_DATA(mm_clks);
GATE_CLK_DATA(mminfra_config_clks);
+GATE_CLK_DATA(ufs_config_ao_clks);
+GATE_CLK_DATA(ufs_config_pdn_clks);
GATE_CLK_DATA(vlpcfg_ao_clks);
static const struct udevice_id of_match_mt8189_clk_gate[] = {
@@ -1962,6 +2004,8 @@ static const struct udevice_id of_match_mt8189_clk_gate[] = {
{ .compatible = "mediatek,mt8189-iic-wrap", .data = (ulong)&imp_clks_data },
{ .compatible = "mediatek,mt8189-dispsys", .data = (ulong)&mm_clks_data },
{ .compatible = "mediatek,mt8189-mm-infra", .data = (ulong)&mminfra_config_clks_data },
+ { .compatible = "mediatek,mt8189-ufscfg-ao", .data = (ulong)&ufs_config_ao_clks_data },
+ { .compatible = "mediatek,mt8189-ufscfg-pdn", .data = (ulong)&ufs_config_pdn_clks_data },
{ .compatible = "mediatek,mt8189-vlpcfg-ao", .data = (ulong)&vlpcfg_ao_clks_data },
{ }
};
diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c
index 3557aeac3d5..9d0a6cd79cf 100644
--- a/drivers/clk/mediatek/clk-mtk.c
+++ b/drivers/clk/mediatek/clk-mtk.c
@@ -204,10 +204,6 @@ static ulong mtk_clk_find_parent_rate(struct clk *clk, int id,
return clk_get_rate(&parent);
}
-const struct clk_ops mtk_clk_apmixedsys_ops;
-const struct clk_ops mtk_clk_topckgen_ops;
-const struct clk_ops mtk_clk_infrasys_ops;
-
static ulong mtk_find_parent_rate(struct mtk_clk_priv *priv, struct clk *clk,
const int parent, u16 flags)
{
@@ -216,15 +212,21 @@ static ulong mtk_find_parent_rate(struct mtk_clk_priv *priv, struct clk *clk,
switch (flags & CLK_PARENT_MASK) {
case CLK_PARENT_APMIXED:
/* APMIXEDSYS can be parent or grandparent. */
- if (dev_get_driver_ops(clk->dev) == &mtk_clk_apmixedsys_ops)
+ if (dev_get_driver_ops(clk->dev) == &mtk_clk_apmixedsys_ops ||
+ dev_get_driver_ops(clk->dev) == &mtk_clk_fixed_pll_ops) {
parent_dev = clk->dev;
- else if (dev_get_driver_ops(priv->parent) == &mtk_clk_apmixedsys_ops)
+ } else if (dev_get_driver_ops(priv->parent) == &mtk_clk_apmixedsys_ops ||
+ dev_get_driver_ops(priv->parent) == &mtk_clk_fixed_pll_ops) {
parent_dev = priv->parent;
- else if (dev_get_driver_ops(dev_get_parent(priv->parent)) == &mtk_clk_apmixedsys_ops)
- parent_dev = dev_get_parent(priv->parent);
- else
- return -EINVAL;
+ } else {
+ struct udevice *grandparent_dev = dev_get_parent(priv->parent);
+ if (dev_get_driver_ops(grandparent_dev) == &mtk_clk_apmixedsys_ops ||
+ dev_get_driver_ops(grandparent_dev) == &mtk_clk_fixed_pll_ops)
+ parent_dev = grandparent_dev;
+ else
+ return -EINVAL;
+ }
break;
case CLK_PARENT_TOPCKGEN:
if (dev_get_driver_ops(clk->dev) == &mtk_clk_topckgen_ops)
diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
index a7a42b2edb6..f33938e1419 100644
--- a/drivers/clk/meson/g12a.c
+++ b/drivers/clk/meson/g12a.c
@@ -946,6 +946,11 @@ static ulong meson_clk_set_rate_by_id(struct clk *clk, unsigned long id,
case CLKID_HDMI:
return meson_clk_set_rate_by_id(clk, CLKID_HDMI_DIV,
rate, current_rate);
+ case CLKID_SD_EMMC_A_CLK0:
+ case CLKID_SD_EMMC_B_CLK0:
+ case CLKID_SD_EMMC_C_CLK0:
+ /* TOFIX: implement rate set for MMC clocks */
+ return 0;
default:
return -ENOENT;
}
diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c
index 218be45c2cb..bf46f4d50bc 100644
--- a/drivers/clk/meson/gxbb.c
+++ b/drivers/clk/meson/gxbb.c
@@ -900,6 +900,11 @@ static ulong meson_clk_set_rate_by_id(struct clk *clk, unsigned long id,
case CLKID_HDMI:
return meson_clk_set_rate_by_id(clk, CLKID_HDMI_DIV,
rate, current_rate);
+ case CLKID_SD_EMMC_A_CLK0:
+ case CLKID_SD_EMMC_B_CLK0:
+ case CLKID_SD_EMMC_C_CLK0:
+ /* TOFIX: implement rate set for MMC clocks */
+ return 0;
default:
return -ENOENT;
}
diff --git a/drivers/clk/owl/Kconfig b/drivers/clk/owl/Kconfig
index c6afef90034..5f3b8fe8ab4 100644
--- a/drivers/clk/owl/Kconfig
+++ b/drivers/clk/owl/Kconfig
@@ -1,8 +1,8 @@
config CLK_OWL
- bool "Actions Semi OWL clock drivers"
- depends on CLK && ARCH_OWL
- help
- Enable support for clock managemet unit present in Actions Semi
+ bool "Actions Semi OWL clock drivers"
+ depends on CLK && ARCH_OWL
+ help
+ Enable support for clock managemet unit present in Actions Semi
Owl series S900/S700 SoCs.
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 8504ed5d656..9ad233c83ac 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -47,6 +47,14 @@ config CLK_QCOM_IPQ9574
on the Snapdragon IPQ9574 SoC. This driver supports the clocks
and resets exposed by the GCC hardware block.
+config CLK_QCOM_MILOS
+ bool "Qualcomm Milos GCC"
+ select CLK_QCOM
+ help
+ Say Y here to enable support for the Global Clock Controller
+ on the Snapdragon Milos SoC. This driver supports the clocks
+ and resets exposed by the GCC hardware block.
+
config CLK_QCOM_QCM2290
bool "Qualcomm QCM2290 GCC"
select CLK_QCOM
@@ -103,6 +111,14 @@ config CLK_QCOM_SM6115
on the Snapdragon SM6115 SoC. This driver supports the clocks
and resets exposed by the GCC hardware block.
+config CLK_QCOM_SM6125
+ bool "Qualcomm SM6125 GCC"
+ select CLK_QCOM
+ help
+ Say Y here to enable support for the Global Clock Controller
+ on the Snapdragon SM6125 SoC. This driver supports the clocks
+ and resets exposed by the GCC hardware block.
+
config CLK_QCOM_SM6350
bool "Qualcomm SM6350 GCC"
select CLK_QCOM
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 82a5b166196..c0d95a6300e 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_CLK_QCOM_APQ8096) += clock-apq8096.o
obj-$(CONFIG_CLK_QCOM_IPQ4019) += clock-ipq4019.o
obj-$(CONFIG_CLK_QCOM_IPQ5424) += clock-ipq5424.o
obj-$(CONFIG_CLK_QCOM_IPQ9574) += clock-ipq9574.o
+obj-$(CONFIG_CLK_QCOM_MILOS) += clock-milos.o
obj-$(CONFIG_CLK_QCOM_QCM2290) += clock-qcm2290.o
obj-$(CONFIG_CLK_QCOM_QCS404) += clock-qcs404.o
obj-$(CONFIG_CLK_QCOM_QCS8300) += clock-qcs8300.o
@@ -16,6 +17,7 @@ obj-$(CONFIG_CLK_QCOM_QCS615) += clock-qcs615.o
obj-$(CONFIG_CLK_QCOM_SA8775P) += clock-sa8775p.o
obj-$(CONFIG_CLK_QCOM_SC7280) += clock-sc7280.o
obj-$(CONFIG_CLK_QCOM_SM6115) += clock-sm6115.o
+obj-$(CONFIG_CLK_QCOM_SM6125) += clock-sm6125.o
obj-$(CONFIG_CLK_QCOM_SM6350) += clock-sm6350.o
obj-$(CONFIG_CLK_QCOM_SM7150) += clock-sm7150.o
obj-$(CONFIG_CLK_QCOM_SM8150) += clock-sm8150.o
diff --git a/drivers/clk/qcom/clock-milos.c b/drivers/clk/qcom/clock-milos.c
new file mode 100644
index 00000000000..54103adc114
--- /dev/null
+++ b/drivers/clk/qcom/clock-milos.c
@@ -0,0 +1,269 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Clock drivers for Qualcomm Milos
+ *
+ * (C) Copyright 2024 Linaro Ltd.
+ * (C) Copyright 2026 Luca Weiss <[email protected]>
+ */
+
+#include <clk-uclass.h>
+#include <dm.h>
+#include <linux/delay.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <linux/bug.h>
+#include <linux/bitops.h>
+#include <dt-bindings/clock/qcom,milos-gcc.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/clock/qcom,sm8650-tcsr.h>
+
+#include "clock-qcom.h"
+
+/* On-board TCXO, TOFIX get from DT */
+#define TCXO_RATE 76800000
+
+/* bi_tcxo_div4 divided after RPMh output */
+#define TCXO_DIV4_RATE (TCXO_RATE / 4)
+
+static const struct freq_tbl ftbl_gcc_qupv3_wrap0_s3_clk_src[] = {
+ F(7372800, CFG_CLK_SRC_GPLL0_EVEN, 1, 384, 15625),
+ F(14745600, CFG_CLK_SRC_GPLL0_EVEN, 1, 768, 15625),
+ F(19200000, CFG_CLK_SRC_CXO, 1, 0, 0),
+ F(29491200, CFG_CLK_SRC_GPLL0_EVEN, 1, 1536, 15625),
+ F(32000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 8, 75),
+ F(48000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 4, 25),
+ F(51200000, CFG_CLK_SRC_GPLL0_EVEN, 1, 64, 375),
+ F(64000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 16, 75),
+ F(75000000, CFG_CLK_SRC_GPLL0_EVEN, 4, 0, 0),
+ F(80000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 4, 15),
+ F(96000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 8, 25),
+ F(100000000, CFG_CLK_SRC_GPLL0, 6, 0, 0),
+ { }
+};
+
+static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk_src[] = {
+ F(400000, CFG_CLK_SRC_CXO, 12, 1, 4),
+ F(25000000, CFG_CLK_SRC_GPLL0_EVEN, 12, 0, 0),
+ F(37500000, CFG_CLK_SRC_GPLL0_EVEN, 8, 0, 0),
+ F(50000000, CFG_CLK_SRC_GPLL0_EVEN, 6, 0, 0),
+ F(100000000, CFG_CLK_SRC_GPLL0_EVEN, 3, 0, 0),
+ /* TOFIX F(202000000, CFG_CLK_SRC_GPLL9, 4, 0, 0), */
+ { }
+};
+
+static const struct freq_tbl ftbl_gcc_usb30_prim_master_clk_src[] = {
+ F(66666667, CFG_CLK_SRC_GPLL0_EVEN, 4.5, 0, 0),
+ F(133333333, CFG_CLK_SRC_GPLL0, 4.5, 0, 0),
+ F(200000000, CFG_CLK_SRC_GPLL0, 3, 0, 0),
+ F(240000000, CFG_CLK_SRC_GPLL0, 2.5, 0, 0),
+ { }
+};
+
+static ulong milos_set_rate(struct clk *clk, ulong rate)
+{
+ struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+ const struct freq_tbl *freq;
+
+ switch (clk->id) {
+ case GCC_QUPV3_WRAP0_S5_CLK: /* UART5 */
+ freq = qcom_find_freq(ftbl_gcc_qupv3_wrap0_s3_clk_src, rate);
+ clk_rcg_set_rate_mnd(priv->base, 0x18500,
+ freq->pre_div, freq->m, freq->n, freq->src, 16);
+ return freq->freq;
+ case GCC_SDCC2_APPS_CLK:
+ freq = qcom_find_freq(ftbl_gcc_sdcc2_apps_clk_src, rate);
+ clk_rcg_set_rate_mnd(priv->base, 0x14018,
+ freq->pre_div, freq->m, freq->n, freq->src, 8);
+ return freq->freq;
+ case GCC_USB30_PRIM_MASTER_CLK:
+ freq = qcom_find_freq(ftbl_gcc_usb30_prim_master_clk_src, rate);
+ clk_rcg_set_rate_mnd(priv->base, 0x3902c,
+ freq->pre_div, freq->m, freq->n, freq->src, 8);
+ return freq->freq;
+ case GCC_USB30_PRIM_MOCK_UTMI_CLK:
+ clk_rcg_set_rate(priv->base, 0x39044, 0, 0);
+ return TCXO_DIV4_RATE;
+ default:
+ return 0;
+ }
+}
+
+static const struct gate_clk milos_clks[] = {
+ GATE_CLK(GCC_AGGRE_USB3_PRIM_AXI_CLK, 0x39090, BIT(0)),
+ GATE_CLK(GCC_QUPV3_WRAP0_S5_CLK, 0x52008, BIT(27)),
+ GATE_CLK(GCC_QUPV3_WRAP_0_M_AHB_CLK, 0x52008, BIT(20)),
+ GATE_CLK(GCC_QUPV3_WRAP_0_S_AHB_CLK, 0x52008, BIT(21)),
+ GATE_CLK(GCC_SDCC2_AHB_CLK, 0x14010, BIT(0)),
+ GATE_CLK(GCC_SDCC2_APPS_CLK, 0x14004, BIT(0)),
+ GATE_CLK(GCC_USB30_PRIM_MASTER_CLK, 0x39018, BIT(0)),
+ GATE_CLK(GCC_USB30_PRIM_MOCK_UTMI_CLK, 0x39028, BIT(0)),
+ GATE_CLK(GCC_USB30_PRIM_SLEEP_CLK, 0x39024, BIT(0)),
+ GATE_CLK(GCC_CFG_NOC_USB3_PRIM_AXI_CLK, 0x3908c, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_AXI_CLK, 0x77018, BIT(0)),
+ GATE_CLK(GCC_AGGRE_UFS_PHY_AXI_CLK, 0x770e4, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_AHB_CLK, 0x77024, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_UNIPRO_CORE_CLK, 0x77068, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_TX_SYMBOL_0_CLK, 0x77028, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_RX_SYMBOL_0_CLK, 0x7702c, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_RX_SYMBOL_1_CLK, 0x770cc, BIT(0)),
+};
+
+static int milos_enable(struct clk *clk)
+{
+ struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+
+ switch (clk->id) {
+ case GCC_AGGRE_USB3_PRIM_AXI_CLK:
+ qcom_gate_clk_en(priv, GCC_USB30_PRIM_MASTER_CLK);
+ break;
+ }
+
+ return qcom_gate_clk_en(priv, clk->id);
+}
+
+static const struct qcom_reset_map milos_gcc_resets[] = {
+ [GCC_QUSB2PHY_PRIM_BCR] = { 0x12000 },
+ [GCC_SDCC1_BCR] = { 0xa3000 },
+ [GCC_SDCC2_BCR] = { 0x14000 },
+ [GCC_UFS_PHY_BCR] = { 0x77000 },
+ [GCC_USB30_PRIM_BCR] = { 0x39000 },
+};
+
+static const struct qcom_power_map milos_gdscs[] = {
+ [UFS_PHY_GDSC] = { 0x77004 },
+ [UFS_MEM_PHY_GDSC] = { 0x9e000 },
+ [USB30_PRIM_GDSC] = { 0x39004 },
+};
+
+static struct msm_clk_data milos_gcc_data = {
+ .resets = milos_gcc_resets,
+ .num_resets = ARRAY_SIZE(milos_gcc_resets),
+ .clks = milos_clks,
+ .num_clks = ARRAY_SIZE(milos_clks),
+ .power_domains = milos_gdscs,
+ .num_power_domains = ARRAY_SIZE(milos_gdscs),
+
+ .enable = milos_enable,
+ .set_rate = milos_set_rate,
+};
+
+static const struct udevice_id gcc_milos_of_match[] = {
+ {
+ .compatible = "qcom,milos-gcc",
+ .data = (ulong)&milos_gcc_data,
+ },
+ { }
+};
+
+U_BOOT_DRIVER(gcc_milos) = {
+ .name = "gcc_milos",
+ .id = UCLASS_NOP,
+ .of_match = gcc_milos_of_match,
+ .bind = qcom_cc_bind,
+ .flags = DM_FLAG_PRE_RELOC | DM_FLAG_DEFAULT_PD_CTRL_OFF,
+};
+
+static ulong milos_rpmh_clk_set_rate(struct clk *clk, ulong rate)
+{
+ return (clk->rate = rate);
+}
+
+static ulong milos_rpmh_clk_get_rate(struct clk *clk)
+{
+ switch (clk->id) {
+ case RPMH_CXO_CLK:
+ return TCXO_DIV4_RATE;
+ default:
+ return clk->rate;
+ }
+}
+
+static int milos_rpmh_clk_nop(struct clk *clk)
+{
+ return 0;
+}
+
+static struct clk_ops milos_rpmh_clk_ops = {
+ .set_rate = milos_rpmh_clk_set_rate,
+ .get_rate = milos_rpmh_clk_get_rate,
+ .enable = milos_rpmh_clk_nop,
+ .disable = milos_rpmh_clk_nop,
+};
+
+static const struct udevice_id milos_rpmh_clk_ids[] = {
+ { .compatible = "qcom,milos-rpmh-clk" },
+ { }
+};
+
+U_BOOT_DRIVER(milos_rpmh_clk) = {
+ .name = "milos_rpmh_clk",
+ .id = UCLASS_CLK,
+ .of_match = milos_rpmh_clk_ids,
+ .ops = &milos_rpmh_clk_ops,
+ .flags = DM_FLAG_DEFAULT_PD_CTRL_OFF,
+};
+
+/* TCSRCC */
+
+static const struct gate_clk milos_tcsr_clks[] = {
+ GATE_CLK(TCSR_PCIE_0_CLKREF_EN, 0x31100, BIT(0)),
+ GATE_CLK(TCSR_PCIE_1_CLKREF_EN, 0x31114, BIT(0)),
+ GATE_CLK(TCSR_UFS_CLKREF_EN, 0x31118, BIT(0)),
+ GATE_CLK(TCSR_UFS_PAD_CLKREF_EN, 0x31104, BIT(0)),
+};
+
+static struct msm_clk_data milos_tcsrcc_data = {
+ .clks = milos_tcsr_clks,
+ .num_clks = ARRAY_SIZE(milos_tcsr_clks),
+};
+
+static int tcsrcc_milos_clk_enable(struct clk *clk)
+{
+ struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+
+ qcom_gate_clk_en(priv, clk->id);
+
+ return 0;
+}
+
+static ulong tcsrcc_milos_clk_get_rate(struct clk *clk)
+{
+ return TCXO_RATE;
+}
+
+static int tcsrcc_milos_clk_probe(struct udevice *dev)
+{
+ struct msm_clk_data *data = (struct msm_clk_data *)dev_get_driver_data(dev);
+ struct msm_clk_priv *priv = dev_get_priv(dev);
+
+ priv->base = dev_read_addr(dev);
+ if (priv->base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->data = data;
+
+ return 0;
+}
+
+static struct clk_ops tcsrcc_milos_clk_ops = {
+ .enable = tcsrcc_milos_clk_enable,
+ .get_rate = tcsrcc_milos_clk_get_rate,
+};
+
+static const struct udevice_id tcsrcc_milos_of_match[] = {
+ {
+ .compatible = "qcom,milos-tcsr",
+ .data = (ulong)&milos_tcsrcc_data,
+ },
+ { }
+};
+
+U_BOOT_DRIVER(tcsrcc_milos) = {
+ .name = "tcsrcc_milos",
+ .id = UCLASS_CLK,
+ .of_match = tcsrcc_milos_of_match,
+ .ops = &tcsrcc_milos_clk_ops,
+ .priv_auto = sizeof(struct msm_clk_priv),
+ .probe = tcsrcc_milos_clk_probe,
+ .flags = DM_FLAG_PRE_RELOC | DM_FLAG_DEFAULT_PD_CTRL_OFF,
+};
diff --git a/drivers/clk/qcom/clock-qcm2290.c b/drivers/clk/qcom/clock-qcm2290.c
index 5a599085b50..c38ff1a1e4a 100644
--- a/drivers/clk/qcom/clock-qcm2290.c
+++ b/drivers/clk/qcom/clock-qcm2290.c
@@ -73,7 +73,7 @@ static const struct pll_vote_clk gpll6_clk = {
.status = 0x6000,
.status_bit = BIT(31),
.ena_vote = 0x79000,
- .vote_bit = BIT(7),
+ .vote_bit = BIT(6),
};
static const struct gate_clk qcm2290_clks[] = {
diff --git a/drivers/clk/qcom/clock-qcs615.c b/drivers/clk/qcom/clock-qcs615.c
index 2087fc38f63..7b3fe49de9c 100644
--- a/drivers/clk/qcom/clock-qcs615.c
+++ b/drivers/clk/qcom/clock-qcs615.c
@@ -19,6 +19,11 @@
#define USB30_PRIM_MASTER_CLK_CMD_RCGR 0xf01c
#define USB3_PRIM_PHY_AUX_CMD_RCGR 0xf060
+#define UFS_PHY_AXI_CLK_CMD_RCGR 0x77020
+#define UFS_PHY_ICE_CORE_CLK_CMD_RCGR 0x77048
+#define UFS_PHY_UNIPRO_CORE_CLK_CMD_RCGR 0x77060
+#define UFS_PHY_PHY_AUX_CLK_CMD_RCGR 0x7707c
+
#define GCC_QUPV3_WRAP0_S0_CLK_ENA_BIT BIT(10)
#define GCC_QUPV3_WRAP0_S1_CLK_ENA_BIT BIT(11)
#define GCC_QUPV3_WRAP0_S2_CLK_ENA_BIT BIT(12)
@@ -33,9 +38,37 @@
#define GCC_QUPV3_WRAP1_S4_CLK_ENA_BIT BIT(26)
#define GCC_QUPV3_WRAP1_S5_CLK_ENA_BIT BIT(27)
+/* UFS PHY AXI clock frequency table */
+static const struct freq_tbl ftbl_gcc_ufs_phy_axi_clk_src[] = {
+ F(25000000, CFG_CLK_SRC_GPLL0_EVEN, 12, 0, 0),
+ F(50000000, CFG_CLK_SRC_GPLL0_EVEN, 6, 0, 0),
+ F(100000000, CFG_CLK_SRC_GPLL0, 6, 0, 0),
+ F(200000000, CFG_CLK_SRC_GPLL0, 3, 0, 0),
+ F(240000000, CFG_CLK_SRC_GPLL0, 2.5, 0, 0),
+ { }
+};
+
+/* UFS PHY ICE CORE clock frequency table */
+static const struct freq_tbl ftbl_gcc_ufs_phy_ice_core_clk_src[] = {
+ F(37500000, CFG_CLK_SRC_GPLL0_EVEN, 8, 0, 0),
+ F(75000000, CFG_CLK_SRC_GPLL0_EVEN, 4, 0, 0),
+ F(150000000, CFG_CLK_SRC_GPLL0, 4, 0, 0),
+ F(300000000, CFG_CLK_SRC_GPLL0, 2, 0, 0),
+ { }
+};
+
+/* UFS PHY UNIPRO CORE clock frequency table */
+static const struct freq_tbl ftbl_gcc_ufs_phy_unipro_core_clk_src[] = {
+ F(37500000, CFG_CLK_SRC_GPLL0_EVEN, 8, 0, 0),
+ F(75000000, CFG_CLK_SRC_GPLL0, 8, 0, 0),
+ F(150000000, CFG_CLK_SRC_GPLL0, 4, 0, 0),
+ { }
+};
+
static ulong qcs615_set_rate(struct clk *clk, ulong rate)
{
struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+ const struct freq_tbl *freq;
if (clk->id < priv->data->num_clks)
debug("%s: %s, requested rate=%ld\n", __func__,
@@ -52,6 +85,24 @@ static ulong qcs615_set_rate(struct clk *clk, ulong rate)
5, 0, 0, CFG_CLK_SRC_GPLL0, 8);
clk_rcg_set_rate(priv->base, USB3_PRIM_PHY_AUX_CMD_RCGR, 0, 0);
return rate;
+ case GCC_UFS_PHY_AXI_CLK:
+ freq = qcom_find_freq(ftbl_gcc_ufs_phy_axi_clk_src, rate);
+ clk_rcg_set_rate_mnd(priv->base, UFS_PHY_AXI_CLK_CMD_RCGR,
+ freq->pre_div, freq->m, freq->n, freq->src, 8);
+ return freq->freq;
+ case GCC_UFS_PHY_UNIPRO_CORE_CLK:
+ freq = qcom_find_freq(ftbl_gcc_ufs_phy_unipro_core_clk_src, rate);
+ clk_rcg_set_rate_mnd(priv->base, UFS_PHY_UNIPRO_CORE_CLK_CMD_RCGR,
+ freq->pre_div, freq->m, freq->n, freq->src, 8);
+ return freq->freq;
+ case GCC_UFS_PHY_ICE_CORE_CLK:
+ freq = qcom_find_freq(ftbl_gcc_ufs_phy_ice_core_clk_src, rate);
+ clk_rcg_set_rate_mnd(priv->base, UFS_PHY_ICE_CORE_CLK_CMD_RCGR,
+ freq->pre_div, freq->m, freq->n, freq->src, 8);
+ return freq->freq;
+ case GCC_UFS_PHY_PHY_AUX_CLK:
+ clk_rcg_set_rate(priv->base, UFS_PHY_PHY_AUX_CLK_CMD_RCGR, 0, CFG_CLK_SRC_CXO);
+ return 19200000;
default:
return 0;
}
@@ -81,7 +132,17 @@ static const struct gate_clk qcs615_clks[] = {
GATE_CLK(GCC_QUPV3_WRAP1_S4_CLK, 0x5200c, GCC_QUPV3_WRAP1_S4_CLK_ENA_BIT),
GATE_CLK(GCC_QUPV3_WRAP1_S5_CLK, 0x5200c, GCC_QUPV3_WRAP1_S5_CLK_ENA_BIT),
GATE_CLK(GCC_DISP_HF_AXI_CLK, 0xb038, BIT(0)),
- GATE_CLK(GCC_DISP_AHB_CLK, 0xb032, BIT(0))
+ GATE_CLK(GCC_DISP_AHB_CLK, 0xb032, BIT(0)),
+ /* UFS clocks */
+ GATE_CLK(GCC_UFS_PHY_AXI_CLK, 0x77010, BIT(0)),
+ GATE_CLK(GCC_AGGRE_UFS_PHY_AXI_CLK, 0x770c0, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_AHB_CLK, 0x77014, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_UNIPRO_CORE_CLK, 0x77040, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_ICE_CORE_CLK, 0x77044, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_TX_SYMBOL_0_CLK, 0x77018, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_RX_SYMBOL_0_CLK, 0x7701c, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_PHY_AUX_CLK, 0x77078, BIT(0)),
+ GATE_CLK(GCC_UFS_MEM_CLKREF_CLK, 0x8c000, BIT(0)),
};
static int qcs615_enable(struct clk *clk)
diff --git a/drivers/clk/qcom/clock-sa8775p.c b/drivers/clk/qcom/clock-sa8775p.c
index 4957abf6f58..7eec4aeae48 100644
--- a/drivers/clk/qcom/clock-sa8775p.c
+++ b/drivers/clk/qcom/clock-sa8775p.c
@@ -19,6 +19,11 @@
#define USB30_PRIM_MASTER_CLK_CMD_RCGR 0x1b028
#define USB3_PRIM_PHY_AUX_CMD_RCGR 0x1b06c
+#define UFS_PHY_AXI_CLK_CMD_RCGR 0x8302c
+#define UFS_PHY_ICE_CORE_CLK_CMD_RCGR 0x83074
+#define UFS_PHY_PHY_AUX_CLK_CMD_RCGR 0x830a8
+#define UFS_PHY_UNIPRO_CORE_CLK_CMD_RCGR 0x8308c
+
#define GCC_QUPV3_WRAP0_S0_CLK_ENA_BIT BIT(10)
#define GCC_QUPV3_WRAP0_S1_CLK_ENA_BIT BIT(11)
#define GCC_QUPV3_WRAP0_S2_CLK_ENA_BIT BIT(12)
@@ -44,9 +49,35 @@
#define GCC_QUPV3_WRAP3_S0_CLK_ENA_BIT BIT(25)
+/* UFS AXI clock frequency table */
+static const struct freq_tbl ftbl_gcc_ufs_phy_axi_clk_src[] = {
+ F(25000000, CFG_CLK_SRC_GPLL0_EVEN, 12, 0, 0),
+ F(75000000, CFG_CLK_SRC_GPLL0_EVEN, 4, 0, 0),
+ F(150000000, CFG_CLK_SRC_GPLL0, 4, 0, 0),
+ F(300000000, CFG_CLK_SRC_GPLL0, 2, 0, 0),
+ { }
+};
+
+/* UFS ICE CORE clock frequency table */
+static const struct freq_tbl ftbl_gcc_ufs_phy_ice_core_clk_src[] = {
+ F(75000000, CFG_CLK_SRC_GPLL0_EVEN, 4, 0, 0),
+ F(150000000, CFG_CLK_SRC_GPLL0, 4, 0, 0),
+ F(300000000, CFG_CLK_SRC_GPLL0, 2, 0, 0),
+ { }
+};
+
+/* UFS UNIPRO CORE clock frequency table */
+static const struct freq_tbl ftbl_gcc_ufs_phy_unipro_core_clk_src[] = {
+ F(75000000, CFG_CLK_SRC_GPLL0_EVEN, 4, 0, 0),
+ F(150000000, CFG_CLK_SRC_GPLL0, 4, 0, 0),
+ F(300000000, CFG_CLK_SRC_GPLL0, 2, 0, 0),
+ { }
+};
+
static ulong sa8775p_set_rate(struct clk *clk, ulong rate)
{
struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+ const struct freq_tbl *freq;
if (clk->id < priv->data->num_clks)
debug("%s: %s, requested rate=%ld\n", __func__,
@@ -63,6 +94,24 @@ static ulong sa8775p_set_rate(struct clk *clk, ulong rate)
5, 0, 0, CFG_CLK_SRC_GPLL0, 8);
clk_rcg_set_rate(priv->base, USB3_PRIM_PHY_AUX_CMD_RCGR, 0, 0);
return rate;
+ case GCC_UFS_PHY_AXI_CLK:
+ freq = qcom_find_freq(ftbl_gcc_ufs_phy_axi_clk_src, rate);
+ clk_rcg_set_rate_mnd(priv->base, UFS_PHY_AXI_CLK_CMD_RCGR,
+ freq->pre_div, freq->m, freq->n, freq->src, 8);
+ return freq->freq;
+ case GCC_UFS_PHY_UNIPRO_CORE_CLK:
+ freq = qcom_find_freq(ftbl_gcc_ufs_phy_unipro_core_clk_src, rate);
+ clk_rcg_set_rate_mnd(priv->base, UFS_PHY_UNIPRO_CORE_CLK_CMD_RCGR,
+ freq->pre_div, freq->m, freq->n, freq->src, 8);
+ return freq->freq;
+ case GCC_UFS_PHY_ICE_CORE_CLK:
+ freq = qcom_find_freq(ftbl_gcc_ufs_phy_ice_core_clk_src, rate);
+ clk_rcg_set_rate_mnd(priv->base, UFS_PHY_ICE_CORE_CLK_CMD_RCGR,
+ freq->pre_div, freq->m, freq->n, freq->src, 8);
+ return freq->freq;
+ case GCC_UFS_PHY_PHY_AUX_CLK:
+ clk_rcg_set_rate(priv->base, UFS_PHY_PHY_AUX_CLK_CMD_RCGR, 0, CFG_CLK_SRC_CXO);
+ return 19200000;
default:
return 0;
}
@@ -106,6 +155,20 @@ static const struct gate_clk sa8775p_clks[] = {
/* QUP Wrapper 3 clocks */
GATE_CLK(GCC_QUPV3_WRAP3_S0_CLK, 0x4b000, GCC_QUPV3_WRAP3_S0_CLK_ENA_BIT),
+
+ /* UFS PHY clocks */
+ GATE_CLK(GCC_UFS_PHY_AXI_CLK, 0x83018, 1),
+ GATE_CLK(GCC_AGGRE_UFS_PHY_AXI_CLK, 0x830d4, 1),
+ GATE_CLK(GCC_UFS_PHY_AHB_CLK, 0x83020, 1),
+ GATE_CLK(GCC_UFS_PHY_UNIPRO_CORE_CLK, 0x83064, 1),
+ GATE_CLK(GCC_UFS_PHY_TX_SYMBOL_0_CLK, 0x83024, 1),
+ GATE_CLK(GCC_UFS_PHY_RX_SYMBOL_0_CLK, 0x83028, 1),
+ GATE_CLK(GCC_UFS_PHY_RX_SYMBOL_1_CLK, 0x830c0, 1),
+ GATE_CLK(GCC_UFS_PHY_PHY_AUX_CLK, 0x830a4, 1),
+ GATE_CLK(GCC_UFS_PHY_ICE_CORE_CLK, 0x8306c, 1),
+
+ /* EDP reference clock (used by UFS PHY) */
+ GATE_CLK(GCC_EDP_REF_CLKREF_EN, 0x97448, 1),
};
static int sa8775p_enable(struct clk *clk)
diff --git a/drivers/clk/qcom/clock-sc7280.c b/drivers/clk/qcom/clock-sc7280.c
index 7b6ed826023..91e3fcc27cb 100644
--- a/drivers/clk/qcom/clock-sc7280.c
+++ b/drivers/clk/qcom/clock-sc7280.c
@@ -23,6 +23,10 @@
#define PCIE_1_AUX_CLK_CMD_RCGR 0x8d058
#define PCIE1_PHY_RCHNG_CMD_RCGR 0x8d03c
#define PCIE_1_PIPE_CLK_PHY_MUX 0x8d054
+#define UFS_PHY_AXI_CLK_CMD_RCGR 0x77024
+#define UFS_PHY_ICE_CORE_CLK_CMD_RCGR 0x7706c
+#define UFS_PHY_PHY_AUX_CLK_CMD_RCGR 0x770a0
+#define UFS_PHY_UNIPRO_CORE_CLK_CMD_RCGR 0x77084
static const struct freq_tbl ftbl_gcc_usb30_prim_master_clk_src[] = {
F(66666667, CFG_CLK_SRC_GPLL0_EVEN, 4.5, 0, 0),
@@ -54,6 +58,33 @@ static const struct freq_tbl ftbl_gcc_qupv3_wrap0_s2_clk_src[] = {
{ }
};
+static const struct freq_tbl ftbl_gcc_ufs_phy_axi_clk_src[] = {
+ F(25000000, CFG_CLK_SRC_GPLL0_EVEN, 12, 0, 0),
+ F(75000000, CFG_CLK_SRC_GPLL0_EVEN, 4, 0, 0),
+ F(150000000, CFG_CLK_SRC_GPLL0_EVEN, 2, 0, 0),
+ F(300000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 0, 0),
+ { }
+};
+
+static const struct freq_tbl ftbl_gcc_ufs_phy_ice_core_clk_src[] = {
+ F(75000000, CFG_CLK_SRC_GPLL0_EVEN, 4, 0, 0),
+ F(150000000, CFG_CLK_SRC_GPLL0_EVEN, 2, 0, 0),
+ F(300000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 0, 0),
+ { }
+};
+
+static const struct freq_tbl ftbl_gcc_ufs_phy_phy_aux_clk_src[] = {
+ F(19200000, CFG_CLK_SRC_CXO, 1, 0, 0),
+ { }
+};
+
+static const struct freq_tbl ftbl_gcc_ufs_phy_unipro_core_clk_src[] = {
+ F(75000000, CFG_CLK_SRC_GPLL0_EVEN, 4, 0, 0),
+ F(150000000, CFG_CLK_SRC_GPLL0_EVEN, 2, 0, 0),
+ F(300000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 0, 0),
+ { }
+};
+
static ulong sc7280_set_rate(struct clk *clk, ulong rate)
{
struct msm_clk_priv *priv = dev_get_priv(clk->dev);
@@ -103,6 +134,26 @@ static ulong sc7280_set_rate(struct clk *clk, ulong rate)
case GCC_PCIE1_PHY_RCHNG_CLK:
clk_rcg_set_rate(priv->base, PCIE1_PHY_RCHNG_CMD_RCGR, 5, CFG_CLK_SRC_GPLL0_EVEN);
return 100000000;
+ case GCC_UFS_PHY_AXI_CLK:
+ freq = qcom_find_freq(ftbl_gcc_ufs_phy_axi_clk_src, rate);
+ clk_rcg_set_rate_mnd(priv->base, UFS_PHY_AXI_CLK_CMD_RCGR,
+ freq->pre_div, freq->m, freq->n, freq->src, 8);
+ return freq->freq;
+ case GCC_UFS_PHY_ICE_CORE_CLK:
+ freq = qcom_find_freq(ftbl_gcc_ufs_phy_ice_core_clk_src, rate);
+ clk_rcg_set_rate_mnd(priv->base, UFS_PHY_ICE_CORE_CLK_CMD_RCGR,
+ freq->pre_div, freq->m, freq->n, freq->src, 8);
+ return freq->freq;
+ case GCC_UFS_PHY_PHY_AUX_CLK:
+ freq = qcom_find_freq(ftbl_gcc_ufs_phy_phy_aux_clk_src, rate);
+ clk_rcg_set_rate_mnd(priv->base, UFS_PHY_PHY_AUX_CLK_CMD_RCGR,
+ freq->pre_div, freq->m, freq->n, freq->src, 8);
+ return freq->freq;
+ case GCC_UFS_PHY_UNIPRO_CORE_CLK:
+ freq = qcom_find_freq(ftbl_gcc_ufs_phy_unipro_core_clk_src, rate);
+ clk_rcg_set_rate_mnd(priv->base, UFS_PHY_UNIPRO_CORE_CLK_CMD_RCGR,
+ freq->pre_div, freq->m, freq->n, freq->src, 8);
+ return freq->freq;
default:
return rate;
}
@@ -116,6 +167,7 @@ static const struct gate_clk sc7280_clks[] = {
GATE_CLK(GCC_USB30_PRIM_MOCK_UTMI_CLK, 0xf01c, 1),
GATE_CLK(GCC_USB3_PRIM_PHY_AUX_CLK, 0xf054, 1),
GATE_CLK(GCC_USB3_PRIM_PHY_COM_AUX_CLK, 0xf058, 1),
+ GATE_CLK(GCC_USB3_PRIM_PHY_PIPE_CLK, 0xf05c, 1),
GATE_CLK(GCC_CFG_NOC_USB3_SEC_AXI_CLK, 0x9e07c, 1),
GATE_CLK(GCC_USB30_SEC_MASTER_CLK, 0x9e010, 1),
GATE_CLK(GCC_AGGRE_USB3_SEC_AXI_CLK, 0x9e080, 1),
@@ -147,6 +199,7 @@ static const struct gate_clk sc7280_clks[] = {
GATE_CLK(GCC_UFS_PHY_AXI_CLK, 0x77010, BIT(0)),
GATE_CLK(GCC_AGGRE_UFS_PHY_AXI_CLK, 0x770cc, BIT(0)),
GATE_CLK(GCC_UFS_PHY_AHB_CLK, 0x77018, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_ICE_CORE_CLK, 0x77064, BIT(0)),
GATE_CLK(GCC_UFS_PHY_UNIPRO_CORE_CLK, 0x7705c, BIT(0)),
GATE_CLK(GCC_UFS_PHY_PHY_AUX_CLK, 0x7709c, BIT(0)),
GATE_CLK(GCC_UFS_PHY_TX_SYMBOL_0_CLK, 0x7701c, BIT(0)),
@@ -155,6 +208,8 @@ static const struct gate_clk sc7280_clks[] = {
GATE_CLK(GCC_UFS_1_CLKREF_EN, 0x8c000, BIT(0)),
GATE_CLK(GCC_SDCC2_AHB_CLK, 0x14008, BIT(0)),
GATE_CLK(GCC_SDCC2_APPS_CLK, 0x14004, BIT(0)),
+ GATE_CLK(GCC_SDCC1_AHB_CLK, 0x75004, BIT(0)),
+ GATE_CLK(GCC_SDCC1_APPS_CLK, 0x75008, BIT(0)),
};
static int sc7280_enable(struct clk *clk)
diff --git a/drivers/clk/qcom/clock-sm6125.c b/drivers/clk/qcom/clock-sm6125.c
new file mode 100644
index 00000000000..1fd72d55e88
--- /dev/null
+++ b/drivers/clk/qcom/clock-sm6125.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Clock drivers for Qualcomm sm6125
+ *
+ * (C) Copyright 2026 Biswapriyo Nath <[email protected]>
+ *
+ */
+
+#include <clk-uclass.h>
+#include <dm.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/bug.h>
+#include <dt-bindings/clock/qcom,gcc-sm6125.h>
+
+#include "clock-qcom.h"
+
+#define GCC_BASE 0x01400000
+
+#define QUPV3_WRAP0_S4_CMD_RCGR 0x1f608
+#define SDCC1_APPS_CLK_CMD_RCGR 0x38028
+#define SDCC2_APPS_CLK_CMD_RCGR 0x1e00c
+
+#define GCC_GPLL0_MODE 0x0
+#define GCC_GPLL3_MODE 0x3000
+#define GCC_GPLL4_MODE 0x4000
+#define GCC_GPLL5_MODE 0x5000
+#define GCC_GPLL6_MODE 0x6000
+#define GCC_GPLL7_MODE 0x7000
+#define GCC_GPLL8_MODE 0x8000
+#define GCC_GPLL9_MODE 0x9000
+
+static const struct freq_tbl ftbl_gcc_qupv3_wrap0_s0_clk_src[] = {
+ F(7372800, CFG_CLK_SRC_GPLL0_AUX2, 1, 384, 15625),
+ F(14745600, CFG_CLK_SRC_GPLL0_AUX2, 1, 768, 15625),
+ F(19200000, CFG_CLK_SRC_CXO, 1, 0, 0),
+ F(29491200, CFG_CLK_SRC_GPLL0_AUX2, 1, 1536, 15625),
+ F(32000000, CFG_CLK_SRC_GPLL0_AUX2, 1, 8, 75),
+ F(48000000, CFG_CLK_SRC_GPLL0_AUX2, 1, 4, 25),
+ F(64000000, CFG_CLK_SRC_GPLL0_AUX2, 1, 16, 75),
+ F(75000000, CFG_CLK_SRC_GPLL0_AUX2, 4, 0, 0),
+ F(80000000, CFG_CLK_SRC_GPLL0_AUX2, 1, 4, 15),
+ F(96000000, CFG_CLK_SRC_GPLL0_AUX2, 1, 8, 25),
+ F(100000000, CFG_CLK_SRC_GPLL0, 6, 0, 0),
+ F(102400000, CFG_CLK_SRC_GPLL0_AUX2, 1, 128, 375),
+ F(112000000, CFG_CLK_SRC_GPLL0_AUX2, 1, 28, 75),
+ F(117964800, CFG_CLK_SRC_GPLL0_AUX2, 1, 6144, 15625),
+ F(120000000, CFG_CLK_SRC_GPLL0_AUX2, 2.5, 0, 0),
+ F(128000000, CFG_CLK_SRC_GPLL6, 3, 0, 0),
+ {}
+};
+
+static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk_src[] = {
+ F(400000, CFG_CLK_SRC_CXO, 12, 1, 4),
+ F(19200000, CFG_CLK_SRC_CXO, 1, 0, 0),
+ F(25000000, CFG_CLK_SRC_GPLL0_AUX2, 12, 0, 0),
+ F(50000000, CFG_CLK_SRC_GPLL0_AUX2, 6, 0, 0),
+ F(100000000, CFG_CLK_SRC_GPLL0_AUX2, 3, 0, 0),
+ {}
+};
+
+static const struct pll_vote_clk gpll0_clk = {
+ .status = 0,
+ .status_bit = BIT(31),
+ .ena_vote = 0x79000,
+ .vote_bit = BIT(0),
+};
+
+static const struct gate_clk sm6125_clks[] = {
+ GATE_CLK(GCC_CFG_NOC_USB3_PRIM_AXI_CLK, 0x1a084, BIT(0)),
+ GATE_CLK(GCC_QUPV3_WRAP0_CORE_2X_CLK, 0x7900c, BIT(9)),
+ GATE_CLK(GCC_QUPV3_WRAP0_CORE_CLK, 0x7900c, BIT(8)),
+ GATE_CLK(GCC_QUPV3_WRAP0_S0_CLK, 0x7900c, BIT(10)),
+ GATE_CLK(GCC_QUPV3_WRAP0_S1_CLK, 0x7900c, BIT(11)),
+ GATE_CLK(GCC_QUPV3_WRAP0_S2_CLK, 0x7900c, BIT(12)),
+ GATE_CLK(GCC_QUPV3_WRAP0_S3_CLK, 0x7900c, BIT(13)),
+ GATE_CLK(GCC_QUPV3_WRAP0_S4_CLK, 0x7900c, BIT(14)),
+ GATE_CLK(GCC_QUPV3_WRAP0_S5_CLK, 0x7900c, BIT(15)),
+ GATE_CLK(GCC_QUPV3_WRAP_0_M_AHB_CLK, 0x7900c, BIT(6)),
+ GATE_CLK(GCC_QUPV3_WRAP_0_S_AHB_CLK, 0x7900c, BIT(7)),
+ GATE_CLK(GCC_SDCC1_AHB_CLK, 0x38008, BIT(0)),
+ GATE_CLK(GCC_SDCC1_APPS_CLK, 0x38004, BIT(0)),
+ GATE_CLK(GCC_SDCC1_ICE_CORE_CLK, 0x3800c, BIT(0)),
+ GATE_CLK(GCC_SDCC2_AHB_CLK, 0x1e008, BIT(0)),
+ GATE_CLK(GCC_SDCC2_APPS_CLK, 0x1e004, BIT(0)),
+ GATE_CLK(GCC_SYS_NOC_CPUSS_AHB_CLK, 0x79004, BIT(0)),
+ GATE_CLK(GCC_SYS_NOC_UFS_PHY_AXI_CLK, 0x45098, BIT(0)),
+ GATE_CLK(GCC_SYS_NOC_USB3_PRIM_AXI_CLK, 0x1a080, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_AHB_CLK, 0x45014, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_AXI_CLK, 0x45010, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_ICE_CORE_CLK, 0x45044, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_PHY_AUX_CLK, 0x45078, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_RX_SYMBOL_0_CLK, 0x4501c, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_TX_SYMBOL_0_CLK, 0x45018, BIT(0)),
+ GATE_CLK(GCC_UFS_PHY_UNIPRO_CORE_CLK, 0x45040, BIT(0)),
+ GATE_CLK(GCC_USB30_PRIM_MASTER_CLK, 0x1a010, BIT(0)),
+ GATE_CLK(GCC_USB30_PRIM_MOCK_UTMI_CLK, 0x1a018, BIT(0)),
+ GATE_CLK(GCC_USB30_PRIM_SLEEP_CLK, 0x1a014, BIT(0)),
+ GATE_CLK(GCC_USB3_PRIM_CLKREF_CLK, 0x80278, BIT(0)),
+ GATE_CLK(GCC_USB3_PRIM_PHY_COM_AUX_CLK, 0x1a054, BIT(0)),
+ GATE_CLK(GCC_USB3_PRIM_PHY_PIPE_CLK, 0x1a058, BIT(0)),
+ GATE_CLK(GCC_AHB2PHY_USB_CLK, 0x1d008, BIT(0)),
+ GATE_CLK(GCC_UFS_MEM_CLKREF_CLK, 0x8c000, BIT(0)),
+};
+
+static ulong sm6125_set_rate(struct clk *clk, ulong rate)
+{
+ struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+ const struct freq_tbl *freq;
+
+ debug("%s: clk %s rate %lu\n", __func__, sm6125_clks[clk->id].name,
+ rate);
+
+ switch (clk->id) {
+ case GCC_QUPV3_WRAP0_S4_CLK:
+ freq = qcom_find_freq(ftbl_gcc_qupv3_wrap0_s0_clk_src, rate);
+ clk_rcg_set_rate_mnd(priv->base, QUPV3_WRAP0_S4_CMD_RCGR,
+ freq->pre_div, freq->m, freq->n, freq->src,
+ 16);
+ return 0;
+ case GCC_SDCC2_APPS_CLK:
+ clk_enable_gpll0(priv->base, &gpll0_clk);
+ freq = qcom_find_freq(ftbl_gcc_sdcc2_apps_clk_src, rate);
+ WARN(freq->src != CFG_CLK_SRC_GPLL0,
+ "SDCC2_APPS_CLK_SRC not set to GPLL0, requested rate %lu\n",
+ rate);
+ clk_rcg_set_rate_mnd(priv->base, SDCC2_APPS_CLK_CMD_RCGR,
+ freq->pre_div, freq->m, freq->n, freq->src,
+ 8);
+ return freq->freq;
+ case GCC_SDCC1_APPS_CLK:
+ /* The firmware turns this on for us and always sets it to this rate */
+ return 384000000;
+ default:
+ return rate;
+ }
+}
+
+static int sm6125_enable(struct clk *clk)
+{
+ struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+
+ if (priv->data->num_clks < clk->id) {
+ debug("%s: unknown clk id %lu\n", __func__, clk->id);
+ return 0;
+ }
+
+ debug("%s: clk %s\n", __func__, sm6125_clks[clk->id].name);
+
+ switch (clk->id) {
+ case GCC_USB30_PRIM_MASTER_CLK:
+ qcom_gate_clk_en(priv, GCC_USB3_PRIM_PHY_COM_AUX_CLK);
+ qcom_gate_clk_en(priv, GCC_USB3_PRIM_CLKREF_CLK);
+ break;
+ }
+
+ return qcom_gate_clk_en(priv, clk->id);
+}
+
+static const struct qcom_reset_map sm6125_gcc_resets[] = {
+ [GCC_QUSB2PHY_PRIM_BCR] = { 0x1c000 },
+ [GCC_QUSB2PHY_SEC_BCR] = { 0x1c004 },
+ [GCC_UFS_PHY_BCR] = { 0x45000 },
+ [GCC_USB30_PRIM_BCR] = { 0x1a000 },
+ [GCC_USB_PHY_CFG_AHB2PHY_BCR] = { 0x1d000 },
+ [GCC_USB3PHY_PHY_PRIM_SP0_BCR] = { 0x1b008 },
+ [GCC_USB3_PHY_PRIM_SP0_BCR] = { 0x1b000 },
+ [GCC_CAMSS_MICRO_BCR] = { 0x560ac },
+};
+
+static const struct qcom_power_map sm6125_gdscs[] = {
+ [USB30_PRIM_GDSC] = { 0x1a004 },
+ [UFS_PHY_GDSC] = { 0x45004 },
+ [CAMSS_VFE0_GDSC] = { 0x54004 },
+ [CAMSS_VFE1_GDSC] = { 0x5403c },
+ [CAMSS_TOP_GDSC] = { 0x5607c },
+ [CAM_CPP_GDSC] = { 0x560bc },
+ [HLOS1_VOTE_TURING_MMU_TBU1_GDSC] = { 0x7d060 },
+ [HLOS1_VOTE_MM_SNOC_MMU_TBU_RT_GDSC] = { 0x80074 },
+ [HLOS1_VOTE_MM_SNOC_MMU_TBU_NRT_GDSC] = { 0x80084 },
+ [HLOS1_VOTE_TURING_MMU_TBU0_GDSC] = { 0x80094 },
+};
+
+static const phys_addr_t sm6125_gpll_addrs[] = {
+ GCC_BASE + GCC_GPLL0_MODE, GCC_BASE + GCC_GPLL3_MODE,
+ GCC_BASE + GCC_GPLL4_MODE, GCC_BASE + GCC_GPLL5_MODE,
+ GCC_BASE + GCC_GPLL6_MODE, GCC_BASE + GCC_GPLL7_MODE,
+ GCC_BASE + GCC_GPLL8_MODE, GCC_BASE + GCC_GPLL9_MODE,
+};
+
+static const phys_addr_t sm6125_rcg_addrs[] = {
+ 0x0141a01c, // GCC_USB30_PRIM_MASTER_CMD_RCGR
+ 0x0141a034, // GCC_USB30_PRIM_MOCK_UTMI_CMD_RCGR
+ 0x0141a060, // GCC_USB3_PRIM_PHY_AUX_CMD_RCGR
+ 0x01438028, // GCC_SDCC1_APPS_CMD_RCGR
+ 0x0141e00c, // GCC_SDCC2_APPS_CMD_RCGR
+ 0x0141f148, // GCC_QUPV3_WRAP0_S0_CMD_RCGR
+ 0x0141f278, // GCC_QUPV3_WRAP0_S1_CMD_RCGR
+ 0x0141f3a8, // GCC_QUPV3_WRAP0_S2_CMD_RCGR
+ 0x0141f4d8, // GCC_QUPV3_WRAP0_S3_CMD_RCGR
+ 0x0141f608, // GCC_QUPV3_WRAP0_S4_CMD_RCGR
+ 0x0141f738, // GCC_QUPV3_WRAP0_S5_CMD_RCGR
+ 0x01445020, // GCC_UFS_PHY_AXI_CMD_RCGR
+ 0x01445048, // GCC_UFS_PHY_ICE_CORE_CMD_RCGR
+ 0x01445060, // GCC_UFS_PHY_UNIPRO_CORE_CMD_RCGR
+ 0x0144507c, // GCC_UFS_PHY_PHY_AUX_CMD_RCGR
+};
+
+static const char *const sm6125_rcg_names[] = {
+ "GCC_USB30_PRIM_MASTER_CMD_RCGR",
+ "GCC_USB30_PRIM_MOCK_UTMI_CMD_RCGR",
+ "GCC_USB3_PRIM_PHY_AUX_CMD_RCGR",
+ "GCC_SDCC1_APPS_CMD_RCGR",
+ "GCC_SDCC2_APPS_CMD_RCGR",
+ "GCC_QUPV3_WRAP0_S0_CMD_RCGR",
+ "GCC_QUPV3_WRAP0_S1_CMD_RCGR",
+ "GCC_QUPV3_WRAP0_S2_CMD_RCGR",
+ "GCC_QUPV3_WRAP0_S3_CMD_RCGR",
+ "GCC_QUPV3_WRAP0_S4_CMD_RCGR",
+ "GCC_QUPV3_WRAP0_S5_CMD_RCGR",
+ "GCC_UFS_PHY_AXI_CMD_RCGR",
+ "GCC_UFS_PHY_ICE_CORE_CMD_RCGR",
+ "GCC_UFS_PHY_UNIPRO_CORE_CMD_RCGR",
+ "GCC_UFS_PHY_PHY_AUX_CMD_RCGR",
+};
+
+static struct msm_clk_data sm6125_gcc_data = {
+ .resets = sm6125_gcc_resets,
+ .num_resets = ARRAY_SIZE(sm6125_gcc_resets),
+ .clks = sm6125_clks,
+ .num_clks = ARRAY_SIZE(sm6125_clks),
+ .power_domains = sm6125_gdscs,
+ .num_power_domains = ARRAY_SIZE(sm6125_gdscs),
+
+ .enable = sm6125_enable,
+ .set_rate = sm6125_set_rate,
+
+ .dbg_pll_addrs = sm6125_gpll_addrs,
+ .num_plls = ARRAY_SIZE(sm6125_gpll_addrs),
+ .dbg_rcg_addrs = sm6125_rcg_addrs,
+ .num_rcgs = ARRAY_SIZE(sm6125_rcg_addrs),
+ .dbg_rcg_names = sm6125_rcg_names,
+};
+
+static const struct udevice_id gcc_sm6125_of_match[] = {
+ {
+ .compatible = "qcom,gcc-sm6125",
+ .data = (ulong)&sm6125_gcc_data,
+ },
+ {}
+};
+
+U_BOOT_DRIVER(gcc_sm6125) = {
+ .name = "gcc_sm6125",
+ .id = UCLASS_NOP,
+ .of_match = gcc_sm6125_of_match,
+ .bind = qcom_cc_bind,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig
index 51c87cc3606..1893b6c4181 100644
--- a/drivers/clk/renesas/Kconfig
+++ b/drivers/clk/renesas/Kconfig
@@ -61,11 +61,11 @@ config CLK_RCAR_GEN3
Enable this to support the clocks on Renesas R-Car Gen3 and Gen4 SoCs.
config CLK_R8A774A1
- bool "Renesas R8A774A1 clock driver"
+ bool "Renesas R8A774A1 clock driver"
def_bool y if R8A774A1
- depends on CLK_RCAR_GEN3
- help
- Enable this to support the clocks on Renesas R8A774A1 SoC.
+ depends on CLK_RCAR_GEN3
+ help
+ Enable this to support the clocks on Renesas R8A774A1 SoC.
config CLK_R8A774B1
bool "Renesas R8A774B1 clock driver"
@@ -157,6 +157,12 @@ config CLK_R8A779H0
help
Enable this to support the clocks on Renesas R8A779H0 SoC.
+config CLK_R8A78000
+ bool "Renesas R8A78000 clock driver"
+ depends on CLK_RENESAS
+ help
+ Enable this to support the clocks on Renesas R8A78000 SoC.
+
config CLK_R9A06G032
bool "Renesas R9A06G032 clock driver"
depends on CLK_RENESAS
diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile
index 354035baf2d..fb8d4c1f2f6 100644
--- a/drivers/clk/renesas/Makefile
+++ b/drivers/clk/renesas/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_CLK_R8A779A0) += r8a779a0-cpg-mssr.o
obj-$(CONFIG_CLK_R8A779F0) += r8a779f0-cpg-mssr.o
obj-$(CONFIG_CLK_R8A779G0) += r8a779g0-cpg-mssr.o
obj-$(CONFIG_CLK_R8A779H0) += r8a779h0-cpg-mssr.o
+obj-$(CONFIG_CLK_R8A78000) += r8a78000-cpg.o
obj-$(CONFIG_CLK_R9A06G032) += r9a06g032-clocks.o
obj-$(CONFIG_CLK_RZG2L) += rzg2l-cpg.o
obj-$(CONFIG_CLK_R9A07G044) += r9a07g044-cpg.o
diff --git a/drivers/clk/renesas/r8a78000-cpg.c b/drivers/clk/renesas/r8a78000-cpg.c
new file mode 100644
index 00000000000..e9ca06476f6
--- /dev/null
+++ b/drivers/clk/renesas/r8a78000-cpg.c
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Renesas R-Car Gen5 CPG driver
+ *
+ * Copyright (C) 2026 Marek Vasut <[email protected]>
+ */
+
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <linux/clk-provider.h>
+
+#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
+#include <scmi_protocols.h>
+
+#include <dt-bindings/clock/r8a78000-clock-scmi.h>
+
+#if IS_ENABLED(CONFIG_CLK_SCMI)
+struct gen5_clk_priv {
+ struct udevice *clk;
+ u32 basever;
+};
+
+static struct clk *gen5_clk_get_by_scmi_id(struct clk *clk)
+{
+ struct gen5_clk_priv *priv = dev_get_priv(clk->dev);
+ struct udevice *sdev;
+ struct uclass *uc;
+
+ uclass_id_foreach_dev(UCLASS_CLK, sdev, uc)
+ if (sdev->seq_ == priv->clk->seq_ + clk->id + 1)
+ return dev_get_clk_ptr(sdev);
+
+ return NULL;
+}
+
+static ulong gen5_clk_round_rate(struct clk *clk, ulong rate)
+{
+ struct clk *scmi = gen5_clk_get_by_scmi_id(clk);
+
+ if (!scmi)
+ return -ENODEV;
+
+ return clk_round_rate(scmi, rate);
+}
+
+static ulong gen5_clk_get_rate(struct clk *clk)
+{
+ struct clk *scmi = gen5_clk_get_by_scmi_id(clk);
+
+ if (!scmi)
+ return -ENODEV;
+
+ return clk_get_rate(scmi);
+}
+
+static ulong gen5_clk_set_rate(struct clk *clk, ulong rate)
+{
+ struct clk *scmi = gen5_clk_get_by_scmi_id(clk);
+
+ if (!scmi)
+ return -ENODEV;
+
+ return clk_set_rate(scmi, rate);
+}
+
+static int gen5_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct clk *scmi = gen5_clk_get_by_scmi_id(clk);
+
+ if (!scmi)
+ return -ENODEV;
+
+ return clk_set_parent(scmi, parent);
+}
+
+static int gen5_clk_enable(struct clk *clk)
+{
+ struct clk *scmi = gen5_clk_get_by_scmi_id(clk);
+
+ if (!scmi)
+ return -ENODEV;
+
+ return clk_enable(scmi);
+}
+
+static int gen5_clk_disable(struct clk *clk)
+{
+ struct clk *scmi = gen5_clk_get_by_scmi_id(clk);
+
+ if (!scmi)
+ return -ENODEV;
+
+ return clk_disable(scmi);
+}
+
+struct clk_map_in {
+ u16 dt_id; /* DT binding clock ID */
+ u16 fw_id; /* SCMI firmware clock ID */
+};
+
+#define GEN5_SCMI_SDK_4_28 0x010a0000
+#define GEN5_SCMI_SDK_4_29 0x010b0000
+#define GEN5_SCMI_SDK_4_30 0x010c0000
+#define GEN5_SCMI_SDK_4_31 0x010d0000
+#define GEN5_SCMI_SDK_4_32 0x010e0000
+
+static const struct clk_map_in gen5_clk_map_dt_sdk_4_28[] = {
+ { SCP_CLOCK_ID_MDLC_UFS0, 202 },
+ { SCP_CLOCK_ID_MDLC_UFS1, 203 },
+ { SCP_CLOCK_ID_MDLC_SDHI0, 204 },
+ { SCP_CLOCK_ID_MDLC_XPCS0, 316 },
+ { SCP_CLOCK_ID_MDLC_XPCS1, 317 },
+ { SCP_CLOCK_ID_MDLC_XPCS2, 318 },
+ { SCP_CLOCK_ID_MDLC_XPCS3, 319 },
+ { SCP_CLOCK_ID_MDLC_XPCS4, 320 },
+ { SCP_CLOCK_ID_MDLC_XPCS5, 321 },
+ { SCP_CLOCK_ID_MDLC_XPCS6, 322 },
+ { SCP_CLOCK_ID_MDLC_XPCS7, 323 },
+ { SCP_CLOCK_ID_MDLC_RSW3, 324 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSN, 325 },
+ { SCP_CLOCK_ID_MDLC_RSW3AES, 326 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES0, 327 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES1, 328 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES2, 329 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES3, 330 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES4, 331 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES5, 332 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES6, 333 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES7, 334 },
+ { SCP_CLOCK_ID_MDLC_RSW3MFWD, 335 },
+ { SCP_CLOCK_ID_MDLC_MPPHY01, 344 },
+ { SCP_CLOCK_ID_MDLC_MPPHY11, 345 },
+ { SCP_CLOCK_ID_MDLC_MPPHY21, 346 },
+ { SCP_CLOCK_ID_MDLC_MPPHY31, 347 },
+ { SCP_CLOCK_ID_MDLC_MPPHY02, 348 },
+ { SCP_CLOCK_ID_CLK_S0D6_PERE_MAIN, 1691 },
+};
+
+static const struct clk_map_in gen5_clk_map_dt_sdk_4_31[] = {
+ { SCP_CLOCK_ID_MDLC_UFS0, 198 },
+ { SCP_CLOCK_ID_MDLC_UFS1, 199 },
+ { SCP_CLOCK_ID_MDLC_SDHI0, 200 },
+ { SCP_CLOCK_ID_MDLC_XPCS0, 312 },
+ { SCP_CLOCK_ID_MDLC_XPCS1, 313 },
+ { SCP_CLOCK_ID_MDLC_XPCS2, 314 },
+ { SCP_CLOCK_ID_MDLC_XPCS3, 315 },
+ { SCP_CLOCK_ID_MDLC_XPCS4, 316 },
+ { SCP_CLOCK_ID_MDLC_XPCS5, 317 },
+ { SCP_CLOCK_ID_MDLC_XPCS6, 318 },
+ { SCP_CLOCK_ID_MDLC_XPCS7, 319 },
+ { SCP_CLOCK_ID_MDLC_RSW3, 320 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSN, 321 },
+ { SCP_CLOCK_ID_MDLC_RSW3AES, 322 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES0, 323 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES1, 324 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES2, 325 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES3, 326 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES4, 327 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES5, 328 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES6, 329 },
+ { SCP_CLOCK_ID_MDLC_RSW3TSNTES7, 330 },
+ { SCP_CLOCK_ID_MDLC_RSW3MFWD, 331 },
+ { SCP_CLOCK_ID_MDLC_MPPHY01, 340 },
+ { SCP_CLOCK_ID_MDLC_MPPHY11, 341 },
+ { SCP_CLOCK_ID_MDLC_MPPHY21, 342 },
+ { SCP_CLOCK_ID_MDLC_MPPHY31, 343 },
+ { SCP_CLOCK_ID_MDLC_MPPHY02, 344 },
+ { SCP_CLOCK_ID_CLK_S0D6_PERE_MAIN, 1687 },
+};
+
+static int gen5_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
+{
+ struct gen5_clk_priv *priv = dev_get_priv(clk->dev);
+ const struct clk_map_in *map;
+ unsigned int map_size;
+ int i;
+
+ if (args->args_count != 1) {
+ debug("Invalid args_count: %d\n", args->args_count);
+ return -EINVAL;
+ }
+
+ if (priv->basever == GEN5_SCMI_SDK_4_28) {
+ map = gen5_clk_map_dt_sdk_4_28;
+ map_size = ARRAY_SIZE(gen5_clk_map_dt_sdk_4_28);
+ } else if (priv->basever == GEN5_SCMI_SDK_4_31 ||
+ priv->basever == GEN5_SCMI_SDK_4_32) {
+ map = gen5_clk_map_dt_sdk_4_31;
+ map_size = ARRAY_SIZE(gen5_clk_map_dt_sdk_4_31);
+ } else {
+ printf("Unsupported SCMI base protocol version %x\n", priv->basever);
+ return -EINVAL;
+ }
+
+ clk->id = -1;
+ for (i = 0; i < map_size; i++) {
+ if (map[i].dt_id != args->args[0])
+ continue;
+ clk->id = map[i].fw_id;
+ break;
+ }
+
+ if (clk->id == -1)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct clk_ops gen5_clk_ops = {
+ .round_rate = gen5_clk_round_rate,
+ .get_rate = gen5_clk_get_rate,
+ .set_rate = gen5_clk_set_rate,
+ .set_parent = gen5_clk_set_parent,
+ .enable = gen5_clk_enable,
+ .disable = gen5_clk_disable,
+ .of_xlate = gen5_clk_of_xlate,
+};
+
+static int gen5_clk_probe(struct udevice *dev)
+{
+ struct gen5_clk_priv *priv = dev_get_priv(dev);
+ struct udevice *agent;
+ int ret;
+
+ ret = uclass_get_device(UCLASS_SCMI_AGENT, 0, &agent);
+ if (ret)
+ return ret;
+
+ if (!agent)
+ return -ENODEV;
+
+ priv->basever = scmi_impl_version(agent);
+
+ return uclass_get_device_by_driver(UCLASS_CLK, DM_DRIVER_GET(scmi_clock),
+ &priv->clk);
+}
+#else
+static int gen5_clk_enable(struct clk *clk)
+{
+ return 0;
+}
+
+static int gen5_clk_disable(struct clk *clk)
+{
+ return 0;
+}
+
+static int gen5_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;
+ }
+
+ clk->id = args->args[0];
+
+ return 0;
+}
+
+static const struct clk_ops gen5_clk_ops = {
+ .enable = gen5_clk_enable,
+ .disable = gen5_clk_disable,
+ .of_xlate = gen5_clk_of_xlate,
+};
+#endif
+
+static const struct udevice_id r8a78000_mdlc_ids[] = {
+ { .compatible = "renesas,r8a78000-cpg", },
+ { }
+};
+
+U_BOOT_DRIVER(clk_gen5) = {
+ .name = "clk_gen5",
+ .id = UCLASS_CLK,
+ .of_match = r8a78000_mdlc_ids,
+ .priv_auto = CONFIG_IS_ENABLED(CLK_SCMI, (sizeof(struct gen5_clk_priv)), (0)),
+ .ops = &gen5_clk_ops,
+ .probe = CONFIG_IS_ENABLED(CLK_SCMI, (gen5_clk_probe), (NULL)),
+ .flags = DM_FLAG_OS_PREPARE | DM_FLAG_VITAL,
+};
diff --git a/drivers/clk/renesas/rcar-cpg-lib.c b/drivers/clk/renesas/rcar-cpg-lib.c
index 60ab2cb379e..8e992d2c2da 100644
--- a/drivers/clk/renesas/rcar-cpg-lib.c
+++ b/drivers/clk/renesas/rcar-cpg-lib.c
@@ -30,10 +30,6 @@
#define SDnSRCFC_SHIFT 2
#define STPnHCK_TABLE (CPG_SDCKCR_STPnHCK >> SDnSRCFC_SHIFT)
-/* Non-constant mask variant of FIELD_GET/FIELD_PREP */
-#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
-#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
-
static const struct clk_div_table cpg_sdh_div_table[] = {
{ 0, 1 }, { 1, 2 }, { STPnHCK_TABLE | 2, 4 }, { STPnHCK_TABLE | 3, 8 },
{ STPnHCK_TABLE | 4, 16 }, { 0, 0 },
diff --git a/drivers/clk/rockchip/clk_rk3528.c b/drivers/clk/rockchip/clk_rk3528.c
index bcdc0f930d2..cf8c3a62349 100644
--- a/drivers/clk/rockchip/clk_rk3528.c
+++ b/drivers/clk/rockchip/clk_rk3528.c
@@ -1335,6 +1335,7 @@ static ulong rk3528_clk_get_rate(struct clk *clk)
DPLL);
break;
+ case CLK_REF_USB3OTG:
case TCLK_EMMC:
case TCLK_WDT_NS:
rate = OSC_HZ;
@@ -1455,6 +1456,7 @@ static ulong rk3528_clk_set_rate(struct clk *clk, ulong rate)
priv->ppll_hz = rockchip_pll_get_rate(&rk3528_pll_clks[PPLL],
priv->cru, PPLL);
break;
+ case CLK_REF_USB3OTG:
case TCLK_EMMC:
case TCLK_WDT_NS:
return (rate == OSC_HZ) ? 0 : -EINVAL;
diff --git a/drivers/clk/rockchip/clk_rk3576.c b/drivers/clk/rockchip/clk_rk3576.c
index 1026af27ca1..db8ce25852f 100644
--- a/drivers/clk/rockchip/clk_rk3576.c
+++ b/drivers/clk/rockchip/clk_rk3576.c
@@ -1549,6 +1549,24 @@ static ulong rk3576_gmac_get_clk(struct rk3576_clk_priv *priv, ulong clk_id)
con = readl(&cru->clksel_con[31]);
div = (con & CLK_GMAC1_125M_DIV_MASK) >> CLK_GMAC1_125M_DIV_SHIFT;
return DIV_TO_RATE(priv->cpll_hz, div);
+ case REFCLKO25M_GMAC0_OUT:
+ con = readl(&cru->clksel_con[36]);
+ div = (con & CLK_REFCLKO25M_GMAC0_DIV_MASK) >> CLK_REFCLKO25M_GMAC0_DIV_SHIFT;
+ src = (con & CLK_REFCLKO25M_GMAC0_SEL_MASK) >> CLK_REFCLKO25M_GMAC0_SEL_SHIFT;
+ if (src == CLK_REFCLKO25M_GMAC0_SEL_CPLL)
+ p_rate = priv->cpll_hz;
+ else
+ p_rate = priv->gpll_hz;
+ return DIV_TO_RATE(p_rate, div);
+ case REFCLKO25M_GMAC1_OUT:
+ con = readl(&cru->clksel_con[36]);
+ div = (con & CLK_REFCLKO25M_GMAC1_DIV_MASK) >> CLK_REFCLKO25M_GMAC1_DIV_SHIFT;
+ src = (con & CLK_REFCLKO25M_GMAC1_SEL_MASK) >> CLK_REFCLKO25M_GMAC1_SEL_SHIFT;
+ if (src == CLK_REFCLKO25M_GMAC1_SEL_CPLL)
+ p_rate = priv->cpll_hz;
+ else
+ p_rate = priv->gpll_hz;
+ return DIV_TO_RATE(p_rate, div);
default:
return -ENOENT;
}
@@ -1608,6 +1626,34 @@ static ulong rk3576_gmac_set_clk(struct rk3576_clk_priv *priv,
CLK_GMAC1_125M_DIV_MASK,
(div - 1) << CLK_GMAC1_125M_DIV_SHIFT);
break;
+ case REFCLKO25M_GMAC0_OUT:
+ if (!(priv->gpll_hz % rate)) {
+ src = CLK_REFCLKO25M_GMAC0_SEL_GPLL;
+ div = priv->gpll_hz / rate;
+ } else {
+ src = CLK_REFCLKO25M_GMAC0_SEL_CPLL;
+ div = priv->cpll_hz / rate;
+ }
+ rk_clrsetreg(&cru->clksel_con[36],
+ CLK_REFCLKO25M_GMAC0_SEL_MASK |
+ CLK_REFCLKO25M_GMAC0_DIV_MASK,
+ src << CLK_REFCLKO25M_GMAC0_SEL_SHIFT |
+ (div - 1) << CLK_REFCLKO25M_GMAC0_DIV_SHIFT);
+ break;
+ case REFCLKO25M_GMAC1_OUT:
+ if (!(priv->gpll_hz % rate)) {
+ src = CLK_REFCLKO25M_GMAC1_SEL_GPLL;
+ div = priv->gpll_hz / rate;
+ } else {
+ src = CLK_REFCLKO25M_GMAC1_SEL_CPLL;
+ div = priv->cpll_hz / rate;
+ }
+ rk_clrsetreg(&cru->clksel_con[36],
+ CLK_REFCLKO25M_GMAC1_SEL_MASK |
+ CLK_REFCLKO25M_GMAC1_DIV_MASK,
+ src << CLK_REFCLKO25M_GMAC1_SEL_SHIFT |
+ (div - 1) << CLK_REFCLKO25M_GMAC1_DIV_SHIFT);
+ break;
default:
return -ENOENT;
}
@@ -1987,6 +2033,8 @@ static ulong rk3576_clk_get_rate(struct clk *clk)
case HCLK_SDIO:
rate = rk3576_mmc_get_clk(priv, clk->id);
break;
+ case CLK_REF_USB3OTG0:
+ case CLK_REF_USB3OTG1:
case TCLK_EMMC:
case TCLK_WDT0:
rate = OSC_HZ;
@@ -2014,6 +2062,8 @@ static ulong rk3576_clk_get_rate(struct clk *clk)
case CLK_GMAC1_PTP_REF:
case CLK_GMAC0_125M_SRC:
case CLK_GMAC1_125M_SRC:
+ case REFCLKO25M_GMAC0_OUT:
+ case REFCLKO25M_GMAC1_OUT:
rate = rk3576_gmac_get_clk(priv, clk->id);
break;
case CLK_UART_FRAC_0:
@@ -2151,6 +2201,8 @@ static ulong rk3576_clk_set_rate(struct clk *clk, ulong rate)
case HCLK_SDIO:
ret = rk3576_mmc_set_clk(priv, clk->id, rate);
break;
+ case CLK_REF_USB3OTG0:
+ case CLK_REF_USB3OTG1:
case TCLK_EMMC:
case TCLK_WDT0:
ret = OSC_HZ;
@@ -2193,6 +2245,8 @@ static ulong rk3576_clk_set_rate(struct clk *clk, ulong rate)
case CLK_GMAC1_PTP_REF:
case CLK_GMAC0_125M_SRC:
case CLK_GMAC1_125M_SRC:
+ case REFCLKO25M_GMAC0_OUT:
+ case REFCLKO25M_GMAC1_OUT:
ret = rk3576_gmac_set_clk(priv, clk->id, rate);
break;
case CLK_UART_FRAC_0:
diff --git a/drivers/clk/sophgo/clk-cv1800b.c b/drivers/clk/sophgo/clk-cv1800b.c
index d946ea57a46..248a69321fc 100644
--- a/drivers/clk/sophgo/clk-cv1800b.c
+++ b/drivers/clk/sophgo/clk-cv1800b.c
@@ -500,9 +500,12 @@ static int cv1800b_register_clk(struct udevice *dev)
{
struct clk osc;
ulong osc_rate;
- void *base = devfdt_get_addr_ptr(dev);
+ void __iomem *base = dev_read_addr_ptr(dev);
int i, ret;
+ if (!base)
+ return -EINVAL;
+
ret = clk_get_by_index(dev, 0, &osc);
if (ret) {
pr_err("Failed to get clock\n");
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/stm32/Kconfig b/drivers/clk/stm32/Kconfig
index 4e488136eac..e63385d3051 100644
--- a/drivers/clk/stm32/Kconfig
+++ b/drivers/clk/stm32/Kconfig
@@ -49,7 +49,7 @@ config CLK_STM32MP21
config CLK_STM32MP25
bool "Enable RCC clock driver for STM32MP25"
depends on ARCH_STM32MP && CLK
- default y if STM32MP25X
+ default y if STM32MP23X || STM32MP25X
select CLK_STM32_CORE
help
Enable the STM32 clock (RCC) driver. Enable support for
diff --git a/drivers/clk/sunxi/clk_sunxi.c b/drivers/clk/sunxi/clk_sunxi.c
index 842a0541bd6..046d5d1605a 100644
--- a/drivers/clk/sunxi/clk_sunxi.c
+++ b/drivers/clk/sunxi/clk_sunxi.c
@@ -64,7 +64,7 @@ static int sunxi_clk_disable(struct clk *clk)
return sunxi_set_gate(clk, false);
}
-struct clk_ops sunxi_clk_ops = {
+static const struct clk_ops sunxi_clk_ops = {
.enable = sunxi_clk_enable,
.disable = sunxi_clk_disable,
};
diff --git a/drivers/clk/ti/clk-ctrl.c b/drivers/clk/ti/clk-ctrl.c
index c5c97dc35c4..e3564277982 100644
--- a/drivers/clk/ti/clk-ctrl.c
+++ b/drivers/clk/ti/clk-ctrl.c
@@ -8,7 +8,11 @@
#include <dm.h>
#include <dm/device_compat.h>
#include <clk-uclass.h>
-#include <asm/arch-am33xx/clock.h>
+#include <asm/ti-common/omap_clock.h>
+#include <asm/io.h>
+#include <linux/iopoll.h>
+
+#define LDELAY 1000000
struct clk_ti_ctrl_offs {
fdt_addr_t start;
@@ -33,10 +37,66 @@ static int clk_ti_ctrl_check_offs(struct clk *clk, fdt_addr_t offs)
return -EFAULT;
}
+#define IDLEST_DISABLED (MODULE_CLKCTRL_IDLEST_DISABLED << MODULE_CLKCTRL_IDLEST_SHIFT)
+#define IDLEST_TRANSITION (MODULE_CLKCTRL_IDLEST_TRANSITIONING << MODULE_CLKCTRL_IDLEST_SHIFT)
+static inline void wait_for_clk_disable(u32 addr)
+{
+ 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);
+
+ wait_for_clk_disable(addr);
+
+ return 0;
+}
+
+static inline void wait_for_clk_enable(u32 addr)
+{
+ 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);
+
+ wait_for_clk_enable(addr);
+
+ return 0;
+}
+
static int clk_ti_ctrl_disable(struct clk *clk)
{
struct clk_ti_ctrl_priv *priv = dev_get_priv(clk->dev);
- u32 *clk_modules[2] = { };
fdt_addr_t offs;
int err;
@@ -47,16 +107,13 @@ static int clk_ti_ctrl_disable(struct clk *clk)
return err;
}
- clk_modules[0] = (u32 *)(offs);
- dev_dbg(clk->dev, "disable module @ %p\n", clk_modules[0]);
- do_disable_clocks(NULL, clk_modules, 1);
- return 0;
+ dev_dbg(clk->dev, "disable module @ %x\n", offs);
+ return clk_ti_ctrl_disable_clock_module(offs);
}
static int clk_ti_ctrl_enable(struct clk *clk)
{
struct clk_ti_ctrl_priv *priv = dev_get_priv(clk->dev);
- u32 *clk_modules[2] = { };
fdt_addr_t offs;
int err;
@@ -67,10 +124,8 @@ static int clk_ti_ctrl_enable(struct clk *clk)
return err;
}
- clk_modules[0] = (u32 *)(offs);
- dev_dbg(clk->dev, "enable module @ %p\n", clk_modules[0]);
- do_enable_clocks(NULL, clk_modules, 1);
- return 0;
+ dev_dbg(clk->dev, "enable module @ %x\n", offs);
+ return clk_ti_ctrl_enable_clock_module(offs);
}
static ulong clk_ti_ctrl_get_rate(struct clk *clk)