All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Peng Fan (OSS)" <peng.fan@oss.nxp.com>
To: sbabic@denx.de, festevam@gmail.com
Cc: u-boot@lists.denx.de, uboot-imx@nxp.com, Peng Fan <peng.fan@nxp.com>
Subject: [PATCH V5 16/44] arm: imx8ulp: add clock support
Date: Sat,  7 Aug 2021 16:00:45 +0800	[thread overview]
Message-ID: <20210807080113.16020-17-peng.fan@oss.nxp.com> (raw)
In-Reply-To: <20210807080113.16020-1-peng.fan@oss.nxp.com>

From: Peng Fan <peng.fan@nxp.com>

Add i.MX8ULP clock support

Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 arch/arm/include/asm/arch-imx8ulp/cgc.h      | 130 ++++++
 arch/arm/include/asm/arch-imx8ulp/clock.h    |   9 +-
 arch/arm/include/asm/arch-imx8ulp/imx-regs.h |   1 +
 arch/arm/include/asm/arch-imx8ulp/pcc.h      | 139 ++++++
 arch/arm/mach-imx/imx8ulp/Makefile           |   2 +-
 arch/arm/mach-imx/imx8ulp/cgc.c              | 455 +++++++++++++++++++
 arch/arm/mach-imx/imx8ulp/clock.c            | 374 ++++++++++++++-
 arch/arm/mach-imx/imx8ulp/pcc.c              | 449 ++++++++++++++++++
 arch/arm/mach-imx/imx8ulp/soc.c              |   3 +
 9 files changed, 1558 insertions(+), 4 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-imx8ulp/cgc.h
 create mode 100644 arch/arm/include/asm/arch-imx8ulp/pcc.h
 create mode 100644 arch/arm/mach-imx/imx8ulp/cgc.c
 create mode 100644 arch/arm/mach-imx/imx8ulp/pcc.c

diff --git a/arch/arm/include/asm/arch-imx8ulp/cgc.h b/arch/arm/include/asm/arch-imx8ulp/cgc.h
new file mode 100644
index 0000000000..34a15fb59c
--- /dev/null
+++ b/arch/arm/include/asm/arch-imx8ulp/cgc.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021 NXP
+ */
+
+#ifndef _ASM_ARCH_CGC_H
+#define _ASM_ARCH_CGC_H
+
+enum cgc1_clk {
+	DUMMY0_CLK,
+	DUMMY1_CLK,
+	LPOSC,
+	XBAR_BUSCLK,
+	SOSC,
+	SOSC_DIV1,
+	SOSC_DIV2,
+	SOSC_DIV3,
+	FRO,
+	FRO_DIV1,
+	FRO_DIV2,
+	FRO_DIV3,
+	PLL2,
+	PLL3,
+	PLL3_VCODIV,
+	PLL3_PFD0,
+	PLL3_PFD1,
+	PLL3_PFD2,
+	PLL3_PFD3,
+	PLL3_PFD0_DIV1,
+	PLL3_PFD0_DIV2,
+	PLL3_PFD1_DIV1,
+	PLL3_PFD1_DIV2,
+	PLL3_PFD2_DIV1,
+	PLL3_PFD2_DIV2,
+	PLL3_PFD3_DIV1,
+	PLL3_PFD3_DIV2,
+};
+
+struct cgc1_regs {
+	u32 verid;
+	u32 rsvd1[4];
+	u32 ca35clk;
+	u32 rsvd2[2];
+	u32 clkoutcfg;
+	u32 rsvd3[4];
+	u32 nicclk;
+	u32 xbarclk;
+	u32 rsvd4[21];
+	u32 clkdivrst;
+	u32 rsvd5[29];
+	u32 soscdiv;
+	u32 rsvd6[63];
+	u32 frodiv;
+	u32 rsvd7[189];
+	u32 pll2csr;
+	u32 rsvd8[3];
+	u32 pll2cfg;
+	u32 rsvd9;
+	u32 pll2denom;
+	u32 pll2num;
+	u32 pll2ss;
+	u32 rsvd10[55];
+	u32 pll3csr;
+	u32 pll3div_vco;
+	u32 pll3div_pfd0;
+	u32 pll3div_pfd1;
+	u32 pll3cfg;
+	u32 pll3pfdcfg;
+	u32 pll3denom;
+	u32 pll3num;
+	u32 pll3ss;
+	u32 pll3lock;
+	u32 rsvd11[54];
+	u32 enetstamp;
+	u32 rsvd12[67];
+	u32 pllusbcfg;
+	u32 rsvd13[59];
+	u32 aud_clk1;
+	u32 sai5_4_clk;
+	u32 tpm6_7clk;
+	u32 mqs1clk;
+	u32 rsvd14[60];
+	u32 lvdscfg;
+};
+
+struct cgc2_regs {
+	u32 verid;
+	u32 rsvd1[4];
+	u32 hificlk;
+	u32 rsvd2[2];
+	u32 clkoutcfg;
+	u32 rsvd3[6];
+	u32 niclpavclk;
+	u32 ddrclk;
+	u32 rsvd4[19];
+	u32 clkdivrst;
+	u32 rsvd5[29];
+	u32 soscdiv;
+	u32 rsvd6[63];
+	u32 frodiv;
+	u32 rsvd7[253];
+	u32 pll4csr;
+	u32 pll4div_vco;
+	u32 pll4div_pfd0;
+	u32 pll4div_pfd1;
+	u32 pll4cfg;
+	u32 pll4pfdcfg;
+	u32 pll4denom;
+	u32 pll4num;
+	u32 pll4ss;
+	u32 pll4lock;
+	u32 rsvd8[128];
+	u32 aud_clk2;
+	u32 sai7_6_clk;
+	u32 tpm8clk;
+	u32 rsvd9[1];
+	u32 spdifclk;
+	u32 rsvd10[59];
+	u32 lvdscfg;
+};
+
+u32 cgc1_clk_get_rate(enum cgc1_clk clk);
+void cgc1_pll3_init(void);
+void cgc1_pll2_init(void);
+void cgc1_soscdiv_init(void);
+void cgc1_init_core_clk(void);
+void cgc2_pll4_init(void);
+void cgc2_ddrclk_config(u32 src, u32 div);
+u32 cgc1_sosc_div(enum cgc1_clk clk);
+#endif
diff --git a/arch/arm/include/asm/arch-imx8ulp/clock.h b/arch/arm/include/asm/arch-imx8ulp/clock.h
index e145c33f01..58e3356e32 100644
--- a/arch/arm/include/asm/arch-imx8ulp/clock.h
+++ b/arch/arm/include/asm/arch-imx8ulp/clock.h
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * Copyright 2020 NXP
+ * Copyright 2021 NXP
  */
 
 #ifndef _ASM_ARCH_IMX8ULP_CLOCK_H
@@ -17,6 +17,7 @@ enum mxc_clock {
 	MXC_DDR_CLK,
 	MXC_ESDHC_CLK,
 	MXC_ESDHC2_CLK,
+	MXC_ESDHC3_CLK,
 	MXC_I2C_CLK,
 };
 
@@ -26,9 +27,15 @@ u32 get_lpuart_clk(void);
 int enable_i2c_clk(unsigned char enable, unsigned int i2c_num);
 u32 imx_get_i2cclk(unsigned int i2c_num);
 #endif
+void enable_usboh3_clk(unsigned char enable);
+int enable_usb_pll(ulong usb_phy_base);
 #ifdef CONFIG_MXC_OCOTP
 void enable_ocotp_clk(unsigned char enable);
 #endif
 void init_clk_usdhc(u32 index);
+void init_clk_fspi(int index);
+void init_clk_ddr(void);
+int set_ddr_clk(u32 phy_freq_mhz);
 void clock_init(void);
+void cgc1_enet_stamp_sel(u32 clk_src);
 #endif
diff --git a/arch/arm/include/asm/arch-imx8ulp/imx-regs.h b/arch/arm/include/asm/arch-imx8ulp/imx-regs.h
index 982f2a9b28..5231155089 100644
--- a/arch/arm/include/asm/arch-imx8ulp/imx-regs.h
+++ b/arch/arm/include/asm/arch-imx8ulp/imx-regs.h
@@ -7,6 +7,7 @@
 #define _IMX8ULP_REGS_H_
 #define ARCH_MXC
 
+#include <linux/bitops.h>
 #include <linux/sizes.h>
 
 #define PBRIDGE0_BASE		0x28000000
