stm32mp1: clk: configure pll1 with OPP
The PLL1 node (st,pll1) is optional in device tree, the max supported frequency define in OPP node is used when the node is absent. Signed-off-by: Patrick Delaunay <patrick.delaunay@st.com> Reviewed-by: Patrice Chotard <patrice.chotard@st.com>
This commit is contained in:
		
							parent
							
								
									918e9c3d63
								
							
						
					
					
						commit
						37ad8377af
					
				|  | @ -87,6 +87,10 @@ Optional Properties: | ||||||
|   are listed with associated reg 0 to 3. |   are listed with associated reg 0 to 3. | ||||||
|   PLLx is off when the associated node is absent or deactivated. |   PLLx is off when the associated node is absent or deactivated. | ||||||
| 
 | 
 | ||||||
|  |   For PLL1, when the node is absent, the frequency of the OPP node is used | ||||||
|  |   to compute the PLL setting (see compatible "operating-points-v2" in | ||||||
|  |   opp/opp.txt for details). | ||||||
|  | 
 | ||||||
|   Here are the available properties for each PLL node: |   Here are the available properties for each PLL node: | ||||||
|     - compatible: should be "st,stm32mp1-pll" |     - compatible: should be "st,stm32mp1-pll" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ | ||||||
| #include <linux/bitops.h> | #include <linux/bitops.h> | ||||||
| #include <linux/io.h> | #include <linux/io.h> | ||||||
| #include <linux/iopoll.h> | #include <linux/iopoll.h> | ||||||
|  | #include <asm/arch/sys_proto.h> | ||||||
| #include <dt-bindings/clock/stm32mp1-clks.h> | #include <dt-bindings/clock/stm32mp1-clks.h> | ||||||
| #include <dt-bindings/clock/stm32mp1-clksrc.h> | #include <dt-bindings/clock/stm32mp1-clksrc.h> | ||||||
| 
 | 
 | ||||||
