diff options
Diffstat (limited to 'arch/arm/mach-imx/imx8ulp/cgc.c')
-rw-r--r-- | arch/arm/mach-imx/imx8ulp/cgc.c | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/imx8ulp/cgc.c b/arch/arm/mach-imx/imx8ulp/cgc.c new file mode 100644 index 0000000000..7bfc3862cd --- /dev/null +++ b/arch/arm/mach-imx/imx8ulp/cgc.c @@ -0,0 +1,455 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 NXP + */ + +#include <common.h> +#include <div64.h> +#include <asm/io.h> +#include <errno.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/cgc.h> +#include <asm/arch/sys_proto.h> +#include <asm/global_data.h> +#include <linux/delay.h> + +DECLARE_GLOBAL_DATA_PTR; + +static struct cgc1_regs *cgc1_regs = (struct cgc1_regs *)0x292C0000UL; +static struct cgc2_regs *cgc2_regs = (struct cgc2_regs *)0x2da60000UL; + +void cgc1_soscdiv_init(void) +{ + /* Configure SOSC/FRO DIV1 ~ DIV3 */ + clrbits_le32(&cgc1_regs->soscdiv, BIT(7)); + clrbits_le32(&cgc1_regs->soscdiv, BIT(15)); + clrbits_le32(&cgc1_regs->soscdiv, BIT(23)); + clrbits_le32(&cgc1_regs->soscdiv, BIT(31)); + + clrbits_le32(&cgc1_regs->frodiv, BIT(7)); +} + +void cgc1_pll2_init(void) +{ + u32 reg; + + if (readl(&cgc1_regs->pll2csr) & BIT(23)) + clrbits_le32(&cgc1_regs->pll2csr, BIT(23)); + + /* Disable PLL2 */ + clrbits_le32(&cgc1_regs->pll2csr, BIT(0)); + mdelay(1); + + /* wait valid bit false */ + while ((readl(&cgc1_regs->pll2csr) & BIT(24))) + ; + + /* Select SOSC as source, freq = 31 * 24 =744mhz */ + reg = 31 << 16; + writel(reg, &cgc1_regs->pll2cfg); + + /* Enable PLL2 */ + setbits_le32(&cgc1_regs->pll2csr, BIT(0)); + + /* Wait for PLL2 clock ready */ + while (!(readl(&cgc1_regs->pll2csr) & BIT(24))) + ; +} + +static void cgc1_set_a35_clk(u32 clk_src, u32 div_core) +{ + u32 reg; + + /* ulock */ + if (readl(&cgc1_regs->ca35clk) & BIT(31)) + clrbits_le32(&cgc1_regs->ca35clk, BIT(31)); + + reg = readl(&cgc1_regs->ca35clk); + reg &= ~GENMASK(29, 21); + reg |= ((clk_src & 0x3) << 28); + reg |= (((div_core - 1) & 0x3f) << 21); + writel(reg, &cgc1_regs->ca35clk); + + while (!(readl(&cgc1_regs->ca35clk) & BIT(27))) + ; +} + +void cgc1_init_core_clk(void) +{ + u32 reg = readl(&cgc1_regs->ca35clk); + + /* if already selected to PLL2, switch to FRO firstly */ + if (((reg >> 28) & 0x3) == 0x1) + cgc1_set_a35_clk(0, 1); + + /* Set pll2 to 750Mhz for 1V */ + cgc1_pll2_init(); + + /* Set A35 clock to pll2 */ + cgc1_set_a35_clk(1, 1); +} + +void cgc1_enet_stamp_sel(u32 clk_src) +{ + writel((clk_src & 0x7) << 24, &cgc1_regs->enetstamp); +} + +void cgc1_pll3_init(void) +{ + /* Gate off VCO */ + setbits_le32(&cgc1_regs->pll3div_vco, BIT(7)); + + /* Disable PLL3 */ + clrbits_le32(&cgc1_regs->pll3csr, BIT(0)); + + /* Gate off PFDxDIV */ + setbits_le32(&cgc1_regs->pll3div_pfd0, BIT(7) | BIT(15) | BIT(23) | BIT(31)); + setbits_le32(&cgc1_regs->pll3div_pfd1, BIT(7) | BIT(15) | BIT(23) | BIT(31)); + + /* Gate off PFDx */ + setbits_le32(&cgc1_regs->pll3pfdcfg, BIT(7)); + setbits_le32(&cgc1_regs->pll3pfdcfg, BIT(15)); + setbits_le32(&cgc1_regs->pll3pfdcfg, BIT(23)); + setbits_le32(&cgc1_regs->pll3pfdcfg, BIT(31)); + + /* Select SOSC as source */ + clrbits_le32(&cgc1_regs->pll3cfg, BIT(0)); + + //setbits_le32(&cgc1_regs->pll3cfg, 22 << 16); + writel(22 << 16, &cgc1_regs->pll3cfg); + + writel(578, &cgc1_regs->pll3num); + writel(1000, &cgc1_regs->pll3denom); + + /* Enable PLL3 */ + setbits_le32(&cgc1_regs->pll3csr, BIT(0)); + + /* Wait for PLL3 clock ready */ + while (!(readl(&cgc1_regs->pll3csr) & BIT(24))) + ; + /* Gate on VCO */ + clrbits_le32(&cgc1_regs->pll3div_vco, BIT(7)); + + /* + * PFD0: 380MHz/396/396/328 + */ + clrbits_le32(&cgc1_regs->pll3pfdcfg, 0x3F); + setbits_le32(&cgc1_regs->pll3pfdcfg, 25 << 0); + clrbits_le32(&cgc1_regs->pll3pfdcfg, BIT(7)); + while (!(readl(&cgc1_regs->pll3pfdcfg) & BIT(6))) + ; + + clrbits_le32(&cgc1_regs->pll3pfdcfg, 0x3F << 8); + setbits_le32(&cgc1_regs->pll3pfdcfg, 24 << 8); + clrbits_le32(&cgc1_regs->pll3pfdcfg, BIT(15)); + while (!(readl(&cgc1_regs->pll3pfdcfg) & BIT(14))) + ; + + clrbits_le32(&cgc1_regs->pll3pfdcfg, 0x3F << 16); + setbits_le32(&cgc1_regs->pll3pfdcfg, 24 << 16); + clrbits_le32(&cgc1_regs->pll3pfdcfg, BIT(23)); + while (!(readl(&cgc1_regs->pll3pfdcfg) & BIT(22))) + ; + + clrbits_le32(&cgc1_regs->pll3pfdcfg, 0x3F << 24); + setbits_le32(&cgc1_regs->pll3pfdcfg, 29 << 24); + clrbits_le32(&cgc1_regs->pll3pfdcfg, BIT(31)); + while (!(readl(&cgc1_regs->pll3pfdcfg) & BIT(30))) + ; + + clrbits_le32(&cgc1_regs->pll3div_pfd0, BIT(7)); + clrbits_le32(&cgc1_regs->pll3div_pfd0, BIT(15)); + clrbits_le32(&cgc1_regs->pll3div_pfd0, BIT(23)); + clrbits_le32(&cgc1_regs->pll3div_pfd0, BIT(31)); + + clrbits_le32(&cgc1_regs->pll3div_pfd1, BIT(7)); + clrbits_le32(&cgc1_regs->pll3div_pfd1, BIT(15)); + clrbits_le32(&cgc1_regs->pll3div_pfd1, BIT(23)); + clrbits_le32(&cgc1_regs->pll3div_pfd1, BIT(31)); +} + +void cgc2_pll4_init(void) +{ + /* Disable PFD DIV and clear DIV */ + writel(0x80808080, &cgc2_regs->pll4div_pfd0); + writel(0x80808080, &cgc2_regs->pll4div_pfd1); + + /* Gate off and clear PFD */ + writel(0x80808080, &cgc2_regs->pll4pfdcfg); + + /* Disable PLL4 */ + writel(0x0, &cgc2_regs->pll4csr); + + /* Configure PLL4 to 528Mhz and clock source from SOSC */ + writel(22 << 16, &cgc2_regs->pll4cfg); + writel(0x1, &cgc2_regs->pll4csr); + + /* wait for PLL4 output valid */ + while (!(readl(&cgc2_regs->pll4csr) & BIT(24))) + ; + + /* Enable all 4 PFDs */ + setbits_le32(&cgc2_regs->pll4pfdcfg, 30 << 0); /* 316.8Mhz for NIC_LPAV */ + setbits_le32(&cgc2_regs->pll4pfdcfg, 18 << 8); + setbits_le32(&cgc2_regs->pll4pfdcfg, 12 << 16); + setbits_le32(&cgc2_regs->pll4pfdcfg, 24 << 24); + + clrbits_le32(&cgc2_regs->pll4pfdcfg, BIT(7) | BIT(15) | BIT(23) | BIT(31)); + + while ((readl(&cgc2_regs->pll4pfdcfg) & (BIT(30) | BIT(22) | BIT(14) | BIT(6))) + != (BIT(30) | BIT(22) | BIT(14) | BIT(6))) + ; + + /* Enable PFD DIV */ + clrbits_le32(&cgc2_regs->pll4div_pfd0, BIT(7) | BIT(15) | BIT(23) | BIT(31)); + clrbits_le32(&cgc2_regs->pll4div_pfd1, BIT(7) | BIT(15) | BIT(23) | BIT(31)); +} + +void cgc2_ddrclk_config(u32 src, u32 div) +{ + writel((src << 28) | (div << 21), &cgc2_regs->ddrclk); + /* wait for DDRCLK switching done */ + while (!(readl(&cgc2_regs->ddrclk) & BIT(27))) + ; +} + +u32 decode_pll(enum cgc1_clk pll) +{ + u32 reg, infreq, mult; + u32 num, denom; + + infreq = 24000000U; + /* + * Alought there are four choices for the bypass src, + * we choose SOSC 24M which is the default set in ROM. + * TODO: check more the comments + */ + switch (pll) { + case PLL2: + reg = readl(&cgc1_regs->pll2csr); + if (!(reg & BIT(24))) + return 0; + + reg = readl(&cgc1_regs->pll2cfg); + mult = (reg >> 16) & 0x7F; + denom = readl(&cgc1_regs->pll2denom) & 0x3FFFFFFF; + num = readl(&cgc1_regs->pll2num) & 0x3FFFFFFF; + + return (u64)infreq * mult + (u64)infreq * num / denom; + case PLL3: + reg = readl(&cgc1_regs->pll3csr); + if (!(reg & BIT(24))) + return 0; + + reg = readl(&cgc1_regs->pll3cfg); + mult = (reg >> 16) & 0x7F; + denom = readl(&cgc1_regs->pll3denom) & 0x3FFFFFFF; + num = readl(&cgc1_regs->pll3num) & 0x3FFFFFFF; + + return (u64)infreq * mult + (u64)infreq * num / denom; + default: + printf("Unsupported pll clocks %d\n", pll); + break; + } + + return 0; +} + +u32 cgc1_pll3_vcodiv_rate(void) +{ + u32 reg, gate, div; + + reg = readl(&cgc1_regs->pll3div_vco); + gate = BIT(7) & reg; + div = reg & 0x3F; + + return gate ? 0 : decode_pll(PLL3) / (div + 1); +} + +u32 cgc1_pll3_pfd_rate(enum cgc1_clk clk) +{ + u32 index, gate, vld, reg; + + switch (clk) { + case PLL3_PFD0: + index = 0; + break; + case PLL3_PFD1: + index = 1; + break; + case PLL3_PFD2: + index = 2; + break; + case PLL3_PFD3: + index = 3; + break; + default: + return 0; + } + + reg = readl(&cgc1_regs->pll3pfdcfg); + gate = reg & (BIT(7) << (index * 8)); + vld = reg & (BIT(6) << (index * 8)); + + if (gate || !vld) + return 0; + + return (u64)decode_pll(PLL3) * 18 / ((reg >> (index * 8)) & 0x3F); +} + +u32 cgc1_pll3_pfd_div(enum cgc1_clk clk) +{ + void __iomem *base; + u32 pfd, index, gate, reg; + + switch (clk) { + case PLL3_PFD0_DIV1: + base = &cgc1_regs->pll3div_pfd0; + pfd = PLL3_PFD0; + index = 0; + break; + case PLL3_PFD0_DIV2: + base = &cgc1_regs->pll3div_pfd0; + pfd = PLL3_PFD0; + index = 1; + break; + case PLL3_PFD1_DIV1: + base = &cgc1_regs->pll3div_pfd0; + pfd = PLL3_PFD1; + index = 2; + break; + case PLL3_PFD1_DIV2: + base = &cgc1_regs->pll3div_pfd0; + pfd = PLL3_PFD1; + index = 3; + break; + case PLL3_PFD2_DIV1: + base = &cgc1_regs->pll3div_pfd1; + pfd = PLL3_PFD2; + index = 0; + break; + case PLL3_PFD2_DIV2: + base = &cgc1_regs->pll3div_pfd1; + pfd = PLL3_PFD2; + index = 1; + break; + case PLL3_PFD3_DIV1: + base = &cgc1_regs->pll3div_pfd1; + pfd = PLL3_PFD3; + index = 2; + break; + case PLL3_PFD3_DIV2: + base = &cgc1_regs->pll3div_pfd1; + pfd = PLL3_PFD3; + index = 3; + break; + default: + return 0; + } + + reg = readl(base); + gate = reg & (BIT(7) << (index * 8)); + + if (gate) + return 0; + + return cgc1_pll3_pfd_rate(pfd) / (((reg >> (index * 8)) & 0x3F) + 1); +} + +u32 cgc1_sosc_div(enum cgc1_clk clk) +{ + u32 reg, gate, index; + + switch (clk) { + case SOSC: + return 24000000; + case SOSC_DIV1: + index = 0; + break; + case SOSC_DIV2: + index = 1; + break; + case SOSC_DIV3: + index = 2; + break; + default: + return 0; + } + + reg = readl(&cgc1_regs->soscdiv); + gate = reg & (BIT(7) << (index * 8)); + + if (gate) + return 0; + + return 24000000 / (((reg >> (index * 8)) & 0x3F) + 1); +} + +u32 cgc1_fro_div(enum cgc1_clk clk) +{ + u32 reg, gate, vld, index; + + switch (clk) { + case FRO: + return 192000000; + case FRO_DIV1: + index = 0; + break; + case FRO_DIV2: + index = 1; + break; + case FRO_DIV3: + index = 2; + break; + default: + return 0; + } + + reg = readl(&cgc1_regs->frodiv); + gate = reg & (BIT(7) << (index * 8)); + vld = reg & (BIT(6) << (index * 8)); + + if (gate || !vld) + return 0; + + return 24000000 / (((reg >> (index * 8)) & 0x3F) + 1); +} + +u32 cgc1_clk_get_rate(enum cgc1_clk clk) +{ + switch (clk) { + case SOSC: + case SOSC_DIV1: + case SOSC_DIV2: + case SOSC_DIV3: + return cgc1_sosc_div(clk); + case FRO: + case FRO_DIV1: + case FRO_DIV2: + case FRO_DIV3: + return cgc1_fro_div(clk); + case PLL2: + return decode_pll(PLL2); + case PLL3: + return decode_pll(PLL3); + case PLL3_VCODIV: + return cgc1_pll3_vcodiv_rate(); + case PLL3_PFD0: + case PLL3_PFD1: + case PLL3_PFD2: + case PLL3_PFD3: + return cgc1_pll3_pfd_rate(clk); + case PLL3_PFD0_DIV1: + case PLL3_PFD0_DIV2: + case PLL3_PFD1_DIV1: + case PLL3_PFD1_DIV2: + case PLL3_PFD2_DIV1: + case PLL3_PFD2_DIV2: + case PLL3_PFD3_DIV1: + case PLL3_PFD3_DIV2: + return cgc1_pll3_pfd_div(clk); + default: + printf("Unsupported cgc1 clock: %d\n", clk); + return 0; + } +} |