diff --git a/arch/arm/include/asm/arch-imx8ulp/pcc.h b/arch/arm/include/asm/arch-imx8ulp/pcc.h
new file mode 100644
index 0000000000..091d0175dd
--- /dev/null
+++ b/arch/arm/include/asm/arch-imx8ulp/pcc.h
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021 NXP
+ */
+
+#ifndef _ASM_ARCH_IMX8ULP_PCC_H
+#define _ASM_ARCH_IMX8ULP_PCC_H
+
+#include <asm/arch/cgc.h>
+
+enum pcc3_entry {
+	DMA1_MP_PCC3_SLOT = 1,
+	DMA1_CH0_PCC3_SLOT = 2,
+	DMA1_CH1_PCC3_SLOT = 3,
+	DMA1_CH2_PCC3_SLOT = 4,
+	DMA1_CH3_PCC3_SLOT = 5,
+	DMA1_CH4_PCC3_SLOT = 6,
+	DMA1_CH5_PCC3_SLOT = 7,
+	DMA1_CH6_PCC3_SLOT = 8,
+	DMA1_CH7_PCC3_SLOT = 9,
+	DMA1_CH8_PCC3_SLOT = 10,
+	DMA1_CH9_PCC3_SLOT = 11,
+	DMA1_CH10_PCC3_SLOT = 12,
+	DMA1_CH11_PCC3_SLOT = 13,
+	DMA1_CH12_PCC3_SLOT = 14,
+	DMA1_CH13_PCC3_SLOT = 15,
+	DMA1_CH14_PCC3_SLOT = 16,
+	DMA1_CH15_PCC3_SLOT = 17,
+	DMA1_CH16_PCC3_SLOT = 18,
+	DMA1_CH17_PCC3_SLOT = 19,
+	DMA1_CH18_PCC3_SLOT = 20,
+	DMA1_CH19_PCC3_SLOT = 21,
+	DMA1_CH20_PCC3_SLOT = 22,
+	DMA1_CH21_PCC3_SLOT = 23,
+	DMA1_CH22_PCC3_SLOT = 24,
+	DMA1_CH23_PCC3_SLOT = 25,
+	DMA1_CH24_PCC3_SLOT = 26,
+	DMA1_CH25_PCC3_SLOT = 27,
+	DMA1_CH26_PCC3_SLOT = 28,
+	DMA1_CH27_PCC3_SLOT = 29,
+	DMA1_CH28_PCC3_SLOT = 30,
+	DMA1_CH29_PCC3_SLOT = 31,
+	DMA1_CH30_PCC3_SLOT = 32,
+	DMA1_CH31_PCC3_SLOT = 33,
+	MU0_B_PCC3_SLOT = 34,
+	MU3_A_PCC3_SLOT = 35,
+	LLWU1_PCC3_SLOT = 38,
+	UPOWER_PCC3_SLOT = 40,
+	WDOG3_PCC3_SLOT = 42,
+	WDOG4_PCC3_SLOT = 43,
+	XRDC_MGR_PCC3_SLOT = 47,
+	SEMA42_1_PCC3_SLOT = 48,
+	ROMCP1_PCC3_SLOT = 49,
+	LPIT1_PCC3_SLOT = 50,
+	TPM4_PCC3_SLOT = 51,
+	TPM5_PCC3_SLOT = 52,
+	FLEXIO1_PCC3_SLOT = 53,
+	I3C2_PCC3_SLOT = 54,
+	LPI2C4_PCC3_SLOT = 55,
+	LPI2C5_PCC3_SLOT = 56,
+	LPUART4_PCC3_SLOT = 57,
+	LPUART5_PCC3_SLOT = 58,
+	LPSPI4_PCC3_SLOT = 59,
+	LPSPI5_PCC3_SLOT = 60,
+};
+
+enum pcc4_entry {
+	FLEXSPI2_PCC4_SLOT = 1,
+	TPM6_PCC4_SLOT = 2,
+	TPM7_PCC4_SLOT = 3,
+	LPI2C6_PCC4_SLOT = 4,
+	LPI2C7_PCC4_SLOT = 5,
+	LPUART6_PCC4_SLOT = 6,
+	LPUART7_PCC4_SLOT = 7,
+	SAI4_PCC4_SLOT = 8,
+	SAI5_PCC4_SLOT = 9,
+	PCTLE_PCC4_SLOT = 10,
+	PCTLF_PCC4_SLOT = 11,
+	SDHC0_PCC4_SLOT = 13,
+	SDHC1_PCC4_SLOT = 14,
+	SDHC2_PCC4_SLOT = 15,
+	USB0_PCC4_SLOT = 16,
+	USBPHY_PCC4_SLOT = 17,
+	USB1_PCC4_SLOT = 18,
+	USB1PHY_PCC4_SLOT = 19,
+	USB_XBAR_PCC4_SLOT = 20,
+	ENET_PCC4_SLOT = 21,
+	SFA1_PCC4_SLOT = 22,
+	RGPIOE_PCC4_SLOT = 30,
+	RGPIOF_PCC4_SLOT = 31,
+};
+
+/* PCC registers */
+#define PCC_PR_OFFSET	31
+#define PCC_PR_MASK		(0x1 << PCC_PR_OFFSET)
+#define PCC_CGC_OFFSET	30
+#define PCC_CGC_MASK	(0x1 << PCC_CGC_OFFSET)
+#define PCC_INUSE_OFFSET	29
+#define PCC_INUSE_MASK		(0x1 << PCC_INUSE_OFFSET)
+#define PCC_PCS_OFFSET	24
+#define PCC_PCS_MASK	(0x7 << PCC_PCS_OFFSET)
+#define PCC_FRAC_OFFSET	3
+#define PCC_FRAC_MASK	(0x1 << PCC_FRAC_OFFSET)
+#define PCC_PCD_OFFSET	0
+#define PCC_PCD_MASK	(0x7 << PCC_PCD_OFFSET)
+
+enum pcc_clksrc_type {
+	CLKSRC_PER_PLAT = 0,
+	CLKSRC_PER_BUS = 1,
+	CLKSRC_NO_PCS = 2,
+};
+
+enum pcc_div_type {
+	PCC_HAS_DIV,
+	PCC_NO_DIV,
+};
+
+enum pcc_rst_b {
+	PCC_HAS_RST_B,
+	PCC_NO_RST_B,
+};
+
+/* This structure keeps info for each pcc slot */
+struct pcc_entry {
+	u32 pcc_base;
+	u32 pcc_slot;
+	enum pcc_clksrc_type clksrc;
+	enum pcc_div_type div;
+	enum pcc_rst_b rst_b;
+};
+
+int pcc_clock_enable(int pcc_controller, int pcc_clk_slot, bool enable);
+int pcc_clock_sel(int pcc_controller, int pcc_clk_slot, enum cgc1_clk src);
+int pcc_clock_div_config(int pcc_controller, int pcc_clk_slot, bool frac, u8 div);
+bool pcc_clock_is_enable(int pcc_controller, int pcc_clk_slot);
+int pcc_clock_get_clksrc(int pcc_controller, int pcc_clk_slot, enum cgc1_clk *src);
+int pcc_reset_peripheral(int pcc_controller, int pcc_clk_slot, bool reset);
+u32 pcc_clock_get_rate(int pcc_controller, int pcc_clk_slot);
+#endif
diff --git a/arch/arm/mach-imx/imx8ulp/Makefile b/arch/arm/mach-imx/imx8ulp/Makefile
index e8970dc04f..78c81d78bb 100644
--- a/arch/arm/mach-imx/imx8ulp/Makefile
+++ b/arch/arm/mach-imx/imx8ulp/Makefile
@@ -4,4 +4,4 @@
 #
 
 obj-y += lowlevel_init.o
-obj-y += soc.o clock.o iomux.o
+obj-y += soc.o clock.o iomux.o pcc.o cgc.o
diff --git a/arch/arm/mach-imx/imx8ulp/cgc.c b/arch/arm/mach-imx/imx8ulp/cgc.c
new file mode 100644
index 0000000000..7bfc3862cd
--- /dev/null
+++ b/arch/arm/mach-imx/imx8ulp/cgc.c
@@ -0,0 +1,455 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 NXP
+ */
+
+#include <common.h>
+#include <div64.h>
+#include <asm/io.h>
+#include <errno.h>
+#include <asm/arch/imx-regs.h>
+#include <asm/arch/cgc.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/global_data.h>
+#include <linux/delay.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static struct cgc1_regs *cgc1_regs = (struct cgc1_regs *)0x292C0000UL;
+static struct cgc2_regs *cgc2_regs = (struct cgc2_regs *)0x2da60000UL;
+
+void cgc1_soscdiv_init(void)
+{
+	/* Configure SOSC/FRO DIV1 ~ DIV3 */
+	clrbits_le32(&cgc1_regs->soscdiv, BIT(7));
+	clrbits_le32(&cgc1_regs->soscdiv, BIT(15));
+	clrbits_le32(&cgc1_regs->soscdiv, BIT(23));
+	clrbits_le32(&cgc1_regs->soscdiv, BIT(31));
+
+	clrbits_le32(&cgc1_regs->frodiv, BIT(7));
+}
+
+void cgc1_pll2_init(void)
+{
+	u32 reg;
+
+	if (readl(&cgc1_regs->pll2csr) & BIT(23))
+		clrbits_le32(&cgc1_regs->pll2csr, BIT(23));
+
+	/* Disable PLL2 */
+	clrbits_le32(&cgc1_regs->pll2csr, BIT(0));
+	mdelay(1);
+
+	/* wait valid bit false */
+	while ((readl(&cgc1_regs->pll2csr) & BIT(24)))
+		;
+
+	/* Select SOSC as source, freq = 31 * 24 =744mhz */
+	reg = 31 << 16;
+	writel(reg, &cgc1_regs->pll2cfg);
+
+	/* Enable PLL2 */
+	setbits_le32(&cgc1_regs->pll2csr, BIT(0));
+
+	/* Wait for PLL2 clock ready */
+	while (!(readl(&cgc1_regs->pll2csr) & BIT(24)))
+		;
+}
+
+static void cgc1_set_a35_clk(u32 clk_src, u32 div_core)
+{
+	u32 reg;
+
+	/* ulock */
+	if (readl(&cgc1_regs->ca35clk) & BIT(31))
+		clrbits_le32(&cgc1_regs->ca35clk, BIT(31));
+
+	reg = readl(&cgc1_regs->ca35clk);
+	reg &= ~GENMASK(29, 21);
+	reg |= ((clk_src & 0x3) << 28);
+	reg |= (((div_core - 1) & 0x3f) << 21);
+	writel(reg, &cgc1_regs->ca35clk);
+
+	while (!(readl(&cgc1_regs->ca35clk) & BIT(27)))
+		;
+}
+
+void cgc1_init_core_clk(void)
+{
+	u32 reg = readl(&cgc1_regs->ca35clk);
+
+	/* if already selected to PLL2, switch to FRO firstly */
+	if (((reg >> 28) & 0x3) == 0x1)
+		cgc1_set_a35_clk(0, 1);
+
+	/* Set pll2 to 750Mhz for 1V  */
+	cgc1_pll2_init();
+
+	/* Set A35 clock to pll2 */
+	cgc1_set_a35_clk(1, 1);
+}
+
+void cgc1_enet_stamp_sel(u32 clk_src)
+{
+	writel((clk_src & 0x7) << 24, &cgc1_regs->enetstamp);
+}
+
+void cgc1_pll3_init(void)
+{
+	/* Gate off VCO */
+	setbits_le32(&cgc1_regs->pll3div_vco, BIT(7));
+
+	/* Disable PLL3 */
+	clrbits_le32(&cgc1_regs->pll3csr, BIT(0));
+
+	/* Gate off PFDxDIV */
+	setbits_le32(&cgc1_regs->pll3div_pfd0, BIT(7) | BIT(15) | BIT(23) | BIT(31));
+	setbits_le32(&cgc1_regs->pll3div_pfd1, BIT(7) | BIT(15) | BIT(23) | BIT(31));
+
+	/* Gate off PFDx */
+	setbits_le32(&cgc1_regs->pll3pfdcfg, BIT(7));
+	setbits_le32(&cgc1_regs->pll3pfdcfg, BIT(15));
+	setbits_le32(&cgc1_regs->pll3pfdcfg, BIT(23));
+	setbits_le32(&cgc1_regs->pll3pfdcfg, BIT(31));
+
+	/* Select SOSC as source */
+	clrbits_le32(&cgc1_regs->pll3cfg, BIT(0));
+
+	//setbits_le32(&cgc1_regs->pll3cfg, 22 << 16);
+	writel(22 << 16, &cgc1_regs->pll3cfg);
+
+	writel(578, &cgc1_regs->pll3num);
+	writel(1000, &cgc1_regs->pll3denom);
+
+	/* Enable PLL3 */
+	setbits_le32(&cgc1_regs->pll3csr, BIT(0));
+
+	/* Wait for PLL3 clock ready */
+	while (!(readl(&cgc1_regs->pll3csr) & BIT(24)))
+		;
+	/* Gate on VCO */
+	clrbits_le32(&cgc1_regs->pll3div_vco, BIT(7));
+
+	/*
+	 * PFD0: 380MHz/396/396/328
+	 */
+	clrbits_le32(&cgc1_regs->pll3pfdcfg, 0x3F);
+	setbits_le32(&cgc1_regs->pll3pfdcfg, 25 << 0);
+	clrbits_le32(&cgc1_regs->pll3pfdcfg, BIT(7));
+	while (!(readl(&cgc1_regs->pll3pfdcfg) & BIT(6)))
+		;
+
+	clrbits_le32(&cgc1_regs->pll3pfdcfg, 0x3F << 8);
+	setbits_le32(&cgc1_regs->pll3pfdcfg, 24 << 8);
+	clrbits_le32(&cgc1_regs->pll3pfdcfg, BIT(15));
+	while (!(readl(&cgc1_regs->pll3pfdcfg) & BIT(14)))
+		;
+
+	clrbits_le32(&cgc1_regs->pll3pfdcfg, 0x3F << 16);
+	setbits_le32(&cgc1_regs->pll3pfdcfg, 24 << 16);
+	clrbits_le32(&cgc1_regs->pll3pfdcfg, BIT(23));
+	while (!(readl(&cgc1_regs->pll3pfdcfg) & BIT(22)))
+		;
+
+	clrbits_le32(&cgc1_regs->pll3pfdcfg, 0x3F << 24);
+	setbits_le32(&cgc1_regs->pll3pfdcfg, 29 << 24);
+	clrbits_le32(&cgc1_regs->pll3pfdcfg, BIT(31));
+	while (!(readl(&cgc1_regs->pll3pfdcfg) & BIT(30)))
+		;
+
+	clrbits_le32(&cgc1_regs->pll3div_pfd0, BIT(7));
+	clrbits_le32(&cgc1_regs->pll3div_pfd0, BIT(15));
+	clrbits_le32(&cgc1_regs->pll3div_pfd0, BIT(23));
+	clrbits_le32(&cgc1_regs->pll3div_pfd0, BIT(31));
+
+	clrbits_le32(&cgc1_regs->pll3div_pfd1, BIT(7));
+	clrbits_le32(&cgc1_regs->pll3div_pfd1, BIT(15));
+	clrbits_le32(&cgc1_regs->pll3div_pfd1, BIT(23));
+	clrbits_le32(&cgc1_regs->pll3div_pfd1, BIT(31));
+}
+
+void cgc2_pll4_init(void)
+{
+	/* Disable PFD DIV and clear DIV */
+	writel(0x80808080, &cgc2_regs->pll4div_pfd0);
+	writel(0x80808080, &cgc2_regs->pll4div_pfd1);
+
+	/* Gate off and clear PFD  */
+	writel(0x80808080, &cgc2_regs->pll4pfdcfg);
+
+	/* Disable PLL4 */
+	writel(0x0, &cgc2_regs->pll4csr);
+
+	/* Configure PLL4 to 528Mhz and clock source from SOSC */
+	writel(22 << 16, &cgc2_regs->pll4cfg);
+	writel(0x1, &cgc2_regs->pll4csr);
+
+	/* wait for PLL4 output valid */
+	while (!(readl(&cgc2_regs->pll4csr) & BIT(24)))
+		;
+
+	/* Enable all 4 PFDs */
+	setbits_le32(&cgc2_regs->pll4pfdcfg, 30 << 0); /* 316.8Mhz for NIC_LPAV */
+	setbits_le32(&cgc2_regs->pll4pfdcfg, 18 << 8);
+	setbits_le32(&cgc2_regs->pll4pfdcfg, 12 << 16);
+	setbits_le32(&cgc2_regs->pll4pfdcfg, 24 << 24);
+
+	clrbits_le32(&cgc2_regs->pll4pfdcfg, BIT(7) | BIT(15) | BIT(23) | BIT(31));
+
+	while ((readl(&cgc2_regs->pll4pfdcfg) & (BIT(30) | BIT(22) | BIT(14) | BIT(6)))
+		!= (BIT(30) | BIT(22) | BIT(14) | BIT(6)))
+		;
+
+	/* Enable PFD DIV */
+	clrbits_le32(&cgc2_regs->pll4div_pfd0, BIT(7) | BIT(15) | BIT(23) | BIT(31));
+	clrbits_le32(&cgc2_regs->pll4div_pfd1, BIT(7) | BIT(15) | BIT(23) | BIT(31));
+}
+
+void cgc2_ddrclk_config(u32 src, u32 div)
+{
+	writel((src << 28) | (div << 21), &cgc2_regs->ddrclk);
+	/* wait for DDRCLK switching done */
+	while (!(readl(&cgc2_regs->ddrclk) & BIT(27)))
+		;
+}
+
+u32 decode_pll(enum cgc1_clk pll)
+{
+	u32 reg, infreq, mult;
+	u32 num, denom;
+
+	infreq = 24000000U;
+	/*
+	 * Alought there are four choices for the bypass src,
+	 * we choose SOSC 24M which is the default set in ROM.
+	 * TODO: check more the comments
+	 */
+	switch (pll) {
+	case PLL2:
+		reg = readl(&cgc1_regs->pll2csr);
+		if (!(reg & BIT(24)))
+			return 0;
+
+		reg = readl(&cgc1_regs->pll2cfg);
+		mult = (reg >> 16) & 0x7F;
+		denom = readl(&cgc1_regs->pll2denom) & 0x3FFFFFFF;
+		num = readl(&cgc1_regs->pll2num) & 0x3FFFFFFF;
+
+		return (u64)infreq * mult + (u64)infreq * num / denom;
+	case PLL3:
+		reg = readl(&cgc1_regs->pll3csr);
+		if (!(reg & BIT(24)))
+			return 0;
+
+		reg = readl(&cgc1_regs->pll3cfg);
+		mult = (reg >> 16) & 0x7F;
+		denom = readl(&cgc1_regs->pll3denom) & 0x3FFFFFFF;
+		num = readl(&cgc1_regs->pll3num) & 0x3FFFFFFF;
+
+		return (u64)infreq * mult + (u64)infreq * num / denom;
+	default:
+		printf("Unsupported pll clocks %d\n", pll);
+		break;
+	}
+
+	return 0;
+}
+
+u32 cgc1_pll3_vcodiv_rate(void)
+{
+	u32 reg, gate, div;
+
+	reg = readl(&cgc1_regs->pll3div_vco);
+	gate = BIT(7) & reg;
+	div = reg & 0x3F;
+
+	return gate ? 0 : decode_pll(PLL3) / (div + 1);
+}
+
+u32 cgc1_pll3_pfd_rate(enum cgc1_clk clk)
+{
+	u32 index, gate, vld, reg;
+
+	switch (clk) {
+	case PLL3_PFD0:
+		index = 0;
+		break;
+	case PLL3_PFD1:
+		index = 1;
+		break;
+	case PLL3_PFD2:
+		index = 2;
+		break;
+	case PLL3_PFD3:
+		index = 3;
+		break;
+	default:
+		return 0;
+	}
+
+	reg = readl(&cgc1_regs->pll3pfdcfg);
+	gate = reg & (BIT(7) << (index * 8));
+	vld = reg & (BIT(6) << (index * 8));
+
+	if (gate || !vld)
+		return 0;
+
+	return (u64)decode_pll(PLL3) * 18 / ((reg >> (index * 8)) & 0x3F);
+}
+
+u32 cgc1_pll3_pfd_div(enum cgc1_clk clk)
+{
+	void __iomem *base;
+	u32 pfd, index, gate, reg;
+
+	switch (clk) {
+	case PLL3_PFD0_DIV1:
+		base = &cgc1_regs->pll3div_pfd0;
+		pfd = PLL3_PFD0;
+		index = 0;
+		break;
+	case PLL3_PFD0_DIV2:
+		base = &cgc1_regs->pll3div_pfd0;
+		pfd = PLL3_PFD0;
+		index = 1;
+		break;
+	case PLL3_PFD1_DIV1:
+		base = &cgc1_regs->pll3div_pfd0;
+		pfd = PLL3_PFD1;
+		index = 2;
+		break;
+	case PLL3_PFD1_DIV2:
+		base = &cgc1_regs->pll3div_pfd0;
+		pfd = PLL3_PFD1;
+		index = 3;
+		break;
+	case PLL3_PFD2_DIV1:
+		base = &cgc1_regs->pll3div_pfd1;
+		pfd = PLL3_PFD2;
+		index = 0;
+		break;
+	case PLL3_PFD2_DIV2:
+		base = &cgc1_regs->pll3div_pfd1;
+		pfd = PLL3_PFD2;
+		index = 1;
+		break;
+	case PLL3_PFD3_DIV1:
+		base = &cgc1_regs->pll3div_pfd1;
+		pfd = PLL3_PFD3;
+		index = 2;
+		break;
+	case PLL3_PFD3_DIV2:
+		base = &cgc1_regs->pll3div_pfd1;
+		pfd = PLL3_PFD3;
+		index = 3;
+		break;
+	default:
+		return 0;
+	}
+
+	reg = readl(base);
+	gate = reg & (BIT(7) << (index * 8));
+
+	if (gate)
+		return 0;
+
+	return cgc1_pll3_pfd_rate(pfd) / (((reg >> (index * 8)) & 0x3F) + 1);
+}
+
+u32 cgc1_sosc_div(enum cgc1_clk clk)
+{
+	u32 reg, gate, index;
+
+	switch (clk) {
+	case SOSC:
+		return 24000000;
+	case SOSC_DIV1:
+		index = 0;
+		break;
+	case SOSC_DIV2:
+		index = 1;
+		break;
+	case SOSC_DIV3:
+		index = 2;
+		break;
+	default:
+		return 0;
+	}
+
+	reg = readl(&cgc1_regs->soscdiv);
+	gate = reg & (BIT(7) << (index * 8));
+
+	if (gate)
+		return 0;
+
+	return 24000000 / (((reg >> (index * 8)) & 0x3F) + 1);
+}
+
+u32 cgc1_fro_div(enum cgc1_clk clk)
+{
+	u32 reg, gate, vld, index;
+
+	switch (clk) {
+	case FRO:
+		return 192000000;
+	case FRO_DIV1:
+		index = 0;
+		break;
+	case FRO_DIV2:
+		index = 1;
+		break;
+	case FRO_DIV3:
+		index = 2;
+		break;
+	default:
+		return 0;
+	}
+
+	reg = readl(&cgc1_regs->frodiv);
+	gate = reg & (BIT(7) << (index * 8));
+	vld = reg & (BIT(6) << (index * 8));
+
+	if (gate || !vld)
+		return 0;
+
+	return 24000000 / (((reg >> (index * 8)) & 0x3F) + 1);
+}
+
+u32 cgc1_clk_get_rate(enum cgc1_clk clk)
+{
+	switch (clk) {
+	case SOSC:
+	case SOSC_DIV1:
+	case SOSC_DIV2:
+	case SOSC_DIV3:
+		return cgc1_sosc_div(clk);
+	case FRO:
+	case FRO_DIV1:
+	case FRO_DIV2:
+	case FRO_DIV3:
+		return cgc1_fro_div(clk);
+	case PLL2:
+		return decode_pll(PLL2);
+	case PLL3:
+		return decode_pll(PLL3);
+	case PLL3_VCODIV:
+		return cgc1_pll3_vcodiv_rate();
+	case PLL3_PFD0:
+	case PLL3_PFD1:
+	case PLL3_PFD2:
+	case PLL3_PFD3:
+		return cgc1_pll3_pfd_rate(clk);
+	case PLL3_PFD0_DIV1:
+	case PLL3_PFD0_DIV2:
+	case PLL3_PFD1_DIV1:
+	case PLL3_PFD1_DIV2:
+	case PLL3_PFD2_DIV1:
+	case PLL3_PFD2_DIV2:
+	case PLL3_PFD3_DIV1:
+	case PLL3_PFD3_DIV2:
+		return cgc1_pll3_pfd_div(clk);
+	default:
+		printf("Unsupported cgc1 clock: %d\n", clk);
+		return 0;
+	}
+}
diff --git a/arch/arm/mach-imx/imx8ulp/clock.c b/arch/arm/mach-imx/imx8ulp/clock.c
index f866809fc2..ebbaad4106 100644
--- a/arch/arm/mach-imx/imx8ulp/clock.c
+++ b/arch/arm/mach-imx/imx8ulp/clock.c
@@ -4,24 +4,394 @@
  */
 
 #include <common.h>
