summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2019-08-23 10:03:13 -0400
committerTom Rini <trini@konsulko.com>2019-08-23 10:03:13 -0400
commitb242d1b13e43906af288d8a8bdf396ba2cfbf2d1 (patch)
tree8746e9a5a9966b3df3a695814e1291c07b768c21
parentc399dca83434290ac502207f2d6db13c59d89267 (diff)
parentf62ec5c4bba2556202523b0f48655dacbe90753a (diff)
downloadu-boot-WIP/23Aug2019.tar.gz
Merge https://gitlab.denx.de/u-boot/custodians/u-boot-clkWIP/23Aug2019
-rw-r--r--cmd/clk.c79
-rw-r--r--drivers/clk/Kconfig2
-rw-r--r--drivers/clk/clk-uclass.c77
-rw-r--r--drivers/clk/clk.c1
-rw-r--r--drivers/clk/clk_fixed_rate.c1
-rw-r--r--drivers/clk/clk_sandbox_ccf.c15
-rw-r--r--drivers/clk/imx/Kconfig16
-rw-r--r--drivers/clk/imx/Makefile2
-rw-r--r--drivers/clk/imx/clk-composite-8m.c170
-rw-r--r--drivers/clk/imx/clk-imx8mm.c415
-rw-r--r--drivers/clk/imx/clk-pll14xx.c381
-rw-r--r--drivers/clk/imx/clk.h25
-rw-r--r--include/clk.h1
-rw-r--r--include/sandbox-clk.h3
-rw-r--r--test/dm/clk_ccf.c28
15 files changed, 1178 insertions, 38 deletions
diff --git a/cmd/clk.c b/cmd/clk.c
index 5402c87de7..74ad868500 100644
--- a/cmd/clk.c
+++ b/cmd/clk.c
@@ -7,51 +7,70 @@
#include <clk.h>
#if defined(CONFIG_DM) && defined(CONFIG_CLK)
#include <dm.h>
+#include <dm/device.h>
+#include <dm/root.h>
#include <dm/device-internal.h>
+#include <linux/clk-provider.h>
#endif
-int __weak soc_clk_dump(void)
-{
#if defined(CONFIG_DM) && defined(CONFIG_CLK)
- struct udevice *dev;
- struct uclass *uc;
- struct clk clk;
- int ret;
- ulong rate;
-
- /* Device addresses start at 1 */
- ret = uclass_get(UCLASS_CLK, &uc);
- if (ret)
- return ret;
-
- uclass_foreach_dev(dev, uc) {
- memset(&clk, 0, sizeof(clk));
- ret = device_probe(dev);
- if (ret)
- goto noclk;
+static void show_clks(struct udevice *dev, int depth, int last_flag)
+{
+ int i, is_last;
+ struct udevice *child;
+ struct clk *clkp;
+ u32 rate;
+
+ clkp = dev_get_clk_ptr(dev);
+ if (device_get_uclass_id(dev) == UCLASS_CLK && clkp) {
+ rate = clk_get_rate(clkp);
+
+ printf(" %-12u %8d ", rate, clkp->enable_count);
+
+ for (i = depth; i >= 0; i--) {
+ is_last = (last_flag >> i) & 1;
+ if (i) {
+ if (is_last)
+ printf(" ");
+ else
+ printf("| ");
+ } else {
+ if (is_last)
+ printf("`-- ");
+ else
+ printf("|-- ");
+ }
+ }
- ret = clk_request(dev, &clk);
- if (ret)
- goto noclk;
+ printf("%s\n", dev->name);
+ }
- rate = clk_get_rate(&clk);
- clk_free(&clk);
+ list_for_each_entry(child, &dev->child_head, sibling_node) {
+ is_last = list_is_last(&child->sibling_node, &dev->child_head);
+ show_clks(child, depth + 1, (last_flag << 1) | is_last);
+ }
+}
- if (rate == -ENODEV)
- goto noclk;
+int __weak soc_clk_dump(void)
+{
+ struct udevice *root;
- printf("%-30.30s : %lu Hz\n", dev->name, rate);
- continue;
- noclk:
- printf("%-30.30s : ? Hz\n", dev->name);
+ root = dm_root();
+ if (root) {
+ printf(" Rate Usecnt Name\n");
+ printf("------------------------------------------\n");
+ show_clks(root, -1, 0);
}
return 0;
+}
#else
+int __weak soc_clk_dump(void)
+{
puts("Not implemented\n");
return 1;
-#endif
}
+#endif
static int do_clk_dump(cmd_tbl_t *cmdtp, int flag, int argc,
char *const argv[])
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index a3f0171b45..82cd8f623c 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -48,7 +48,6 @@ config CLK_BOSTON
config SPL_CLK_CCF
bool "SPL Common Clock Framework [CCF] support "
- depends on SPL_CLK_IMX6Q
help
Enable this option if you want to (re-)use the Linux kernel's Common
Clock Framework [CCF] code in U-Boot's SPL.
@@ -62,7 +61,6 @@ config SPL_CLK_COMPOSITE_CCF
config CLK_CCF
bool "Common Clock Framework [CCF] support "
- depends on CLK_IMX6Q || SANDBOX_CLK_CCF
help
Enable this option if you want to (re-)use the Linux kernel's Common
Clock Framework [CCF] code in U-Boot's clock driver.
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index c66b6f3c4e..64c181f4ad 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -449,13 +449,45 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
int clk_enable(struct clk *clk)
{
const struct clk_ops *ops = clk_dev_ops(clk->dev);
+ struct clk *clkp = NULL;
+ int ret;
debug("%s(clk=%p)\n", __func__, clk);
- if (!ops->enable)
- return -ENOSYS;
+ if (CONFIG_IS_ENABLED(CLK_CCF)) {
+ /* Take id 0 as a non-valid clk, such as dummy */
+ if (clk->id && !clk_get_by_id(clk->id, &clkp)) {
+ if (clkp->enable_count) {
+ clkp->enable_count++;
+ return 0;
+ }
+ if (clkp->dev->parent &&
+ device_get_uclass_id(clkp->dev) == UCLASS_CLK) {
+ ret = clk_enable(dev_get_clk_ptr(clkp->dev->parent));
+ if (ret) {
+ printf("Enable %s failed\n",
+ clkp->dev->parent->name);
+ return ret;
+ }
+ }
+ }
- return ops->enable(clk);
+ if (ops->enable) {
+ ret = ops->enable(clk);
+ if (ret) {
+ printf("Enable %s failed\n", clk->dev->name);
+ return ret;
+ }
+ }
+ if (clkp)
+ clkp->enable_count++;
+ } else {
+ if (!ops->enable)
+ return -ENOSYS;
+ return ops->enable(clk);
+ }
+
+ return 0;
}
int clk_enable_bulk(struct clk_bulk *bulk)
@@ -474,13 +506,46 @@ int clk_enable_bulk(struct clk_bulk *bulk)
int clk_disable(struct clk *clk)
{
const struct clk_ops *ops = clk_dev_ops(clk->dev);
+ struct clk *clkp = NULL;
+ int ret;
debug("%s(clk=%p)\n", __func__, clk);
- if (!ops->disable)
- return -ENOSYS;
+ if (CONFIG_IS_ENABLED(CLK_CCF)) {
+ if (clk->id && !clk_get_by_id(clk->id, &clkp)) {
+ if (clkp->enable_count == 0) {
+ printf("clk %s already disabled\n",
+ clkp->dev->name);
+ return 0;
+ }
- return ops->disable(clk);
+ if (--clkp->enable_count > 0)
+ return 0;
+ }
+
+ if (ops->disable) {
+ ret = ops->disable(clk);
+ if (ret)
+ return ret;
+ }
+
+ if (clkp && clkp->dev->parent &&
+ device_get_uclass_id(clkp->dev) == UCLASS_CLK) {
+ ret = clk_disable(dev_get_clk_ptr(clkp->dev->parent));
+ if (ret) {
+ printf("Disable %s failed\n",
+ clkp->dev->parent->name);
+ return ret;
+ }
+ }
+ } else {
+ if (!ops->disable)
+ return -ENOSYS;
+
+ return ops->disable(clk);
+ }
+
+ return 0;
}
int clk_disable_bulk(struct clk_bulk *bulk)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 39b3087067..1cf9987f6c 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -40,6 +40,7 @@ int clk_register(struct clk *clk, const char *drv_name,
return ret;
}
+ clk->enable_count = 0;
/* Store back pointer to clk from udevice */
clk->dev->uclass_priv = clk;
diff --git a/drivers/clk/clk_fixed_rate.c b/drivers/clk/clk_fixed_rate.c
index 08cce0d79b..f51126793e 100644
--- a/drivers/clk/clk_fixed_rate.c
+++ b/drivers/clk/clk_fixed_rate.c
@@ -27,6 +27,7 @@ static int clk_fixed_rate_ofdata_to_platdata(struct udevice *dev)
/* Make fixed rate clock accessible from higher level struct clk */
dev->uclass_priv = clk;
clk->dev = dev;
+ clk->enable_count = 0;
return 0;
}
diff --git a/drivers/clk/clk_sandbox_ccf.c b/drivers/clk/clk_sandbox_ccf.c
index e126f18d8e..9fa27229e1 100644
--- a/drivers/clk/clk_sandbox_ccf.c
+++ b/drivers/clk/clk_sandbox_ccf.c
@@ -25,6 +25,18 @@ struct clk_pllv3 {
u32 div_shift;
};
+int sandbox_clk_enable_count(struct clk *clk)
+{
+ struct clk *clkp = NULL;
+ int ret;
+
+ ret = clk_get_by_id(clk->id, &clkp);
+ if (ret)
+ return 0;
+
+ return clkp->enable_count;
+}
+
static ulong clk_pllv3_get_rate(struct clk *clk)
{
unsigned long parent_rate = clk_get_parent_rate(clk);
@@ -254,6 +266,9 @@ static int sandbox_clk_ccf_probe(struct udevice *dev)
sandbox_clk_composite("i2c", i2c_sels, ARRAY_SIZE(i2c_sels),
&reg, 0));
+ clk_dm(SANDBOX_CLK_I2C_ROOT,
+ sandbox_clk_gate2("i2c_root", "i2c", base + 0x7c, 0));
+
return 0;
}
diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig
index 3e6a980c8c..aae69cf9b0 100644
--- a/drivers/clk/imx/Kconfig
+++ b/drivers/clk/imx/Kconfig
@@ -20,3 +20,19 @@ config CLK_IMX8
select CLK
help
This enables support clock driver for i.MX8 platforms.
+
+config SPL_CLK_IMX8MM
+ bool "SPL clock support for i.MX8MM"
+ depends on ARCH_IMX8M && SPL
+ select SPL_CLK
+ select SPL_CLK_CCF
+ help
+ This enables SPL DM/DTS support for clock driver in i.MX8MM
+
+config CLK_IMX8MM
+ bool "Clock support for i.MX8MM"
+ depends on ARCH_IMX8M
+ select CLK
+ select CLK_CCF
+ help
+ This enables support clock driver for i.MX8MM platforms.
diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index 105a58ca90..5ad7967fe9 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -10,3 +10,5 @@ ifdef CONFIG_CLK_IMX8
obj-$(CONFIG_IMX8QXP) += clk-imx8qxp.o
obj-$(CONFIG_IMX8QM) += clk-imx8qm.o
endif
+obj-$(CONFIG_$(SPL_TPL_)CLK_IMX8MM) += clk-imx8mm.o clk-pll14xx.o \
+ clk-composite-8m.o
diff --git a/drivers/clk/imx/clk-composite-8m.c b/drivers/clk/imx/clk-composite-8m.c
new file mode 100644
index 0000000000..95120d6559
--- /dev/null
+++ b/drivers/clk/imx/clk-composite-8m.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <linux/clk-provider.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_IMX_COMPOSITE "imx_clk_composite"
+
+#define PCG_PREDIV_SHIFT 16
+#define PCG_PREDIV_WIDTH 3
+#define PCG_PREDIV_MAX 8
+
+#define PCG_DIV_SHIFT 0
+#define PCG_DIV_WIDTH 6
+#define PCG_DIV_MAX 64
+
+#define PCG_PCS_SHIFT 24
+#define PCG_PCS_MASK 0x7
+
+#define PCG_CGC_SHIFT 28
+
+static unsigned long imx8m_clk_composite_divider_recalc_rate(struct clk *clk)
+{
+ struct clk_divider *divider = (struct clk_divider *)to_clk_divider(clk);
+ struct clk_composite *composite = (struct clk_composite *)clk->data;
+ ulong parent_rate = clk_get_parent_rate(&composite->clk);
+ unsigned long prediv_rate;
+ unsigned int prediv_value;
+ unsigned int div_value;
+
+ debug("%s: name %s prate: %lu reg: %p\n", __func__,
+ (&composite->clk)->dev->name, parent_rate, divider->reg);
+ prediv_value = readl(divider->reg) >> divider->shift;
+ prediv_value &= clk_div_mask(divider->width);
+
+ prediv_rate = divider_recalc_rate(clk, parent_rate, prediv_value,
+ NULL, divider->flags,
+ divider->width);
+
+ div_value = readl(divider->reg) >> PCG_DIV_SHIFT;
+ div_value &= clk_div_mask(PCG_DIV_WIDTH);
+
+ return divider_recalc_rate(clk, prediv_rate, div_value, NULL,
+ divider->flags, PCG_DIV_WIDTH);
+}
+
+static int imx8m_clk_composite_compute_dividers(unsigned long rate,
+ unsigned long parent_rate,
+ int *prediv, int *postdiv)
+{
+ int div1, div2;
+ int error = INT_MAX;
+ int ret = -EINVAL;
+
+ *prediv = 1;
+ *postdiv = 1;
+
+ for (div1 = 1; div1 <= PCG_PREDIV_MAX; div1++) {
+ for (div2 = 1; div2 <= PCG_DIV_MAX; div2++) {
+ int new_error = ((parent_rate / div1) / div2) - rate;
+
+ if (abs(new_error) < abs(error)) {
+ *prediv = div1;
+ *postdiv = div2;
+ error = new_error;
+ ret = 0;
+ }
+ }
+ }
+ return ret;
+}
+
+/*
+ * The clk are bound to a dev, because it is part of composite clk
+ * use composite clk to get dev
+ */
+static ulong imx8m_clk_composite_divider_set_rate(struct clk *clk,
+ unsigned long rate)
+{
+ struct clk_divider *divider = (struct clk_divider *)to_clk_divider(clk);
+ struct clk_composite *composite = (struct clk_composite *)clk->data;
+ ulong parent_rate = clk_get_parent_rate(&composite->clk);
+ int prediv_value;
+ int div_value;
+ int ret;
+ u32 val;
+
+ ret = imx8m_clk_composite_compute_dividers(rate, parent_rate,
+ &prediv_value, &div_value);
+ if (ret)
+ return ret;
+
+ val = readl(divider->reg);
+ val &= ~((clk_div_mask(divider->width) << divider->shift) |
+ (clk_div_mask(PCG_DIV_WIDTH) << PCG_DIV_SHIFT));
+
+ val |= (u32)(prediv_value - 1) << divider->shift;
+ val |= (u32)(div_value - 1) << PCG_DIV_SHIFT;
+ writel(val, divider->reg);
+
+ return clk_get_rate(&composite->clk);
+}
+
+static const struct clk_ops imx8m_clk_composite_divider_ops = {
+ .get_rate = imx8m_clk_composite_divider_recalc_rate,
+ .set_rate = imx8m_clk_composite_divider_set_rate,
+};
+
+struct clk *imx8m_clk_composite_flags(const char *name,
+ const char * const *parent_names,
+ int num_parents, void __iomem *reg,
+ unsigned long flags)
+{
+ struct clk *clk = ERR_PTR(-ENOMEM);
+ struct clk_divider *div = NULL;
+ struct clk_gate *gate = NULL;
+ struct clk_mux *mux = NULL;
+
+ mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ goto fail;
+
+ mux->reg = reg;
+ mux->shift = PCG_PCS_SHIFT;
+ mux->mask = PCG_PCS_MASK;
+ mux->num_parents = num_parents;
+ mux->flags = flags;
+ mux->parent_names = parent_names;
+
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+ if (!div)
+ goto fail;
+
+ div->reg = reg;
+ div->shift = PCG_PREDIV_SHIFT;
+ div->width = PCG_PREDIV_WIDTH;
+ div->flags = CLK_DIVIDER_ROUND_CLOSEST | flags;
+
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ goto fail;
+
+ gate->reg = reg;
+ gate->bit_idx = PCG_CGC_SHIFT;
+ gate->flags = flags;
+
+ clk = clk_register_composite(NULL, name,
+ parent_names, num_parents,
+ &mux->clk, &clk_mux_ops, &div->clk,
+ &imx8m_clk_composite_divider_ops,
+ &gate->clk, &clk_gate_ops, flags);
+ if (IS_ERR(clk))
+ goto fail;
+
+ return clk;
+
+fail:
+ kfree(gate);
+ kfree(div);
+ kfree(mux);
+ return ERR_CAST(clk);
+}
diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c
new file mode 100644
index 0000000000..f4913e70ab
--- /dev/null
+++ b/drivers/clk/imx/clk-imx8mm.c
@@ -0,0 +1,415 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP
+ * Peng Fan <peng.fan@nxp.com>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/imx-regs.h>
+#include <dt-bindings/clock/imx8mm-clock.h>
+
+#include "clk.h"
+
+#define PLL_1416X_RATE(_rate, _m, _p, _s) \
+ { \
+ .rate = (_rate), \
+ .mdiv = (_m), \
+ .pdiv = (_p), \
+ .sdiv = (_s), \
+ }
+
+#define PLL_1443X_RATE(_rate, _m, _p, _s, _k) \
+ { \
+ .rate = (_rate), \
+ .mdiv = (_m), \
+ .pdiv = (_p), \
+ .sdiv = (_s), \
+ .kdiv = (_k), \
+ }
+
+static const struct imx_pll14xx_rate_table imx8mm_pll1416x_tbl[] = {
+ PLL_1416X_RATE(1800000000U, 225, 3, 0),
+ PLL_1416X_RATE(1600000000U, 200, 3, 0),
+ PLL_1416X_RATE(1200000000U, 300, 3, 1),
+ PLL_1416X_RATE(1000000000U, 250, 3, 1),
+ PLL_1416X_RATE(800000000U, 200, 3, 1),
+ PLL_1416X_RATE(750000000U, 250, 2, 2),
+ PLL_1416X_RATE(700000000U, 350, 3, 2),
+ PLL_1416X_RATE(600000000U, 300, 3, 2),
+};
+
+static const struct imx_pll14xx_rate_table imx8mm_drampll_tbl[] = {
+ PLL_1443X_RATE(650000000U, 325, 3, 2, 0),
+};
+
+static struct imx_pll14xx_clk imx8mm_dram_pll __initdata = {
+ .type = PLL_1443X,
+ .rate_table = imx8mm_drampll_tbl,
+ .rate_count = ARRAY_SIZE(imx8mm_drampll_tbl),
+};
+
+static struct imx_pll14xx_clk imx8mm_arm_pll __initdata = {
+ .type = PLL_1416X,
+ .rate_table = imx8mm_pll1416x_tbl,
+ .rate_count = ARRAY_SIZE(imx8mm_pll1416x_tbl),
+};
+
+static struct imx_pll14xx_clk imx8mm_sys_pll __initdata = {
+ .type = PLL_1416X,
+ .rate_table = imx8mm_pll1416x_tbl,
+ .rate_count = ARRAY_SIZE(imx8mm_pll1416x_tbl),
+};
+
+static const char *pll_ref_sels[] = { "clock-osc-24m", "dummy", "dummy", "dummy", };
+static const char *dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", };
+static const char *arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", };
+static const char *sys_pll1_bypass_sels[] = {"sys_pll1", "sys_pll1_ref_sel", };
+static const char *sys_pll2_bypass_sels[] = {"sys_pll2", "sys_pll2_ref_sel", };
+static const char *sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_sel", };
+
+static const char *imx8mm_a53_sels[] = {"clock-osc-24m", "arm_pll_out", "sys_pll2_500m", "sys_pll2_1000m",
+ "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "sys_pll3_out", };
+
+static const char *imx8mm_ahb_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_800m", "sys_pll1_400m",
+ "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", };
+
+static const char *imx8mm_enet_axi_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_250m",
+ "sys_pll2_200m", "audio_pll1_out", "video_pll1_out", "sys_pll3_out", };
+
+static const char *imx8mm_nand_usdhc_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_200m",
+ "sys_pll1_133m", "sys_pll3_out", "sys_pll2_250m", "audio_pll1_out", };
+
+static const char *imx8mm_usdhc1_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m",
+ "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", };
+
+static const char *imx8mm_usdhc2_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m",
+ "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", };
+
+static const char *imx8mm_i2c1_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out",
+ "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", };
+
+static const char *imx8mm_i2c2_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out",
+ "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", };
+
+static const char *imx8mm_i2c3_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out",
+ "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", };
+
+static const char *imx8mm_i2c4_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out",
+ "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", };
+
+static const char *imx8mm_wdog_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_160m", "vpu_pll_out",
+ "sys_pll2_125m", "sys_pll3_out", "sys_pll1_80m", "sys_pll2_166m", };
+
+static const char *imx8mm_usdhc3_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m",
+ "sys_pll3_out", "sys_pll1_266m", "audio_pll2_clk", "sys_pll1_100m", };
+
+static ulong imx8mm_clk_get_rate(struct clk *clk)
+{
+ struct clk *c;
+ int ret;
+
+ debug("%s(#%lu)\n", __func__, clk->id);
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ return clk_get_rate(c);
+}
+
+static ulong imx8mm_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ struct clk *c;
+ int ret;
+
+ debug("%s(#%lu), rate: %lu\n", __func__, clk->id, rate);
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ return clk_set_rate(c, rate);
+}
+
+static int __imx8mm_clk_enable(struct clk *clk, bool enable)
+{
+ struct clk *c;
+ int ret;
+
+ debug("%s(#%lu) en: %d\n", __func__, clk->id, enable);
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ if (enable)
+ ret = clk_enable(c);
+ else
+ ret = clk_disable(c);
+
+ return ret;
+}
+
+static int imx8mm_clk_disable(struct clk *clk)
+{
+ return __imx8mm_clk_enable(clk, 0);
+}
+
+static int imx8mm_clk_enable(struct clk *clk)
+{
+ return __imx8mm_clk_enable(clk, 1);
+}
+
+static struct clk_ops imx8mm_clk_ops = {
+ .set_rate = imx8mm_clk_set_rate,
+ .get_rate = imx8mm_clk_get_rate,
+ .enable = imx8mm_clk_enable,
+ .disable = imx8mm_clk_disable,
+};
+
+static int imx8mm_clk_probe(struct udevice *dev)
+{
+ void __iomem *base;
+
+ base = (void *)ANATOP_BASE_ADDR;
+
+ clk_dm(IMX8MM_DRAM_PLL_REF_SEL,
+ imx_clk_mux("dram_pll_ref_sel", base + 0x50, 0, 2,
+ pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
+ clk_dm(IMX8MM_ARM_PLL_REF_SEL,
+ imx_clk_mux("arm_pll_ref_sel", base + 0x84, 0, 2,
+ pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
+ clk_dm(IMX8MM_SYS_PLL1_REF_SEL,
+ imx_clk_mux("sys_pll1_ref_sel", base + 0x94, 0, 2,
+ pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
+ clk_dm(IMX8MM_SYS_PLL2_REF_SEL,
+ imx_clk_mux("sys_pll2_ref_sel", base + 0x104, 0, 2,
+ pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
+ clk_dm(IMX8MM_SYS_PLL3_REF_SEL,
+ imx_clk_mux("sys_pll3_ref_sel", base + 0x114, 0, 2,
+ pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
+
+ clk_dm(IMX8MM_DRAM_PLL,
+ imx_clk_pll14xx("dram_pll", "dram_pll_ref_sel",
+ base + 0x50, &imx8mm_dram_pll));
+ clk_dm(IMX8MM_ARM_PLL,
+ imx_clk_pll14xx("arm_pll", "arm_pll_ref_sel",
+ base + 0x84, &imx8mm_arm_pll));
+ clk_dm(IMX8MM_SYS_PLL1,
+ imx_clk_pll14xx("sys_pll1", "sys_pll1_ref_sel",
+ base + 0x94, &imx8mm_sys_pll));
+ clk_dm(IMX8MM_SYS_PLL2,
+ imx_clk_pll14xx("sys_pll2", "sys_pll2_ref_sel",
+ base + 0x104, &imx8mm_sys_pll));
+ clk_dm(IMX8MM_SYS_PLL3,
+ imx_clk_pll14xx("sys_pll3", "sys_pll3_ref_sel",
+ base + 0x114, &imx8mm_sys_pll));
+
+ /* PLL bypass out */
+ clk_dm(IMX8MM_DRAM_PLL_BYPASS,
+ imx_clk_mux_flags("dram_pll_bypass", base + 0x50, 4, 1,
+ dram_pll_bypass_sels,
+ ARRAY_SIZE(dram_pll_bypass_sels),
+ CLK_SET_RATE_PARENT));
+ clk_dm(IMX8MM_ARM_PLL_BYPASS,
+ imx_clk_mux_flags("arm_pll_bypass", base + 0x84, 4, 1,
+ arm_pll_bypass_sels,
+ ARRAY_SIZE(arm_pll_bypass_sels),
+ CLK_SET_RATE_PARENT));
+ clk_dm(IMX8MM_SYS_PLL1_BYPASS,
+ imx_clk_mux_flags("sys_pll1_bypass", base + 0x94, 4, 1,
+ sys_pll1_bypass_sels,
+ ARRAY_SIZE(sys_pll1_bypass_sels),
+ CLK_SET_RATE_PARENT));
+ clk_dm(IMX8MM_SYS_PLL2_BYPASS,
+ imx_clk_mux_flags("sys_pll2_bypass", base + 0x104, 4, 1,
+ sys_pll2_bypass_sels,
+ ARRAY_SIZE(sys_pll2_bypass_sels),
+ CLK_SET_RATE_PARENT));
+ clk_dm(IMX8MM_SYS_PLL3_BYPASS,
+ imx_clk_mux_flags("sys_pll3_bypass", base + 0x114, 4, 1,
+ sys_pll3_bypass_sels,
+ ARRAY_SIZE(sys_pll3_bypass_sels),
+ CLK_SET_RATE_PARENT));
+
+ /* PLL out gate */
+ clk_dm(IMX8MM_DRAM_PLL_OUT,
+ imx_clk_gate("dram_pll_out", "dram_pll_bypass",
+ base + 0x50, 13));
+ clk_dm(IMX8MM_ARM_PLL_OUT,
+ imx_clk_gate("arm_pll_out", "arm_pll_bypass",
+ base + 0x84, 11));
+ clk_dm(IMX8MM_SYS_PLL1_OUT,
+ imx_clk_gate("sys_pll1_out", "sys_pll1_bypass",
+ base + 0x94, 11));
+ clk_dm(IMX8MM_SYS_PLL2_OUT,
+ imx_clk_gate("sys_pll2_out", "sys_pll2_bypass",
+ base + 0x104, 11));
+ clk_dm(IMX8MM_SYS_PLL3_OUT,
+ imx_clk_gate("sys_pll3_out", "sys_pll3_bypass",
+ base + 0x114, 11));
+
+ /* SYS PLL fixed output */
+ clk_dm(IMX8MM_SYS_PLL1_40M,
+ imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20));
+ clk_dm(IMX8MM_SYS_PLL1_80M,
+ imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10));
+ clk_dm(IMX8MM_SYS_PLL1_100M,
+ imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8));
+ clk_dm(IMX8MM_SYS_PLL1_133M,
+ imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6));
+ clk_dm(IMX8MM_SYS_PLL1_160M,
+ imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5));
+ clk_dm(IMX8MM_SYS_PLL1_200M,
+ imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4));
+ clk_dm(IMX8MM_SYS_PLL1_266M,
+ imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3));
+ clk_dm(IMX8MM_SYS_PLL1_400M,
+ imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2));
+ clk_dm(IMX8MM_SYS_PLL1_800M,
+ imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1));
+
+ clk_dm(IMX8MM_SYS_PLL2_50M,
+ imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20));
+ clk_dm(IMX8MM_SYS_PLL2_100M,
+ imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10));
+ clk_dm(IMX8MM_SYS_PLL2_125M,
+ imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8));
+ clk_dm(IMX8MM_SYS_PLL2_166M,
+ imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6));
+ clk_dm(IMX8MM_SYS_PLL2_200M,
+ imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5));
+ clk_dm(IMX8MM_SYS_PLL2_250M,
+ imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4));
+ clk_dm(IMX8MM_SYS_PLL2_333M,
+ imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3));
+ clk_dm(IMX8MM_SYS_PLL2_500M,
+ imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2));
+ clk_dm(IMX8MM_SYS_PLL2_1000M,
+ imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1));
+
+ base = dev_read_addr_ptr(dev);
+ if (base == (void *)FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ clk_dm(IMX8MM_CLK_A53_SRC,
+ imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3,
+ imx8mm_a53_sels, ARRAY_SIZE(imx8mm_a53_sels)));
+ clk_dm(IMX8MM_CLK_A53_CG,
+ imx_clk_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28));
+ clk_dm(IMX8MM_CLK_A53_DIV,
+ imx_clk_divider2("arm_a53_div", "arm_a53_cg",
+ base + 0x8000, 0, 3));
+
+ clk_dm(IMX8MM_CLK_AHB,
+ imx8m_clk_composite_critical("ahb", imx8mm_ahb_sels,
+ base + 0x9000));
+ clk_dm(IMX8MM_CLK_IPG_ROOT,
+ imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1));
+
+ clk_dm(IMX8MM_CLK_ENET_AXI,
+ imx8m_clk_composite("enet_axi", imx8mm_enet_axi_sels,
+ base + 0x8880));
+ clk_dm(IMX8MM_CLK_NAND_USDHC_BUS,
+ imx8m_clk_composite_critical("nand_usdhc_bus",
+ imx8mm_nand_usdhc_sels,
+ base + 0x8900));
+
+ /* IP */
+ clk_dm(IMX8MM_CLK_USDHC1,
+ imx8m_clk_composite("usdhc1", imx8mm_usdhc1_sels,
+ base + 0xac00));
+ clk_dm(IMX8MM_CLK_USDHC2,
+ imx8m_clk_composite("usdhc2", imx8mm_usdhc2_sels,
+ base + 0xac80));
+ clk_dm(IMX8MM_CLK_I2C1,
+ imx8m_clk_composite("i2c1", imx8mm_i2c1_sels, base + 0xad00));
+ clk_dm(IMX8MM_CLK_I2C2,
+ imx8m_clk_composite("i2c2", imx8mm_i2c2_sels, base + 0xad80));
+ clk_dm(IMX8MM_CLK_I2C3,
+ imx8m_clk_composite("i2c3", imx8mm_i2c3_sels, base + 0xae00));
+ clk_dm(IMX8MM_CLK_I2C4,
+ imx8m_clk_composite("i2c4", imx8mm_i2c4_sels, base + 0xae80));
+ clk_dm(IMX8MM_CLK_WDOG,
+ imx8m_clk_composite("wdog", imx8mm_wdog_sels, base + 0xb900));
+ clk_dm(IMX8MM_CLK_USDHC3,
+ imx8m_clk_composite("usdhc3", imx8mm_usdhc3_sels,
+ base + 0xbc80));
+
+ clk_dm(IMX8MM_CLK_I2C1_ROOT,
+ imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0));
+ clk_dm(IMX8MM_CLK_I2C2_ROOT,
+ imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0));
+ clk_dm(IMX8MM_CLK_I2C3_ROOT,
+ imx_clk_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0));
+ clk_dm(IMX8MM_CLK_I2C4_ROOT,
+ imx_clk_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0));
+ clk_dm(IMX8MM_CLK_OCOTP_ROOT,
+ imx_clk_gate4("ocotp_root_clk", "ipg_root", base + 0x4220, 0));
+ clk_dm(IMX8MM_CLK_USDHC1_ROOT,
+ imx_clk_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0));
+ clk_dm(IMX8MM_CLK_USDHC2_ROOT,
+ imx_clk_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0));
+ clk_dm(IMX8MM_CLK_WDOG1_ROOT,
+ imx_clk_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0));
+ clk_dm(IMX8MM_CLK_WDOG2_ROOT,
+ imx_clk_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0));
+ clk_dm(IMX8MM_CLK_WDOG3_ROOT,
+ imx_clk_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0));
+ clk_dm(IMX8MM_CLK_USDHC3_ROOT,
+ imx_clk_gate4("usdhc3_root_clk", "usdhc3", base + 0x45e0, 0));
+
+#ifdef CONFIG_SPL_BUILD
+ struct clk *clkp, *clkp1;
+
+ clk_get_by_id(IMX8MM_CLK_WDOG1_ROOT, &clkp);
+ clk_enable(clkp);
+ clk_get_by_id(IMX8MM_CLK_WDOG2_ROOT, &clkp);
+ clk_enable(clkp);
+ clk_get_by_id(IMX8MM_CLK_WDOG3_ROOT, &clkp);
+ clk_enable(clkp);
+
+ /* Configure SYS_PLL3 to 750MHz */
+ clk_get_by_id(IMX8MM_SYS_PLL3, &clkp);
+ clk_set_rate(clkp, 750000000UL);
+ clk_enable(clkp);
+
+ /* Configure ARM to sys_pll2_500m */
+ clk_get_by_id(IMX8MM_CLK_A53_SRC, &clkp);
+ clk_get_by_id(IMX8MM_SYS_PLL2_OUT, &clkp1);
+ clk_enable(clkp1);
+ clk_get_by_id(IMX8MM_SYS_PLL2_500M, &clkp1);
+ clk_set_parent(clkp, clkp1);
+
+ /* Configure ARM PLL to 1.2GHz */
+ clk_get_by_id(IMX8MM_ARM_PLL, &clkp1);
+ clk_set_rate(clkp1, 1200000000UL);
+ clk_get_by_id(IMX8MM_ARM_PLL_OUT, &clkp1);
+ clk_enable(clkp1);
+ clk_set_parent(clkp, clkp1);
+
+ /* Configure DIV to 1.2GHz */
+ clk_get_by_id(IMX8MM_CLK_A53_DIV, &clkp1);
+ clk_set_rate(clkp1, 1200000000UL);
+#endif
+
+ return 0;
+}
+
+static const struct udevice_id imx8mm_clk_ids[] = {
+ { .compatible = "fsl,imx8mm-ccm" },
+ { },
+};
+
+U_BOOT_DRIVER(imx8mm_clk) = {
+ .name = "clk_imx8mm",
+ .id = UCLASS_CLK,
+ .of_match = imx8mm_clk_ids,
+ .ops = &imx8mm_clk_ops,
+ .probe = imx8mm_clk_probe,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c
new file mode 100644
index 0000000000..2246beb21b
--- /dev/null
+++ b/drivers/clk/imx/clk-pll14xx.c
@@ -0,0 +1,381 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2017-2019 NXP.
+ *
+ * Peng Fan <peng.fan@nxp.com>
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <linux/clk-provider.h>
+#include <linux/iopoll.h>
+#include <clk.h>
+#include <div64.h>
+
+#include "clk.h"
+
+#define UBOOT_DM_CLK_IMX_PLL1443X "imx_clk_pll1443x"
+#define UBOOT_DM_CLK_IMX_PLL1416X "imx_clk_pll1416x"
+
+#define GNRL_CTL 0x0
+#define DIV_CTL 0x4
+#define LOCK_STATUS BIT(31)
+#define LOCK_SEL_MASK BIT(29)
+#define CLKE_MASK BIT(11)
+#define RST_MASK BIT(9)
+#define BYPASS_MASK BIT(4)
+#define MDIV_SHIFT 12
+#define MDIV_MASK GENMASK(21, 12)
+#define PDIV_SHIFT 4
+#define PDIV_MASK GENMASK(9, 4)
+#define SDIV_SHIFT 0
+#define SDIV_MASK GENMASK(2, 0)
+#define KDIV_SHIFT 0
+#define KDIV_MASK GENMASK(15, 0)
+
+#define LOCK_TIMEOUT_US 10000
+
+struct clk_pll14xx {
+ struct clk clk;
+ void __iomem *base;
+ enum imx_pll14xx_type type;
+ const struct imx_pll14xx_rate_table *rate_table;
+ int rate_count;
+};
+
+#define to_clk_pll14xx(_clk) container_of(_clk, struct clk_pll14xx, clk)
+
+static const struct imx_pll14xx_rate_table *imx_get_pll_settings(
+ struct clk_pll14xx *pll, unsigned long rate)
+{
+ const struct imx_pll14xx_rate_table *rate_table = pll->rate_table;
+ int i;
+
+ for (i = 0; i < pll->rate_count; i++)
+ if (rate == rate_table[i].rate)
+ return &rate_table[i];
+
+ return NULL;
+}
+
+static unsigned long clk_pll1416x_recalc_rate(struct clk *clk)
+{
+ struct clk_pll14xx *pll = to_clk_pll14xx(dev_get_clk_ptr(clk->dev));
+ u64 fvco = clk_get_parent_rate(clk);
+ u32 mdiv, pdiv, sdiv, pll_div;
+
+ pll_div = readl(pll->base + 4);
+ mdiv = (pll_div & MDIV_MASK) >> MDIV_SHIFT;
+ pdiv = (pll_div & PDIV_MASK) >> PDIV_SHIFT;
+ sdiv = (pll_div & SDIV_MASK) >> SDIV_SHIFT;
+
+ fvco *= mdiv;
+ do_div(fvco, pdiv << sdiv);
+
+ return fvco;
+}
+
+static unsigned long clk_pll1443x_recalc_rate(struct clk *clk)
+{
+ struct clk_pll14xx *pll = to_clk_pll14xx(dev_get_clk_ptr(clk->dev));
+ u64 fvco = clk_get_parent_rate(clk);
+ u32 mdiv, pdiv, sdiv, pll_div_ctl0, pll_div_ctl1;
+ short int kdiv;
+
+ pll_div_ctl0 = readl(pll->base + 4);
+ pll_div_ctl1 = readl(pll->base + 8);
+ mdiv = (pll_div_ctl0 & MDIV_MASK) >> MDIV_SHIFT;
+ pdiv = (pll_div_ctl0 & PDIV_MASK) >> PDIV_SHIFT;
+ sdiv = (pll_div_ctl0 & SDIV_MASK) >> SDIV_SHIFT;
+ kdiv = pll_div_ctl1 & KDIV_MASK;
+
+ /* fvco = (m * 65536 + k) * Fin / (p * 65536) */
+ fvco *= (mdiv * 65536 + kdiv);
+ pdiv *= 65536;
+
+ do_div(fvco, pdiv << sdiv);
+
+ return fvco;
+}
+
+static inline bool clk_pll1416x_mp_change(const struct imx_pll14xx_rate_table *rate,
+ u32 pll_div)
+{
+ u32 old_mdiv, old_pdiv;
+
+ old_mdiv = (pll_div & MDIV_MASK) >> MDIV_SHIFT;
+ old_pdiv = (pll_div & PDIV_MASK) >> PDIV_SHIFT;
+
+ return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv;
+}
+
+static inline bool clk_pll1443x_mpk_change(const struct imx_pll14xx_rate_table *rate,
+ u32 pll_div_ctl0, u32 pll_div_ctl1)
+{
+ u32 old_mdiv, old_pdiv, old_kdiv;
+
+ old_mdiv = (pll_div_ctl0 & MDIV_MASK) >> MDIV_SHIFT;
+ old_pdiv = (pll_div_ctl0 & PDIV_MASK) >> PDIV_SHIFT;
+ old_kdiv = (pll_div_ctl1 & KDIV_MASK) >> KDIV_SHIFT;
+
+ return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv ||
+ rate->kdiv != old_kdiv;
+}
+
+static inline bool clk_pll1443x_mp_change(const struct imx_pll14xx_rate_table *rate,
+ u32 pll_div_ctl0, u32 pll_div_ctl1)
+{
+ u32 old_mdiv, old_pdiv, old_kdiv;
+
+ old_mdiv = (pll_div_ctl0 & MDIV_MASK) >> MDIV_SHIFT;
+ old_pdiv = (pll_div_ctl0 & PDIV_MASK) >> PDIV_SHIFT;
+ old_kdiv = (pll_div_ctl1 & KDIV_MASK) >> KDIV_SHIFT;
+
+ return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv ||
+ rate->kdiv != old_kdiv;
+}
+
+static int clk_pll14xx_wait_lock(struct clk_pll14xx *pll)
+{
+ u32 val;
+
+ return readl_poll_timeout(pll->base, val, val & LOCK_TIMEOUT_US,
+ LOCK_TIMEOUT_US);
+}
+
+static ulong clk_pll1416x_set_rate(struct clk *clk, unsigned long drate)
+{
+ struct clk_pll14xx *pll = to_clk_pll14xx(dev_get_clk_ptr(clk->dev));
+ const struct imx_pll14xx_rate_table *rate;
+ u32 tmp, div_val;
+ int ret;
+
+ rate = imx_get_pll_settings(pll, drate);
+ if (!rate) {
+ pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
+ drate, "xxxx");
+ return -EINVAL;
+ }
+
+ tmp = readl(pll->base + 4);
+
+ if (!clk_pll1416x_mp_change(rate, tmp)) {
+ tmp &= ~(SDIV_MASK) << SDIV_SHIFT;
+ tmp |= rate->sdiv << SDIV_SHIFT;
+ writel(tmp, pll->base + 4);
+
+ return clk_pll1416x_recalc_rate(clk);
+ }
+
+ /* Bypass clock and set lock to pll output lock */
+ tmp = readl(pll->base);
+ tmp |= LOCK_SEL_MASK;
+ writel(tmp, pll->base);
+
+ /* Enable RST */
+ tmp &= ~RST_MASK;
+ writel(tmp, pll->base);
+
+ /* Enable BYPASS */
+ tmp |= BYPASS_MASK;
+ writel(tmp, pll->base);
+
+
+ div_val = (rate->mdiv << MDIV_SHIFT) | (rate->pdiv << PDIV_SHIFT) |
+ (rate->sdiv << SDIV_SHIFT);
+ writel(div_val, pll->base + 0x4);
+
+ /*
+ * According to SPEC, t3 - t2 need to be greater than
+ * 1us and 1/FREF, respectively.
+ * FREF is FIN / Prediv, the prediv is [1, 63], so choose
+ * 3us.
+ */
+ udelay(3);
+
+ /* Disable RST */
+ tmp |= RST_MASK;
+ writel(tmp, pll->base);
+
+ /* Wait Lock */
+ ret = clk_pll14xx_wait_lock(pll);
+ if (ret)
+ return ret;
+
+ /* Bypass */
+ tmp &= ~BYPASS_MASK;
+ writel(tmp, pll->base);
+
+ return clk_pll1416x_recalc_rate(clk);
+}
+
+static ulong clk_pll1443x_set_rate(struct clk *clk, unsigned long drate)
+{
+ struct clk_pll14xx *pll = to_clk_pll14xx(dev_get_clk_ptr(clk->dev));
+ const struct imx_pll14xx_rate_table *rate;
+ u32 tmp, div_val;
+ int ret;
+
+ rate = imx_get_pll_settings(pll, drate);
+ if (!rate) {
+ pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
+ drate, "===");
+ return -EINVAL;
+ }
+
+ tmp = readl(pll->base + 4);
+ div_val = readl(pll->base + 8);
+
+ if (!clk_pll1443x_mpk_change(rate, tmp, div_val)) {
+ tmp &= ~(SDIV_MASK) << SDIV_SHIFT;
+ tmp |= rate->sdiv << SDIV_SHIFT;
+ writel(tmp, pll->base + 4);
+
+ return clk_pll1443x_recalc_rate(clk);
+ }
+
+ tmp = readl(pll->base);
+
+ /* Enable RST */
+ tmp &= ~RST_MASK;
+ writel(tmp, pll->base);
+
+ /* Enable BYPASS */
+ tmp |= BYPASS_MASK;
+ writel(tmp, pll->base);
+
+ div_val = (rate->mdiv << MDIV_SHIFT) | (rate->pdiv << PDIV_SHIFT) |
+ (rate->sdiv << SDIV_SHIFT);
+ writel(div_val, pll->base + 0x4);
+ writel(rate->kdiv << KDIV_SHIFT, pll->base + 0x8);
+
+ /*
+ * According to SPEC, t3 - t2 need to be greater than
+ * 1us and 1/FREF, respectively.
+ * FREF is FIN / Prediv, the prediv is [1, 63], so choose
+ * 3us.
+ */
+ udelay(3);
+
+ /* Disable RST */
+ tmp |= RST_MASK;
+ writel(tmp, pll->base);
+
+ /* Wait Lock*/
+ ret = clk_pll14xx_wait_lock(pll);
+ if (ret)
+ return ret;
+
+ /* Bypass */
+ tmp &= ~BYPASS_MASK;
+ writel(tmp, pll->base);
+
+ return clk_pll1443x_recalc_rate(clk);
+}
+
+static int clk_pll14xx_prepare(struct clk *clk)
+{
+ struct clk_pll14xx *pll = to_clk_pll14xx(dev_get_clk_ptr(clk->dev));
+ u32 val;
+
+ /*
+ * RESETB = 1 from 0, PLL starts its normal
+ * operation after lock time
+ */
+ val = readl(pll->base + GNRL_CTL);
+ val |= RST_MASK;
+ writel(val, pll->base + GNRL_CTL);
+
+ return clk_pll14xx_wait_lock(pll);
+}
+
+static int clk_pll14xx_unprepare(struct clk *clk)
+{
+ struct clk_pll14xx *pll = to_clk_pll14xx(dev_get_clk_ptr(clk->dev));
+ u32 val;
+
+ /*
+ * Set RST to 0, power down mode is enabled and
+ * every digital block is reset
+ */
+ val = readl(pll->base + GNRL_CTL);
+ val &= ~RST_MASK;
+ writel(val, pll->base + GNRL_CTL);
+
+ return 0;
+}
+
+static const struct clk_ops clk_pll1416x_ops = {
+ .enable = clk_pll14xx_prepare,
+ .disable = clk_pll14xx_unprepare,
+ .set_rate = clk_pll1416x_set_rate,
+ .get_rate = clk_pll1416x_recalc_rate,
+};
+
+static const struct clk_ops clk_pll1443x_ops = {
+ .enable = clk_pll14xx_prepare,
+ .disable = clk_pll14xx_unprepare,
+ .set_rate = clk_pll1443x_set_rate,
+ .get_rate = clk_pll1443x_recalc_rate,
+};
+
+struct clk *imx_clk_pll14xx(const char *name, const char *parent_name,
+ void __iomem *base,
+ const struct imx_pll14xx_clk *pll_clk)
+{
+ struct clk_pll14xx *pll;
+ struct clk *clk;
+ char *type_name;
+ int ret;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ switch (pll_clk->type) {
+ case PLL_1416X:
+ type_name = UBOOT_DM_CLK_IMX_PLL1416X;
+ break;
+ case PLL_1443X:
+ type_name = UBOOT_DM_CLK_IMX_PLL1443X;
+ break;
+ default:
+ pr_err("%s: Unknown pll type for pll clk %s\n",
+ __func__, name);
+ return ERR_PTR(-EINVAL);
+ };
+
+ pll->base = base;
+ pll->type = pll_clk->type;
+ pll->rate_table = pll_clk->rate_table;
+ pll->rate_count = pll_clk->rate_count;
+
+ clk = &pll->clk;
+
+ ret = clk_register(clk, type_name, 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_pll1443x) = {
+ .name = UBOOT_DM_CLK_IMX_PLL1443X,
+ .id = UCLASS_CLK,
+ .ops = &clk_pll1443x_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+U_BOOT_DRIVER(clk_pll1416x) = {
+ .name = UBOOT_DM_CLK_IMX_PLL1416X,
+ .id = UCLASS_CLK,
+ .ops = &clk_pll1416x_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index 1d480d8722..4956e04a92 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -20,6 +20,31 @@ enum imx_pllv3_type {
IMX_PLLV3_DDR_IMX7,
};
+enum imx_pll14xx_type {
+ PLL_1416X,
+ PLL_1443X,
+};
+
+/* NOTE: Rate table should be kept sorted in descending order. */
+struct imx_pll14xx_rate_table {
+ unsigned int rate;
+ unsigned int pdiv;
+ unsigned int mdiv;
+ unsigned int sdiv;
+ unsigned int kdiv;
+};
+
+struct imx_pll14xx_clk {
+ enum imx_pll14xx_type type;
+ const struct imx_pll14xx_rate_table *rate_table;
+ int rate_count;
+ int flags;
+};
+
+struct clk *imx_clk_pll14xx(const char *name, const char *parent_name,
+ void __iomem *base,
+ const struct imx_pll14xx_clk *pll_clk);
+
struct clk *clk_register_gate2(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 bit_idx, u8 cgr_val,
diff --git a/include/clk.h b/include/clk.h
index 3ca2796b57..18b2e3ca54 100644
--- a/include/clk.h
+++ b/include/clk.h
@@ -61,6 +61,7 @@ struct clk {
struct udevice *dev;
long long rate; /* in HZ */
u32 flags;
+ int enable_count;
/*
* Written by of_xlate. In the future, we might add more fields here.
*/
diff --git a/include/sandbox-clk.h b/include/sandbox-clk.h
index f449de1364..296cddfbb0 100644
--- a/include/sandbox-clk.h
+++ b/include/sandbox-clk.h
@@ -20,6 +20,7 @@ enum {
SANDBOX_CLK_USDHC1_SEL,
SANDBOX_CLK_USDHC2_SEL,
SANDBOX_CLK_I2C,
+ SANDBOX_CLK_I2C_ROOT,
};
enum sandbox_pllv3_type {
@@ -74,4 +75,6 @@ static inline struct clk *sandbox_clk_mux(const char *name, void __iomem *reg,
width, 0);
}
+int sandbox_clk_enable_count(struct clk *clk);
+
#endif /* __SANDBOX_CLK_H__ */
diff --git a/test/dm/clk_ccf.c b/test/dm/clk_ccf.c
index bbc4b500e8..ae3a4d8a76 100644
--- a/test/dm/clk_ccf.c
+++ b/test/dm/clk_ccf.c
@@ -64,6 +64,34 @@ static int dm_test_clk_ccf(struct unit_test_state *uts)
rate = clk_get_rate(clk);
ut_asserteq(rate, 60000000);
+#if CONFIG_IS_ENABLED(CLK_CCF)
+ /* Test clk tree enable/disable */
+ ret = clk_get_by_id(SANDBOX_CLK_I2C_ROOT, &clk);
+ ut_assertok(ret);
+ ut_asserteq_str("i2c_root", clk->dev->name);
+
+ ret = clk_enable(clk);
+ ut_assertok(ret);
+
+ ret = sandbox_clk_enable_count(clk);
+ ut_asserteq(ret, 1);
+
+ ret = clk_get_by_id(SANDBOX_CLK_I2C, &pclk);
+ ut_assertok(ret);
+
+ ret = sandbox_clk_enable_count(pclk);
+ ut_asserteq(ret, 1);
+
+ ret = clk_disable(clk);
+ ut_assertok(ret);
+
+ ret = sandbox_clk_enable_count(clk);
+ ut_asserteq(ret, 0);
+
+ ret = sandbox_clk_enable_count(pclk);
+ ut_asserteq(ret, 0);
+#endif
+
return 1;
}