diff options
author | Peng Fan | 2021-08-07 16:00:45 +0800 |
---|---|---|
committer | Stefano Babic | 2021-08-09 14:46:51 +0200 |
commit | a84dab4f70a47b17f92b36f8859d12b3083a0126 (patch) | |
tree | af927b44e4591f840a6e2e1a2177bd91b3765e4a /arch | |
parent | 166bc7fba05db2435ebc7b5b5954f0ed35f82b14 (diff) |
arm: imx8ulp: add clock support
Add i.MX8ULP clock support
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/include/asm/arch-imx8ulp/cgc.h | 130 | ||||
-rw-r--r-- | arch/arm/include/asm/arch-imx8ulp/clock.h | 9 | ||||
-rw-r--r-- | arch/arm/include/asm/arch-imx8ulp/imx-regs.h | 1 | ||||
-rw-r--r-- | arch/arm/include/asm/arch-imx8ulp/pcc.h | 139 | ||||
-rw-r--r-- | arch/arm/mach-imx/imx8ulp/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-imx/imx8ulp/cgc.c | 455 | ||||
-rw-r--r-- | arch/arm/mach-imx/imx8ulp/clock.c | 374 | ||||
-rw-r--r-- | arch/arm/mach-imx/imx8ulp/pcc.c | 449 | ||||
-rw-r--r-- | arch/arm/mach-imx/imx8ulp/soc.c | 3 |
9 files changed, 1558 insertions, 4 deletions
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 00000000000..34a15fb59c5 --- /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 e145c33f012..58e3356e32f 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 982f2a9b28f..52311550895 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 00000000000..091d0175ddd --- /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 e8970dc04f1..78c81d78bb6 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 00000000000..7bfc3862cd3 --- /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 f866809fc2b..ebbaad4106c 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 00000000000..711b685cd7b --- /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 3029589d5f1..6f4b5063869 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; } |