+#include <command.h>
 #include <div64.h>
+#include <asm/arch/imx-regs.h>
 #include <asm/io.h>
 #include <errno.h>
 #include <asm/arch/clock.h>
+#include <asm/arch/pcc.h>
+#include <asm/arch/cgc.h>
 #include <asm/arch/sys_proto.h>
+#include <asm/global_data.h>
+#include <linux/delay.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
+#define PLL_USB_EN_USB_CLKS_MASK	(0x01 << 6)
+#define PLL_USB_PWR_MASK		(0x01 << 12)
+#define PLL_USB_ENABLE_MASK		(0x01 << 13)
+#define PLL_USB_BYPASS_MASK		(0x01 << 16)
+#define PLL_USB_REG_ENABLE_MASK		(0x01 << 21)
+#define PLL_USB_DIV_SEL_MASK		(0x07 << 22)
+#define PLL_USB_LOCK_MASK		(0x01 << 31)
+#define PCC5_LPDDR4_ADDR 0x2da70108
+
+static void lpuart_set_clk(u32 index, enum cgc1_clk clk)
+{
+	const u32 lpuart_pcc_slots[] = {
+		LPUART4_PCC3_SLOT,
+		LPUART5_PCC3_SLOT,
+		LPUART6_PCC4_SLOT,
+		LPUART7_PCC4_SLOT,
+	};
+
+	const u32 lpuart_pcc[] = {
+		3, 3, 4, 4,
+	};
+
+	if (index > 3)
+		return;
+
+	pcc_clock_enable(lpuart_pcc[index], lpuart_pcc_slots[index], false);
+	pcc_clock_sel(lpuart_pcc[index], lpuart_pcc_slots[index], clk);
+	pcc_clock_enable(lpuart_pcc[index], lpuart_pcc_slots[index], true);
+
+	pcc_reset_peripheral(lpuart_pcc[index], lpuart_pcc_slots[index], false);
+}
+
+static void init_clk_lpuart(void)
+{
+	u32 index = 0, i;
+
+	const u32 lpuart_array[] = {
+		LPUART4_RBASE,
+		LPUART5_RBASE,
+		LPUART6_RBASE,
+		LPUART7_RBASE,
+	};
+
+	for (i = 0; i < 4; i++) {
+		if (lpuart_array[i] == LPUART_BASE) {
+			index = i;
+			break;
+		}
+	}
+
+	lpuart_set_clk(index, SOSC_DIV2);
+}
+
+void init_clk_fspi(int index)
+{
+	pcc_clock_enable(4, FLEXSPI2_PCC4_SLOT, false);
+	pcc_clock_sel(4, FLEXSPI2_PCC4_SLOT, PLL3_PFD2_DIV1);
+	pcc_clock_div_config(4, FLEXSPI2_PCC4_SLOT, false, 8);
+	pcc_clock_enable(4, FLEXSPI2_PCC4_SLOT, true);
+	pcc_reset_peripheral(4, FLEXSPI2_PCC4_SLOT, false);
+}
+
+void setclkout_ddr(void)
+{
+	writel(0x12800000, 0x2DA60020);
+	writel(0xa00, 0x298C0000); /* PTD0 */
+}
+
+void ddrphy_pll_lock(void)
+{
+	writel(0x00011542, 0x2E065964);
+	writel(0x00011542, 0x2E06586C);
+
+	writel(0x00000B01, 0x2E062000);
+	writel(0x00000B01, 0x2E060000);
+}
+
+void init_clk_ddr(void)
+{
+	/* enable pll4 and ddrclk*/
+	cgc2_pll4_init();
+	cgc2_ddrclk_config(1, 1);
+
+	/* enable ddr pcc */
+	writel(0xd0000000, PCC5_LPDDR4_ADDR);
+
+	/* for debug */
+	/* setclkout_ddr(); */
+}
+
+int set_ddr_clk(u32 phy_freq_mhz)
+{
+	debug("%s %u\n", __func__, phy_freq_mhz);
+
+	if (phy_freq_mhz == 48) {
+		writel(0x90000000, PCC5_LPDDR4_ADDR); /* disable ddr pcc */
+		cgc2_ddrclk_config(2, 0); /* 24Mhz DDR clock */
+		writel(0xd0000000, PCC5_LPDDR4_ADDR); /* enable ddr pcc */
+	} else if (phy_freq_mhz == 384) {
+		writel(0x90000000, PCC5_LPDDR4_ADDR); /* disable ddr pcc */
+		cgc2_ddrclk_config(0, 0); /* 192Mhz DDR clock */
+		writel(0xd0000000, PCC5_LPDDR4_ADDR); /* enable ddr pcc */
+	} else if (phy_freq_mhz == 528) {
+		writel(0x90000000, PCC5_LPDDR4_ADDR); /* disable ddr pcc */
+		cgc2_ddrclk_config(4, 1); /* 264Mhz DDR clock */
+		writel(0xd0000000, PCC5_LPDDR4_ADDR); /* enable ddr pcc */
+	} else if (phy_freq_mhz == 264) {
+		writel(0x90000000, PCC5_LPDDR4_ADDR); /* disable ddr pcc */
+		cgc2_ddrclk_config(4, 3); /* 132Mhz DDR clock */
+		writel(0xd0000000, PCC5_LPDDR4_ADDR); /* enable ddr pcc */
+	} else if (phy_freq_mhz == 192) {
+		writel(0x90000000, PCC5_LPDDR4_ADDR); /* disable ddr pcc */
+		cgc2_ddrclk_config(0, 1); /* 96Mhz DDR clock */
+		writel(0xd0000000, PCC5_LPDDR4_ADDR); /* enable ddr pcc */
+	} else if (phy_freq_mhz == 96) {
+		writel(0x90000000, PCC5_LPDDR4_ADDR); /* disable ddr pcc */
+		cgc2_ddrclk_config(0, 3); /* 48Mhz DDR clock */
+		writel(0xd0000000, PCC5_LPDDR4_ADDR); /* enable ddr pcc */
+	} else {
+		printf("ddr phy clk %uMhz is not supported\n", phy_freq_mhz);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 void clock_init(void)
 {
+	cgc1_soscdiv_init();
+	cgc1_init_core_clk();
+
+	init_clk_lpuart();
+
+	pcc_clock_enable(4, SDHC0_PCC4_SLOT, false);
+	pcc_clock_sel(4, SDHC0_PCC4_SLOT, PLL3_PFD1_DIV2);
+	pcc_clock_enable(4, SDHC0_PCC4_SLOT, true);
+	pcc_reset_peripheral(4, SDHC0_PCC4_SLOT, false);
+
+	pcc_clock_enable(4, SDHC1_PCC4_SLOT, false);
+	pcc_clock_sel(4, SDHC1_PCC4_SLOT, PLL3_PFD2_DIV1);
+	pcc_clock_enable(4, SDHC1_PCC4_SLOT, true);
+	pcc_reset_peripheral(4, SDHC1_PCC4_SLOT, false);
+
+	pcc_clock_enable(4, SDHC2_PCC4_SLOT, false);
+	pcc_clock_sel(4, SDHC2_PCC4_SLOT, PLL3_PFD2_DIV1);
+	pcc_clock_enable(4, SDHC2_PCC4_SLOT, true);
+	pcc_reset_peripheral(4, SDHC2_PCC4_SLOT, false);
+
+	/* Enable upower mu1 clk */
+	pcc_clock_enable(3, UPOWER_PCC3_SLOT, true);
+
+	/*
+	 * Enable clock division
+	 * TODO: may not needed after ROM ready.
+	 */
+}
+
+#if IS_ENABLED(CONFIG_SYS_I2C_IMX_LPI2C)
+int enable_i2c_clk(unsigned char enable, u32 i2c_num)
+{
+	/* Set parent to FIRC DIV2 clock */
+	const u32 lpi2c_pcc_clks[] = {
+		LPI2C4_PCC3_SLOT << 8 | 3,
+		LPI2C5_PCC3_SLOT << 8 | 3,
+		LPI2C6_PCC4_SLOT << 8 | 4,
+		LPI2C7_PCC4_SLOT << 8 | 4,
+	};
+
+	if (i2c_num < 4 || i2c_num > 7)
+		return -EINVAL;
+
+	if (enable) {
+		pcc_clock_enable(lpi2c_pcc_clks[i2c_num - 4] & 0xff,
+				 lpi2c_pcc_clks[i2c_num - 4] >> 8, false);
+		pcc_clock_sel(lpi2c_pcc_clks[i2c_num - 4] & 0xff,
+			      lpi2c_pcc_clks[i2c_num - 4] >> 8, SOSC_DIV2);
+		pcc_clock_enable(lpi2c_pcc_clks[i2c_num - 4] & 0xff,
+				 lpi2c_pcc_clks[i2c_num - 4] >> 8, true);
+		pcc_reset_peripheral(lpi2c_pcc_clks[i2c_num - 4] & 0xff,
+				     lpi2c_pcc_clks[i2c_num - 4] >> 8, false);
+	} else {
+		pcc_clock_enable(lpi2c_pcc_clks[i2c_num - 4] & 0xff,
+				 lpi2c_pcc_clks[i2c_num - 4] >> 8, false);
+	}
+	return 0;
+}
+
+u32 imx_get_i2cclk(u32 i2c_num)
+{
+	const u32 lpi2c_pcc_clks[] = {
+		LPI2C4_PCC3_SLOT << 8 | 3,
+		LPI2C5_PCC3_SLOT << 8 | 3,
+		LPI2C6_PCC4_SLOT << 8 | 4,
+		LPI2C7_PCC4_SLOT << 8 | 4,
+	};
+
+	if (i2c_num < 4 || i2c_num > 7)
+		return 0;
+
+	return pcc_clock_get_rate(lpi2c_pcc_clks[i2c_num - 4] & 0xff,
+				  lpi2c_pcc_clks[i2c_num - 4] >> 8);
+}
+#endif
+
+void enable_usboh3_clk(unsigned char enable)
+{
+	if (enable) {
+		pcc_clock_enable(4, USB0_PCC4_SLOT, true);
+		pcc_clock_enable(4, USBPHY_PCC4_SLOT, true);
+		pcc_reset_peripheral(4, USB0_PCC4_SLOT, false);
+		pcc_reset_peripheral(4, USBPHY_PCC4_SLOT, false);
+
+#ifdef CONFIG_USB_MAX_CONTROLLER_COUNT
+		if (CONFIG_USB_MAX_CONTROLLER_COUNT > 1) {
+			pcc_clock_enable(4, USB1_PCC4_SLOT, true);
+			pcc_clock_enable(4, USB1PHY_PCC4_SLOT, true);
+			pcc_reset_peripheral(4, USB1_PCC4_SLOT, false);
+			pcc_reset_peripheral(4, USB1PHY_PCC4_SLOT, false);
+		}
+#endif
+
+		pcc_clock_enable(4, USB_XBAR_PCC4_SLOT, true);
+	} else {
+		pcc_clock_enable(4, USB0_PCC4_SLOT, false);
+		pcc_clock_enable(4, USB1_PCC4_SLOT, false);
+		pcc_clock_enable(4, USBPHY_PCC4_SLOT, false);
+		pcc_clock_enable(4, USB1PHY_PCC4_SLOT, false);
+		pcc_clock_enable(4, USB_XBAR_PCC4_SLOT, false);
+	}
 }
 
-unsigned int mxc_get_clock(enum mxc_clock clk)
+int enable_usb_pll(ulong usb_phy_base)
 {
+	u32 sosc_rate;
+	s32 timeout = 1000000;
+
+	struct usbphy_regs *usbphy =
+		(struct usbphy_regs *)usb_phy_base;
+
+	sosc_rate = cgc1_sosc_div(SOSC);
+	if (!sosc_rate)
+		return -EPERM;
+
+	if (!(readl(&usbphy->usb1_pll_480_ctrl) & PLL_USB_LOCK_MASK)) {
+		writel(0x1c00000, &usbphy->usb1_pll_480_ctrl_clr);
+
+		switch (sosc_rate) {
+		case 24000000:
+			writel(0xc00000, &usbphy->usb1_pll_480_ctrl_set);
+			break;
+
+		case 30000000:
+			writel(0x800000, &usbphy->usb1_pll_480_ctrl_set);
+			break;
+
+		case 19200000:
+			writel(0x1400000, &usbphy->usb1_pll_480_ctrl_set);
+			break;
+
+		default:
+			writel(0xc00000, &usbphy->usb1_pll_480_ctrl_set);
+			break;
+		}
+
+		/* Enable the regulator first */
+		writel(PLL_USB_REG_ENABLE_MASK,
+		       &usbphy->usb1_pll_480_ctrl_set);
+
+		/* Wait at least 15us */
+		udelay(15);
+
+		/* Enable the power */
+		writel(PLL_USB_PWR_MASK, &usbphy->usb1_pll_480_ctrl_set);
+
+		/* Wait lock */
+		while (timeout--) {
+			if (readl(&usbphy->usb1_pll_480_ctrl) &
+			    PLL_USB_LOCK_MASK)
+				break;
+		}
+
+		if (timeout <= 0) {
+			/* If timeout, we power down the pll */
+			writel(PLL_USB_PWR_MASK,
+			       &usbphy->usb1_pll_480_ctrl_clr);
+			return -ETIME;
+		}
+	}
+
+	/* Clear the bypass */
+	writel(PLL_USB_BYPASS_MASK, &usbphy->usb1_pll_480_ctrl_clr);
+
+	/* Enable the PLL clock out to USB */
+	writel((PLL_USB_EN_USB_CLKS_MASK | PLL_USB_ENABLE_MASK),
+	       &usbphy->usb1_pll_480_ctrl_set);
+
 	return 0;
 }
 
+u32 mxc_get_clock(enum mxc_clock clk)
+{
+	switch (clk) {
+	case MXC_ESDHC_CLK:
+		return pcc_clock_get_rate(4, SDHC0_PCC4_SLOT);
+	case MXC_ESDHC2_CLK:
+		return pcc_clock_get_rate(4, SDHC1_PCC4_SLOT);
+	case MXC_ESDHC3_CLK:
+		return pcc_clock_get_rate(4, SDHC2_PCC4_SLOT);
+	case MXC_ARM_CLK:
+		return cgc1_clk_get_rate(PLL2);
+	default:
+		return 0;
+	}
+}
+
 u32 get_lpuart_clk(void)
 {
-	return 24000000;
+	int index = 0;
+
+	const u32 lpuart_array[] = {
+		LPUART4_RBASE,
+		LPUART5_RBASE,
+		LPUART6_RBASE,
+		LPUART7_RBASE,
+	};
+
+	const u32 lpuart_pcc_slots[] = {
+		LPUART4_PCC3_SLOT,
+		LPUART5_PCC3_SLOT,
+		LPUART6_PCC4_SLOT,
+		LPUART7_PCC4_SLOT,
+	};
+
+	const u32 lpuart_pcc[] = {
+		3, 3, 4, 4,
+	};
+
+	for (index = 0; index < 4; index++) {
+		if (lpuart_array[index] == LPUART_BASE)
+			break;
+	}
+
+	if (index > 3)
+		return 0;
+
+	return pcc_clock_get_rate(lpuart_pcc[index], lpuart_pcc_slots[index]);
+}
+
+#ifndef CONFIG_SPL_BUILD
+/*
+ * Dump some core clockes.
+ */
+int do_mx8ulp_showclocks(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+	printf("SDHC0 %8d MHz\n", pcc_clock_get_rate(4, SDHC0_PCC4_SLOT) / 1000000);
+	printf("SDHC1 %8d MHz\n", pcc_clock_get_rate(4, SDHC1_PCC4_SLOT) / 1000000);
+	printf("SDHC2 %8d MHz\n", pcc_clock_get_rate(4, SDHC2_PCC4_SLOT) / 1000000);
+
+	printf("SOSC %8d MHz\n", cgc1_clk_get_rate(SOSC) / 1000000);
+	printf("FRO %8d MHz\n", cgc1_clk_get_rate(FRO) / 1000000);
+	printf("PLL2 %8d MHz\n", cgc1_clk_get_rate(PLL2) / 1000000);
+	printf("PLL3 %8d MHz\n", cgc1_clk_get_rate(PLL3) / 1000000);
+	printf("PLL3_VCODIV %8d MHz\n", cgc1_clk_get_rate(PLL3_VCODIV) / 1000000);
+	printf("PLL3_PFD0 %8d MHz\n", cgc1_clk_get_rate(PLL3_PFD0) / 1000000);
+	printf("PLL3_PFD1 %8d MHz\n", cgc1_clk_get_rate(PLL3_PFD1) / 1000000);
+	printf("PLL3_PFD2 %8d MHz\n", cgc1_clk_get_rate(PLL3_PFD2) / 1000000);
+	printf("PLL3_PFD3 %8d MHz\n", cgc1_clk_get_rate(PLL3_PFD3) / 1000000);
+
+	return 0;
 }
+
+U_BOOT_CMD(
+	clocks,	CONFIG_SYS_MAXARGS, 1, do_mx8ulp_showclocks,
+	"display clocks",
+	""
+);
+#endif
diff --git a/arch/arm/mach-imx/imx8ulp/pcc.c b/arch/arm/mach-imx/imx8ulp/pcc.c
new file mode 100644
index 0000000000..711b685cd7
--- /dev/null
+++ b/arch/arm/mach-imx/imx8ulp/pcc.c
@@ -0,0 +1,449 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 NXP
+ */
+
+#include <common.h>
+#include <div64.h>
+#include <asm/io.h>
+#include <errno.h>
+#include <asm/arch/imx-regs.h>
+#include <asm/arch/pcc.h>
+#include <asm/arch/cgc.h>
+#include <asm/arch/sys_proto.h>
+
+#define cgc1_clk_TYPES 2
+#define cgc1_clk_NUM 8
+
+static enum cgc1_clk pcc3_clksrc[][8] = {
+	{
+	},
+	{	DUMMY0_CLK,
+		LPOSC,
+		SOSC_DIV2,
+		FRO_DIV2,
+		XBAR_BUSCLK,
+		PLL3_PFD1_DIV1,
+		PLL3_PFD0_DIV2,
+		PLL3_PFD0_DIV1
+	}
+};
+
+static enum cgc1_clk pcc4_clksrc[][8] = {
+	{
+		DUMMY0_CLK,
+		SOSC_DIV1,
+		FRO_DIV1,
+		PLL3_PFD3_DIV2,
+		PLL3_PFD3_DIV1,
+		PLL3_PFD2_DIV2,
+		PLL3_PFD2_DIV1,
+		PLL3_PFD1_DIV2
+	},
+	{
+		DUMMY0_CLK,
+		DUMMY1_CLK,
+		LPOSC,
+		SOSC_DIV2,
+		FRO_DIV2,
+		XBAR_BUSCLK,
+		PLL3_VCODIV,
+		PLL3_PFD0_DIV1
+	}
+};
+
+static struct pcc_entry pcc3_arrays[] = {
+	{PCC3_RBASE, DMA1_MP_PCC3_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH0_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH1_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH2_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH3_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH4_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH5_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH6_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH7_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH8_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH9_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH10_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH11_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH12_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH13_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH14_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH15_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH16_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH17_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH18_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH19_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH20_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH21_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH22_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH23_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH24_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH25_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH26_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH27_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH28_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH29_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH30_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, DMA1_CH31_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, MU0_B_PCC3_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, MU3_A_PCC3_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, LLWU1_PCC3_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, UPOWER_PCC3_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, WDOG3_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
+	{PCC3_RBASE, WDOG4_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
+	{PCC3_RBASE, XRDC_MGR_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, SEMA42_1_PCC3_SLOT,	CLKSRC_PER_BUS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, ROMCP1_PCC3_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
+	{PCC3_RBASE, LPIT1_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
+	{PCC3_RBASE, TPM4_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
+	{PCC3_RBASE, TPM5_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
+	{PCC3_RBASE, FLEXIO1_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
+	{PCC3_RBASE, I3C2_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
+	{PCC3_RBASE, LPI2C4_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
+	{PCC3_RBASE, LPI2C5_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
+	{PCC3_RBASE, LPUART4_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
+	{PCC3_RBASE, LPUART5_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
+	{PCC3_RBASE, LPSPI4_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
+	{PCC3_RBASE, LPSPI5_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
+	{}
+};
+
+static struct pcc_entry pcc4_arrays[] = {
+	{PCC4_RBASE, FLEXSPI2_PCC4_SLOT,	CLKSRC_PER_PLAT, PCC_HAS_DIV, PCC_HAS_RST_B },
+	{PCC4_RBASE, TPM6_PCC4_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B },
+	{PCC4_RBASE, TPM7_PCC4_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B },
+	{PCC4_RBASE, LPI2C6_PCC4_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B },
+	{PCC4_RBASE, LPI2C7_PCC4_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B },
+	{PCC4_RBASE, LPUART6_PCC4_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B },
+	{PCC4_RBASE, LPUART7_PCC4_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B },
+	{PCC4_RBASE, SAI4_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B },
+	{PCC4_RBASE, SAI5_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B },
+	{PCC4_RBASE, PCTLE_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B },
+	{PCC4_RBASE, PCTLF_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B },
+	{PCC4_RBASE, SDHC0_PCC4_SLOT,		CLKSRC_PER_PLAT, PCC_HAS_DIV, PCC_HAS_RST_B },
+	{PCC4_RBASE, SDHC1_PCC4_SLOT,		CLKSRC_PER_PLAT, PCC_HAS_DIV, PCC_HAS_RST_B },
+	{PCC4_RBASE, SDHC2_PCC4_SLOT,		CLKSRC_PER_PLAT, PCC_HAS_DIV, PCC_HAS_RST_B },
+	{PCC4_RBASE, USB0_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B },
+	{PCC4_RBASE, USBPHY_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B },
+	{PCC4_RBASE, USB1_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B },
+	{PCC4_RBASE, USB1PHY_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B },
+	{PCC4_RBASE, USB_XBAR_PCC4_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B },
+	{PCC4_RBASE, ENET_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B },
+	{PCC4_RBASE, SFA1_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B },
+	{PCC4_RBASE, RGPIOE_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B },
+	{PCC4_RBASE, RGPIOF_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B },
+	{}
+};
+
+static int find_pcc_entry(int pcc_controller, int pcc_clk_slot, struct pcc_entry **out)
+{
+	struct pcc_entry *pcc_array;
+	int index = 0;
+
+	switch (pcc_controller) {
+	case 3:
+		pcc_array = pcc3_arrays;
+		*out = &pcc3_arrays[0];
+		break;
+	case 4:
+		pcc_array = pcc4_arrays;
+		*out = &pcc4_arrays[0];
+		break;
+	default:
+		printf("Not supported pcc_controller: %d\n", pcc_controller);
+		return -EINVAL;
+	}
+
+	while (pcc_array->pcc_base) {
+		if (pcc_array->pcc_slot == pcc_clk_slot)
+			return index;
+
+		pcc_array++;
+		index++;
+	}
+
+	return -ENOENT;
+}
+
+int pcc_clock_enable(int pcc_controller, int pcc_clk_slot, bool enable)
+{
+	u32 val;
+	void __iomem *reg;
+	int clk;
+	struct pcc_entry *pcc_array;
+
+	clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array);
+	if (clk < 0)
+		return -EINVAL;
+
+	reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4);
+
+	val = readl(reg);
+
+	debug("%s: clk %d, reg 0x%p, val 0x%x, enable %d\n", __func__, clk, reg, val, enable);
+
+	if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK))
+		return -EPERM;
+
+	if (enable)
+		val |= PCC_CGC_MASK;
+	else
+		val &= ~PCC_CGC_MASK;
+
+	writel(val, reg);
+
+	debug("%s: val 0x%x\n", __func__, val);
+
+	return 0;
+}
+
+/* The clock source select needs clock is disabled */
+int pcc_clock_sel(int pcc_controller, int pcc_clk_slot, enum cgc1_clk src)
+{
+	u32 val, i, clksrc_type;
+	void __iomem *reg;
+	struct pcc_entry *pcc_array;
+	enum cgc1_clk *cgc1_clk_array;
+	int clk;
+
+	clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array);
+	if (clk < 0)
+		return -EINVAL;
+
+	reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4);
+
+	clksrc_type = pcc_array[clk].clksrc;
+	if (clksrc_type >= CLKSRC_NO_PCS) {
+		printf("No PCS field for the PCC %d, clksrc type %d\n",
+		       clk, clksrc_type);
+		return -EPERM;
+	}
+
+	if (pcc_controller == 3)
+		cgc1_clk_array = pcc3_clksrc[clksrc_type];
+	else
+		cgc1_clk_array = pcc4_clksrc[clksrc_type];
+
+	for (i = 0; i < cgc1_clk_NUM; i++) {
+		if (cgc1_clk_array[i] == src) {
+			/* Find the clock src, then set it to PCS */
+			break;
+		}
+	}
+
+	if (i == cgc1_clk_NUM) {
+		printf("No parent in PCS of PCC %d, invalid scg_clk %d\n", clk, src);
+		return -EINVAL;
+	}
+
+	val = readl(reg);
+
+	debug("%s: clk %d, reg 0x%p, val 0x%x, clksrc_type %d\n",
+	      __func__, clk, reg, val, clksrc_type);
+
+	if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK) ||
+	    (val & PCC_CGC_MASK)) {
+		printf("Not permit to select clock source val = 0x%x\n", val);
+		return -EPERM;
+	}
+
+	val &= ~PCC_PCS_MASK;
+	val |= i << PCC_PCS_OFFSET;
+
+	writel(val, reg);
+
+	debug("%s: val 0x%x\n", __func__, val);
+
+	return 0;
+}
+
+int pcc_clock_div_config(int pcc_controller, int pcc_clk_slot, bool frac, u8 div)
+{
+	u32 val;
+	void __iomem *reg;
+	struct pcc_entry *pcc_array;
+	int clk;
+
+	clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array);
+	if (clk < 0)
+		return -EINVAL;
+
+	reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4);
+
+	if (div > 8 || (div == 1 && frac != 0))
+		return -EINVAL;
+
+	if (pcc_array[clk].div >= PCC_NO_DIV) {
+		printf("No DIV/FRAC field for the PCC %d\n", clk);
+		return -EPERM;
+	}
+
+	val = readl(reg);
+
+	if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK) ||
+	    (val & PCC_CGC_MASK)) {
+		printf("Not permit to set div/frac val = 0x%x\n", val);
+		return -EPERM;
+	}
+
+	if (frac)
+		val |= PCC_FRAC_MASK;
+	else
+		val &= ~PCC_FRAC_MASK;
+
+	val &= ~PCC_PCD_MASK;
+	val |= (div - 1) & PCC_PCD_MASK;
+
+	writel(val, reg);
+
+	return 0;
+}
+
+bool pcc_clock_is_enable(int pcc_controller, int pcc_clk_slot)
+{
+	u32 val;
+	void __iomem *reg;
+	struct pcc_entry *pcc_array;
+	int clk;
+
+	clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array);
+	if (clk < 0)
+		return -EINVAL;
+
+	reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4);
+	val = readl(reg);
+
+	if ((val & PCC_INUSE_MASK) || (val & PCC_CGC_MASK))
+		return true;
+
+	return false;
+}
+
+int pcc_clock_get_clksrc(int pcc_controller, int pcc_clk_slot, enum cgc1_clk *src)
+{
+	u32 val, clksrc_type;
+	void __iomem *reg;
+	struct pcc_entry *pcc_array;
+	int clk;
+	enum cgc1_clk *cgc1_clk_array;
+
+	clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array);
+	if (clk < 0)
+		return -EINVAL;
+
+	clksrc_type = pcc_array[clk].clksrc;
+	if (clksrc_type >= CLKSRC_NO_PCS) {
+		printf("No PCS field for the PCC %d, clksrc type %d\n",
+		       pcc_clk_slot, clksrc_type);
+		return -EPERM;
+	}
+
+	reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4);
+
+	val = readl(reg);
+
+	debug("%s: clk %d, reg 0x%p, val 0x%x, type %d\n",
+	      __func__, pcc_clk_slot, reg, val, clksrc_type);
+
+	if (!(val & PCC_PR_MASK)) {
+		printf("This pcc slot is not present = 0x%x\n", val);
+		return -EPERM;
+	}
+
+	val &= PCC_PCS_MASK;
+	val = (val >> PCC_PCS_OFFSET);
+
+	if (!val) {
+		printf("Clock source is off\n");
+		return -EIO;
+	}
+
+	if (pcc_controller == 3)
+		cgc1_clk_array = pcc3_clksrc[clksrc_type];
+	else
+		cgc1_clk_array = pcc4_clksrc[clksrc_type];
+
+	*src = cgc1_clk_array[val];
+
+	debug("%s: parent cgc1 clk %d\n", __func__, *src);
+
+	return 0;
+}
+
+int pcc_reset_peripheral(int pcc_controller, int pcc_clk_slot, bool reset)
+{
+	u32 val;
+	void __iomem *reg;
+	struct pcc_entry *pcc_array;
+	int clk;
+
+	clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array);
+	if (clk < 0)
+		return -EINVAL;
+
+	if (pcc_array[clk].rst_b == PCC_NO_RST_B)
+		return 0;
+
+	reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4);
+
+	val = readl(reg);
+
+	debug("%s: clk %d, reg 0x%p, val 0x%x\n", __func__, pcc_clk_slot, reg, val);
+
+	if (!(val & PCC_PR_MASK)) {
+		printf("This pcc slot is not present = 0x%x\n", val);
+		return -EPERM;
+	}
+
+	if (reset)
+		val &= ~BIT(28);
+	else
+		val |= BIT(28);
+
+	writel(val, reg);
+
+	debug("%s: clk %d, reg 0x%p, val 0x%x\n", __func__, pcc_clk_slot, reg, val);
+
+	return 0;
+}
+
+u32 pcc_clock_get_rate(int pcc_controller, int pcc_clk_slot)
+{
+	u32 val, rate, frac, div;
+	void __iomem *reg;
+	enum cgc1_clk parent;
+	int ret;
+	int clk;
+	struct pcc_entry *pcc_array;
+
+	clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array);
+	if (clk < 0)
+		return -EINVAL;
+
+	ret = pcc_clock_get_clksrc(pcc_controller, pcc_clk_slot, &parent);
+	if (ret)
+		return 0;
+
+	rate = cgc1_clk_get_rate(parent);
+
+	debug("%s: parent rate %u\n", __func__, rate);
+
+	if (pcc_array[clk].div == PCC_HAS_DIV) {
+		reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base +
+						  pcc_array[clk].pcc_slot * 4);
+		val = readl(reg);
+
+		frac = (val & PCC_FRAC_MASK) >> PCC_FRAC_OFFSET;
+		div = (val & PCC_PCD_MASK) >> PCC_PCD_OFFSET;
+
+		/*
+		 * Theoretically don't have overflow in the calc,
+		 * the rate won't exceed 2G
+		 */
+		rate = rate * (frac + 1) / (div + 1);
+	}
+
+	debug("%s: rate %u\n", __func__, rate);
+	return rate;
+}
diff --git a/arch/arm/mach-imx/imx8ulp/soc.c b/arch/arm/mach-imx/imx8ulp/soc.c
index 3029589d5f..6f4b506386 100644
--- a/arch/arm/mach-imx/imx8ulp/soc.c
+++ b/arch/arm/mach-imx/imx8ulp/soc.c
@@ -287,5 +287,8 @@ void get_board_serial(struct tag_serialnr *serialnr)
 
 int arch_cpu_init(void)
 {
+	if (IS_ENABLED(CONFIG_SPL_BUILD))
+		clock_init();
+
 	return 0;
 }
