| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright 2022 NXP |
| * |
| * Peng Fan <peng.fan@nxp.com> |
| */ |
| |
| #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> |
| #include <phy.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(800000000U, 1, 200, 6, 0, 1), /* 800Mhz */ |
| 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), |
| FRAC_PLL_RATE(233000000U, 1, 174, 18, 3, 4), /* 233Mhz */ |
| }; |
| |
| /* 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; |
| } |
| |
| /* Clear PLL HW CTRL SEL */ |
| setbits_le32(®->ctrl.reg_clr, PLL_CTRL_HW_CTRL_SEL); |
| |
| /* 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); |
| |
| /* wait 5us */ |
| udelay(5); |
| |
| /* power up the PLL and wait lock (max wait time 100 us) */ |
| writel(PLL_CTRL_POWERUP, ®->ctrl.reg_set); |
| |
| udelay(100); |
| |
| 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) |
| { |
| struct imx_fracpll_rate_table *rate; |
| struct ana_pll_reg *reg; |
| u32 pll_status; |
| int i; |
| |
| 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); |
| |
| /* wait 5us */ |
| udelay(5); |
| |
| /* power up the PLL and wait lock (max wait time 100 us) */ |
| writel(PLL_CTRL_POWERUP, ®->ctrl.reg_set); |
| |
| udelay(100); |
| |
| pll_status = readl(®->pll_status); |
| if (pll_status & PLL_STATUS_PLL_LOCK) { |
| writel(PLL_CTRL_CLKMUX_EN, ®->ctrl.reg_set); |
| |
| /* 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; |
| } |
| |
| /* 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); |
| |
| /* |
| * 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); |
| |
| 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); |
| } |
| |
| 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_voltage_mode(VOLT_LOW_DRIVE)) |
| 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_XPL_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(625): |
| ccm_clk_root_cfg(DRAM_ALT_CLK_ROOT, SYS_PLL_PFD2, 1); |
| break; |
| 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); |
| } |
| |
| void set_arm_clk(ulong freq) |
| { |
| /* Increase ARM clock to 1.7Ghz */ |
| ccm_shared_gpr_set(SHARED_GPR_A55_CLK, SHARED_GPR_A55_CLK_SEL_CCM); |
| configure_intpll(ARM_PLL_CLK, freq); |
| ccm_shared_gpr_set(SHARED_GPR_A55_CLK, SHARED_GPR_A55_CLK_SEL_PLL); |
| } |
| |
| void set_arm_core_max_clk(void) |
| { |
| /* Increase ARM clock to max rate according to speed grade */ |
| u32 speed = get_cpu_speed_grade_hz(); |
| |
| set_arm_clk(speed); |
| } |
| |
| #endif |
| |
| struct imx_clk_setting imx_clk_ld_settings[] = { |
| /* Set A55 clk to 500M */ |
| {ARM_A55_CLK_ROOT, SYS_PLL_PFD0, 2}, |
| /* Set A55 periphal to 200M */ |
| {ARM_A55_PERIPH_CLK_ROOT, SYS_PLL_PFD1, 4}, |
| /* Set A55 mtr bus to 133M */ |
| {ARM_A55_MTR_BUS_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3}, |
| |
| /* ELE to 133M */ |
| {ELE_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3}, |
| /* Bus_wakeup to 133M */ |
| {BUS_WAKEUP_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3}, |
| /* Bus_AON to 133M */ |
| {BUS_AON_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3}, |
| /* M33 to 133M */ |
| {M33_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3}, |
| /* WAKEUP_AXI to 200M */ |
| {WAKEUP_AXI_CLK_ROOT, SYS_PLL_PFD1, 4}, |
| /* SWO TRACE to 133M */ |
| {SWO_TRACE_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3}, |
| /* M33 systetick to 24M */ |
| {M33_SYSTICK_CLK_ROOT, OSC_24M_CLK, 1}, |
| /* NIC to 250M */ |
| {NIC_CLK_ROOT, SYS_PLL_PFD0, 4}, |
| /* NIC_APB to 133M */ |
| {NIC_APB_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3} |
| }; |
| |
| struct imx_clk_setting imx_clk_settings[] = { |
| /* |
| * Set A55 clk to 500M. This clock root is normally used as intermediate |
| * clock source for A55 core/DSU when doing ARM PLL reconfig. set it to |
| * 500MHz(LD mode frequency) should be ok. |
| */ |
| {ARM_A55_CLK_ROOT, SYS_PLL_PFD0, 2}, |
| /* Set A55 periphal to 333M */ |
| {ARM_A55_PERIPH_CLK_ROOT, SYS_PLL_PFD0, 3}, |
| /* Set A55 mtr bus to 133M */ |
| {ARM_A55_MTR_BUS_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3}, |
| /* ELE to 200M */ |
| {ELE_CLK_ROOT, SYS_PLL_PFD1_DIV2, 2}, |
| /* Bus_wakeup to 133M */ |
| {BUS_WAKEUP_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3}, |
| /* Bus_AON to 133M */ |
| {BUS_AON_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3}, |
| /* M33 to 200M */ |
| {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 |
| */ |
| {WAKEUP_AXI_CLK_ROOT, SYS_PLL_PFD2, 2}, |
| /* SWO TRACE to 133M */ |
| {SWO_TRACE_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3}, |
| /* M33 systetick to 24M */ |
| {M33_SYSTICK_CLK_ROOT, OSC_24M_CLK, 1}, |
| /* NIC to 400M */ |
| {NIC_CLK_ROOT, SYS_PLL_PFD1, 2}, |
| /* NIC_APB to 133M */ |
| {NIC_APB_CLK_ROOT, SYS_PLL_PFD1_DIV2, 3} |
| }; |
| |
| void bus_clock_init_low_drive(void) |
| { |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(imx_clk_ld_settings); i++) { |
| ccm_clk_root_cfg(imx_clk_ld_settings[i].clk_root, |
| imx_clk_ld_settings[i].src, imx_clk_ld_settings[i].div); |
| } |
| } |
| |
| void bus_clock_init(void) |
| { |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(imx_clk_settings); i++) { |
| ccm_clk_root_cfg(imx_clk_settings[i].clk_root, |
| imx_clk_settings[i].src, imx_clk_settings[i].div); |
| } |
| } |
| |
| int clock_init_early(void) |
| { |
| int i; |
| |
| /* allow for non-secure access */ |
| 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; |
| } |
| |
| /* Set bus and A55 core clock per voltage mode */ |
| int clock_init_late(void) |
| { |
| if (is_voltage_mode(VOLT_LOW_DRIVE)) { |
| bus_clock_init_low_drive(); |
| set_arm_core_max_clk(); |
| } else { |
| bus_clock_init(); |
| } |
| |
| 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); |
| } |
| |
| #if defined(CONFIG_IMX93) && defined(CONFIG_DWC_ETH_QOS) |
| static int imx93_eqos_interface_init(struct udevice *dev, phy_interface_t interface_type) |
| { |
| struct blk_ctrl_wakeupmix_regs *bctrl = |
| (struct blk_ctrl_wakeupmix_regs *)BLK_CTRL_WAKEUPMIX_BASE_ADDR; |
| |
| clrbits_le32(&bctrl->eqos_gpr, |
| BCTRL_GPR_ENET_QOS_INTF_MODE_MASK | |
| BCTRL_GPR_ENET_QOS_CLK_GEN_EN); |
| |
| switch (interface_type) { |
| case PHY_INTERFACE_MODE_MII: |
| setbits_le32(&bctrl->eqos_gpr, |
| BCTRL_GPR_ENET_QOS_INTF_SEL_MII | |
| BCTRL_GPR_ENET_QOS_CLK_GEN_EN); |
| break; |
| case PHY_INTERFACE_MODE_RMII: |
| setbits_le32(&bctrl->eqos_gpr, |
| BCTRL_GPR_ENET_QOS_INTF_SEL_RMII | |
| BCTRL_GPR_ENET_QOS_CLK_GEN_EN); |
| break; |
| case PHY_INTERFACE_MODE_RGMII: |
| case PHY_INTERFACE_MODE_RGMII_ID: |
| case PHY_INTERFACE_MODE_RGMII_RXID: |
| case PHY_INTERFACE_MODE_RGMII_TXID: |
| setbits_le32(&bctrl->eqos_gpr, |
| BCTRL_GPR_ENET_QOS_INTF_SEL_RGMII | |
| BCTRL_GPR_ENET_QOS_CLK_GEN_EN); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| #else |
| static int imx93_eqos_interface_init(struct udevice *dev, phy_interface_t interface_type) |
| { |
| return 0; |
| } |
| #endif |
| |
| int board_interface_eth_init(struct udevice *dev, phy_interface_t interface_type) |
| { |
| if (IS_ENABLED(CONFIG_IMX93) && |
| IS_ENABLED(CONFIG_DWC_ETH_QOS) && |
| device_is_compatible(dev, "nxp,imx93-dwmac-eqos")) |
| return imx93_eqos_interface_init(dev, interface_type); |
| |
| if (IS_ENABLED(CONFIG_IMX93) && |
| IS_ENABLED(CONFIG_FEC_MXC) && |
| device_is_compatible(dev, "fsl,imx93-fec")) |
| return 0; |
| |
| return -EINVAL; |
| } |
| |
| 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; |
| } |
| |
| /* |
| * Dump some clockes. |
| */ |
| #ifndef CONFIG_XPL_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 |