diff --git a/cmd/clk.c b/cmd/clk.c index 5402c87de7295ca8bd5f374a788b360dcc1e5a7e..74ad8685002461bb431c13dc32e41ef61c976b77 100644 --- a/cmd/clk.c +++ b/cmd/clk.c @@ -7,51 +7,70 @@ #include #if defined(CONFIG_DM) && defined(CONFIG_CLK) #include +#include +#include #include +#include #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 a3f0171b45fb5ec60361f25711ded00a12191418..82cd8f623c45f5f45a3c53460c5a1eb07854c18d 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 c66b6f3c4ebb4417ce73ab1964d4487af3358dc2..64c181f4ad3009e30cf6bbff3ce7714d3201c33f 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 39b3087067a63b097b4c6b5d03e378c66f7b1fb6..1cf9987f6cbb8d87ca69d08ee1c361c62f8ee78f 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 08cce0d79b71033a7d387d4e6ffe045c04958b8a..f51126793eaed285756b4af8d5cb157e8ef11934 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 e126f18d8e96256f90914e0d0aaa548e849767db..9fa27229e18668305e41d65d7e6a8e1390f4d657 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), ®, 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 3e6a980c8c3933d4aa0905bb1afea072b18eaeb2..aae69cf9b08abb31f430030552e8110b9dd3fa8b 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 105a58ca907eddb8c5b869a7f905a13b5034fa51..5ad7967fe982855af35828d249f79a3b755f4592 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 0000000000000000000000000000000000000000..95120d6559cd96ffcee3709d9a8d56bfef233485 --- /dev/null +++ b/drivers/clk/imx/clk-composite-8m.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#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 0000000000000000000000000000000000000000..f4913e70abf5b97c5dfcfdd30fb45ecdee1306af --- /dev/null +++ b/drivers/clk/imx/clk-imx8mm.c @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP + * Peng Fan + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 0000000000000000000000000000000000000000..2246beb21b2889e7d9779a2986c52035f105d79e --- /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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 1d480d8722b623ef760349e6b9ab4b1958e68b09..4956e04a9253076175ed6c7c564fd19e80384382 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 3ca2796b5774990dae8560bb7f4e12fd3f135d8b..18b2e3ca54c53ab37d2ac743d9327b7c71424dc0 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 f449de13649d2ff55a1078507e08fcf66d62a7c7..296cddfbb0ef7575a386e185c61e7af87df496a4 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 bbc4b500e83d7985daf7cce4ea911e9d6fb759ad..ae3a4d8a76a429caee686eb5b0edc526dae660dc 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; }