-- 
2.30.0


  parent reply	other threads:[~2021-08-07  7:30 UTC|newest]

Thread overview: 46+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-08-07  8:00 [PATCH V5 00/44] imx: add i.MX8ULP support Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 01/44] arm: imx: add i.MX8ULP basic Kconfig option Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 02/44] arm: imx: add i.MX8ULP cpu type and helper Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 03/44] arm: imx: sys_proto: move boot mode define to common header Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 04/44] arm: imx8ulp: support print cpu info Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 05/44] imx: imx8ulp: add get reset cause Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 06/44] arm: imx: basic i.MX8ULP support Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 07/44] arm: imx8: Move container parser and image to mach-imx common folder Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 08/44] arm: imx8: Move container image header file to mach-imx Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 09/44] arm: imx: parse-container: guard included header files Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 10/44] arm: imx8ulp: add container support Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 11/44] arm: imx: move container Kconfig under mach-imx Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 12/44] driver: misc: Add MU and S400 API to communicate with Sentinel Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 13/44] net: fec_mxc: support i.MX8ULP Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 14/44] pinctrl: Add pinctrl driver for imx8ulp Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 15/44] driver: serial: fsl_lpuart: support i.MX8ULP Peng Fan (OSS)
2021-08-07  8:00 ` Peng Fan (OSS) [this message]
2021-08-07  8:00 ` [PATCH V5 17/44] drivers: mmc: fsl_esdhc_imx: " Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 18/44] arm: imx8ulp: soc: Change to use CMC1 to get bootcfg Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 19/44] arm: imx8ulp: Enable full L2 cache in SPL Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 20/44] arm: imx8ulp: disable wdog3 Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 21/44] arm: imx8ulp: Update the reset vector in u-boot Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 22/44] drivers: misc: s400_api: Update S400_SUCCESS_IND to 0xd6 Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 23/44] drivers: misc: imx8ulp: Add S400 API for image authentication Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 24/44] drivers: misc: imx8ulp: Update S400 API for release RDC Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 25/44] drivers: misc: s400_api: Update API for fuse read and write Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 26/44] arm: imx8ulp: release and configure XRDC at early phase Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 27/44] arm: imx8ulp: add rdc support Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 28/44] arm: imx8ulp: add trdc release request Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 29/44] arm: imx8ulp: release trdc and assign lpav from RTD to APD Peng Fan (OSS)
2021-08-07  8:00 ` [PATCH V5 30/44] imx8ulp: unify rdc functions Peng Fan (OSS)
2021-08-07  8:01 ` [PATCH V5 31/44] arm: imx8ulp: Probe the S400 MU device in arch init Peng Fan (OSS)
2021-08-07  8:01 ` [PATCH V5 32/44] arm: iMX8ULP: Add boot device relevant functions Peng Fan (OSS)
2021-08-07  8:01 ` [PATCH V5 33/44] arm: imx8ulp: Allocate DCNANO and MIPI_DSI to AD domain Peng Fan (OSS)
2021-08-07  8:01 ` [PATCH V5 34/44] arm: imx8ulp: add dummy imx_get_mac_from_fuse Peng Fan (OSS)
2021-08-07  8:01 ` [PATCH V5 35/44] arm: imx8ulp: add iomuxc support Peng Fan (OSS)
2021-08-07  8:01 ` [PATCH V5 36/44] driver: misc: imx8ulp: Add fuse driver for imx8ulp Peng Fan (OSS)
2021-08-07  8:01 ` [PATCH V5 37/44] imx8ulp: soc: correct reset cause Peng Fan (OSS)
2021-08-07  8:01 ` [PATCH V5 38/44] imx8ulp: Use DGO_GP5 to get boot config Peng Fan (OSS)
2021-08-07  8:01 ` [PATCH V5 39/44] imx8ulp: Add workaround for eMMC boot Peng Fan (OSS)
2021-08-07  8:01 ` [PATCH V5 40/44] imx8ulp: move struct mu_type to common header Peng Fan (OSS)
2021-08-07  8:01 ` [PATCH V5 41/44] imx8ulp: add upower api support Peng Fan (OSS)
2021-08-07  8:01 ` [PATCH V5 42/44] ddr: Add DDR driver for iMX8ULP Peng Fan (OSS)
2021-08-07  8:01 ` [PATCH V5 43/44] arm: dts: add i.MX8ULP dtsi Peng Fan (OSS)
2021-08-07  8:01 ` [PATCH V5 44/44] arm: imx: add i.MX8ULP EVK support Peng Fan (OSS)
2021-08-09 10:08 ` [PATCH V5 00/44] imx: add i.MX8ULP support Stefano Babic

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210807080113.16020-17-peng.fan@oss.nxp.com \
    --to=peng.fan@oss.nxp.com \
    --cc=festevam@gmail.com \
    --cc=peng.fan@nxp.com \
    --cc=sbabic@denx.de \
    --cc=u-boot@lists.denx.de \
    --cc=uboot-imx@nxp.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.