diff options
Diffstat (limited to 'arch/arm/mach-imx/imx9/clock.c')
-rw-r--r-- | arch/arm/mach-imx/imx9/clock.c | 947 |
1 files changed, 947 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/imx9/clock.c b/arch/arm/mach-imx/imx9/clock.c new file mode 100644 index 0000000000..909a770e1c --- /dev/null +++ b/arch/arm/mach-imx/imx9/clock.c @@ -0,0 +1,947 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022 NXP + * + * Peng Fan <peng.fan@nxp.com> + */ + +#include <common.h> +#include <command.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/ccm_regs.h> +#include <asm/arch/sys_proto.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <div64.h> +#include <errno.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <log.h> + +DECLARE_GLOBAL_DATA_PTR; + +static struct anatop_reg *ana_regs = (struct anatop_reg *)ANATOP_BASE_ADDR; + +static struct imx_intpll_rate_table imx9_intpll_tbl[] = { + INT_PLL_RATE(1800000000U, 1, 150, 2), /* 1.8Ghz */ + INT_PLL_RATE(1700000000U, 1, 141, 2), /* 1.7Ghz */ + INT_PLL_RATE(1500000000U, 1, 125, 2), /* 1.5Ghz */ + INT_PLL_RATE(1400000000U, 1, 175, 3), /* 1.4Ghz */ + INT_PLL_RATE(1000000000U, 1, 166, 4), /* 1000Mhz */ + INT_PLL_RATE(900000000U, 1, 150, 4), /* 900Mhz */ +}; + +static struct imx_fracpll_rate_table imx9_fracpll_tbl[] = { + FRAC_PLL_RATE(1000000000U, 1, 166, 4, 2, 3), /* 1000Mhz */ + FRAC_PLL_RATE(933000000U, 1, 155, 4, 1, 2), /* 933Mhz */ + FRAC_PLL_RATE(700000000U, 1, 145, 5, 5, 6), /* 700Mhz */ + FRAC_PLL_RATE(484000000U, 1, 121, 6, 0, 1), + FRAC_PLL_RATE(445333333U, 1, 167, 9, 0, 1), + FRAC_PLL_RATE(466000000U, 1, 155, 8, 1, 3), /* 466Mhz */ + FRAC_PLL_RATE(400000000U, 1, 200, 12, 0, 1), /* 400Mhz */ + FRAC_PLL_RATE(300000000U, 1, 150, 12, 0, 1), +}; + +/* return in khz */ +static u32 decode_pll_vco(struct ana_pll_reg *reg, bool fracpll) +{ + u32 ctrl; + u32 pll_status; + u32 div; + int rdiv, mfi, mfn, mfd; + int clk = 24000; + + ctrl = readl(®->ctrl.reg); + pll_status = readl(®->pll_status); + div = readl(®->div.reg); + + if (!(ctrl & PLL_CTRL_POWERUP)) + return 0; + + if (!(pll_status & PLL_STATUS_PLL_LOCK)) + return 0; + + mfi = (div & GENMASK(24, 16)) >> 16; + rdiv = (div & GENMASK(15, 13)) >> 13; + + if (rdiv == 0) + rdiv = 1; + + if (fracpll) { + mfn = (int)readl(®->num.reg); + mfn >>= 2; + mfd = (int)(readl(®->denom.reg) & GENMASK(29, 0)); + + clk = clk * (mfi * mfd + mfn) / mfd / rdiv; + } else { + clk = clk * mfi / rdiv; + } + + return (u32)clk; +} + +/* return in khz */ +static u32 decode_pll_out(struct ana_pll_reg *reg, bool fracpll) +{ + u32 ctrl = readl(®->ctrl.reg); + u32 div; + + if (ctrl & PLL_CTRL_CLKMUX_BYPASS) + return 24000; + + if (!(ctrl & PLL_CTRL_CLKMUX_EN)) + return 0; + + div = readl(®->div.reg); + div &= 0xff; /* odiv */ + + if (div == 0) + div = 2; + else if (div == 1) + div = 3; + + return decode_pll_vco(reg, fracpll) / div; +} + +/* return in khz */ +static u32 decode_pll_pfd(struct ana_pll_reg *reg, + struct ana_pll_dfs *dfs_reg, bool div2, bool fracpll) +{ + u32 pllvco = decode_pll_vco(reg, fracpll); + u32 dfs_ctrl = readl(&dfs_reg->dfs_ctrl.reg); + u32 dfs_div = readl(&dfs_reg->dfs_div.reg); + u32 mfn, mfi; + u32 output; + + if (dfs_ctrl & PLL_DFS_CTRL_BYPASS) + return pllvco; + + if (!(dfs_ctrl & PLL_DFS_CTRL_ENABLE) || + (div2 && !(dfs_ctrl & PLL_DFS_CTRL_CLKOUT_DIV2)) || + (!div2 && !(dfs_ctrl & PLL_DFS_CTRL_CLKOUT))) + return 0; + + mfn = dfs_div & GENMASK(2, 0); + mfi = (dfs_div & GENMASK(15, 8)) >> 8; + + if (mfn > 3) + return 0; /* valid mfn 0-3 */ + + if (mfi == 0 || mfi == 1) + return 0; /* valid mfi 2-255 */ + + output = (pllvco * 5) / (mfi * 5 + mfn); + + if (div2) + return output >> 1; + + return output; +} + +static u32 decode_pll(enum ccm_clk_src pll) +{ + switch (pll) { + case ARM_PLL_CLK: + return decode_pll_out(&ana_regs->arm_pll, false); + case SYS_PLL_PG: + return decode_pll_out(&ana_regs->sys_pll, false); + case SYS_PLL_PFD0: + return decode_pll_pfd(&ana_regs->sys_pll, + &ana_regs->sys_pll.dfs[0], false, true); + case SYS_PLL_PFD0_DIV2: + return decode_pll_pfd(&ana_regs->sys_pll, + &ana_regs->sys_pll.dfs[0], true, true); + case SYS_PLL_PFD1: + return decode_pll_pfd(&ana_regs->sys_pll, + &ana_regs->sys_pll.dfs[1], false, true); + case SYS_PLL_PFD1_DIV2: + return decode_pll_pfd(&ana_regs->sys_pll, + &ana_regs->sys_pll.dfs[1], true, true); + case SYS_PLL_PFD2: + return decode_pll_pfd(&ana_regs->sys_pll, + &ana_regs->sys_pll.dfs[2], false, true); + case SYS_PLL_PFD2_DIV2: + return decode_pll_pfd(&ana_regs->sys_pll, + &ana_regs->sys_pll.dfs[2], true, true); + case AUDIO_PLL_CLK: + return decode_pll_out(&ana_regs->audio_pll, true); + case DRAM_PLL_CLK: + return decode_pll_out(&ana_regs->dram_pll, true); + case VIDEO_PLL_CLK: + return decode_pll_out(&ana_regs->video_pll, true); + default: + printf("Invalid clock source to decode\n"); + break; + } + + return 0; +} + +int configure_intpll(enum ccm_clk_src pll, u32 freq) +{ + int i; + struct imx_intpll_rate_table *rate; + struct ana_pll_reg *reg; + u32 pll_status; + + for (i = 0; i < ARRAY_SIZE(imx9_intpll_tbl); i++) { + if (freq == imx9_intpll_tbl[i].rate) + break; + } + + if (i == ARRAY_SIZE(imx9_intpll_tbl)) { + debug("No matched freq table %u\n", freq); + return -EINVAL; + } + + rate = &imx9_intpll_tbl[i]; + + /* ROM has configured SYS PLL and PFD, no need for it */ + switch (pll) { + case ARM_PLL_CLK: + reg = &ana_regs->arm_pll; + break; + default: + return -EPERM; + } + + /* Bypass the PLL to ref */ + writel(PLL_CTRL_CLKMUX_BYPASS, ®->ctrl.reg_set); + + /* disable pll and output */ + writel(PLL_CTRL_CLKMUX_EN | PLL_CTRL_POWERUP, ®->ctrl.reg_clr); + + /* Program the ODIV, RDIV, MFI */ + writel((rate->odiv & GENMASK(7, 0)) | + ((rate->rdiv << 13 ) & GENMASK(15, 13)) | + ((rate->mfi << 16) & GENMASK(24, 16)), ®->div.reg); + +#ifndef CONFIG_TARGET_IMX93_EMU + /* wait 5us */ + udelay(5); +#endif + + /* power up the PLL and wait lock (max wait time 100 us) */ + writel(PLL_CTRL_POWERUP, ®->ctrl.reg_set); + +#ifndef CONFIG_TARGET_IMX93_EMU + udelay(100); +#endif + + pll_status = readl(®->pll_status); + if (pll_status & PLL_STATUS_PLL_LOCK) { + writel(PLL_CTRL_CLKMUX_EN, ®->ctrl.reg_set); + + /* clear bypass */ + writel(PLL_CTRL_CLKMUX_BYPASS, ®->ctrl.reg_clr); + + } else { + debug("Fail to lock PLL %u\n", pll); + return -EIO; + } + + return 0; +} + + +int configure_fracpll(enum ccm_clk_src pll, u32 freq) +{ + int i; + struct imx_fracpll_rate_table *rate; + struct ana_pll_reg *reg; + u32 pll_status; + + for (i = 0; i < ARRAY_SIZE(imx9_fracpll_tbl); i++) { + if (freq == imx9_fracpll_tbl[i].rate) + break; + } + + if (i == ARRAY_SIZE(imx9_fracpll_tbl)) { + debug("No matched freq table %u\n", freq); + return -EINVAL; + } + + rate = &imx9_fracpll_tbl[i]; + + switch (pll) { + case SYS_PLL_PG: + reg = &ana_regs->sys_pll; + break; + case DRAM_PLL_CLK: + reg = &ana_regs->dram_pll; + break; + case VIDEO_PLL_CLK: + reg = &ana_regs->video_pll; + break; + default: + return -EPERM; + } + + /* Bypass the PLL to ref */ + writel(PLL_CTRL_CLKMUX_BYPASS, ®->ctrl.reg_set); + + /* disable pll and output */ + writel(PLL_CTRL_CLKMUX_EN | PLL_CTRL_POWERUP, ®->ctrl.reg_clr); + + /* Program the ODIV, RDIV, MFI */ + writel((rate->odiv & GENMASK(7, 0)) | + ((rate->rdiv << 13 ) & GENMASK(15, 13)) | + ((rate->mfi << 16) & GENMASK(24, 16)), ®->div.reg); + + /* Set SPREAD_SPECRUM enable to 0 */ + writel(PLL_SS_EN, ®->ss.reg_clr); + + /* Program NUMERATOR and DENOMINATOR */ + writel((rate->mfn << 2), ®->num.reg); + writel((rate->mfd & GENMASK(29, 0)), ®->denom.reg); + +#ifndef CONFIG_TARGET_IMX93_EMU + /* wait 5us */ + udelay(5); +#endif + + /* power up the PLL and wait lock (max wait time 100 us) */ + writel(PLL_CTRL_POWERUP, ®->ctrl.reg_set); + +#ifndef CONFIG_TARGET_IMX93_EMU + udelay(100); +#endif + + pll_status = readl(®->pll_status); + if (pll_status & PLL_STATUS_PLL_LOCK) { + writel(PLL_CTRL_CLKMUX_EN, ®->ctrl.reg_set); + +#ifndef CONFIG_TARGET_IMX93_EMU + /* check the MFN is updated */ + pll_status = readl(®->pll_status); + if ((pll_status & ~0x3) != (rate->mfn << 2)) { + debug("MFN update not matched, pll_status 0x%x, mfn 0x%x\n", + pll_status, rate->mfn); + return -EIO; + } +#endif + /* clear bypass */ + writel(PLL_CTRL_CLKMUX_BYPASS, ®->ctrl.reg_clr); + + } else { + debug("Fail to lock PLL %u\n", pll); + return -EIO; + } + + return 0; +} + +int configure_pll_pfd(enum ccm_clk_src pll_pfg, u32 mfi, u32 mfn, bool div2_en) +{ + struct ana_pll_dfs *dfs; + struct ana_pll_reg *reg; + u32 dfs_status; + u32 index; + + if (mfn > 3) + return -EINVAL; /* valid mfn 0-3 */ + + if (mfi < 2 || mfi > 255) + return -EINVAL; /* valid mfi 2-255 */ + + switch (pll_pfg) { + case SYS_PLL_PFD0: + reg = &ana_regs->sys_pll; + index = 0; + break; + case SYS_PLL_PFD1: + reg = &ana_regs->sys_pll; + index = 1; + break; + case SYS_PLL_PFD2: + reg = &ana_regs->sys_pll; + index = 2; + break; + default: + return -EPERM; + } + + dfs = ®->dfs[index]; + + /* Bypass the DFS to PLL VCO */ + writel(PLL_DFS_CTRL_BYPASS, &dfs->dfs_ctrl.reg_set); + + /* disable DFS and output */ + writel(PLL_DFS_CTRL_ENABLE | PLL_DFS_CTRL_CLKOUT | + PLL_DFS_CTRL_CLKOUT_DIV2, &dfs->dfs_ctrl.reg_clr); + + writel(((mfi << 8) & GENMASK(15, 8)) | (mfn & GENMASK(2, 0)), + &dfs->dfs_div.reg); + + writel(PLL_DFS_CTRL_CLKOUT, &dfs->dfs_ctrl.reg_set); + if (div2_en) + writel(PLL_DFS_CTRL_CLKOUT_DIV2, &dfs->dfs_ctrl.reg_set); + writel(PLL_DFS_CTRL_ENABLE, &dfs->dfs_ctrl.reg_set); + +#ifndef CONFIG_TARGET_IMX93_EMU + /* + * As HW expert said: after enabling the DFS, clock will start + * coming after 6 cycles output clock period. + * 5us is much bigger than expected, so it will be safe + */ + udelay(5); +#endif + + dfs_status = readl(®->dfs_status); + + if (!(dfs_status & (1 << index))) { + debug("DFS lock failed\n"); + return -EIO; + } + + /* Bypass the DFS to PLL VCO */ + writel(PLL_DFS_CTRL_BYPASS, &dfs->dfs_ctrl.reg_clr); + + return 0; +} + +int update_fracpll_mfn(enum ccm_clk_src pll, int mfn) +{ + struct ana_pll_reg *reg; + bool repoll = false; + u32 pll_status; + int count = 20; + + switch (pll) { + case AUDIO_PLL_CLK: + reg = &ana_regs->audio_pll; + break; + case DRAM_PLL_CLK: + reg = &ana_regs->dram_pll; + break; + case VIDEO_PLL_CLK: + reg = &ana_regs->video_pll; + break; + default: + printf("Invalid pll %u for update FRAC PLL MFN\n", pll); + return -EINVAL; + } + + if (readl(®->pll_status) & PLL_STATUS_PLL_LOCK) + repoll = true; + + mfn <<= 2; + writel(mfn, ®->num); + + if (repoll) { + do { + pll_status = readl(®->pll_status); + udelay(5); + count--; + } while (((pll_status & ~0x3) != (u32)mfn) && count > 0); + + if (count <= 0) { + printf("update MFN timeout, pll_status 0x%x, mfn 0x%x\n", + pll_status, mfn); + return -EIO; + } + } + + return 0; +} + +int update_pll_pfd_mfn(enum ccm_clk_src pll_pfd, u32 mfn) +{ + struct ana_pll_dfs *dfs; + u32 val; + u32 index; + + switch (pll_pfd) { + case SYS_PLL_PFD0: + case SYS_PLL_PFD0_DIV2: + index = 0; + break; + case SYS_PLL_PFD1: + case SYS_PLL_PFD1_DIV2: + index = 1; + break; + case SYS_PLL_PFD2: + case SYS_PLL_PFD2_DIV2: + index = 2; + break; + default: + printf("Invalid pfd %u for update PLL PFD MFN\n", pll_pfd); + return -EINVAL; + } + + dfs = &ana_regs->sys_pll.dfs[index]; + + val = readl(&dfs->dfs_div.reg); + val &= ~0x3; + val |= mfn & 0x3; + writel(val, &dfs->dfs_div.reg); + + return 0; +} + +/* return in khz */ +u32 get_clk_src_rate(enum ccm_clk_src source) +{ + u32 ctrl; + bool clk_on; + + switch (source) { + case ARM_PLL_CLK: + ctrl = readl(&ana_regs->arm_pll.ctrl.reg); + case AUDIO_PLL_CLK: + ctrl = readl(&ana_regs->audio_pll.ctrl.reg); + break; + case DRAM_PLL_CLK: + ctrl = readl(&ana_regs->dram_pll.ctrl.reg); + break; + case VIDEO_PLL_CLK: + ctrl = readl(&ana_regs->video_pll.ctrl.reg); + break; + case SYS_PLL_PFD0: + case SYS_PLL_PFD0_DIV2: + ctrl = readl(&ana_regs->sys_pll.dfs[0].dfs_ctrl.reg); + break; + case SYS_PLL_PFD1: + case SYS_PLL_PFD1_DIV2: + ctrl = readl(&ana_regs->sys_pll.dfs[1].dfs_ctrl.reg); + break; + case SYS_PLL_PFD2: + case SYS_PLL_PFD2_DIV2: + ctrl = readl(&ana_regs->sys_pll.dfs[2].dfs_ctrl.reg); + break; + case OSC_24M_CLK: + return 24000; + default: + printf("Invalid clock source to get rate\n"); + return 0; + } + + if (ctrl & PLL_CTRL_HW_CTRL_SEL) { + /* When using HW ctrl, check OSCPLL */ + clk_on = ccm_clk_src_is_clk_on(source); + if (clk_on) + return decode_pll(source); + else + return 0; + } else { + /* controlled by pll registers */ + return decode_pll(source); + } +} + +u32 get_arm_core_clk(void) +{ + u32 val; + ccm_shared_gpr_get(SHARED_GPR_A55_CLK, &val); + + if (val & SHARED_GPR_A55_CLK_SEL_PLL) + return decode_pll(ARM_PLL_CLK) * 1000; + + return ccm_clk_root_get_rate(ARM_A55_CLK_ROOT); +} + +void set_arm_core_max_clk(void) +{ + u32 speed; + + /* Increase ARM clock to max rate according to speed grade */ + speed = get_cpu_speed_grade_hz(); + + ccm_shared_gpr_set(SHARED_GPR_A55_CLK, SHARED_GPR_A55_CLK_SEL_CCM); + configure_intpll(ARM_PLL_CLK, speed); + ccm_shared_gpr_set(SHARED_GPR_A55_CLK, SHARED_GPR_A55_CLK_SEL_PLL); +} + +void set_arm_core_low_drive_clk(void) +{ + ccm_shared_gpr_set(SHARED_GPR_A55_CLK, SHARED_GPR_A55_CLK_SEL_CCM); + configure_intpll(ARM_PLL_CLK, 900000000); + ccm_shared_gpr_set(SHARED_GPR_A55_CLK, SHARED_GPR_A55_CLK_SEL_PLL); +} + +unsigned int mxc_get_clock(enum mxc_clock clk) +{ + switch (clk) { + case MXC_ARM_CLK: + return get_arm_core_clk(); + case MXC_IPG_CLK: + return ccm_clk_root_get_rate(BUS_WAKEUP_CLK_ROOT); + case MXC_CSPI_CLK: + return ccm_clk_root_get_rate(LPSPI1_CLK_ROOT); + case MXC_ESDHC_CLK: + return ccm_clk_root_get_rate(USDHC1_CLK_ROOT); + case MXC_ESDHC2_CLK: + return ccm_clk_root_get_rate(USDHC2_CLK_ROOT); + case MXC_ESDHC3_CLK: + return ccm_clk_root_get_rate(USDHC3_CLK_ROOT); + case MXC_UART_CLK: + return ccm_clk_root_get_rate(LPUART1_CLK_ROOT); + case MXC_FLEXSPI_CLK: + return ccm_clk_root_get_rate(FLEXSPI1_CLK_ROOT); + default: + return -1; + }; + + return -1; +}; + +int enable_i2c_clk(unsigned char enable, u32 i2c_num) +{ + if (i2c_num > 7) + return -EINVAL; + + if (enable) { + /* 24M */ + ccm_lpcg_on(CCGR_I2C1 + i2c_num, false); + ccm_clk_root_cfg(LPI2C1_CLK_ROOT + i2c_num, OSC_24M_CLK, 1); + ccm_lpcg_on(CCGR_I2C1 + i2c_num, true); + } else { + ccm_lpcg_on(CCGR_I2C1 + i2c_num, false); + } + + return 0; +} + +u32 imx_get_i2cclk(u32 i2c_num) +{ + if (i2c_num > 7) + return -EINVAL; + + return ccm_clk_root_get_rate(LPI2C1_CLK_ROOT + i2c_num); +} + +u32 get_lpuart_clk(void) +{ + return mxc_get_clock(MXC_UART_CLK); +} + +void init_uart_clk(u32 index) +{ + switch(index) { + case LPUART1_CLK_ROOT: + /* 24M */ + ccm_lpcg_on(CCGR_URT1, false); + ccm_clk_root_cfg(LPUART1_CLK_ROOT, OSC_24M_CLK, 1); + ccm_lpcg_on(CCGR_URT1, true); + break; + default: + break; + } +} + +void init_clk_usdhc(u32 index) +{ + u32 div; + if (IS_ENABLED(CONFIG_IMX9_LOW_DRIVE_MODE)) + div = 3; /* 266.67 Mhz */ + else + div = 2; /* 400 Mhz */ + + switch (index) { + case 0: + ccm_lpcg_on(CCGR_USDHC1, 0); + ccm_clk_root_cfg(USDHC1_CLK_ROOT, SYS_PLL_PFD1, div); + ccm_lpcg_on(CCGR_USDHC1, 1); + break; + case 1: + ccm_lpcg_on(CCGR_USDHC2, 0); + ccm_clk_root_cfg(USDHC2_CLK_ROOT, SYS_PLL_PFD1, div); + ccm_lpcg_on(CCGR_USDHC2, 1); + break; + case 2: + ccm_lpcg_on(CCGR_USDHC3, 0); + ccm_clk_root_cfg(USDHC3_CLK_ROOT, SYS_PLL_PFD1, div); + ccm_lpcg_on(CCGR_USDHC3, 1); + break; + default: + return; + }; +} + +void enable_usboh3_clk(unsigned char enable) +{ + if (enable) { + ccm_clk_root_cfg(HSIO_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3); + ccm_lpcg_on(CCGR_USBC, 1); + } else { + ccm_lpcg_on(CCGR_USBC, 0); + } +} + +#ifdef CONFIG_SPL_BUILD +void dram_pll_init(ulong pll_val) +{ + configure_fracpll(DRAM_PLL_CLK, pll_val); +} + +void dram_enable_bypass(ulong clk_val) +{ + switch (clk_val) { + case MHZ(400): + ccm_clk_root_cfg(DRAM_ALT_CLK_ROOT, SYS_PLL_PFD1, 2); + break; + case MHZ(333): + ccm_clk_root_cfg(DRAM_ALT_CLK_ROOT, SYS_PLL_PFD0, 3); + break; + case MHZ(200): + ccm_clk_root_cfg(DRAM_ALT_CLK_ROOT, SYS_PLL_PFD1, 4); + break; + case MHZ(100): + ccm_clk_root_cfg(DRAM_ALT_CLK_ROOT, SYS_PLL_PFD1, 8); + break; + default: + printf("No matched freq table %lu\n", clk_val); + return; + } + + /* Set DRAM APB to 133Mhz */ + ccm_clk_root_cfg(DRAM_APB_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3); + /* Switch from DRAM clock root from PLL to CCM */ + ccm_shared_gpr_set(SHARED_GPR_DRAM_CLK, SHARED_GPR_DRAM_CLK_SEL_CCM); +} + +void dram_disable_bypass(void) +{ + /* Set DRAM APB to 133Mhz */ + ccm_clk_root_cfg(DRAM_APB_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3); + /* Switch from DRAM clock root from CCM to PLL */ + ccm_shared_gpr_set(SHARED_GPR_DRAM_CLK, SHARED_GPR_DRAM_CLK_SEL_PLL); +} +#endif + +void bus_clock_init_low_drive(void) +{ + /* Set A55 clk to 500M */ + ccm_clk_root_cfg(ARM_A55_CLK_ROOT, SYS_PLL_PFD0, 2); + /* Set A55 periphal to 200M */ + ccm_clk_root_cfg(ARM_A55_PERIPH_CLK_ROOT, SYS_PLL_PFD1, 4); + /* Set A55 mtr bus to 133M */ + ccm_clk_root_cfg(ARM_A55_MTR_BUS_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3); + + /* Sentinel to 133M */ + ccm_clk_root_cfg(SENTINEL_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3); + /* Bus_wakeup to 133M */ + ccm_clk_root_cfg(BUS_WAKEUP_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3); + /* Bus_AON to 133M */ + ccm_clk_root_cfg(BUS_AON_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3); + /* M33 to 133M */ + ccm_clk_root_cfg(M33_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3); + /* WAKEUP_AXI to 200M */ + ccm_clk_root_cfg(WAKEUP_AXI_CLK_ROOT, SYS_PLL_PFD1, 4); + /* SWO TRACE to 133M */ + ccm_clk_root_cfg(SWO_TRACE_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3); + /* M33 systetick to 24M */ + ccm_clk_root_cfg(M33_SYSTICK_CLK_ROOT, OSC_24M_CLK, 1); + /* NIC to 250M */ + ccm_clk_root_cfg(NIC_CLK_ROOT, SYS_PLL_PFD0, 4); + /* NIC_APB to 133M */ + ccm_clk_root_cfg(NIC_APB_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3); +} + +void bus_clock_init(void) +{ + /* Set A55 periphal to 333M */ + ccm_clk_root_cfg(ARM_A55_PERIPH_CLK_ROOT, SYS_PLL_PFD0, 3); + /* Set A55 mtr bus to 133M */ + ccm_clk_root_cfg(ARM_A55_MTR_BUS_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3); + + /* Sentinel to 200M */ + ccm_clk_root_cfg(SENTINEL_CLK_ROOT, SYS_PLL_PFD1_DIV2, 2); + /* Bus_wakeup to 133M */ + ccm_clk_root_cfg(BUS_WAKEUP_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3); + /* Bus_AON to 133M */ + ccm_clk_root_cfg(BUS_AON_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3); + /* M33 to 200M */ + ccm_clk_root_cfg(M33_CLK_ROOT, SYS_PLL_PFD1_DIV2, 2); + /* WAKEUP_AXI to 312.5M, because of FEC only can support to 320M for generating MII clock at 2.5M */ + ccm_clk_root_cfg(WAKEUP_AXI_CLK_ROOT, SYS_PLL_PFD2, 2); + /* SWO TRACE to 133M */ + ccm_clk_root_cfg(SWO_TRACE_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3); + /* M33 systetick to 24M */ + ccm_clk_root_cfg(M33_SYSTICK_CLK_ROOT, OSC_24M_CLK, 1); + /* NIC to 400M */ + ccm_clk_root_cfg(NIC_CLK_ROOT, SYS_PLL_PFD1, 2); + /* NIC_APB to 133M */ + ccm_clk_root_cfg(NIC_APB_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3); +} + +int clock_init(void) +{ + if (IS_ENABLED(CONFIG_IMX9_LOW_DRIVE_MODE)){ + bus_clock_init_low_drive(); + set_arm_core_low_drive_clk(); + } else { + bus_clock_init(); + } + + /* allow for non-secure access */ + int i; + for (i = 0; i < OSCPLL_END; i++) + ccm_clk_src_tz_access(i, true, false, false); + + for (i = 0; i < CLK_ROOT_NUM; i++) + ccm_clk_root_tz_access(i, true, false, false); + + for (i = 0; i < CCGR_NUM; i++) + ccm_lpcg_tz_access(i, true, false, false); + + for (i = 0; i < SHARED_GPR_NUM; i++) + ccm_shared_gpr_tz_access(i, true, false, false); + + return 0; +} + +int set_clk_eqos(enum enet_freq type) +{ + u32 eqos_post_div; + + switch (type) { + case ENET_125MHZ: + eqos_post_div = 2; /* 250M clock */ + break; + case ENET_50MHZ: + eqos_post_div = 5; /* 100M clock */ + break; + case ENET_25MHZ: + eqos_post_div = 10; /* 50M clock*/ + break; + default: + return -EINVAL; + } + + /* disable the clock first */ + ccm_lpcg_on(CCGR_ENETQOS, false); + + ccm_clk_root_cfg(ENET_CLK_ROOT, SYS_PLL_PFD0_DIV2, eqos_post_div); + ccm_clk_root_cfg(ENET_TIMER2_CLK_ROOT, SYS_PLL_PFD0_DIV2, 5); + + /* enable clock */ + ccm_lpcg_on(CCGR_ENETQOS, true); + + return 0; +} + +u32 imx_get_eqos_csr_clk(void) +{ + return ccm_clk_root_get_rate(WAKEUP_AXI_CLK_ROOT); +} + +u32 imx_get_fecclk(void) +{ + return ccm_clk_root_get_rate(WAKEUP_AXI_CLK_ROOT); +} + +int set_clk_enet(enum enet_freq type) +{ + u32 div; + + /* disable the clock first */ + ccm_lpcg_on(CCGR_ENET1, false); + + switch (type) { + case ENET_125MHZ: + div = 2; /* 250Mhz */ + break; + case ENET_50MHZ: + div = 5; /* 100Mhz */ + break; + case ENET_25MHZ: + div = 10; /* 50Mhz */ + break; + default: + return -EINVAL; + } + + ccm_clk_root_cfg(ENET_REF_CLK_ROOT, SYS_PLL_PFD0_DIV2, div); + ccm_clk_root_cfg(ENET_TIMER1_CLK_ROOT, SYS_PLL_PFD0_DIV2, 5); + +#ifdef CONFIG_FEC_MXC_25M_REF_CLK + ccm_clk_root_cfg(ENET_REF_PHY_CLK_ROOT, SYS_PLL_PFD0_DIV2, 20); +#endif + + /* enable clock */ + ccm_lpcg_on(CCGR_ENET1, true); + + return 0; +} + +void mxs_set_lcdclk(u32 base_addr, u32 freq) +{ + u32 div, i, krate, temp; + u32 best = 0, best_div = 0, best_pll = 0; + + debug("%s to set rate to %dkhz\n", __func__, freq); + + for (i = 0; i < ARRAY_SIZE(imx9_fracpll_tbl); i++) { + krate = imx9_fracpll_tbl[i].rate / 1000; + div = (krate + freq - 1) / freq; + + if (div > 256) + continue; + + temp = krate / div; + if (best == 0 || temp > best) { + best = temp; + best_div = div; + best_pll = imx9_fracpll_tbl[i].rate; + } + } + + if (best == 0) { + printf("Can't find parent clock for LCDIF, target freq: %u\n", freq); + return; + } + + /* Select to video PLL */ + debug("%s, best_pll = %u, div = %u\n", __func__, best_pll, best_div); + + configure_fracpll(VIDEO_PLL_CLK, best_pll); + ccm_clk_root_cfg(MEDIA_DISP_PIX_CLK_ROOT, VIDEO_PLL_CLK, best_div); +} + +/* + * Dump some clockes. + */ +#ifndef CONFIG_SPL_BUILD +int do_showclocks(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) +{ + u32 freq; + + freq = decode_pll(ARM_PLL_CLK); + printf("ARM_PLL %8d MHz\n", freq / 1000); + freq = decode_pll(DRAM_PLL_CLK); + printf("DRAM_PLL %8d MHz\n", freq / 1000); + freq = decode_pll(SYS_PLL_PFD0); + printf("SYS_PLL_PFD0 %8d MHz\n", freq / 1000); + freq = decode_pll(SYS_PLL_PFD0_DIV2); + printf("SYS_PLL_PFD0_DIV2 %8d MHz\n", freq / 1000); + freq = decode_pll(SYS_PLL_PFD1); + printf("SYS_PLL_PFD1 %8d MHz\n", freq / 1000); + freq = decode_pll(SYS_PLL_PFD1_DIV2); + printf("SYS_PLL_PFD1_DIV2 %8d MHz\n", freq / 1000); + freq = decode_pll(SYS_PLL_PFD2); + printf("SYS_PLL_PFD2 %8d MHz\n", freq / 1000); + freq = decode_pll(SYS_PLL_PFD2_DIV2); + printf("SYS_PLL_PFD2_DIV2 %8d MHz\n", freq / 1000); + freq = mxc_get_clock(MXC_ARM_CLK); + printf("ARM CORE %8d MHz\n", freq / 1000000); + freq = mxc_get_clock(MXC_IPG_CLK); + printf("IPG %8d MHz\n", freq / 1000000); + freq = mxc_get_clock(MXC_UART_CLK); + printf("UART3 %8d MHz\n", freq / 1000000); + freq = mxc_get_clock(MXC_ESDHC_CLK); + printf("USDHC1 %8d MHz\n", freq / 1000000); + freq = mxc_get_clock(MXC_FLEXSPI_CLK); + printf("FLEXSPI %8d MHz\n", freq / 1000000); + + return 0; +} + +U_BOOT_CMD( + clocks, CONFIG_SYS_MAXARGS, 1, do_showclocks, + "display clocks", + "" +); +#endif + |