[U-Boot,V2,28/51] clk: imx: add i.MX8MM clk driver
diff mbox series

Message ID 20190708015333.20411-29-peng.fan@nxp.com
State Rejected
Delegated to: Stefano Babic
Headers show
Series
  • Support i.MX8MM/N
Related show

Commit Message

Peng Fan July 8, 2019, 1:39 a.m. UTC
Add i.MX8MM clk driver support.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 arch/arm/mach-imx/imx8m/clock_imx8mm.c | 203 +++++++++++++++++----------------
 drivers/clk/imx/Makefile               |   1 +
 drivers/clk/imx/clk-imx8mm.c           | 106 +++++++++++++++++
 3 files changed, 211 insertions(+), 99 deletions(-)
 create mode 100644 drivers/clk/imx/clk-imx8mm.c

Comments

Schrempf Frieder July 10, 2019, 2:22 p.m. UTC | #1
Hi Peng,

On 08.07.19 03:39, Peng Fan wrote:
> Add i.MX8MM clk driver support.
> 
> Signed-off-by: Peng Fan <peng.fan@nxp.com>
> ---
>   arch/arm/mach-imx/imx8m/clock_imx8mm.c | 203 +++++++++++++++++----------------
>   drivers/clk/imx/Makefile               |   1 +
>   drivers/clk/imx/clk-imx8mm.c           | 106 +++++++++++++++++
>   3 files changed, 211 insertions(+), 99 deletions(-)
>   create mode 100644 drivers/clk/imx/clk-imx8mm.c
> 
> diff --git a/arch/arm/mach-imx/imx8m/clock_imx8mm.c b/arch/arm/mach-imx/imx8m/clock_imx8mm.c
> index 07399023d5..541561f276 100644
> --- a/arch/arm/mach-imx/imx8m/clock_imx8mm.c
> +++ b/arch/arm/mach-imx/imx8m/clock_imx8mm.c
> @@ -119,6 +119,110 @@ void init_wdog_clk(void)
>   	clock_enable(CCGR_WDOG3, 1);
>   }
>   
> +static int intpll_configure(enum pll_clocks pll, ulong freq)
> +{
> +	void __iomem *pll_gnrl_ctl, __iomem *pll_div_ctl;
> +	u32 pll_div_ctl_val, pll_clke_masks;
> +
> +	switch (pll) {
> +	case ANATOP_SYSTEM_PLL1:
> +		pll_gnrl_ctl = &ana_pll->sys_pll1_gnrl_ctl;
> +		pll_div_ctl = &ana_pll->sys_pll1_div_ctl;
> +		pll_clke_masks = INTPLL_DIV20_CLKE_MASK |
> +			INTPLL_DIV10_CLKE_MASK | INTPLL_DIV8_CLKE_MASK |
> +			INTPLL_DIV6_CLKE_MASK | INTPLL_DIV5_CLKE_MASK |
> +			INTPLL_DIV4_CLKE_MASK | INTPLL_DIV3_CLKE_MASK |
> +			INTPLL_DIV2_CLKE_MASK | INTPLL_CLKE_MASK;
> +		break;
> +	case ANATOP_SYSTEM_PLL2:
> +		pll_gnrl_ctl = &ana_pll->sys_pll2_gnrl_ctl;
> +		pll_div_ctl = &ana_pll->sys_pll2_div_ctl;
> +		pll_clke_masks = INTPLL_DIV20_CLKE_MASK |
> +			INTPLL_DIV10_CLKE_MASK | INTPLL_DIV8_CLKE_MASK |
> +			INTPLL_DIV6_CLKE_MASK | INTPLL_DIV5_CLKE_MASK |
> +			INTPLL_DIV4_CLKE_MASK | INTPLL_DIV3_CLKE_MASK |
> +			INTPLL_DIV2_CLKE_MASK | INTPLL_CLKE_MASK;
> +		break;
> +	case ANATOP_SYSTEM_PLL3:
> +		pll_gnrl_ctl = &ana_pll->sys_pll3_gnrl_ctl;
> +		pll_div_ctl = &ana_pll->sys_pll3_div_ctl;
> +		pll_clke_masks = INTPLL_CLKE_MASK;
> +		break;
> +	case ANATOP_ARM_PLL:
> +		pll_gnrl_ctl = &ana_pll->arm_pll_gnrl_ctl;
> +		pll_div_ctl = &ana_pll->arm_pll_div_ctl;
> +		pll_clke_masks = INTPLL_CLKE_MASK;
> +		break;
> +	case ANATOP_GPU_PLL:
> +		pll_gnrl_ctl = &ana_pll->gpu_pll_gnrl_ctl;
> +		pll_div_ctl = &ana_pll->gpu_pll_div_ctl;
> +		pll_clke_masks = INTPLL_CLKE_MASK;
> +		break;
> +	case ANATOP_VPU_PLL:
> +		pll_gnrl_ctl = &ana_pll->vpu_pll_gnrl_ctl;
> +		pll_div_ctl = &ana_pll->vpu_pll_div_ctl;
> +		pll_clke_masks = INTPLL_CLKE_MASK;
> +		break;
> +	default:
> +		return -EINVAL;
> +	};
> +
> +	switch (freq) {
> +	case MHZ(600):
> +		/* 24 * 0x12c / 3 / 2 ^ 2 */
> +		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0x12c) |
> +			INTPLL_PRE_DIV_VAL(3) | INTPLL_POST_DIV_VAL(2);
> +		break;
> +	case MHZ(750):
> +		/* 24 * 0xfa / 2 / 2 ^ 2 */
> +		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xfa) |
> +			INTPLL_PRE_DIV_VAL(2) | INTPLL_POST_DIV_VAL(2);
> +		break;
> +	case MHZ(800):
> +		/* 24 * 0x190 / 3 / 2 ^ 2 */
> +		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0x190) |
> +			INTPLL_PRE_DIV_VAL(3) | INTPLL_POST_DIV_VAL(2);
> +		break;
> +	case MHZ(1000):
> +		/* 24 * 0xfa / 3 / 2 ^ 1 */
> +		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xfa) |
> +			INTPLL_PRE_DIV_VAL(3) | INTPLL_POST_DIV_VAL(1);
> +		break;
> +	case MHZ(1200):
> +		/* 24 * 0xc8 / 2 / 2 ^ 1 */
> +		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xc8) |
> +			INTPLL_PRE_DIV_VAL(2) | INTPLL_POST_DIV_VAL(1);
> +		break;
> +	case MHZ(2000):
> +		/* 24 * 0xfa / 3 / 2 ^ 0 */
> +		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xfa) |
> +			INTPLL_PRE_DIV_VAL(3) | INTPLL_POST_DIV_VAL(0);
> +		break;
> +	default:
> +		return -EINVAL;
> +	};
> +	/* Bypass clock and set lock to pll output lock */
> +	setbits_le32(pll_gnrl_ctl, INTPLL_BYPASS_MASK |
> +		     INTPLL_LOCK_SEL_MASK);
> +	/* Enable reset */
> +	clrbits_le32(pll_gnrl_ctl, INTPLL_RST_MASK);
> +	/* Configure */
> +	writel(pll_div_ctl_val, pll_div_ctl);
> +
> +	__udelay(100);
> +
> +	/* Disable reset */
> +	setbits_le32(pll_gnrl_ctl, INTPLL_RST_MASK);
> +	/* Wait Lock */
> +	while (!(readl(pll_gnrl_ctl) & INTPLL_LOCK_MASK))
> +		;
> +	/* Clear bypass */
> +	clrbits_le32(pll_gnrl_ctl, INTPLL_BYPASS_MASK);
> +	setbits_le32(pll_gnrl_ctl, pll_clke_masks);
> +
> +	return 0;
> +}
> +
>   int clock_init(void)
>   {
>   	u32 val_cfg0;
> @@ -467,105 +571,6 @@ int fracpll_configure(enum pll_clocks pll, u32 freq)
>   	return 0;
>   }
>   
> -int intpll_configure(enum pll_clocks pll, ulong freq)
> -{
> -	void __iomem *pll_gnrl_ctl, __iomem *pll_div_ctl;
> -	u32 pll_div_ctl_val, pll_clke_masks;
> -
> -	switch (pll) {
> -	case ANATOP_SYSTEM_PLL1:
> -		pll_gnrl_ctl = &ana_pll->sys_pll1_gnrl_ctl;
> -		pll_div_ctl = &ana_pll->sys_pll1_div_ctl;
> -		pll_clke_masks = INTPLL_DIV20_CLKE_MASK |
> -			INTPLL_DIV10_CLKE_MASK | INTPLL_DIV8_CLKE_MASK |
> -			INTPLL_DIV6_CLKE_MASK | INTPLL_DIV5_CLKE_MASK |
> -			INTPLL_DIV4_CLKE_MASK | INTPLL_DIV3_CLKE_MASK |
> -			INTPLL_DIV2_CLKE_MASK | INTPLL_CLKE_MASK;
> -		break;
> -	case ANATOP_SYSTEM_PLL2:
> -		pll_gnrl_ctl = &ana_pll->sys_pll2_gnrl_ctl;
> -		pll_div_ctl = &ana_pll->sys_pll2_div_ctl;
> -		pll_clke_masks = INTPLL_DIV20_CLKE_MASK |
> -			INTPLL_DIV10_CLKE_MASK | INTPLL_DIV8_CLKE_MASK |
> -			INTPLL_DIV6_CLKE_MASK | INTPLL_DIV5_CLKE_MASK |
> -			INTPLL_DIV4_CLKE_MASK | INTPLL_DIV3_CLKE_MASK |
> -			INTPLL_DIV2_CLKE_MASK | INTPLL_CLKE_MASK;
> -		break;
> -	case ANATOP_SYSTEM_PLL3:
> -		pll_gnrl_ctl = &ana_pll->sys_pll3_gnrl_ctl;
> -		pll_div_ctl = &ana_pll->sys_pll3_div_ctl;
> -		pll_clke_masks = INTPLL_CLKE_MASK;
> -		break;
> -	case ANATOP_ARM_PLL:
> -		pll_gnrl_ctl = &ana_pll->arm_pll_gnrl_ctl;
> -		pll_div_ctl = &ana_pll->arm_pll_div_ctl;
> -		pll_clke_masks = INTPLL_CLKE_MASK;
> -		break;
> -	case ANATOP_GPU_PLL:
> -		pll_gnrl_ctl = &ana_pll->gpu_pll_gnrl_ctl;
> -		pll_div_ctl = &ana_pll->gpu_pll_div_ctl;
> -		pll_clke_masks = INTPLL_CLKE_MASK;
> -		break;
> -	case ANATOP_VPU_PLL:
> -		pll_gnrl_ctl = &ana_pll->vpu_pll_gnrl_ctl;
> -		pll_div_ctl = &ana_pll->vpu_pll_div_ctl;
> -		pll_clke_masks = INTPLL_CLKE_MASK;
> -		break;
> -	default:
> -		return -EINVAL;
> -	};
> -
> -	switch (freq) {
> -	case MHZ(750):
> -		/* 24 * 0xfa / 2 / 2 ^ 2 */
> -		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xfa) |
> -			INTPLL_PRE_DIV_VAL(2) | INTPLL_POST_DIV_VAL(2);
> -		break;
> -	case MHZ(800):
> -		/* 24 * 0x190 / 3 / 2 ^ 2 */
> -		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0x190) |
> -			INTPLL_PRE_DIV_VAL(3) | INTPLL_POST_DIV_VAL(2);
> -		break;
> -	case MHZ(1000):
> -		/* 24 * 0xfa / 3 / 2 ^ 1 */
> -		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xfa) |
> -			INTPLL_PRE_DIV_VAL(3) | INTPLL_POST_DIV_VAL(1);
> -		break;
> -	case MHZ(1200):
> -		/* 24 * 0xc8 / 2 / 2 ^ 1 */
> -		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xc8) |
> -			INTPLL_PRE_DIV_VAL(2) | INTPLL_POST_DIV_VAL(1);
> -		break;
> -	case MHZ(2000):
> -		/* 24 * 0xfa / 3 / 2 ^ 0 */
> -		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xfa) |
> -			INTPLL_PRE_DIV_VAL(3) | INTPLL_POST_DIV_VAL(0);
> -		break;
> -	default:
> -		return -EINVAL;
> -	};
> -	/* Bypass clock and set lock to pll output lock */
> -	setbits_le32(pll_gnrl_ctl, INTPLL_BYPASS_MASK |
> -		     INTPLL_LOCK_SEL_MASK);
> -	/* Enable reset */
> -	clrbits_le32(pll_gnrl_ctl, INTPLL_RST_MASK);
> -	/* Configure */
> -	writel(pll_div_ctl_val, pll_div_ctl);
> -
> -	__udelay(100);
> -
> -	/* Disable reset */
> -	setbits_le32(pll_gnrl_ctl, INTPLL_RST_MASK);
> -	/* Wait Lock */
> -	while (!(readl(pll_gnrl_ctl) & INTPLL_LOCK_MASK))
> -		;
> -	/* Clear bypass */
> -	clrbits_le32(pll_gnrl_ctl, INTPLL_BYPASS_MASK);
> -	setbits_le32(pll_gnrl_ctl, pll_clke_masks);
> -
> -	return 0;
> -}
> -
>   u32 get_root_src_clk(enum clk_root_src root_src)
>   {
>   	switch (root_src) {
> diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
> index eb379c188a..b55566f2e9 100644
> --- a/drivers/clk/imx/Makefile
> +++ b/drivers/clk/imx/Makefile
> @@ -8,3 +8,4 @@ ifdef CONFIG_CLK_IMX8
>   obj-$(CONFIG_IMX8QXP) += clk-imx8qxp.o
>   obj-$(CONFIG_IMX8QM) += clk-imx8qm.o
>   endif
> +obj-$(CONFIG_CLK_IMX8MM) += clk-imx8mm.o
> diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c
> new file mode 100644
> index 0000000000..1e0669494f
> --- /dev/null
> +++ b/drivers/clk/imx/clk-imx8mm.c
> @@ -0,0 +1,106 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2019 NXP
> + * Peng Fan <peng.fan@nxp.com>
> + */
> +
> +#include <common.h>
> +#include <clk-uclass.h>
> +#include <dm.h>
> +#include <asm/arch/clock.h>
> +#include <dt-bindings/clock/imx8mm-clock.h>
> +
> +static ulong imx8mm_clk_get_rate(struct clk *clk)
> +{
> +	debug("%s(#%lu)\n", __func__, clk->id);
> +
> +	switch (clk->id) {
> +	case IMX8MM_CLK_USDHC1_ROOT:
> +		return get_root_clk(USDHC1_CLK_ROOT);
> +	case IMX8MM_CLK_USDHC2_ROOT:
> +		return get_root_clk(USDHC2_CLK_ROOT);
> +	case IMX8MM_CLK_USDHC3_ROOT:
> +		return get_root_clk(USDHC3_CLK_ROOT);
> +	case IMX8MM_CLK_I2C1:

I tested I2C on my i.MX8MM board and it doesn't work (it did work with 
the CCF-based patches). It seems like IMX8MM_CLK_I2CX should be 
IMX8MM_CLK_I2CX_ROOT here and also for all the other occurrences below.
Changing this makes I2C work again.

And by the way: I'm not sure which side to take in the CCF or non-CCF 
discussion for i.MX8MM. I can understand both points of view and 
whichever decision is made I will support it, but it would be nice if we 
could settle for something soon.

Thanks,
Frieder

> +		return get_root_clk(I2C1_CLK_ROOT);
> +	case IMX8MM_CLK_I2C2:
> +		return get_root_clk(I2C2_CLK_ROOT);
> +	case IMX8MM_CLK_I2C3:
> +		return get_root_clk(I2C3_CLK_ROOT);
> +	case IMX8MM_CLK_I2C4:
> +		return get_root_clk(I2C4_CLK_ROOT);
> +	}
> +
> +	return 0;
> +}
> +
> +static int __imx8mm_clk_enable(struct clk *clk, bool enable)
> +{
> +	switch (clk->id) {
> +	case IMX8MM_CLK_USDHC1_ROOT:
> +		return clock_enable(CCGR_USDHC1, enable);
> +	case IMX8MM_CLK_USDHC2_ROOT:
> +		return clock_enable(CCGR_USDHC2, enable);
> +	case IMX8MM_CLK_USDHC3_ROOT:
> +		return clock_enable(CCGR_USDHC3, enable);
> +	case IMX8MM_CLK_I2C1:
> +		return clock_enable(CCGR_I2C1, enable);
> +	case IMX8MM_CLK_I2C2:
> +		return clock_enable(CCGR_I2C2, enable);
> +	case IMX8MM_CLK_I2C3:
> +		return clock_enable(CCGR_I2C3, enable);
> +	case IMX8MM_CLK_I2C4:
> +		return clock_enable(CCGR_I2C4, enable);
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int imx8mm_clk_disable(struct clk *clk)
> +{
> +	debug("%s(#%lu)\n", __func__, clk->id);
> +
> +	return __imx8mm_clk_enable(clk, false);
> +}
> +
> +static int imx8mm_clk_enable(struct clk *clk)
> +{
> +	debug("%s(#%lu)\n", __func__, clk->id);
> +
> +	return __imx8mm_clk_enable(clk, true);
> +}
> +
> +static ulong imx8mm_clk_set_rate(struct clk *clk, unsigned long rate)
> +{
> +	debug("%s(#%lu)\n", __func__, clk->id);
> +
> +	/* TODO: */
> +
> +	return imx8mm_clk_get_rate(clk);
> +}
> +
> +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)
> +{
> +	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,
> +};
>

Patch
diff mbox series

diff --git a/arch/arm/mach-imx/imx8m/clock_imx8mm.c b/arch/arm/mach-imx/imx8m/clock_imx8mm.c
index 07399023d5..541561f276 100644
--- a/arch/arm/mach-imx/imx8m/clock_imx8mm.c
+++ b/arch/arm/mach-imx/imx8m/clock_imx8mm.c
@@ -119,6 +119,110 @@  void init_wdog_clk(void)
 	clock_enable(CCGR_WDOG3, 1);
 }
 
+static int intpll_configure(enum pll_clocks pll, ulong freq)
+{
+	void __iomem *pll_gnrl_ctl, __iomem *pll_div_ctl;
+	u32 pll_div_ctl_val, pll_clke_masks;
+
+	switch (pll) {
+	case ANATOP_SYSTEM_PLL1:
+		pll_gnrl_ctl = &ana_pll->sys_pll1_gnrl_ctl;
+		pll_div_ctl = &ana_pll->sys_pll1_div_ctl;
+		pll_clke_masks = INTPLL_DIV20_CLKE_MASK |
+			INTPLL_DIV10_CLKE_MASK | INTPLL_DIV8_CLKE_MASK |
+			INTPLL_DIV6_CLKE_MASK | INTPLL_DIV5_CLKE_MASK |
+			INTPLL_DIV4_CLKE_MASK | INTPLL_DIV3_CLKE_MASK |
+			INTPLL_DIV2_CLKE_MASK | INTPLL_CLKE_MASK;
+		break;
+	case ANATOP_SYSTEM_PLL2:
+		pll_gnrl_ctl = &ana_pll->sys_pll2_gnrl_ctl;
+		pll_div_ctl = &ana_pll->sys_pll2_div_ctl;
+		pll_clke_masks = INTPLL_DIV20_CLKE_MASK |
+			INTPLL_DIV10_CLKE_MASK | INTPLL_DIV8_CLKE_MASK |
+			INTPLL_DIV6_CLKE_MASK | INTPLL_DIV5_CLKE_MASK |
+			INTPLL_DIV4_CLKE_MASK | INTPLL_DIV3_CLKE_MASK |
+			INTPLL_DIV2_CLKE_MASK | INTPLL_CLKE_MASK;
+		break;
+	case ANATOP_SYSTEM_PLL3:
+		pll_gnrl_ctl = &ana_pll->sys_pll3_gnrl_ctl;
+		pll_div_ctl = &ana_pll->sys_pll3_div_ctl;
+		pll_clke_masks = INTPLL_CLKE_MASK;
+		break;
+	case ANATOP_ARM_PLL:
+		pll_gnrl_ctl = &ana_pll->arm_pll_gnrl_ctl;
+		pll_div_ctl = &ana_pll->arm_pll_div_ctl;
+		pll_clke_masks = INTPLL_CLKE_MASK;
+		break;
+	case ANATOP_GPU_PLL:
+		pll_gnrl_ctl = &ana_pll->gpu_pll_gnrl_ctl;
+		pll_div_ctl = &ana_pll->gpu_pll_div_ctl;
+		pll_clke_masks = INTPLL_CLKE_MASK;
+		break;
+	case ANATOP_VPU_PLL:
+		pll_gnrl_ctl = &ana_pll->vpu_pll_gnrl_ctl;
+		pll_div_ctl = &ana_pll->vpu_pll_div_ctl;
+		pll_clke_masks = INTPLL_CLKE_MASK;
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	switch (freq) {
+	case MHZ(600):
+		/* 24 * 0x12c / 3 / 2 ^ 2 */
+		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0x12c) |
+			INTPLL_PRE_DIV_VAL(3) | INTPLL_POST_DIV_VAL(2);
+		break;
+	case MHZ(750):
+		/* 24 * 0xfa / 2 / 2 ^ 2 */
+		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xfa) |
+			INTPLL_PRE_DIV_VAL(2) | INTPLL_POST_DIV_VAL(2);
+		break;
+	case MHZ(800):
+		/* 24 * 0x190 / 3 / 2 ^ 2 */
+		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0x190) |
+			INTPLL_PRE_DIV_VAL(3) | INTPLL_POST_DIV_VAL(2);
+		break;
+	case MHZ(1000):
+		/* 24 * 0xfa / 3 / 2 ^ 1 */
+		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xfa) |
+			INTPLL_PRE_DIV_VAL(3) | INTPLL_POST_DIV_VAL(1);
+		break;
+	case MHZ(1200):
+		/* 24 * 0xc8 / 2 / 2 ^ 1 */
+		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xc8) |
+			INTPLL_PRE_DIV_VAL(2) | INTPLL_POST_DIV_VAL(1);
+		break;
+	case MHZ(2000):
+		/* 24 * 0xfa / 3 / 2 ^ 0 */
+		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xfa) |
+			INTPLL_PRE_DIV_VAL(3) | INTPLL_POST_DIV_VAL(0);
+		break;
+	default:
+		return -EINVAL;
+	};
+	/* Bypass clock and set lock to pll output lock */
+	setbits_le32(pll_gnrl_ctl, INTPLL_BYPASS_MASK |
+		     INTPLL_LOCK_SEL_MASK);
+	/* Enable reset */
+	clrbits_le32(pll_gnrl_ctl, INTPLL_RST_MASK);
+	/* Configure */
+	writel(pll_div_ctl_val, pll_div_ctl);
+
+	__udelay(100);
+
+	/* Disable reset */
+	setbits_le32(pll_gnrl_ctl, INTPLL_RST_MASK);
+	/* Wait Lock */
+	while (!(readl(pll_gnrl_ctl) & INTPLL_LOCK_MASK))
+		;
+	/* Clear bypass */
+	clrbits_le32(pll_gnrl_ctl, INTPLL_BYPASS_MASK);
+	setbits_le32(pll_gnrl_ctl, pll_clke_masks);
+
+	return 0;
+}
+
 int clock_init(void)
 {
 	u32 val_cfg0;
@@ -467,105 +571,6 @@  int fracpll_configure(enum pll_clocks pll, u32 freq)
 	return 0;
 }
 
