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:
Patrick Delaunay 2020-05-25 12:19:44 +02:00
parent 918e9c3d63
commit 37ad8377af
2 changed files with 266 additions and 28 deletions

View File

@ -87,6 +87,10 @@ Optional Properties:
are listed with associated reg 0 to 3.
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:
- compatible: should be "st,stm32mp1-pll"

View File

@ -17,6 +17,7 @@
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <asm/arch/sys_proto.h>
#include <dt-bindings/clock/stm32mp1-clks.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
/* define characteristic of PLL according type */
#define DIVM_MIN 0
#define DIVM_MAX 63
#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] = {
[PLL_800] = {
.refclk_min = 4,
@ -1190,6 +1201,208 @@ static ulong stm32mp1_clk_get_rate(struct clk *clk)
}
#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,
u32 mask_on)
{
@ -1661,9 +1874,12 @@ static int stm32mp1_clktree(struct udevice *dev)
unsigned int clksrc[CLKSRC_NB];
unsigned int clkdiv[CLKDIV_NB];
unsigned int pllcfg[_PLL_NB][PLLCFG_NB];
ofnode plloff[_PLL_NB];
int ret, len;
uint i;
unsigned int pllfracv[_PLL_NB];
unsigned int pllcsg[_PLL_NB][PLLCSG_NB];
bool pllcfg_valid[_PLL_NB];
bool pllcsg_set[_PLL_NB];
int ret;
int i, len;
int lse_css = 0;
const u32 *pkcs_cell;
@ -1683,16 +1899,43 @@ static int stm32mp1_clktree(struct udevice *dev)
/* check mandatory field in each pll */
for (i = 0; i < _PLL_NB; i++) {
char name[12];
ofnode node;
sprintf(name, "st,pll@%d", i);
plloff[i] = dev_read_subnode(dev, name);
if (!ofnode_valid(plloff[i]))
continue;
ret = ofnode_read_u32_array(plloff[i], "cfg",
pllcfg[i], PLLCFG_NB);
if (ret < 0) {
debug("field cfg invalid: error %d\n", ret);
return -FDT_ERR_NOTFOUND;
node = dev_read_subnode(dev, name);
pllcfg_valid[i] = ofnode_valid(node);
pllcsg_set[i] = false;
if (pllcfg_valid[i]) {
debug("DT for PLL %d @ %s\n", i, name);
ret = ofnode_read_u32_array(node, "cfg",
pllcfg[i], PLLCFG_NB);
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 */
debug("configure PLLs\n");
for (i = 0; i < _PLL_NB; i++) {
u32 fracv;
u32 csg[PLLCSG_NB];
debug("configure PLL %d @ %d\n", i,
ofnode_to_offset(plloff[i]));
if (!ofnode_valid(plloff[i]))
if (!pllcfg_valid[i])
continue;
fracv = ofnode_read_u32_default(plloff[i], "frac", 0);
pll_config(priv, i, pllcfg[i], fracv);
ret = ofnode_read_u32_array(plloff[i], "csg", csg, PLLCSG_NB);
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;
}
debug("configure PLL %d\n", i);
pll_config(priv, i, pllcfg[i], pllfracv[i]);
if (pllcsg_set[i])
pll_csg(priv, i, pllcsg[i]);
pll_start(priv, i);
}
/* wait and start PLLs ouptut when ready */
for (i = 0; i < _PLL_NB; i++) {
if (!ofnode_valid(plloff[i]))
if (!pllcfg_valid[i])
continue;
debug("output PLL %d\n", i);
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 */
if (!(gd->flags & GD_FLG_RELOC))
result = stm32mp1_clktree(dev);
if (result)
printf("clock tree initialization failed (%d)\n", result);
#endif
#ifndef CONFIG_SPL_BUILD