u-boot/arch/arm/mach-imx/imx9/clock.c
Peng Fan 4a5c5d56f5 imx9: clock: config arm alt root to 500mhz
Config the A55 alt root clock to 500MHz(LD mode frequency)
by default. Normally, this clock root is only used as an
intermediate clock soure for A55 core/dsu when change the
ARM PLL frequency.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
2023-05-21 16:54:41 +02:00

909 lines
21 KiB
C

// 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(&reg->ctrl.reg);
pll_status = readl(&reg->pll_status);
div = readl(&reg->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(&reg->num.reg);
mfn >>= 2;
mfd = (int)(readl(&reg->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(&reg->ctrl.reg);
u32 div;
if (ctrl & PLL_CTRL_CLKMUX_BYPASS)
return 24000;
if (!(ctrl & PLL_CTRL_CLKMUX_EN))
return 0;
div = readl(&reg->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(&reg->ctrl.reg_clr, PLL_CTRL_HW_CTRL_SEL);
/* Bypass the PLL to ref */
writel(PLL_CTRL_CLKMUX_BYPASS, &reg->ctrl.reg_set);
/* disable pll and output */
writel(PLL_CTRL_CLKMUX_EN | PLL_CTRL_POWERUP, &reg->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)), &reg->div.reg);
/* wait 5us */
udelay(5);
/* power up the PLL and wait lock (max wait time 100 us) */
writel(PLL_CTRL_POWERUP, &reg->ctrl.reg_set);
udelay(100);
pll_status = readl(&reg->pll_status);
if (pll_status & PLL_STATUS_PLL_LOCK) {
writel(PLL_CTRL_CLKMUX_EN, &reg->ctrl.reg_set);
/* clear bypass */
writel(PLL_CTRL_CLKMUX_BYPASS, &reg->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, &reg->ctrl.reg_set);
/* disable pll and output */
writel(PLL_CTRL_CLKMUX_EN | PLL_CTRL_POWERUP, &reg->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)), &reg->div.reg);
/* Set SPREAD_SPECRUM enable to 0 */
writel(PLL_SS_EN, &reg->ss.reg_clr);
/* Program NUMERATOR and DENOMINATOR */
writel((rate->mfn << 2), &reg->num.reg);
writel((rate->mfd & GENMASK(29, 0)), &reg->denom.reg);
/* wait 5us */
udelay(5);
/* power up the PLL and wait lock (max wait time 100 us) */
writel(PLL_CTRL_POWERUP, &reg->ctrl.reg_set);
udelay(100);
pll_status = readl(&reg->pll_status);
if (pll_status & PLL_STATUS_PLL_LOCK) {
writel(PLL_CTRL_CLKMUX_EN, &reg->ctrl.reg_set);
/* check the MFN is updated */
pll_status = readl(&reg->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, &reg->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 = &reg->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(&reg->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(&reg->pll_status) & PLL_STATUS_PLL_LOCK)
repoll = true;
mfn <<= 2;
writel(mfn, &reg->num);
if (repoll) {
do {
pll_status = readl(&reg->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_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);
}
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
#if IS_ENABLED(CONFIG_IMX9_LOW_DRIVE_MODE)
struct imx_clk_setting imx_clk_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},
/* Sentinel to 133M */
{SENTINEL_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}
};
#else
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},
/* Sentinel to 200M */
{SENTINEL_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}
};
#endif
int 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);
}
if (IS_ENABLED(CONFIG_IMX9_LOW_DRIVE_MODE))
set_arm_clk(MHZ(900));
/* 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;
}
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;
}
/*
* 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