-int intpll_configure(enum pll_clocks pll, ulong freq)
-{
-	void __iomem *pll_gnrl_ctl, __iomem *pll_div_ctl;
-	u32 pll_div_ctl_val, pll_clke_masks;
-
-	switch (pll) {
-	case ANATOP_SYSTEM_PLL1:
-		pll_gnrl_ctl = &ana_pll->sys_pll1_gnrl_ctl;
-		pll_div_ctl = &ana_pll->sys_pll1_div_ctl;
-		pll_clke_masks = INTPLL_DIV20_CLKE_MASK |
-			INTPLL_DIV10_CLKE_MASK | INTPLL_DIV8_CLKE_MASK |
-			INTPLL_DIV6_CLKE_MASK | INTPLL_DIV5_CLKE_MASK |
-			INTPLL_DIV4_CLKE_MASK | INTPLL_DIV3_CLKE_MASK |
-			INTPLL_DIV2_CLKE_MASK | INTPLL_CLKE_MASK;
-		break;
-	case ANATOP_SYSTEM_PLL2:
-		pll_gnrl_ctl = &ana_pll->sys_pll2_gnrl_ctl;
-		pll_div_ctl = &ana_pll->sys_pll2_div_ctl;
-		pll_clke_masks = INTPLL_DIV20_CLKE_MASK |
-			INTPLL_DIV10_CLKE_MASK | INTPLL_DIV8_CLKE_MASK |
-			INTPLL_DIV6_CLKE_MASK | INTPLL_DIV5_CLKE_MASK |
-			INTPLL_DIV4_CLKE_MASK | INTPLL_DIV3_CLKE_MASK |
-			INTPLL_DIV2_CLKE_MASK | INTPLL_CLKE_MASK;
-		break;
-	case ANATOP_SYSTEM_PLL3:
-		pll_gnrl_ctl = &ana_pll->sys_pll3_gnrl_ctl;
-		pll_div_ctl = &ana_pll->sys_pll3_div_ctl;
-		pll_clke_masks = INTPLL_CLKE_MASK;
-		break;
-	case ANATOP_ARM_PLL:
-		pll_gnrl_ctl = &ana_pll->arm_pll_gnrl_ctl;
-		pll_div_ctl = &ana_pll->arm_pll_div_ctl;
-		pll_clke_masks = INTPLL_CLKE_MASK;
-		break;
-	case ANATOP_GPU_PLL:
-		pll_gnrl_ctl = &ana_pll->gpu_pll_gnrl_ctl;
-		pll_div_ctl = &ana_pll->gpu_pll_div_ctl;
-		pll_clke_masks = INTPLL_CLKE_MASK;
-		break;
-	case ANATOP_VPU_PLL:
-		pll_gnrl_ctl = &ana_pll->vpu_pll_gnrl_ctl;
-		pll_div_ctl = &ana_pll->vpu_pll_div_ctl;
-		pll_clke_masks = INTPLL_CLKE_MASK;
-		break;
-	default:
-		return -EINVAL;
-	};
-
-	switch (freq) {
-	case MHZ(750):
-		/* 24 * 0xfa / 2 / 2 ^ 2 */
-		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xfa) |
-			INTPLL_PRE_DIV_VAL(2) | INTPLL_POST_DIV_VAL(2);
-		break;
-	case MHZ(800):
-		/* 24 * 0x190 / 3 / 2 ^ 2 */
-		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0x190) |
-			INTPLL_PRE_DIV_VAL(3) | INTPLL_POST_DIV_VAL(2);
-		break;
-	case MHZ(1000):
-		/* 24 * 0xfa / 3 / 2 ^ 1 */
-		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xfa) |
-			INTPLL_PRE_DIV_VAL(3) | INTPLL_POST_DIV_VAL(1);
-		break;
-	case MHZ(1200):
-		/* 24 * 0xc8 / 2 / 2 ^ 1 */
-		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xc8) |
-			INTPLL_PRE_DIV_VAL(2) | INTPLL_POST_DIV_VAL(1);
-		break;
-	case MHZ(2000):
-		/* 24 * 0xfa / 3 / 2 ^ 0 */
-		pll_div_ctl_val = INTPLL_MAIN_DIV_VAL(0xfa) |
-			INTPLL_PRE_DIV_VAL(3) | INTPLL_POST_DIV_VAL(0);
-		break;
-	default:
-		return -EINVAL;
-	};
-	/* Bypass clock and set lock to pll output lock */
-	setbits_le32(pll_gnrl_ctl, INTPLL_BYPASS_MASK |
-		     INTPLL_LOCK_SEL_MASK);
-	/* Enable reset */
-	clrbits_le32(pll_gnrl_ctl, INTPLL_RST_MASK);
-	/* Configure */
-	writel(pll_div_ctl_val, pll_div_ctl);
-
-	__udelay(100);
-
-	/* Disable reset */
-	setbits_le32(pll_gnrl_ctl, INTPLL_RST_MASK);
-	/* Wait Lock */
-	while (!(readl(pll_gnrl_ctl) & INTPLL_LOCK_MASK))
-		;
-	/* Clear bypass */
-	clrbits_le32(pll_gnrl_ctl, INTPLL_BYPASS_MASK);
-	setbits_le32(pll_gnrl_ctl, pll_clke_masks);
-
-	return 0;
-}
-
 u32 get_root_src_clk(enum clk_root_src root_src)
 {
 	switch (root_src) {
diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index eb379c188a..b55566f2e9 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -8,3 +8,4 @@  ifdef CONFIG_CLK_IMX8
 obj-$(CONFIG_IMX8QXP) += clk-imx8qxp.o
 obj-$(CONFIG_IMX8QM) += clk-imx8qm.o
 endif
+obj-$(CONFIG_CLK_IMX8MM) += clk-imx8mm.o
diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c
new file mode 100644
index 0000000000..1e0669494f
--- /dev/null
+++ b/drivers/clk/imx/clk-imx8mm.c
@@ -0,0 +1,106 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP
+ * Peng Fan <peng.fan@nxp.com>
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <asm/arch/clock.h>
+#include <dt-bindings/clock/imx8mm-clock.h>
+
+static ulong imx8mm_clk_get_rate(struct clk *clk)
+{
+	debug("%s(#%lu)\n", __func__, clk->id);
+
+	switch (clk->id) {
+	case IMX8MM_CLK_USDHC1_ROOT:
+		return get_root_clk(USDHC1_CLK_ROOT);
+	case IMX8MM_CLK_USDHC2_ROOT:
+		return get_root_clk(USDHC2_CLK_ROOT);
+	case IMX8MM_CLK_USDHC3_ROOT:
+		return get_root_clk(USDHC3_CLK_ROOT);
+	case IMX8MM_CLK_I2C1:
+		return get_root_clk(I2C1_CLK_ROOT);
+	case IMX8MM_CLK_I2C2:
+		return get_root_clk(I2C2_CLK_ROOT);
+	case IMX8MM_CLK_I2C3:
+		return get_root_clk(I2C3_CLK_ROOT);
+	case IMX8MM_CLK_I2C4:
+		return get_root_clk(I2C4_CLK_ROOT);
+	}
+
+	return 0;
+}
+
+static int __imx8mm_clk_enable(struct clk *clk, bool enable)
+{
+	switch (clk->id) {
+	case IMX8MM_CLK_USDHC1_ROOT:
+		return clock_enable(CCGR_USDHC1, enable);
+	case IMX8MM_CLK_USDHC2_ROOT:
+		return clock_enable(CCGR_USDHC2, enable);
+	case IMX8MM_CLK_USDHC3_ROOT:
+		return clock_enable(CCGR_USDHC3, enable);
+	case IMX8MM_CLK_I2C1:
+		return clock_enable(CCGR_I2C1, enable);
+	case IMX8MM_CLK_I2C2:
+		return clock_enable(CCGR_I2C2, enable);
+	case IMX8MM_CLK_I2C3:
+		return clock_enable(CCGR_I2C3, enable);
+	case IMX8MM_CLK_I2C4:
+		return clock_enable(CCGR_I2C4, enable);
+	}
+
+	return -EINVAL;
+}
+
+static int imx8mm_clk_disable(struct clk *clk)
+{
+	debug("%s(#%lu)\n", __func__, clk->id);
+
+	return __imx8mm_clk_enable(clk, false);
+}
+
+static int imx8mm_clk_enable(struct clk *clk)
+{
+	debug("%s(#%lu)\n", __func__, clk->id);
+
+	return __imx8mm_clk_enable(clk, true);
+}
+
+static ulong imx8mm_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	debug("%s(#%lu)\n", __func__, clk->id);
+
+	/* TODO: */
+
+	return imx8mm_clk_get_rate(clk);
+}
+
+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)
+{
+	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,
+};