|  | @ -644,8 +645,18 @@ static const struct stm32mp1_clk_sel stm32mp1_clk_sel[_PARENT_SEL_NB] = { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #ifdef STM32MP1_CLOCK_TREE_INIT | #ifdef STM32MP1_CLOCK_TREE_INIT | ||||||
|  | 
 | ||||||
| /* define characteristic of PLL according type */ | /* define characteristic of PLL according type */ | ||||||
|  | #define DIVM_MIN	0 | ||||||
|  | #define DIVM_MAX	63 | ||||||
| #define DIVN_MIN	24 | #define DIVN_MIN	24 | ||||||
|  | #define DIVP_MIN	0 | ||||||
|  | #define DIVP_MAX	127 | ||||||
|  | #define FRAC_MAX	8192 | ||||||
|  | 
 | ||||||
|  | #define PLL1600_VCO_MIN	800000000 | ||||||
|  | #define PLL1600_VCO_MAX	1600000000 | ||||||
|  | 
 | ||||||
| static const struct stm32mp1_pll stm32mp1_pll[PLL_TYPE_NB] = { | static const struct stm32mp1_pll stm32mp1_pll[PLL_TYPE_NB] = { | ||||||
| 	[PLL_800] = { | 	[PLL_800] = { | ||||||
| 		.refclk_min = 4, | 		.refclk_min = 4, | ||||||
|  | @ -1190,6 +1201,208 @@ static ulong stm32mp1_clk_get_rate(struct clk *clk) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #ifdef STM32MP1_CLOCK_TREE_INIT | #ifdef STM32MP1_CLOCK_TREE_INIT | ||||||
|  | 
 | ||||||
|  | bool stm32mp1_supports_opp(u32 opp_id, u32 cpu_type) | ||||||
|  | { | ||||||
|  | 	unsigned int id; | ||||||
|  | 
 | ||||||
|  | 	switch (opp_id) { | ||||||
|  | 	case 1: | ||||||
|  | 	case 2: | ||||||
|  | 		id = opp_id; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		id = 1; /* default value */ | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	switch (cpu_type) { | ||||||
|  | 	case CPU_STM32MP157Fxx: | ||||||
|  | 	case CPU_STM32MP157Dxx: | ||||||
|  | 	case CPU_STM32MP153Fxx: | ||||||
|  | 	case CPU_STM32MP153Dxx: | ||||||
|  | 	case CPU_STM32MP151Fxx: | ||||||
|  | 	case CPU_STM32MP151Dxx: | ||||||
|  | 		return true; | ||||||
|  | 	default: | ||||||
|  | 		return id == 1; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * gets OPP parameters (frequency in KHz and voltage in mV) from | ||||||
|  |  * an OPP table subnode. Platform HW support capabilities are also checked. | ||||||
|  |  * Returns 0 on success and a negative FDT error code on failure. | ||||||
|  |  */ | ||||||
|  | static int stm32mp1_get_opp(u32 cpu_type, ofnode subnode, | ||||||
|  | 			    u32 *freq_khz, u32 *voltage_mv) | ||||||
|  | { | ||||||
|  | 	u32 opp_hw; | ||||||
|  | 	u64 read_freq_64; | ||||||
|  | 	u32 read_voltage_32; | ||||||
|  | 
 | ||||||
|  | 	*freq_khz = 0; | ||||||
|  | 	*voltage_mv = 0; | ||||||
|  | 
 | ||||||
|  | 	opp_hw = ofnode_read_u32_default(subnode, "opp-supported-hw", 0); | ||||||
|  | 	if (opp_hw) | ||||||
|  | 		if (!stm32mp1_supports_opp(opp_hw, cpu_type)) | ||||||
|  | 			return -FDT_ERR_BADVALUE; | ||||||
|  | 
 | ||||||
|  | 	read_freq_64 = ofnode_read_u64_default(subnode, "opp-hz", 0) / | ||||||
|  | 		       1000ULL; | ||||||
|  | 	read_voltage_32 = ofnode_read_u32_default(subnode, "opp-microvolt", 0) / | ||||||
|  | 			  1000U; | ||||||
|  | 
 | ||||||
|  | 	if (!read_voltage_32 || !read_freq_64) | ||||||
|  | 		return -FDT_ERR_NOTFOUND; | ||||||
|  | 
 | ||||||
|  | 	/* Frequency value expressed in KHz must fit on 32 bits */ | ||||||
|  | 	if (read_freq_64 > U32_MAX) | ||||||
|  | 		return -FDT_ERR_BADVALUE; | ||||||
|  | 
 | ||||||
|  | 	/* Millivolt value must fit on 16 bits */ | ||||||
|  | 	if (read_voltage_32 > U16_MAX) | ||||||
|  | 		return -FDT_ERR_BADVALUE; | ||||||
|  | 
 | ||||||
|  | 	*freq_khz = (u32)read_freq_64; | ||||||
|  | 	*voltage_mv = read_voltage_32; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * parses OPP table in DT and finds the parameters for the | ||||||
|  |  * highest frequency supported by the HW platform. | ||||||
|  |  * Returns 0 on success and a negative FDT error code on failure. | ||||||
|  |  */ | ||||||
|  | int stm32mp1_get_max_opp_freq(struct stm32mp1_clk_priv *priv, u64 *freq_hz) | ||||||
|  | { | ||||||
|  | 	ofnode node, subnode; | ||||||
|  | 	int ret; | ||||||
|  | 	u32 freq = 0U, voltage = 0U; | ||||||
|  | 	u32 cpu_type = get_cpu_type(); | ||||||
|  | 
 | ||||||
|  | 	node = ofnode_by_compatible(ofnode_null(), "operating-points-v2"); | ||||||
|  | 	if (!ofnode_valid(node)) | ||||||
|  | 		return -FDT_ERR_NOTFOUND; | ||||||
|  | 
 | ||||||
|  | 	ofnode_for_each_subnode(subnode, node) { | ||||||
|  | 		unsigned int read_freq; | ||||||
|  | 		unsigned int read_voltage; | ||||||
|  | 
 | ||||||
|  | 		ret = stm32mp1_get_opp(cpu_type, subnode, | ||||||
|  | 				       &read_freq, &read_voltage); | ||||||
|  | 		if (ret) | ||||||
|  | 			continue; | ||||||
|  | 
 | ||||||
|  | 		if (read_freq > freq) { | ||||||
|  | 			freq = read_freq; | ||||||
|  | 			voltage = read_voltage; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!freq || !voltage) | ||||||
|  | 		return -FDT_ERR_NOTFOUND; | ||||||
|  | 
 | ||||||
|  | 	*freq_hz = (u64)1000U * freq; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int stm32mp1_pll1_opp(struct stm32mp1_clk_priv *priv, int clksrc, | ||||||
|  | 			     u32 *pllcfg, u32 *fracv) | ||||||
|  | { | ||||||
|  | 	u32 post_divm; | ||||||
|  | 	u32 input_freq; | ||||||
|  | 	u64 output_freq; | ||||||
|  | 	u64 freq; | ||||||
|  | 	u64 vco; | ||||||
|  | 	u32 divm, divn, divp, frac; | ||||||
|  | 	int i, ret; | ||||||
|  | 	u32 diff; | ||||||
|  | 	u32 best_diff = U32_MAX; | ||||||
|  | 
 | ||||||
|  | 	/* PLL1 is 1600 */ | ||||||
|  | 	const u32 DIVN_MAX = stm32mp1_pll[PLL_1600].divn_max; | ||||||
|  | 	const u32 POST_DIVM_MIN = stm32mp1_pll[PLL_1600].refclk_min * 1000000U; | ||||||
|  | 	const u32 POST_DIVM_MAX = stm32mp1_pll[PLL_1600].refclk_max * 1000000U; | ||||||
|  | 
 | ||||||
|  | 	ret = stm32mp1_get_max_opp_freq(priv, &output_freq); | ||||||
|  | 	if (ret) { | ||||||
|  | 		debug("PLL1 OPP configuration not found (%d).\n", ret); | ||||||
|  | 		return ret; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	switch (clksrc) { | ||||||
|  | 	case CLK_PLL12_HSI: | ||||||
|  | 		input_freq = stm32mp1_clk_get_fixed(priv, _HSI); | ||||||
|  | 		break; | ||||||
|  | 	case CLK_PLL12_HSE: | ||||||
|  | 		input_freq = stm32mp1_clk_get_fixed(priv, _HSE); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		return -EINTR; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* Following parameters have always the same value */ | ||||||
|  | 	pllcfg[PLLCFG_Q] = 0; | ||||||
|  | 	pllcfg[PLLCFG_R] = 0; | ||||||
|  | 	pllcfg[PLLCFG_O] = PQR(1, 0, 0); | ||||||
|  | 
 | ||||||
|  | 	for (divm = DIVM_MAX; divm >= DIVM_MIN; divm--)	{ | ||||||
|  | 		post_divm = (u32)(input_freq / (divm + 1)); | ||||||
|  | 		if (post_divm < POST_DIVM_MIN || post_divm > POST_DIVM_MAX) | ||||||
|  | 			continue; | ||||||
|  | 
 | ||||||
|  | 		for (divp = DIVP_MIN; divp <= DIVP_MAX; divp++) { | ||||||
|  | 			freq = output_freq * (divm + 1) * (divp + 1); | ||||||
|  | 			divn = (u32)((freq / input_freq) - 1); | ||||||
|  | 			if (divn < DIVN_MIN || divn > DIVN_MAX) | ||||||
|  | 				continue; | ||||||
|  | 
 | ||||||
|  | 			frac = (u32)(((freq * FRAC_MAX) / input_freq) - | ||||||
|  | 				     ((divn + 1) * FRAC_MAX)); | ||||||
|  | 			/* 2 loops to refine the fractional part */ | ||||||
|  | 			for (i = 2; i != 0; i--) { | ||||||
|  | 				if (frac > FRAC_MAX) | ||||||
|  | 					break; | ||||||
|  | 
 | ||||||
|  | 				vco = (post_divm * (divn + 1)) + | ||||||
|  | 				      ((post_divm * (u64)frac) / | ||||||
|  | 				       FRAC_MAX); | ||||||
|  | 				if (vco < (PLL1600_VCO_MIN / 2) || | ||||||
|  | 				    vco > (PLL1600_VCO_MAX / 2)) { | ||||||
|  | 					frac++; | ||||||
|  | 					continue; | ||||||
|  | 				} | ||||||
|  | 				freq = vco / (divp + 1); | ||||||
|  | 				if (output_freq < freq) | ||||||
|  | 					diff = (u32)(freq - output_freq); | ||||||
|  | 				else | ||||||
|  | 					diff = (u32)(output_freq - freq); | ||||||
|  | 				if (diff < best_diff)  { | ||||||
|  | 					pllcfg[PLLCFG_M] = divm; | ||||||
|  | 					pllcfg[PLLCFG_N] = divn; | ||||||
|  | 					pllcfg[PLLCFG_P] = divp; | ||||||
|  | 					*fracv = frac; | ||||||
|  | 
 | ||||||
|  | 					if (diff == 0) | ||||||
|  | 						return 0; | ||||||
|  | 
 | ||||||
|  | 					best_diff = diff; | ||||||
|  | 				} | ||||||
|  | 				frac++; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (best_diff == U32_MAX) | ||||||
|  | 		return -1; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void stm32mp1_ls_osc_set(int enable, fdt_addr_t rcc, u32 offset, | static void stm32mp1_ls_osc_set(int enable, fdt_addr_t rcc, u32 offset, | ||||||
| 				u32 mask_on) | 				u32 mask_on) | ||||||
| { | { | ||||||
|  | @ -1661,9 +1874,12 @@ static int stm32mp1_clktree(struct udevice *dev) | ||||||
| 	unsigned int clksrc[CLKSRC_NB]; | 	unsigned int clksrc[CLKSRC_NB]; | ||||||
| 	unsigned int clkdiv[CLKDIV_NB]; | 	unsigned int clkdiv[CLKDIV_NB]; | ||||||
| 	unsigned int pllcfg[_PLL_NB][PLLCFG_NB]; | 	unsigned int pllcfg[_PLL_NB][PLLCFG_NB]; | ||||||
| 	ofnode plloff[_PLL_NB]; | 	unsigned int pllfracv[_PLL_NB]; | ||||||
| 	int ret, len; | 	unsigned int pllcsg[_PLL_NB][PLLCSG_NB]; | ||||||
| 	uint i; | 	bool pllcfg_valid[_PLL_NB]; | ||||||
|  | 	bool pllcsg_set[_PLL_NB]; | ||||||
|  | 	int ret; | ||||||
|  | 	int i, len; | ||||||
| 	int lse_css = 0; | 	int lse_css = 0; | ||||||
| 	const u32 *pkcs_cell; | 	const u32 *pkcs_cell; | ||||||
| 
 | 
 | ||||||
|  | @ -1683,16 +1899,43 @@ static int stm32mp1_clktree(struct udevice *dev) | ||||||
| 	/* check mandatory field in each pll */ | 	/* check mandatory field in each pll */ | ||||||
| 	for (i = 0; i < _PLL_NB; i++) { | 	for (i = 0; i < _PLL_NB; i++) { | ||||||
| 		char name[12]; | 		char name[12]; | ||||||
|  | 		ofnode node; | ||||||
| 
 | 
 | ||||||
| 		sprintf(name, "st,pll@%d", i); | 		sprintf(name, "st,pll@%d", i); | ||||||
| 		plloff[i] = dev_read_subnode(dev, name); | 		node = dev_read_subnode(dev, name); | ||||||
| 		if (!ofnode_valid(plloff[i])) | 		pllcfg_valid[i] = ofnode_valid(node); | ||||||
| 			continue; | 		pllcsg_set[i] = false; | ||||||
| 		ret = ofnode_read_u32_array(plloff[i], "cfg", | 		if (pllcfg_valid[i]) { | ||||||
| 					    pllcfg[i], PLLCFG_NB); | 			debug("DT for PLL %d @ %s\n", i, name); | ||||||
| 		if (ret < 0) { | 			ret = ofnode_read_u32_array(node, "cfg", | ||||||
| 			debug("field cfg invalid: error %d\n", ret); | 						    pllcfg[i], PLLCFG_NB); | ||||||
| 			return -FDT_ERR_NOTFOUND; | 			if (ret < 0) { | ||||||
|  | 				debug("field cfg invalid: error %d\n", ret); | ||||||
|  | 				return -FDT_ERR_NOTFOUND; | ||||||
|  | 			} | ||||||
|  | 			pllfracv[i] = ofnode_read_u32_default(node, "frac", 0); | ||||||
|  | 
 | ||||||
|  | 			ret = ofnode_read_u32_array(node, "csg", pllcsg[i], | ||||||
|  | 						    PLLCSG_NB); | ||||||
|  | 			if (!ret) { | ||||||
|  | 				pllcsg_set[i] = true; | ||||||
|  | 			} else if (ret != -FDT_ERR_NOTFOUND) { | ||||||
|  | 				debug("invalid csg node for pll@%d res=%d\n", | ||||||
|  | 				      i, ret); | ||||||
|  | 				return ret; | ||||||
|  | 			} | ||||||
|  | 		} else if (i == _PLL1)	{ | ||||||
|  | 			/* use OPP for PLL1 for A7 CPU */ | ||||||
|  | 			debug("DT for PLL %d with OPP\n", i); | ||||||
|  | 			ret = stm32mp1_pll1_opp(priv, | ||||||
|  | 						clksrc[CLKSRC_PLL12], | ||||||
|  | 						pllcfg[i], | ||||||
|  | 						&pllfracv[i]); | ||||||
|  | 			if (ret) { | ||||||
|  | 				debug("PLL %d with OPP error = %d\n", i, ret); | ||||||
|  | 				return ret; | ||||||
|  | 			} | ||||||
|  | 			pllcfg_valid[i] = true; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -1778,29 +2021,18 @@ static int stm32mp1_clktree(struct udevice *dev) | ||||||
| 	/* configure and start PLLs */ | 	/* configure and start PLLs */ | ||||||
| 	debug("configure PLLs\n"); | 	debug("configure PLLs\n"); | ||||||
| 	for (i = 0; i < _PLL_NB; i++) { | 	for (i = 0; i < _PLL_NB; i++) { | ||||||
| 		u32 fracv; | 		if (!pllcfg_valid[i]) | ||||||
| 		u32 csg[PLLCSG_NB]; |  | ||||||
| 
 |  | ||||||
| 		debug("configure PLL %d @ %d\n", i, |  | ||||||
| 		      ofnode_to_offset(plloff[i])); |  | ||||||
| 		if (!ofnode_valid(plloff[i])) |  | ||||||
| 			continue; | 			continue; | ||||||
| 
 | 		debug("configure PLL %d\n", i); | ||||||
| 		fracv = ofnode_read_u32_default(plloff[i], "frac", 0); | 		pll_config(priv, i, pllcfg[i], pllfracv[i]); | ||||||
| 		pll_config(priv, i, pllcfg[i], fracv); | 		if (pllcsg_set[i]) | ||||||
| 		ret = ofnode_read_u32_array(plloff[i], "csg", csg, PLLCSG_NB); | 			pll_csg(priv, i, pllcsg[i]); | ||||||
| 		if (!ret) { |  | ||||||
| 			pll_csg(priv, i, csg); |  | ||||||
| 		} else if (ret != -FDT_ERR_NOTFOUND) { |  | ||||||
| 			debug("invalid csg node for pll@%d res=%d\n", i, ret); |  | ||||||
| 			return ret; |  | ||||||
| 		} |  | ||||||
| 		pll_start(priv, i); | 		pll_start(priv, i); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* wait and start PLLs ouptut when ready */ | 	/* wait and start PLLs ouptut when ready */ | ||||||
| 	for (i = 0; i < _PLL_NB; i++) { | 	for (i = 0; i < _PLL_NB; i++) { | ||||||
| 		if (!ofnode_valid(plloff[i])) | 		if (!pllcfg_valid[i]) | ||||||
| 			continue; | 			continue; | ||||||
| 		debug("output PLL %d\n", i); | 		debug("output PLL %d\n", i); | ||||||
| 		pll_output(priv, i, pllcfg[i][PLLCFG_O]); | 		pll_output(priv, i, pllcfg[i][PLLCFG_O]); | ||||||
|  | @ -2050,6 +2282,8 @@ static int stm32mp1_clk_probe(struct udevice *dev) | ||||||
| 	/* clock tree init is done only one time, before relocation */ | 	/* clock tree init is done only one time, before relocation */ | ||||||
| 	if (!(gd->flags & GD_FLG_RELOC)) | 	if (!(gd->flags & GD_FLG_RELOC)) | ||||||
| 		result = stm32mp1_clktree(dev); | 		result = stm32mp1_clktree(dev); | ||||||
|  | 	if (result) | ||||||
|  | 		printf("clock tree initialization failed (%d)\n", result); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifndef CONFIG_SPL_BUILD | #ifndef CONFIG_SPL_BUILD | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue