arm: imx8ulp: add clock support
Add i.MX8ULP clock support Signed-off-by: Peng Fan <peng.fan@nxp.com>
This commit is contained in:
		
							parent
							
								
									166bc7fba0
								
							
						
					
					
						commit
						a84dab4f70
					
				|  | @ -0,0 +1,130 @@ | |||
| /* SPDX-License-Identifier: GPL-2.0+ */ | ||||
| /*
 | ||||
|  * Copyright 2021 NXP | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _ASM_ARCH_CGC_H | ||||
| #define _ASM_ARCH_CGC_H | ||||
| 
 | ||||
| enum cgc1_clk { | ||||
| 	DUMMY0_CLK, | ||||
| 	DUMMY1_CLK, | ||||
| 	LPOSC, | ||||
| 	XBAR_BUSCLK, | ||||
| 	SOSC, | ||||
| 	SOSC_DIV1, | ||||
| 	SOSC_DIV2, | ||||
| 	SOSC_DIV3, | ||||
| 	FRO, | ||||
| 	FRO_DIV1, | ||||
| 	FRO_DIV2, | ||||
| 	FRO_DIV3, | ||||
| 	PLL2, | ||||
| 	PLL3, | ||||
| 	PLL3_VCODIV, | ||||
| 	PLL3_PFD0, | ||||
| 	PLL3_PFD1, | ||||
| 	PLL3_PFD2, | ||||
| 	PLL3_PFD3, | ||||
| 	PLL3_PFD0_DIV1, | ||||
| 	PLL3_PFD0_DIV2, | ||||
| 	PLL3_PFD1_DIV1, | ||||
| 	PLL3_PFD1_DIV2, | ||||
| 	PLL3_PFD2_DIV1, | ||||
| 	PLL3_PFD2_DIV2, | ||||
| 	PLL3_PFD3_DIV1, | ||||
| 	PLL3_PFD3_DIV2, | ||||
| }; | ||||
| 
 | ||||
| struct cgc1_regs { | ||||
| 	u32 verid; | ||||
| 	u32 rsvd1[4]; | ||||
| 	u32 ca35clk; | ||||
| 	u32 rsvd2[2]; | ||||
| 	u32 clkoutcfg; | ||||
| 	u32 rsvd3[4]; | ||||
| 	u32 nicclk; | ||||
| 	u32 xbarclk; | ||||
| 	u32 rsvd4[21]; | ||||
| 	u32 clkdivrst; | ||||
| 	u32 rsvd5[29]; | ||||
| 	u32 soscdiv; | ||||
| 	u32 rsvd6[63]; | ||||
| 	u32 frodiv; | ||||
| 	u32 rsvd7[189]; | ||||
| 	u32 pll2csr; | ||||
| 	u32 rsvd8[3]; | ||||
| 	u32 pll2cfg; | ||||
| 	u32 rsvd9; | ||||
| 	u32 pll2denom; | ||||
| 	u32 pll2num; | ||||
| 	u32 pll2ss; | ||||
| 	u32 rsvd10[55]; | ||||
| 	u32 pll3csr; | ||||
| 	u32 pll3div_vco; | ||||
| 	u32 pll3div_pfd0; | ||||
| 	u32 pll3div_pfd1; | ||||
| 	u32 pll3cfg; | ||||
| 	u32 pll3pfdcfg; | ||||
| 	u32 pll3denom; | ||||
| 	u32 pll3num; | ||||
| 	u32 pll3ss; | ||||
| 	u32 pll3lock; | ||||
| 	u32 rsvd11[54]; | ||||
| 	u32 enetstamp; | ||||
| 	u32 rsvd12[67]; | ||||
| 	u32 pllusbcfg; | ||||
| 	u32 rsvd13[59]; | ||||
| 	u32 aud_clk1; | ||||
| 	u32 sai5_4_clk; | ||||
| 	u32 tpm6_7clk; | ||||
| 	u32 mqs1clk; | ||||
| 	u32 rsvd14[60]; | ||||
| 	u32 lvdscfg; | ||||
| }; | ||||
| 
 | ||||
| struct cgc2_regs { | ||||
| 	u32 verid; | ||||
| 	u32 rsvd1[4]; | ||||
| 	u32 hificlk; | ||||
| 	u32 rsvd2[2]; | ||||
| 	u32 clkoutcfg; | ||||
| 	u32 rsvd3[6]; | ||||
| 	u32 niclpavclk; | ||||
| 	u32 ddrclk; | ||||
| 	u32 rsvd4[19]; | ||||
| 	u32 clkdivrst; | ||||
| 	u32 rsvd5[29]; | ||||
| 	u32 soscdiv; | ||||
| 	u32 rsvd6[63]; | ||||
| 	u32 frodiv; | ||||
| 	u32 rsvd7[253]; | ||||
| 	u32 pll4csr; | ||||
| 	u32 pll4div_vco; | ||||
| 	u32 pll4div_pfd0; | ||||
| 	u32 pll4div_pfd1; | ||||
| 	u32 pll4cfg; | ||||
| 	u32 pll4pfdcfg; | ||||
| 	u32 pll4denom; | ||||
| 	u32 pll4num; | ||||
| 	u32 pll4ss; | ||||
| 	u32 pll4lock; | ||||
| 	u32 rsvd8[128]; | ||||
| 	u32 aud_clk2; | ||||
| 	u32 sai7_6_clk; | ||||
| 	u32 tpm8clk; | ||||
| 	u32 rsvd9[1]; | ||||
| 	u32 spdifclk; | ||||
| 	u32 rsvd10[59]; | ||||
| 	u32 lvdscfg; | ||||
| }; | ||||
| 
 | ||||
| u32 cgc1_clk_get_rate(enum cgc1_clk clk); | ||||
| void cgc1_pll3_init(void); | ||||
| void cgc1_pll2_init(void); | ||||
| void cgc1_soscdiv_init(void); | ||||
| void cgc1_init_core_clk(void); | ||||
| void cgc2_pll4_init(void); | ||||
| void cgc2_ddrclk_config(u32 src, u32 div); | ||||
| u32 cgc1_sosc_div(enum cgc1_clk clk); | ||||
| #endif | ||||
|  | @ -1,6 +1,6 @@ | |||
| /* SPDX-License-Identifier: GPL-2.0+ */ | ||||
| /*
 | ||||
|  * Copyright 2020 NXP | ||||
|  * Copyright 2021 NXP | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _ASM_ARCH_IMX8ULP_CLOCK_H | ||||
|  | @ -17,6 +17,7 @@ enum mxc_clock { | |||
| 	MXC_DDR_CLK, | ||||
| 	MXC_ESDHC_CLK, | ||||
| 	MXC_ESDHC2_CLK, | ||||
| 	MXC_ESDHC3_CLK, | ||||
| 	MXC_I2C_CLK, | ||||
| }; | ||||
| 
 | ||||
|  | @ -26,9 +27,15 @@ u32 get_lpuart_clk(void); | |||
| int enable_i2c_clk(unsigned char enable, unsigned int i2c_num); | ||||
| u32 imx_get_i2cclk(unsigned int i2c_num); | ||||
| #endif | ||||
| void enable_usboh3_clk(unsigned char enable); | ||||
| int enable_usb_pll(ulong usb_phy_base); | ||||
| #ifdef CONFIG_MXC_OCOTP | ||||
| void enable_ocotp_clk(unsigned char enable); | ||||
| #endif | ||||
| void init_clk_usdhc(u32 index); | ||||
| void init_clk_fspi(int index); | ||||
| void init_clk_ddr(void); | ||||
| int set_ddr_clk(u32 phy_freq_mhz); | ||||
| void clock_init(void); | ||||
| void cgc1_enet_stamp_sel(u32 clk_src); | ||||
| #endif | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| #define _IMX8ULP_REGS_H_ | ||||
| #define ARCH_MXC | ||||
| 
 | ||||
| #include <linux/bitops.h> | ||||
| #include <linux/sizes.h> | ||||
| 
 | ||||
| #define PBRIDGE0_BASE		0x28000000 | ||||
|  |  | |||
|  | @ -0,0 +1,139 @@ | |||
| /* SPDX-License-Identifier: GPL-2.0+ */ | ||||
| /*
 | ||||
|  * Copyright 2021 NXP | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _ASM_ARCH_IMX8ULP_PCC_H | ||||
| #define _ASM_ARCH_IMX8ULP_PCC_H | ||||
| 
 | ||||
| #include <asm/arch/cgc.h> | ||||
| 
 | ||||
| enum pcc3_entry { | ||||
| 	DMA1_MP_PCC3_SLOT = 1, | ||||
| 	DMA1_CH0_PCC3_SLOT = 2, | ||||
| 	DMA1_CH1_PCC3_SLOT = 3, | ||||
| 	DMA1_CH2_PCC3_SLOT = 4, | ||||
| 	DMA1_CH3_PCC3_SLOT = 5, | ||||
| 	DMA1_CH4_PCC3_SLOT = 6, | ||||
| 	DMA1_CH5_PCC3_SLOT = 7, | ||||
| 	DMA1_CH6_PCC3_SLOT = 8, | ||||
| 	DMA1_CH7_PCC3_SLOT = 9, | ||||
| 	DMA1_CH8_PCC3_SLOT = 10, | ||||
| 	DMA1_CH9_PCC3_SLOT = 11, | ||||
| 	DMA1_CH10_PCC3_SLOT = 12, | ||||
| 	DMA1_CH11_PCC3_SLOT = 13, | ||||
| 	DMA1_CH12_PCC3_SLOT = 14, | ||||
| 	DMA1_CH13_PCC3_SLOT = 15, | ||||
| 	DMA1_CH14_PCC3_SLOT = 16, | ||||
| 	DMA1_CH15_PCC3_SLOT = 17, | ||||
| 	DMA1_CH16_PCC3_SLOT = 18, | ||||
| 	DMA1_CH17_PCC3_SLOT = 19, | ||||
| 	DMA1_CH18_PCC3_SLOT = 20, | ||||
| 	DMA1_CH19_PCC3_SLOT = 21, | ||||
| 	DMA1_CH20_PCC3_SLOT = 22, | ||||
| 	DMA1_CH21_PCC3_SLOT = 23, | ||||
| 	DMA1_CH22_PCC3_SLOT = 24, | ||||
| 	DMA1_CH23_PCC3_SLOT = 25, | ||||
| 	DMA1_CH24_PCC3_SLOT = 26, | ||||
| 	DMA1_CH25_PCC3_SLOT = 27, | ||||
| 	DMA1_CH26_PCC3_SLOT = 28, | ||||
| 	DMA1_CH27_PCC3_SLOT = 29, | ||||
| 	DMA1_CH28_PCC3_SLOT = 30, | ||||
| 	DMA1_CH29_PCC3_SLOT = 31, | ||||
| 	DMA1_CH30_PCC3_SLOT = 32, | ||||
| 	DMA1_CH31_PCC3_SLOT = 33, | ||||
| 	MU0_B_PCC3_SLOT = 34, | ||||
| 	MU3_A_PCC3_SLOT = 35, | ||||
| 	LLWU1_PCC3_SLOT = 38, | ||||
| 	UPOWER_PCC3_SLOT = 40, | ||||
| 	WDOG3_PCC3_SLOT = 42, | ||||
| 	WDOG4_PCC3_SLOT = 43, | ||||
| 	XRDC_MGR_PCC3_SLOT = 47, | ||||
| 	SEMA42_1_PCC3_SLOT = 48, | ||||
| 	ROMCP1_PCC3_SLOT = 49, | ||||
| 	LPIT1_PCC3_SLOT = 50, | ||||
| 	TPM4_PCC3_SLOT = 51, | ||||
| 	TPM5_PCC3_SLOT = 52, | ||||
| 	FLEXIO1_PCC3_SLOT = 53, | ||||
| 	I3C2_PCC3_SLOT = 54, | ||||
| 	LPI2C4_PCC3_SLOT = 55, | ||||
| 	LPI2C5_PCC3_SLOT = 56, | ||||
| 	LPUART4_PCC3_SLOT = 57, | ||||
| 	LPUART5_PCC3_SLOT = 58, | ||||
| 	LPSPI4_PCC3_SLOT = 59, | ||||
| 	LPSPI5_PCC3_SLOT = 60, | ||||
| }; | ||||
| 
 | ||||
| enum pcc4_entry { | ||||
| 	FLEXSPI2_PCC4_SLOT = 1, | ||||
| 	TPM6_PCC4_SLOT = 2, | ||||
| 	TPM7_PCC4_SLOT = 3, | ||||
| 	LPI2C6_PCC4_SLOT = 4, | ||||
| 	LPI2C7_PCC4_SLOT = 5, | ||||
| 	LPUART6_PCC4_SLOT = 6, | ||||
| 	LPUART7_PCC4_SLOT = 7, | ||||
| 	SAI4_PCC4_SLOT = 8, | ||||
| 	SAI5_PCC4_SLOT = 9, | ||||
| 	PCTLE_PCC4_SLOT = 10, | ||||
| 	PCTLF_PCC4_SLOT = 11, | ||||
| 	SDHC0_PCC4_SLOT = 13, | ||||
| 	SDHC1_PCC4_SLOT = 14, | ||||
| 	SDHC2_PCC4_SLOT = 15, | ||||
| 	USB0_PCC4_SLOT = 16, | ||||
| 	USBPHY_PCC4_SLOT = 17, | ||||
| 	USB1_PCC4_SLOT = 18, | ||||
| 	USB1PHY_PCC4_SLOT = 19, | ||||
| 	USB_XBAR_PCC4_SLOT = 20, | ||||
| 	ENET_PCC4_SLOT = 21, | ||||
| 	SFA1_PCC4_SLOT = 22, | ||||
| 	RGPIOE_PCC4_SLOT = 30, | ||||
| 	RGPIOF_PCC4_SLOT = 31, | ||||
| }; | ||||
| 
 | ||||
| /* PCC registers */ | ||||
| #define PCC_PR_OFFSET	31 | ||||
| #define PCC_PR_MASK		(0x1 << PCC_PR_OFFSET) | ||||
| #define PCC_CGC_OFFSET	30 | ||||
| #define PCC_CGC_MASK	(0x1 << PCC_CGC_OFFSET) | ||||
| #define PCC_INUSE_OFFSET	29 | ||||
| #define PCC_INUSE_MASK		(0x1 << PCC_INUSE_OFFSET) | ||||
| #define PCC_PCS_OFFSET	24 | ||||
| #define PCC_PCS_MASK	(0x7 << PCC_PCS_OFFSET) | ||||
| #define PCC_FRAC_OFFSET	3 | ||||
| #define PCC_FRAC_MASK	(0x1 << PCC_FRAC_OFFSET) | ||||
| #define PCC_PCD_OFFSET	0 | ||||
| #define PCC_PCD_MASK	(0x7 << PCC_PCD_OFFSET) | ||||
| 
 | ||||
