mirror of
				https://source.denx.de/u-boot/u-boot.git
				synced 2025-10-31 08:21:36 +01:00 
			
		
		
		
	Update PLL3/PLL4 PFD and USDHC clocks to meet maximum frequency restrictions. Detail clock rate changes in the patch: PLL3 PFD2: 389M -> 324M PLL3 PFD3: 336M -> 389M PLL3 PFD3: DIV1 336M -> 389M (OD), 194M (ND/LD) PLL3 PFD3: DIV2 336M -> 194M (OD), 97M (ND/LD) PLL4 PFD0: 792M -> 594M PLL4 PFD2: 792M -> 316.8M NIC_AP: 96M (ND) -> 192M, 48M (LD) -> 96M NIC_LPAV: 198 (ND) -> 192M, 99M (LD) -> 96M USDHC0: PLL3 PFD3 DIV1, 389M (OD), 194M (ND/LD) USDHC1: PLL3 PFD3 DIV2, 194M (OD), 97M (ND/LD) USDHC2: PLL3 PFD3 DIV2, 194M (OD), 97M (ND/LD) Signed-off-by: Ye Li <ye.li@nxp.com> Reviewed-by: Peng Fan <peng.fan@nxp.com>
		
			
				
	
	
		
			739 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			739 lines
		
	
	
		
			17 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/clock.h>
 | |
| #include <asm/arch/sys_proto.h>
 | |
| #include <asm/global_data.h>
 | |
| #include <linux/delay.h>
 | |
| #include <hang.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(ulong freq)
 | |
| {
 | |
| 	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 */
 | |
| 	reg = (freq / MHZ(24)) << 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(ulong freq)
 | |
| {
 | |
| 	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);
 | |
| 
 | |
| 	cgc1_pll2_init(freq);
 | |
| 
 | |
| 	/* 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(ulong freq)
 | |
| {
 | |
| 	/* 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));
 | |
| 
 | |
| 	switch (freq) {
 | |
| 	case 540672000:
 | |
| 		writel(0x16 << 16, &cgc1_regs->pll3cfg);
 | |
| 		writel(0x16e3600, &cgc1_regs->pll3denom);
 | |
| 		writel(0xc15c00, &cgc1_regs->pll3num);
 | |
| 		break;
 | |
| 	default:
 | |
| 		hang();
 | |
| 	}
 | |
| 
 | |
| 	/* 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));
 | |
| 
 | |
| 	clrbits_le32(&cgc1_regs->pll3pfdcfg, 0x3F);
 | |
| 	setbits_le32(&cgc1_regs->pll3pfdcfg, 30 << 0); /* PFD0 324M */
 | |
| 	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, 25 << 8);	/* PFD1 389M */
 | |
| 	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, 30 << 16);	/* PFD2 324M */
 | |
| 	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, 25 << 24);	/* PFD3 389M */
 | |
| 	clrbits_le32(&cgc1_regs->pll3pfdcfg, BIT(31));
 | |
| 	while (!(readl(&cgc1_regs->pll3pfdcfg) & BIT(30)))
 | |
| 		;
 | |
| 
 | |
| 	clrbits_le32(&cgc1_regs->pll3div_pfd0, 0x3f3f3f3f);
 | |
| 	if (IS_ENABLED(CONFIG_IMX8ULP_LD_MODE) || IS_ENABLED(CONFIG_IMX8ULP_ND_MODE))
 | |
| 		clrsetbits_le32(&cgc1_regs->pll3div_pfd1, 0x3f3f3f3f, 0x03010000); /* Set PFD3 DIV1 to 194M, PFD3 DIV2 to 97M */
 | |
| 	else
 | |
| 		clrsetbits_le32(&cgc1_regs->pll3div_pfd1, 0x3f3f3f3f, 0x01000000); /* Set PFD3 DIV1 to 389M, PFD3 DIV2 to 194M */
 | |
| 	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));
 | |
| 
 | |
| 	/* NIC_AP:
 | |
| 	 * OD source PLL3 PFD0, 324M
 | |
| 	 * ND source FRO192, 192M
 | |
| 	 * LD source FRO192, 96M
 | |
| 	*/
 | |
| 	if (IS_ENABLED(CONFIG_IMX8ULP_LD_MODE)) {
 | |
| 		clrsetbits_le32(&cgc1_regs->nicclk, GENMASK(26, 21), 1 << 21);
 | |
| 	} else {
 | |
| 		clrbits_le32(&cgc1_regs->nicclk, GENMASK(26, 21));
 | |
| 	}
 | |
| 
 | |
| 	if (!IS_ENABLED(CONFIG_IMX8ULP_LD_MODE) && !IS_ENABLED(CONFIG_IMX8ULP_ND_MODE)) {
 | |
| 		/* nicclk select pll3 pfd0 */
 | |
| 		clrsetbits_le32(&cgc1_regs->nicclk, GENMASK(29, 28), BIT(28));
 | |
| 		while (!(readl(&cgc1_regs->nicclk) & BIT(27)))
 | |
| 			;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void cgc2_pll4_init(bool pll4_reset)
 | |
| {
 | |
| 	/* Check the NICLPAV first to ensure not from PLL4 PFD1 clock */
 | |
| 	if ((readl(&cgc2_regs->niclpavclk) & GENMASK(29, 28)) == BIT(28)) {
 | |
| 		/* switch to FRO 192 first */
 | |
| 		clrbits_le32(&cgc2_regs->niclpavclk, GENMASK(29, 28));
 | |
| 		while (!(readl(&cgc2_regs->niclpavclk) & BIT(27)))
 | |
| 			;
 | |
| 	}
 | |
| 
 | |
| 	/* 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);
 | |
| 
 | |
| 	if (pll4_reset) {
 | |
| 		/* 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, 18 << 0); /* 528 */
 | |
| 	setbits_le32(&cgc2_regs->pll4pfdcfg, 30 << 8); /* 316.8Mhz for NIC_LPAV */
 | |
| 	setbits_le32(&cgc2_regs->pll4pfdcfg, 30 << 16); /* 316.8Mhz */
 | |
| 	setbits_le32(&cgc2_regs->pll4pfdcfg, 24 << 24); /* 396Mhz */
 | |
| 
 | |
| 	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));
 | |
| 
 | |
| 	/* NIC_LPAV:
 | |
| 	 * OD source PLL4 PFD1, 316.8M
 | |
| 	 * ND source FRO192, 192M
 | |
| 	 * LD source FRO192, 96M
 | |
| 	*/
 | |
| 	if (IS_ENABLED(CONFIG_IMX8ULP_LD_MODE)) {
 | |
| 		clrsetbits_le32(&cgc2_regs->niclpavclk, GENMASK(26, 21), 1 << 21);
 | |
| 	} else {
 | |
| 		clrbits_le32(&cgc2_regs->niclpavclk, GENMASK(26, 21));
 | |
| 	}
 | |
| 
 | |
| 	if (!IS_ENABLED(CONFIG_IMX8ULP_LD_MODE) && !IS_ENABLED(CONFIG_IMX8ULP_ND_MODE)) {
 | |
| 		clrsetbits_le32(&cgc2_regs->niclpavclk, GENMASK(29, 28), BIT(28));
 | |
| 		while (!(readl(&cgc2_regs->niclpavclk) & BIT(27)))
 | |
| 			;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void cgc2_pll4_pfd_config(enum cgc_clk pllpfd, u32 pfd)
 | |
| {
 | |
| 	void __iomem *reg = &cgc2_regs->pll4div_pfd0;
 | |
| 	u32 halt_mask = BIT(7) | BIT(15);
 | |
| 	u32 pfd_shift = (pllpfd - PLL4_PFD0) * 8;
 | |
| 	u32 val;
 | |
| 
 | |
| 	if (pllpfd < PLL4_PFD0 || pllpfd > PLL4_PFD3)
 | |
| 		return;
 | |
| 
 | |
| 	if ((pllpfd - PLL4_PFD0) >> 1)
 | |
| 		reg = &cgc2_regs->pll4div_pfd1;
 | |
| 
 | |
| 	halt_mask = halt_mask << (((pllpfd - PLL4_PFD0) & 0x1) * 16);
 | |
| 
 | |
| 	/* halt pfd div */
 | |
| 	setbits_le32(reg, halt_mask);
 | |
| 
 | |
| 	/* gate pfd */
 | |
| 	setbits_le32(&cgc2_regs->pll4pfdcfg, BIT(7) << pfd_shift);
 | |
| 
 | |
| 	val = readl(&cgc2_regs->pll4pfdcfg);
 | |
| 	val &= ~(0x3f << pfd_shift);
 | |
| 	val |= (pfd << pfd_shift);
 | |
| 	writel(val, &cgc2_regs->pll4pfdcfg);
 | |
| 
 | |
| 	/* ungate */
 | |
| 	clrbits_le32(&cgc2_regs->pll4pfdcfg, BIT(7) << pfd_shift);
 | |
| 
 | |
| 	/* Wait stable */
 | |
| 	while ((readl(&cgc2_regs->pll4pfdcfg) & (BIT(6) << pfd_shift))
 | |
| 		!= (BIT(6) << pfd_shift))
 | |
| 		;
 | |
| 
 | |
| 	/* enable pfd div */
 | |
| 	clrbits_le32(reg, halt_mask);
 | |
| }
 | |
| 
 | |
| void cgc2_pll4_pfddiv_config(enum cgc_clk pllpfddiv, u32 div)
 | |
| {
 | |
| 	void __iomem *reg = &cgc2_regs->pll4div_pfd0;
 | |
| 	u32 shift = ((pllpfddiv - PLL4_PFD0_DIV1) & 0x3) * 8;
 | |
| 
 | |
| 	if (pllpfddiv < PLL4_PFD0_DIV1 || pllpfddiv > PLL4_PFD3_DIV2)
 | |
| 		return;
 | |
| 
 | |
| 	if ((pllpfddiv - PLL4_PFD0_DIV1) >> 2)
 | |
| 		reg = &cgc2_regs->pll4div_pfd1;
 | |
| 
 | |
| 	/* Halt pfd div */
 | |
| 	setbits_le32(reg, BIT(7) << shift);
 | |
| 
 | |
| 	/* Clear div */
 | |
| 	clrbits_le32(reg, 0x3f << shift);
 | |
| 
 | |
| 	/* Set div*/
 | |
| 	setbits_le32(reg, div << shift);
 | |
| 
 | |
| 	/* Enable pfd div */
 | |
| 	clrbits_le32(reg, BIT(7) << shift);
 | |
| }
 | |
| 
 | |
| void cgc2_ddrclk_config(u32 src, u32 div)
 | |
| {
 | |
| 	/* If reg lock is set, wait until unlock by HW */
 | |
| 	/* This lock is triggered by div updating and ddrclk halt status change, */
 | |
| 	while ((readl(&cgc2_regs->ddrclk) & BIT(31)))
 | |
| 		;
 | |
| 
 | |
| 	writel((src << 28) | (div << 21), &cgc2_regs->ddrclk);
 | |
| 	/* wait for DDRCLK switching done */
 | |
| 	while (!(readl(&cgc2_regs->ddrclk) & BIT(27)))
 | |
| 		;
 | |
| }
 | |
| 
 | |
| void cgc2_ddrclk_wait_unlock(void)
 | |
| {
 | |
| 	while ((readl(&cgc2_regs->ddrclk) & BIT(31)))
 | |
| 		;
 | |
| }
 | |
| 
 | |
| void cgc2_lpav_init(enum cgc_clk clk)
 | |
| {
 | |
| 	u32 i, scs, reg;
 | |
| 	const enum cgc_clk src[] = {FRO, PLL4_PFD1, SOSC, LVDS};
 | |
| 
 | |
| 	reg = readl(&cgc2_regs->niclpavclk);
 | |
| 	scs = (reg >> 28) & 0x3;
 | |
| 
 | |
| 	for (i = 0; i < 4; i++) {
 | |
| 		if (clk == src[i]) {
 | |
| 			if (scs == i)
 | |
| 				return;
 | |
| 
 | |
| 			reg &= ~(0x3 << 28);
 | |
| 			reg |= (i << 28);
 | |
| 
 | |
| 			writel(reg, &cgc2_regs->niclpavclk);
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (i == 4)
 | |
| 		printf("Invalid clock source [%u] for LPAV\n", clk);
 | |
| }
 | |
| 
 | |
| u32 cgc2_nic_get_rate(enum cgc_clk clk)
 | |
| {
 | |
| 	u32 reg, rate;
 | |
| 	u32 scs, lpav_axi_clk, lpav_ahb_clk, lpav_bus_clk;
 | |
| 	const enum cgc_clk src[] = {FRO, PLL4_PFD1, SOSC, LVDS};
 | |
| 
 | |
| 	reg = readl(&cgc2_regs->niclpavclk);
 | |
| 	scs = (reg >> 28) & 0x3;
 | |
| 	lpav_axi_clk = ((reg >> 21) & 0x3f) + 1;
 | |
| 	lpav_ahb_clk = ((reg >> 14) & 0x3f) + 1;
 | |
| 	lpav_bus_clk = ((reg >> 7) & 0x3f) + 1;
 | |
| 
 | |
| 	rate = cgc_clk_get_rate(src[scs]);
 | |
| 
 | |
| 	switch (clk) {
 | |
| 	case LPAV_AXICLK:
 | |
| 		rate = rate / lpav_axi_clk;
 | |
| 		break;
 | |
| 	case LPAV_AHBCLK:
 | |
| 		rate = rate / (lpav_axi_clk * lpav_ahb_clk);
 | |
| 		break;
 | |
| 	case LPAV_BUSCLK:
 | |
| 		rate = rate / (lpav_axi_clk * lpav_bus_clk);
 | |
| 		break;
 | |
| 	default:
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	return rate;
 | |
| }
 | |
| 
 | |
| u32 decode_pll(enum cgc_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;
 | |
| 	case PLL4:
 | |
| 		reg = readl(&cgc2_regs->pll4csr);
 | |
| 		if (!(reg & BIT(24)))
 | |
| 			return 0;
 | |
| 
 | |
| 		reg = readl(&cgc2_regs->pll4cfg);
 | |
| 		mult = (reg >> 16) & 0x7F;
 | |
| 		denom = readl(&cgc2_regs->pll4denom) & 0x3FFFFFFF;
 | |
| 		num = readl(&cgc2_regs->pll4num) & 0x3FFFFFFF;
 | |
| 
 | |
| 		return (u64)infreq * mult + (u64)infreq * num / denom;
 | |
| 	default:
 | |
| 		printf("Unsupported pll clocks %d\n", pll);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| u32 cgc_pll_vcodiv_rate(enum cgc_clk clk)
 | |
| {
 | |
| 	u32 reg, gate, div;
 | |
| 	void __iomem *plldiv_vco;
 | |
| 	enum cgc_clk pll;
 | |
| 
 | |
| 	if (clk == PLL3_VCODIV) {
 | |
| 		plldiv_vco = &cgc1_regs->pll3div_vco;
 | |
| 		pll = PLL3;
 | |
| 	} else {
 | |
| 		plldiv_vco = &cgc2_regs->pll4div_vco;
 | |
| 		pll = PLL4;
 | |
| 	}
 | |
| 
 | |
| 	reg = readl(plldiv_vco);
 | |
| 	gate = BIT(7) & reg;
 | |
| 	div = reg & 0x3F;
 | |
| 
 | |
| 	return gate ? 0 : decode_pll(pll) / (div + 1);
 | |
| }
 | |
| 
 | |
| u32 cgc_pll_pfd_rate(enum cgc_clk clk)
 | |
| {
 | |
| 	u32 index, gate, vld, reg;
 | |
| 	void __iomem *pllpfdcfg;
 | |
| 	enum cgc_clk pll;
 | |
| 
 | |
| 	switch (clk) {
 | |
| 	case PLL3_PFD0:
 | |
| 	case PLL3_PFD1:
 | |
| 	case PLL3_PFD2:
 | |
| 	case PLL3_PFD3:
 | |
| 		index = clk - PLL3_PFD0;
 | |
| 		pllpfdcfg = &cgc1_regs->pll3pfdcfg;
 | |
| 		pll = PLL3;
 | |
| 		break;
 | |
| 	case PLL4_PFD0:
 | |
| 	case PLL4_PFD1:
 | |
| 	case PLL4_PFD2:
 | |
| 	case PLL4_PFD3:
 | |
| 		index = clk - PLL4_PFD0;
 | |
| 		pllpfdcfg = &cgc2_regs->pll4pfdcfg;
 | |
| 		pll = PLL4;
 | |
| 		break;
 | |
| 	default:
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	reg = readl(pllpfdcfg);
 | |
| 	gate = reg & (BIT(7) << (index * 8));
 | |
| 	vld = reg & (BIT(6) << (index * 8));
 | |
| 
 | |
| 	if (gate || !vld)
 | |
| 		return 0;
 | |
| 
 | |
| 	return (u64)decode_pll(pll) * 18 / ((reg >> (index * 8)) & 0x3F);
 | |
| }
 | |
| 
 | |
| u32 cgc_pll_pfd_div(enum cgc_clk clk)
 | |
| {
 | |
| 	void __iomem *base;
 | |
| 	u32 pfd, index, gate, reg;
 | |
| 
 | |
| 	switch (clk) {
 | |
| 	case PLL3_PFD0_DIV1:
 | |
| 	case PLL3_PFD0_DIV2:
 | |
| 		base = &cgc1_regs->pll3div_pfd0;
 | |
| 		pfd = PLL3_PFD0;
 | |
| 		index = clk - PLL3_PFD0_DIV1;
 | |
| 		break;
 | |
| 	case PLL3_PFD1_DIV1:
 | |
| 	case PLL3_PFD1_DIV2:
 | |
| 		base = &cgc1_regs->pll3div_pfd0;
 | |
| 		pfd = PLL3_PFD1;
 | |
| 		index = clk - PLL3_PFD0_DIV1;
 | |
| 		break;
 | |
| 	case PLL3_PFD2_DIV1:
 | |
| 	case PLL3_PFD2_DIV2:
 | |
| 		base = &cgc1_regs->pll3div_pfd1;
 | |
| 		pfd = PLL3_PFD2;
 | |
| 		index = clk - PLL3_PFD2_DIV1;
 | |
| 		break;
 | |
| 	case PLL3_PFD3_DIV1:
 | |
| 	case PLL3_PFD3_DIV2:
 | |
| 		base = &cgc1_regs->pll3div_pfd1;
 | |
| 		pfd = PLL3_PFD3;
 | |
| 		index = clk - PLL3_PFD2_DIV1;
 | |
| 		break;
 | |
| 	case PLL4_PFD0_DIV1:
 | |
| 	case PLL4_PFD0_DIV2:
 | |
| 		base = &cgc2_regs->pll4div_pfd0;
 | |
| 		pfd = PLL4_PFD0;
 | |
| 		index = clk - PLL4_PFD0_DIV1;
 | |
| 		break;
 | |
| 	case PLL4_PFD1_DIV1:
 | |
| 	case PLL4_PFD1_DIV2:
 | |
| 		base = &cgc2_regs->pll4div_pfd0;
 | |
| 		pfd = PLL4_PFD1;
 | |
| 		index = clk - PLL4_PFD0_DIV1;
 | |
| 		break;
 | |
| 	case PLL4_PFD2_DIV1:
 | |
| 	case PLL4_PFD2_DIV2:
 | |
| 		base = &cgc2_regs->pll4div_pfd1;
 | |
| 		pfd = PLL4_PFD2;
 | |
| 		index = clk - PLL4_PFD2_DIV1;
 | |
| 		break;
 | |
| 	case PLL4_PFD3_DIV1:
 | |
| 	case PLL4_PFD3_DIV2:
 | |
| 		base = &cgc2_regs->pll4div_pfd1;
 | |
| 		pfd = PLL4_PFD3;
 | |
| 		index = clk - PLL4_PFD2_DIV1;
 | |
| 		break;
 | |
| 	default:
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	reg = readl(base);
 | |
| 	gate = reg & (BIT(7) << (index * 8));
 | |
| 
 | |
| 	if (gate)
 | |
| 		return 0;
 | |
| 
 | |
| 	return cgc_pll_pfd_rate(pfd) / (((reg >> (index * 8)) & 0x3F) + 1);
 | |
| }
 | |
| 
 | |
| u32 cgc1_nic_get_rate(enum cgc_clk clk)
 | |
| {
 | |
| 	u32 reg, rate;
 | |
| 	u32 scs, nic_ad_divplat, nic_per_divplat;
 | |
| 	u32 xbar_ad_divplat, xbar_divbus, ad_slow;
 | |
| 	const enum cgc_clk src[] = {FRO, PLL3_PFD0, SOSC, LVDS};
 | |
| 
 | |
| 	reg = readl(&cgc1_regs->nicclk);
 | |
| 	scs = (reg >> 28) & 0x3;
 | |
| 	nic_ad_divplat = ((reg >> 21) & 0x3f) + 1;
 | |
| 	nic_per_divplat = ((reg >> 14) & 0x3f) + 1;
 | |
| 
 | |
| 	reg = readl(&cgc1_regs->xbarclk);
 | |
| 	xbar_ad_divplat = ((reg >> 14) & 0x3f) + 1;
 | |
| 	xbar_divbus = ((reg >> 7) & 0x3f) + 1;
 | |
| 	ad_slow = (reg & 0x3f) + 1;
 | |
| 
 | |
| 	rate = cgc_clk_get_rate(src[scs]);
 | |
| 
 | |
| 	switch (clk) {
 | |
| 	case NIC_APCLK:
 | |
| 		rate = rate / nic_ad_divplat;
 | |
| 		break;
 | |
| 	case NIC_PERCLK:
 | |
| 		rate = rate / (nic_ad_divplat * nic_per_divplat);
 | |
| 		break;
 | |
| 	case XBAR_APCLK:
 | |
| 		rate = rate / (nic_ad_divplat * xbar_ad_divplat);
 | |
| 		break;
 | |
| 	case XBAR_BUSCLK:
 | |
| 		rate = rate / (nic_ad_divplat * xbar_ad_divplat * xbar_divbus);
 | |
| 		break;
 | |
| 	case AD_SLOWCLK:
 | |
| 		rate = rate / (nic_ad_divplat * xbar_ad_divplat * ad_slow);
 | |
| 		break;
 | |
| 	default:
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	return rate;
 | |
| }
 | |
| 
 | |
| u32 cgc1_sosc_div(enum cgc_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 cgc_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 cgc_clk_get_rate(enum cgc_clk clk)
 | |
| {
 | |
| 	switch (clk) {
 | |
| 	case LVDS:
 | |
| 		return 0; /* No external LVDS clock used */
 | |
| 	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:
 | |
| 	case PLL3:
 | |
| 	case PLL4:
 | |
| 		return decode_pll(clk);
 | |
| 	case PLL3_VCODIV:
 | |
| 	case PLL4_VCODIV:
 | |
| 		return cgc_pll_vcodiv_rate(clk);
 | |
| 	case PLL3_PFD0:
 | |
| 	case PLL3_PFD1:
 | |
| 	case PLL3_PFD2:
 | |
| 	case PLL3_PFD3:
 | |
| 	case PLL4_PFD0:
 | |
| 	case PLL4_PFD1:
 | |
| 	case PLL4_PFD2:
 | |
| 	case PLL4_PFD3:
 | |
| 		return cgc_pll_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:
 | |
| 	case PLL4_PFD0_DIV1:
 | |
| 	case PLL4_PFD0_DIV2:
 | |
| 	case PLL4_PFD1_DIV1:
 | |
| 	case PLL4_PFD1_DIV2:
 | |
| 	case PLL4_PFD2_DIV1:
 | |
| 	case PLL4_PFD2_DIV2:
 | |
| 	case PLL4_PFD3_DIV1:
 | |
| 	case PLL4_PFD3_DIV2:
 | |
| 		return cgc_pll_pfd_div(clk);
 | |
| 	case NIC_APCLK:
 | |
| 	case NIC_PERCLK:
 | |
| 	case XBAR_APCLK:
 | |
| 	case XBAR_BUSCLK:
 | |
| 	case AD_SLOWCLK:
 | |
| 		return cgc1_nic_get_rate(clk);
 | |
| 	case LPAV_AXICLK:
 | |
| 	case LPAV_AHBCLK:
 | |
| 	case LPAV_BUSCLK:
 | |
| 		return cgc2_nic_get_rate(clk);
 | |
| 	default:
 | |
| 		printf("Unsupported cgc clock: %d\n", clk);
 | |
| 		return 0;
 | |
| 	}
 | |
| }
 |