456 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			456 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			C
		
	
	
	
// 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;
 | 
						|
	}
 | 
						|
}
 |