| enum pcc_clksrc_type { | ||||
| 	CLKSRC_PER_PLAT = 0, | ||||
| 	CLKSRC_PER_BUS = 1, | ||||
| 	CLKSRC_NO_PCS = 2, | ||||
| }; | ||||
| 
 | ||||
| enum pcc_div_type { | ||||
| 	PCC_HAS_DIV, | ||||
| 	PCC_NO_DIV, | ||||
| }; | ||||
| 
 | ||||
| enum pcc_rst_b { | ||||
| 	PCC_HAS_RST_B, | ||||
| 	PCC_NO_RST_B, | ||||
| }; | ||||
| 
 | ||||
| /* This structure keeps info for each pcc slot */ | ||||
| struct pcc_entry { | ||||
| 	u32 pcc_base; | ||||
| 	u32 pcc_slot; | ||||
| 	enum pcc_clksrc_type clksrc; | ||||
| 	enum pcc_div_type div; | ||||
| 	enum pcc_rst_b rst_b; | ||||
| }; | ||||
| 
 | ||||
| int pcc_clock_enable(int pcc_controller, int pcc_clk_slot, bool enable); | ||||
| int pcc_clock_sel(int pcc_controller, int pcc_clk_slot, enum cgc1_clk src); | ||||
| int pcc_clock_div_config(int pcc_controller, int pcc_clk_slot, bool frac, u8 div); | ||||
| bool pcc_clock_is_enable(int pcc_controller, int pcc_clk_slot); | ||||
| int pcc_clock_get_clksrc(int pcc_controller, int pcc_clk_slot, enum cgc1_clk *src); | ||||
| int pcc_reset_peripheral(int pcc_controller, int pcc_clk_slot, bool reset); | ||||
| u32 pcc_clock_get_rate(int pcc_controller, int pcc_clk_slot); | ||||
| #endif | ||||
|  | @ -4,4 +4,4 @@ | |||
| #
 | ||||
| 
 | ||||
| obj-y += lowlevel_init.o | ||||
| obj-y += soc.o clock.o iomux.o | ||||
| obj-y += soc.o clock.o iomux.o pcc.o cgc.o | ||||
|  |  | |||
|  | @ -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; | ||||
| 	} | ||||
| } | ||||
|  | @ -4,24 +4,394 @@ | |||
|  */ | ||||
| 
 | ||||
| #include <common.h> | ||||
| #include <command.h> | ||||
| #include <div64.h> | ||||
| #include <asm/arch/imx-regs.h> | ||||
| #include <asm/io.h> | ||||
| #include <errno.h> | ||||
| #include <asm/arch/clock.h> | ||||
| #include <asm/arch/pcc.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; | ||||
| 
 | ||||
| void clock_init(void) | ||||
| #define PLL_USB_EN_USB_CLKS_MASK	(0x01 << 6) | ||||
| #define PLL_USB_PWR_MASK		(0x01 << 12) | ||||
| #define PLL_USB_ENABLE_MASK		(0x01 << 13) | ||||
| #define PLL_USB_BYPASS_MASK		(0x01 << 16) | ||||
| #define PLL_USB_REG_ENABLE_MASK		(0x01 << 21) | ||||
| #define PLL_USB_DIV_SEL_MASK		(0x07 << 22) | ||||
| #define PLL_USB_LOCK_MASK		(0x01 << 31) | ||||
| #define PCC5_LPDDR4_ADDR 0x2da70108 | ||||
| 
 | ||||
| static void lpuart_set_clk(u32 index, enum cgc1_clk clk) | ||||
| { | ||||
| 	const u32 lpuart_pcc_slots[] = { | ||||
| 		LPUART4_PCC3_SLOT, | ||||
| 		LPUART5_PCC3_SLOT, | ||||
| 		LPUART6_PCC4_SLOT, | ||||
| 		LPUART7_PCC4_SLOT, | ||||
| 	}; | ||||
| 
 | ||||
| 	const u32 lpuart_pcc[] = { | ||||
| 		3, 3, 4, 4, | ||||
| 	}; | ||||
| 
 | ||||
| 	if (index > 3) | ||||
| 		return; | ||||
| 
 | ||||
| 	pcc_clock_enable(lpuart_pcc[index], lpuart_pcc_slots[index], false); | ||||
| 	pcc_clock_sel(lpuart_pcc[index], lpuart_pcc_slots[index], clk); | ||||
| 	pcc_clock_enable(lpuart_pcc[index], lpuart_pcc_slots[index], true); | ||||
| 
 | ||||
| 	pcc_reset_peripheral(lpuart_pcc[index], lpuart_pcc_slots[index], false); | ||||
| } | ||||
| 
 | ||||
| unsigned int mxc_get_clock(enum mxc_clock clk) | ||||
| static void init_clk_lpuart(void) | ||||
| { | ||||
| 	u32 index = 0, i; | ||||
| 
 | ||||
| 	const u32 lpuart_array[] = { | ||||
| 		LPUART4_RBASE, | ||||
| 		LPUART5_RBASE, | ||||
| 		LPUART6_RBASE, | ||||
| 		LPUART7_RBASE, | ||||
| 	}; | ||||
| 
 | ||||
| 	for (i = 0; i < 4; i++) { | ||||
| 		if (lpuart_array[i] == LPUART_BASE) { | ||||
| 			index = i; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	lpuart_set_clk(index, SOSC_DIV2); | ||||
| } | ||||
| 
 | ||||
| void init_clk_fspi(int index) | ||||
| { | ||||
| 	pcc_clock_enable(4, FLEXSPI2_PCC4_SLOT, false); | ||||
| 	pcc_clock_sel(4, FLEXSPI2_PCC4_SLOT, PLL3_PFD2_DIV1); | ||||
| 	pcc_clock_div_config(4, FLEXSPI2_PCC4_SLOT, false, 8); | ||||
| 	pcc_clock_enable(4, FLEXSPI2_PCC4_SLOT, true); | ||||
| 	pcc_reset_peripheral(4, FLEXSPI2_PCC4_SLOT, false); | ||||
| } | ||||
| 
 | ||||
| void setclkout_ddr(void) | ||||
| { | ||||
| 	writel(0x12800000, 0x2DA60020); | ||||
| 	writel(0xa00, 0x298C0000); /* PTD0 */ | ||||
| } | ||||
| 
 | ||||
| void ddrphy_pll_lock(void) | ||||
| { | ||||
| 	writel(0x00011542, 0x2E065964); | ||||
| 	writel(0x00011542, 0x2E06586C); | ||||
| 
 | ||||
| 	writel(0x00000B01, 0x2E062000); | ||||
| 	writel(0x00000B01, 0x2E060000); | ||||
| } | ||||
| 
 | ||||
| void init_clk_ddr(void) | ||||
| { | ||||
| 	/* enable pll4 and ddrclk*/ | ||||
| 	cgc2_pll4_init(); | ||||
| 	cgc2_ddrclk_config(1, 1); | ||||
| 
 | ||||
| 	/* enable ddr pcc */ | ||||
| 	writel(0xd0000000, PCC5_LPDDR4_ADDR); | ||||
| 
 | ||||
| 	/* for debug */ | ||||
| 	/* setclkout_ddr(); */ | ||||
| } | ||||
| 
 | ||||
| int set_ddr_clk(u32 phy_freq_mhz) | ||||
| { | ||||
| 	debug("%s %u\n", __func__, phy_freq_mhz); | ||||
| 
 | ||||
| 	if (phy_freq_mhz == 48) { | ||||
| 		writel(0x90000000, PCC5_LPDDR4_ADDR); /* disable ddr pcc */ | ||||
| 		cgc2_ddrclk_config(2, 0); /* 24Mhz DDR clock */ | ||||
| 		writel(0xd0000000, PCC5_LPDDR4_ADDR); /* enable ddr pcc */ | ||||
| 	} else if (phy_freq_mhz == 384) { | ||||
| 		writel(0x90000000, PCC5_LPDDR4_ADDR); /* disable ddr pcc */ | ||||
| 		cgc2_ddrclk_config(0, 0); /* 192Mhz DDR clock */ | ||||
| 		writel(0xd0000000, PCC5_LPDDR4_ADDR); /* enable ddr pcc */ | ||||
| 	} else if (phy_freq_mhz == 528) { | ||||
| 		writel(0x90000000, PCC5_LPDDR4_ADDR); /* disable ddr pcc */ | ||||
| 		cgc2_ddrclk_config(4, 1); /* 264Mhz DDR clock */ | ||||
| 		writel(0xd0000000, PCC5_LPDDR4_ADDR); /* enable ddr pcc */ | ||||
| 	} else if (phy_freq_mhz == 264) { | ||||
| 		writel(0x90000000, PCC5_LPDDR4_ADDR); /* disable ddr pcc */ | ||||
| 		cgc2_ddrclk_config(4, 3); /* 132Mhz DDR clock */ | ||||
| 		writel(0xd0000000, PCC5_LPDDR4_ADDR); /* enable ddr pcc */ | ||||
| 	} else if (phy_freq_mhz == 192) { | ||||
| 		writel(0x90000000, PCC5_LPDDR4_ADDR); /* disable ddr pcc */ | ||||
| 		cgc2_ddrclk_config(0, 1); /* 96Mhz DDR clock */ | ||||
| 		writel(0xd0000000, PCC5_LPDDR4_ADDR); /* enable ddr pcc */ | ||||
| 	} else if (phy_freq_mhz == 96) { | ||||
| 		writel(0x90000000, PCC5_LPDDR4_ADDR); /* disable ddr pcc */ | ||||
| 		cgc2_ddrclk_config(0, 3); /* 48Mhz DDR clock */ | ||||
| 		writel(0xd0000000, PCC5_LPDDR4_ADDR); /* enable ddr pcc */ | ||||
| 	} else { | ||||
| 		printf("ddr phy clk %uMhz is not supported\n", phy_freq_mhz); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void clock_init(void) | ||||
| { | ||||
| 	cgc1_soscdiv_init(); | ||||
| 	cgc1_init_core_clk(); | ||||
| 
 | ||||
| 	init_clk_lpuart(); | ||||
| 
 | ||||
| 	pcc_clock_enable(4, SDHC0_PCC4_SLOT, false); | ||||
| 	pcc_clock_sel(4, SDHC0_PCC4_SLOT, PLL3_PFD1_DIV2); | ||||
| 	pcc_clock_enable(4, SDHC0_PCC4_SLOT, true); | ||||
| 	pcc_reset_peripheral(4, SDHC0_PCC4_SLOT, false); | ||||
| 
 | ||||
| 	pcc_clock_enable(4, SDHC1_PCC4_SLOT, false); | ||||
| 	pcc_clock_sel(4, SDHC1_PCC4_SLOT, PLL3_PFD2_DIV1); | ||||
| 	pcc_clock_enable(4, SDHC1_PCC4_SLOT, true); | ||||
| 	pcc_reset_peripheral(4, SDHC1_PCC4_SLOT, false); | ||||
| 
 | ||||
| 	pcc_clock_enable(4, SDHC2_PCC4_SLOT, false); | ||||
| 	pcc_clock_sel(4, SDHC2_PCC4_SLOT, PLL3_PFD2_DIV1); | ||||
| 	pcc_clock_enable(4, SDHC2_PCC4_SLOT, true); | ||||
| 	pcc_reset_peripheral(4, SDHC2_PCC4_SLOT, false); | ||||
| 
 | ||||
| 	/* Enable upower mu1 clk */ | ||||
| 	pcc_clock_enable(3, UPOWER_PCC3_SLOT, true); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Enable clock division | ||||
| 	 * TODO: may not needed after ROM ready. | ||||
| 	 */ | ||||
| } | ||||
| 
 | ||||
| #if IS_ENABLED(CONFIG_SYS_I2C_IMX_LPI2C) | ||||
| int enable_i2c_clk(unsigned char enable, u32 i2c_num) | ||||
| { | ||||
| 	/* Set parent to FIRC DIV2 clock */ | ||||
| 	const u32 lpi2c_pcc_clks[] = { | ||||
| 		LPI2C4_PCC3_SLOT << 8 | 3, | ||||
| 		LPI2C5_PCC3_SLOT << 8 | 3, | ||||
| 		LPI2C6_PCC4_SLOT << 8 | 4, | ||||
| 		LPI2C7_PCC4_SLOT << 8 | 4, | ||||
| 	}; | ||||
| 
 | ||||
| 	if (i2c_num < 4 || i2c_num > 7) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (enable) { | ||||
| 		pcc_clock_enable(lpi2c_pcc_clks[i2c_num - 4] & 0xff, | ||||
| 				 lpi2c_pcc_clks[i2c_num - 4] >> 8, false); | ||||
| 		pcc_clock_sel(lpi2c_pcc_clks[i2c_num - 4] & 0xff, | ||||
| 			      lpi2c_pcc_clks[i2c_num - 4] >> 8, SOSC_DIV2); | ||||
| 		pcc_clock_enable(lpi2c_pcc_clks[i2c_num - 4] & 0xff, | ||||
| 				 lpi2c_pcc_clks[i2c_num - 4] >> 8, true); | ||||
| 		pcc_reset_peripheral(lpi2c_pcc_clks[i2c_num - 4] & 0xff, | ||||
| 				     lpi2c_pcc_clks[i2c_num - 4] >> 8, false); | ||||
| 	} else { | ||||
| 		pcc_clock_enable(lpi2c_pcc_clks[i2c_num - 4] & 0xff, | ||||
| 				 lpi2c_pcc_clks[i2c_num - 4] >> 8, false); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| u32 imx_get_i2cclk(u32 i2c_num) | ||||
| { | ||||
| 	const u32 lpi2c_pcc_clks[] = { | ||||
| 		LPI2C4_PCC3_SLOT << 8 | 3, | ||||
| 		LPI2C5_PCC3_SLOT << 8 | 3, | ||||
| 		LPI2C6_PCC4_SLOT << 8 | 4, | ||||
| 		LPI2C7_PCC4_SLOT << 8 | 4, | ||||
| 	}; | ||||
| 
 | ||||
| 	if (i2c_num < 4 || i2c_num > 7) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return pcc_clock_get_rate(lpi2c_pcc_clks[i2c_num - 4] & 0xff, | ||||
| 				  lpi2c_pcc_clks[i2c_num - 4] >> 8); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| void enable_usboh3_clk(unsigned char enable) | ||||
| { | ||||
| 	if (enable) { | ||||
| 		pcc_clock_enable(4, USB0_PCC4_SLOT, true); | ||||
| 		pcc_clock_enable(4, USBPHY_PCC4_SLOT, true); | ||||
| 		pcc_reset_peripheral(4, USB0_PCC4_SLOT, false); | ||||
| 		pcc_reset_peripheral(4, USBPHY_PCC4_SLOT, false); | ||||
| 
 | ||||
| #ifdef CONFIG_USB_MAX_CONTROLLER_COUNT | ||||
| 		if (CONFIG_USB_MAX_CONTROLLER_COUNT > 1) { | ||||
| 			pcc_clock_enable(4, USB1_PCC4_SLOT, true); | ||||
| 			pcc_clock_enable(4, USB1PHY_PCC4_SLOT, true); | ||||
| 			pcc_reset_peripheral(4, USB1_PCC4_SLOT, false); | ||||
| 			pcc_reset_peripheral(4, USB1PHY_PCC4_SLOT, false); | ||||
| 		} | ||||
| #endif | ||||
| 
 | ||||
| 		pcc_clock_enable(4, USB_XBAR_PCC4_SLOT, true); | ||||
| 	} else { | ||||
| 		pcc_clock_enable(4, USB0_PCC4_SLOT, false); | ||||
| 		pcc_clock_enable(4, USB1_PCC4_SLOT, false); | ||||
| 		pcc_clock_enable(4, USBPHY_PCC4_SLOT, false); | ||||
| 		pcc_clock_enable(4, USB1PHY_PCC4_SLOT, false); | ||||
| 		pcc_clock_enable(4, USB_XBAR_PCC4_SLOT, false); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int enable_usb_pll(ulong usb_phy_base) | ||||
| { | ||||
| 	u32 sosc_rate; | ||||
| 	s32 timeout = 1000000; | ||||
| 
 | ||||
| 	struct usbphy_regs *usbphy = | ||||
| 		(struct usbphy_regs *)usb_phy_base; | ||||
| 
 | ||||
| 	sosc_rate = cgc1_sosc_div(SOSC); | ||||
| 	if (!sosc_rate) | ||||
| 		return -EPERM; | ||||
| 
 | ||||
| 	if (!(readl(&usbphy->usb1_pll_480_ctrl) & PLL_USB_LOCK_MASK)) { | ||||
| 		writel(0x1c00000, &usbphy->usb1_pll_480_ctrl_clr); | ||||
| 
 | ||||
| 		switch (sosc_rate) { | ||||
| 		case 24000000: | ||||
| 			writel(0xc00000, &usbphy->usb1_pll_480_ctrl_set); | ||||
| 			break; | ||||
| 
 | ||||
| 		case 30000000: | ||||
| 			writel(0x800000, &usbphy->usb1_pll_480_ctrl_set); | ||||
| 			break; | ||||
| 
 | ||||
| 		case 19200000: | ||||
| 			writel(0x1400000, &usbphy->usb1_pll_480_ctrl_set); | ||||
| 			break; | ||||
| 
 | ||||
| 		default: | ||||
| 			writel(0xc00000, &usbphy->usb1_pll_480_ctrl_set); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Enable the regulator first */ | ||||
| 		writel(PLL_USB_REG_ENABLE_MASK, | ||||
| 		       &usbphy->usb1_pll_480_ctrl_set); | ||||
| 
 | ||||
| 		/* Wait at least 15us */ | ||||
| 		udelay(15); | ||||
| 
 | ||||
| 		/* Enable the power */ | ||||
| 		writel(PLL_USB_PWR_MASK, &usbphy->usb1_pll_480_ctrl_set); | ||||
| 
 | ||||
| 		/* Wait lock */ | ||||
| 		while (timeout--) { | ||||
| 			if (readl(&usbphy->usb1_pll_480_ctrl) & | ||||
| 			    PLL_USB_LOCK_MASK) | ||||
| 				break; | ||||
| 		} | ||||
| 
 | ||||
| 		if (timeout <= 0) { | ||||
| 			/* If timeout, we power down the pll */ | ||||
| 			writel(PLL_USB_PWR_MASK, | ||||
| 			       &usbphy->usb1_pll_480_ctrl_clr); | ||||
| 			return -ETIME; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Clear the bypass */ | ||||
| 	writel(PLL_USB_BYPASS_MASK, &usbphy->usb1_pll_480_ctrl_clr); | ||||
| 
 | ||||
| 	/* Enable the PLL clock out to USB */ | ||||
| 	writel((PLL_USB_EN_USB_CLKS_MASK | PLL_USB_ENABLE_MASK), | ||||
| 	       &usbphy->usb1_pll_480_ctrl_set); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| u32 mxc_get_clock(enum mxc_clock clk) | ||||
| { | ||||
| 	switch (clk) { | ||||
| 	case MXC_ESDHC_CLK: | ||||
| 		return pcc_clock_get_rate(4, SDHC0_PCC4_SLOT); | ||||
| 	case MXC_ESDHC2_CLK: | ||||
| 		return pcc_clock_get_rate(4, SDHC1_PCC4_SLOT); | ||||
| 	case MXC_ESDHC3_CLK: | ||||
| 		return pcc_clock_get_rate(4, SDHC2_PCC4_SLOT); | ||||
| 	case MXC_ARM_CLK: | ||||
| 		return cgc1_clk_get_rate(PLL2); | ||||
| 	default: | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| u32 get_lpuart_clk(void) | ||||
| { | ||||
| 	return 24000000; | ||||
| 	int index = 0; | ||||
| 
 | ||||
| 	const u32 lpuart_array[] = { | ||||
| 		LPUART4_RBASE, | ||||
| 		LPUART5_RBASE, | ||||
| 		LPUART6_RBASE, | ||||
| 		LPUART7_RBASE, | ||||
| 	}; | ||||
| 
 | ||||
| 	const u32 lpuart_pcc_slots[] = { | ||||
| 		LPUART4_PCC3_SLOT, | ||||
| 		LPUART5_PCC3_SLOT, | ||||
| 		LPUART6_PCC4_SLOT, | ||||
| 		LPUART7_PCC4_SLOT, | ||||
| 	}; | ||||
| 
 | ||||
| 	const u32 lpuart_pcc[] = { | ||||
| 		3, 3, 4, 4, | ||||
| 	}; | ||||
| 
 | ||||
| 	for (index = 0; index < 4; index++) { | ||||
| 		if (lpuart_array[index] == LPUART_BASE) | ||||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (index > 3) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return pcc_clock_get_rate(lpuart_pcc[index], lpuart_pcc_slots[index]); | ||||
| } | ||||
| 
 | ||||
| #ifndef CONFIG_SPL_BUILD | ||||
| /*
 | ||||
|  * Dump some core clockes. | ||||
|  */ | ||||
| int do_mx8ulp_showclocks(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) | ||||
| { | ||||
| 	printf("SDHC0 %8d MHz\n", pcc_clock_get_rate(4, SDHC0_PCC4_SLOT) / 1000000); | ||||
| 	printf("SDHC1 %8d MHz\n", pcc_clock_get_rate(4, SDHC1_PCC4_SLOT) / 1000000); | ||||
| 	printf("SDHC2 %8d MHz\n", pcc_clock_get_rate(4, SDHC2_PCC4_SLOT) / 1000000); | ||||
| 
 | ||||
| 	printf("SOSC %8d MHz\n", cgc1_clk_get_rate(SOSC) / 1000000); | ||||
| 	printf("FRO %8d MHz\n", cgc1_clk_get_rate(FRO) / 1000000); | ||||
| 	printf("PLL2 %8d MHz\n", cgc1_clk_get_rate(PLL2) / 1000000); | ||||
| 	printf("PLL3 %8d MHz\n", cgc1_clk_get_rate(PLL3) / 1000000); | ||||
| 	printf("PLL3_VCODIV %8d MHz\n", cgc1_clk_get_rate(PLL3_VCODIV) / 1000000); | ||||
| 	printf("PLL3_PFD0 %8d MHz\n", cgc1_clk_get_rate(PLL3_PFD0) / 1000000); | ||||
| 	printf("PLL3_PFD1 %8d MHz\n", cgc1_clk_get_rate(PLL3_PFD1) / 1000000); | ||||
| 	printf("PLL3_PFD2 %8d MHz\n", cgc1_clk_get_rate(PLL3_PFD2) / 1000000); | ||||
| 	printf("PLL3_PFD3 %8d MHz\n", cgc1_clk_get_rate(PLL3_PFD3) / 1000000); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| U_BOOT_CMD( | ||||
| 	clocks,	CONFIG_SYS_MAXARGS, 1, do_mx8ulp_showclocks, | ||||
| 	"display clocks", | ||||
| 	"" | ||||
| ); | ||||
| #endif | ||||
|  |  | |||
|  | @ -0,0 +1,449 @@ | |||
| // 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/pcc.h> | ||||
| #include <asm/arch/cgc.h> | ||||
| #include <asm/arch/sys_proto.h> | ||||
| 
 | ||||
| #define cgc1_clk_TYPES 2 | ||||
| #define cgc1_clk_NUM 8 | ||||
| 
 | ||||
| static enum cgc1_clk pcc3_clksrc[][8] = { | ||||
| 	{ | ||||
| 	}, | ||||
| 	{	DUMMY0_CLK, | ||||
| 		LPOSC, | ||||
| 		SOSC_DIV2, | ||||
| 		FRO_DIV2, | ||||
| 		XBAR_BUSCLK, | ||||
| 		PLL3_PFD1_DIV1, | ||||
| 		PLL3_PFD0_DIV2, | ||||
| 		PLL3_PFD0_DIV1 | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| static enum cgc1_clk pcc4_clksrc[][8] = { | ||||
| 	{ | ||||
| 		DUMMY0_CLK, | ||||
| 		SOSC_DIV1, | ||||
| 		FRO_DIV1, | ||||
| 		PLL3_PFD3_DIV2, | ||||
| 		PLL3_PFD3_DIV1, | ||||
| 		PLL3_PFD2_DIV2, | ||||
| 		PLL3_PFD2_DIV1, | ||||
| 		PLL3_PFD1_DIV2 | ||||
| 	}, | ||||
| 	{ | ||||
| 		DUMMY0_CLK, | ||||
| 		DUMMY1_CLK, | ||||
| 		LPOSC, | ||||
| 		SOSC_DIV2, | ||||
| 		FRO_DIV2, | ||||
| 		XBAR_BUSCLK, | ||||
| 		PLL3_VCODIV, | ||||
| 		PLL3_PFD0_DIV1 | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| static struct pcc_entry pcc3_arrays[] = { | ||||
| 	{PCC3_RBASE, DMA1_MP_PCC3_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH0_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH1_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH2_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH3_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH4_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH5_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH6_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH7_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH8_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH9_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH10_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH11_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH12_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH13_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH14_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH15_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH16_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH17_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH18_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH19_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH20_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH21_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH22_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH23_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH24_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH25_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH26_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH27_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH28_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH29_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH30_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, DMA1_CH31_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, MU0_B_PCC3_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, MU3_A_PCC3_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, LLWU1_PCC3_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, UPOWER_PCC3_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, WDOG3_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, | ||||
| 	{PCC3_RBASE, WDOG4_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, | ||||
| 	{PCC3_RBASE, XRDC_MGR_PCC3_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, SEMA42_1_PCC3_SLOT,	CLKSRC_PER_BUS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, ROMCP1_PCC3_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B}, | ||||
| 	{PCC3_RBASE, LPIT1_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, | ||||
| 	{PCC3_RBASE, TPM4_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, | ||||
| 	{PCC3_RBASE, TPM5_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, | ||||
| 	{PCC3_RBASE, FLEXIO1_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, | ||||
| 	{PCC3_RBASE, I3C2_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, | ||||
| 	{PCC3_RBASE, LPI2C4_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, | ||||
| 	{PCC3_RBASE, LPI2C5_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, | ||||
| 	{PCC3_RBASE, LPUART4_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, | ||||
| 	{PCC3_RBASE, LPUART5_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, | ||||
| 	{PCC3_RBASE, LPSPI4_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, | ||||
| 	{PCC3_RBASE, LPSPI5_PCC3_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B}, | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
| static struct pcc_entry pcc4_arrays[] = { | ||||
| 	{PCC4_RBASE, FLEXSPI2_PCC4_SLOT,	CLKSRC_PER_PLAT, PCC_HAS_DIV, PCC_HAS_RST_B }, | ||||
| 	{PCC4_RBASE, TPM6_PCC4_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B }, | ||||
| 	{PCC4_RBASE, TPM7_PCC4_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B }, | ||||
| 	{PCC4_RBASE, LPI2C6_PCC4_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B }, | ||||
| 	{PCC4_RBASE, LPI2C7_PCC4_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B }, | ||||
| 	{PCC4_RBASE, LPUART6_PCC4_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B }, | ||||
| 	{PCC4_RBASE, LPUART7_PCC4_SLOT,		CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B }, | ||||
| 	{PCC4_RBASE, SAI4_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B }, | ||||
| 	{PCC4_RBASE, SAI5_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B }, | ||||
| 	{PCC4_RBASE, PCTLE_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B }, | ||||
| 	{PCC4_RBASE, PCTLF_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B }, | ||||
| 	{PCC4_RBASE, SDHC0_PCC4_SLOT,		CLKSRC_PER_PLAT, PCC_HAS_DIV, PCC_HAS_RST_B }, | ||||
| 	{PCC4_RBASE, SDHC1_PCC4_SLOT,		CLKSRC_PER_PLAT, PCC_HAS_DIV, PCC_HAS_RST_B }, | ||||
| 	{PCC4_RBASE, SDHC2_PCC4_SLOT,		CLKSRC_PER_PLAT, PCC_HAS_DIV, PCC_HAS_RST_B }, | ||||
| 	{PCC4_RBASE, USB0_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B }, | ||||
| 	{PCC4_RBASE, USBPHY_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B }, | ||||
| 	{PCC4_RBASE, USB1_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B }, | ||||
| 	{PCC4_RBASE, USB1PHY_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B }, | ||||
| 	{PCC4_RBASE, USB_XBAR_PCC4_SLOT,	CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B }, | ||||
| 	{PCC4_RBASE, ENET_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B }, | ||||
| 	{PCC4_RBASE, SFA1_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B }, | ||||
| 	{PCC4_RBASE, RGPIOE_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B }, | ||||
| 	{PCC4_RBASE, RGPIOF_PCC4_SLOT,		CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B }, | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
| static int find_pcc_entry(int pcc_controller, int pcc_clk_slot, struct pcc_entry **out) | ||||
| { | ||||
| 	struct pcc_entry *pcc_array; | ||||
| 	int index = 0; | ||||
| 
 | ||||
| 	switch (pcc_controller) { | ||||
| 	case 3: | ||||
| 		pcc_array = pcc3_arrays; | ||||
| 		*out = &pcc3_arrays[0]; | ||||
| 		break; | ||||
| 	case 4: | ||||
| 		pcc_array = pcc4_arrays; | ||||
| 		*out = &pcc4_arrays[0]; | ||||
| 		break; | ||||
| 	default: | ||||
| 		printf("Not supported pcc_controller: %d\n", pcc_controller); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	while (pcc_array->pcc_base) { | ||||
| 		if (pcc_array->pcc_slot == pcc_clk_slot) | ||||
| 			return index; | ||||
| 
 | ||||
| 		pcc_array++; | ||||
| 		index++; | ||||
| 	} | ||||
| 
 | ||||
| 	return -ENOENT; | ||||
| } | ||||
| 
 | ||||
| int pcc_clock_enable(int pcc_controller, int pcc_clk_slot, bool enable) | ||||
| { | ||||
| 	u32 val; | ||||
| 	void __iomem *reg; | ||||
| 	int clk; | ||||
| 	struct pcc_entry *pcc_array; | ||||
| 
 | ||||
| 	clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array); | ||||
| 	if (clk < 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4); | ||||
| 
 | ||||
| 	val = readl(reg); | ||||
| 
 | ||||
| 	debug("%s: clk %d, reg 0x%p, val 0x%x, enable %d\n", __func__, clk, reg, val, enable); | ||||
| 
 | ||||
| 	if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK)) | ||||
| 		return -EPERM; | ||||
| 
 | ||||
| 	if (enable) | ||||
| 		val |= PCC_CGC_MASK; | ||||
| 	else | ||||
| 		val &= ~PCC_CGC_MASK; | ||||
| 
 | ||||
| 	writel(val, reg); | ||||
| 
 | ||||
| 	debug("%s: val 0x%x\n", __func__, val); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* The clock source select needs clock is disabled */ | ||||
| int pcc_clock_sel(int pcc_controller, int pcc_clk_slot, enum cgc1_clk src) | ||||
| { | ||||
| 	u32 val, i, clksrc_type; | ||||
| 	void __iomem *reg; | ||||
| 	struct pcc_entry *pcc_array; | ||||
| 	enum cgc1_clk *cgc1_clk_array; | ||||
| 	int clk; | ||||
| 
 | ||||
| 	clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array); | ||||
| 	if (clk < 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4); | ||||
| 
 | ||||
| 	clksrc_type = pcc_array[clk].clksrc; | ||||
| 	if (clksrc_type >= CLKSRC_NO_PCS) { | ||||
| 		printf("No PCS field for the PCC %d, clksrc type %d\n", | ||||
| 		       clk, clksrc_type); | ||||
| 		return -EPERM; | ||||
| 	} | ||||
| 
 | ||||
| 	if (pcc_controller == 3) | ||||
| 		cgc1_clk_array = pcc3_clksrc[clksrc_type]; | ||||
| 	else | ||||
| 		cgc1_clk_array = pcc4_clksrc[clksrc_type]; | ||||
| 
 | ||||
| 	for (i = 0; i < cgc1_clk_NUM; i++) { | ||||
| 		if (cgc1_clk_array[i] == src) { | ||||
| 			/* Find the clock src, then set it to PCS */ | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (i == cgc1_clk_NUM) { | ||||
| 		printf("No parent in PCS of PCC %d, invalid scg_clk %d\n", clk, src); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	val = readl(reg); | ||||
| 
 | ||||
| 	debug("%s: clk %d, reg 0x%p, val 0x%x, clksrc_type %d\n", | ||||
| 	      __func__, clk, reg, val, clksrc_type); | ||||
| 
 | ||||
| 	if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK) || | ||||
| 	    (val & PCC_CGC_MASK)) { | ||||
| 		printf("Not permit to select clock source val = 0x%x\n", val); | ||||
| 		return -EPERM; | ||||
| 	} | ||||
| 
 | ||||
| 	val &= ~PCC_PCS_MASK; | ||||
| 	val |= i << PCC_PCS_OFFSET; | ||||
| 
 | ||||
| 	writel(val, reg); | ||||
| 
 | ||||
| 	debug("%s: val 0x%x\n", __func__, val); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int pcc_clock_div_config(int pcc_controller, int pcc_clk_slot, bool frac, u8 div) | ||||
| { | ||||
| 	u32 val; | ||||
| 	void __iomem *reg; | ||||
| 	struct pcc_entry *pcc_array; | ||||
| 	int clk; | ||||
| 
 | ||||
| 	clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array); | ||||
| 	if (clk < 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4); | ||||
| 
 | ||||
| 	if (div > 8 || (div == 1 && frac != 0)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (pcc_array[clk].div >= PCC_NO_DIV) { | ||||
| 		printf("No DIV/FRAC field for the PCC %d\n", clk); | ||||
| 		return -EPERM; | ||||
| 	} | ||||
| 
 | ||||
| 	val = readl(reg); | ||||
| 
 | ||||
| 	if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK) || | ||||
| 	    (val & PCC_CGC_MASK)) { | ||||
| 		printf("Not permit to set div/frac val = 0x%x\n", val); | ||||
| 		return -EPERM; | ||||
| 	} | ||||
| 
 | ||||
| 	if (frac) | ||||
| 		val |= PCC_FRAC_MASK; | ||||
| 	else | ||||
| 		val &= ~PCC_FRAC_MASK; | ||||
| 
 | ||||
| 	val &= ~PCC_PCD_MASK; | ||||
| 	val |= (div - 1) & PCC_PCD_MASK; | ||||
| 
 | ||||
| 	writel(val, reg); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| bool pcc_clock_is_enable(int pcc_controller, int pcc_clk_slot) | ||||
| { | ||||
| 	u32 val; | ||||
| 	void __iomem *reg; | ||||
| 	struct pcc_entry *pcc_array; | ||||
| 	int clk; | ||||
| 
 | ||||
| 	clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array); | ||||
| 	if (clk < 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4); | ||||
| 	val = readl(reg); | ||||
| 
 | ||||
| 	if ((val & PCC_INUSE_MASK) || (val & PCC_CGC_MASK)) | ||||
| 		return true; | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| int pcc_clock_get_clksrc(int pcc_controller, int pcc_clk_slot, enum cgc1_clk *src) | ||||
| { | ||||
| 	u32 val, clksrc_type; | ||||
| 	void __iomem *reg; | ||||
| 	struct pcc_entry *pcc_array; | ||||
| 	int clk; | ||||
| 	enum cgc1_clk *cgc1_clk_array; | ||||
| 
 | ||||
| 	clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array); | ||||
| 	if (clk < 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	clksrc_type = pcc_array[clk].clksrc; | ||||
| 	if (clksrc_type >= CLKSRC_NO_PCS) { | ||||
| 		printf("No PCS field for the PCC %d, clksrc type %d\n", | ||||
| 		       pcc_clk_slot, clksrc_type); | ||||
| 		return -EPERM; | ||||
| 	} | ||||
| 
 | ||||
| 	reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4); | ||||
| 
 | ||||
| 	val = readl(reg); | ||||
| 
 | ||||
| 	debug("%s: clk %d, reg 0x%p, val 0x%x, type %d\n", | ||||
| 	      __func__, pcc_clk_slot, reg, val, clksrc_type); | ||||
| 
 | ||||
| 	if (!(val & PCC_PR_MASK)) { | ||||
| 		printf("This pcc slot is not present = 0x%x\n", val); | ||||
| 		return -EPERM; | ||||
| 	} | ||||
| 
 | ||||
| 	val &= PCC_PCS_MASK; | ||||
| 	val = (val >> PCC_PCS_OFFSET); | ||||
| 
 | ||||
| 	if (!val) { | ||||
| 		printf("Clock source is off\n"); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	if (pcc_controller == 3) | ||||
| 		cgc1_clk_array = pcc3_clksrc[clksrc_type]; | ||||
| 	else | ||||
| 		cgc1_clk_array = pcc4_clksrc[clksrc_type]; | ||||
| 
 | ||||
| 	*src = cgc1_clk_array[val]; | ||||
| 
 | ||||
| 	debug("%s: parent cgc1 clk %d\n", __func__, *src); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int pcc_reset_peripheral(int pcc_controller, int pcc_clk_slot, bool reset) | ||||
| { | ||||
| 	u32 val; | ||||
| 	void __iomem *reg; | ||||
| 	struct pcc_entry *pcc_array; | ||||
| 	int clk; | ||||
| 
 | ||||
| 	clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array); | ||||
| 	if (clk < 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (pcc_array[clk].rst_b == PCC_NO_RST_B) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4); | ||||
| 
 | ||||
| 	val = readl(reg); | ||||
| 
 | ||||
| 	debug("%s: clk %d, reg 0x%p, val 0x%x\n", __func__, pcc_clk_slot, reg, val); | ||||
| 
 | ||||
| 	if (!(val & PCC_PR_MASK)) { | ||||
| 		printf("This pcc slot is not present = 0x%x\n", val); | ||||
| 		return -EPERM; | ||||
| 	} | ||||
| 
 | ||||
| 	if (reset) | ||||
| 		val &= ~BIT(28); | ||||
| 	else | ||||
| 		val |= BIT(28); | ||||
| 
 | ||||
| 	writel(val, reg); | ||||
| 
 | ||||
| 	debug("%s: clk %d, reg 0x%p, val 0x%x\n", __func__, pcc_clk_slot, reg, val); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| u32 pcc_clock_get_rate(int pcc_controller, int pcc_clk_slot) | ||||
| { | ||||
| 	u32 val, rate, frac, div; | ||||
| 	void __iomem *reg; | ||||
| 	enum cgc1_clk parent; | ||||
| 	int ret; | ||||
| 	int clk; | ||||
| 	struct pcc_entry *pcc_array; | ||||
| 
 | ||||
| 	clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array); | ||||
| 	if (clk < 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	ret = pcc_clock_get_clksrc(pcc_controller, pcc_clk_slot, &parent); | ||||
| 	if (ret) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	rate = cgc1_clk_get_rate(parent); | ||||
| 
 | ||||
| 	debug("%s: parent rate %u\n", __func__, rate); | ||||
| 
 | ||||
| 	if (pcc_array[clk].div == PCC_HAS_DIV) { | ||||
| 		reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + | ||||
| 						  pcc_array[clk].pcc_slot * 4); | ||||
| 		val = readl(reg); | ||||
| 
 | ||||
| 		frac = (val & PCC_FRAC_MASK) >> PCC_FRAC_OFFSET; | ||||
| 		div = (val & PCC_PCD_MASK) >> PCC_PCD_OFFSET; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Theoretically don't have overflow in the calc, | ||||
| 		 * the rate won't exceed 2G | ||||
| 		 */ | ||||
| 		rate = rate * (frac + 1) / (div + 1); | ||||
| 	} | ||||
| 
 | ||||
| 	debug("%s: rate %u\n", __func__, rate); | ||||
| 	return rate; | ||||
| } | ||||
|  | @ -287,5 +287,8 @@ void get_board_serial(struct tag_serialnr *serialnr) | |||
| 
 | ||||
| int arch_cpu_init(void) | ||||
| { | ||||
| 	if (IS_ENABLED(CONFIG_SPL_BUILD)) | ||||
| 		clock_init(); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue