diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/clk_stm32f.c | 105 |
1 files changed, 74 insertions, 31 deletions
diff --git a/drivers/clk/clk_stm32f.c b/drivers/clk/clk_stm32f.c index e8f6b47acc1..a687e2acd40 100644 --- a/drivers/clk/clk_stm32f.c +++ b/drivers/clk/clk_stm32f.c @@ -57,8 +57,12 @@ #define RCC_PLLSAICFGR_PLLSAIN_MASK GENMASK(14, 6) #define RCC_PLLSAICFGR_PLLSAIP_MASK GENMASK(17, 16) +#define RCC_PLLSAICFGR_PLLSAIQ_MASK GENMASK(27, 24) +#define RCC_PLLSAICFGR_PLLSAIR_MASK GENMASK(30, 28) #define RCC_PLLSAICFGR_PLLSAIN_SHIFT 6 #define RCC_PLLSAICFGR_PLLSAIP_SHIFT 16 +#define RCC_PLLSAICFGR_PLLSAIQ_SHIFT 24 +#define RCC_PLLSAICFGR_PLLSAIR_SHIFT 28 #define RCC_PLLSAICFGR_PLLSAIP_4 BIT(16) #define RCC_PLLSAICFGR_PLLSAIQ_4 BIT(26) #define RCC_PLLSAICFGR_PLLSAIR_2 BIT(29) @@ -87,6 +91,12 @@ #define RCC_APB2ENR_SYSCFGEN BIT(14) #define RCC_APB2ENR_SAI1EN BIT(22) +enum pllsai_div { + PLLSAIP, + PLLSAIQ, + PLLSAIR, +}; + static const struct stm32_clk_info stm32f4_clk_info = { /* 180 MHz */ .sys_pll_psc = { @@ -216,32 +226,57 @@ static int configure_clocks(struct udevice *dev) return 0; } -static unsigned long stm32_clk_pll48clk_rate(struct stm32_clk *priv, - u32 vco) +static bool stm32_clk_get_ck48msel(struct stm32_clk *priv) { struct stm32_rcc_regs *regs = priv->base; - u16 pllq, pllm, pllsain, pllsaip; - bool pllsai; - - pllq = (readl(®s->pllcfgr) & RCC_PLLCFGR_PLLQ_MASK) - >> RCC_PLLCFGR_PLLQ_SHIFT; if (priv->info.v2) /*stm32f7 case */ - pllsai = readl(®s->dckcfgr2) & RCC_DCKCFGRX_CK48MSEL; + return readl(®s->dckcfgr2) & RCC_DCKCFGRX_CK48MSEL; else - pllsai = readl(®s->dckcfgr) & RCC_DCKCFGRX_CK48MSEL; - if (pllsai) { - /* PLL48CLK is selected from PLLSAI, get PLLSAI value */ - pllm = (readl(®s->pllcfgr) & RCC_PLLCFGR_PLLM_MASK); - pllsain = ((readl(®s->pllsaicfgr) & RCC_PLLSAICFGR_PLLSAIN_MASK) - >> RCC_PLLSAICFGR_PLLSAIN_SHIFT); - pllsaip = ((((readl(®s->pllsaicfgr) & RCC_PLLSAICFGR_PLLSAIP_MASK) - >> RCC_PLLSAICFGR_PLLSAIP_SHIFT) + 1) << 1); - return ((priv->hse_rate / pllm) * pllsain) / pllsaip; + return readl(®s->dckcfgr) & RCC_DCKCFGRX_CK48MSEL; +} + +static unsigned long stm32_clk_get_pllsai_vco_rate(struct stm32_clk *priv) +{ + struct stm32_rcc_regs *regs = priv->base; + u16 pllm, pllsain; + + pllm = (readl(®s->pllcfgr) & RCC_PLLCFGR_PLLM_MASK); + pllsain = ((readl(®s->pllsaicfgr) & RCC_PLLSAICFGR_PLLSAIN_MASK) + >> RCC_PLLSAICFGR_PLLSAIN_SHIFT); + + return ((priv->hse_rate / pllm) * pllsain); +} + +static unsigned long stm32_clk_get_pllsai_rate(struct stm32_clk *priv, + enum pllsai_div output) +{ + struct stm32_rcc_regs *regs = priv->base; + u16 pll_div_output; + + switch (output) { + case PLLSAIP: + pll_div_output = ((((readl(®s->pllsaicfgr) + & RCC_PLLSAICFGR_PLLSAIP_MASK) + >> RCC_PLLSAICFGR_PLLSAIP_SHIFT) + 1) << 1); + break; + case PLLSAIQ: + pll_div_output = (readl(®s->pllsaicfgr) + & RCC_PLLSAICFGR_PLLSAIQ_MASK) + >> RCC_PLLSAICFGR_PLLSAIQ_SHIFT; + break; + case PLLSAIR: + pll_div_output = (readl(®s->pllsaicfgr) + & RCC_PLLSAICFGR_PLLSAIR_MASK) + >> RCC_PLLSAICFGR_PLLSAIR_SHIFT; + break; + default: + pr_err("incorrect PLLSAI output %d\n", output); + return -EINVAL; } - /* PLL48CLK is selected from PLLQ */ - return vco / pllq; + + return (stm32_clk_get_pllsai_vco_rate(priv) / pll_div_output); } static bool stm32_get_timpre(struct stm32_clk *priv) @@ -325,7 +360,8 @@ static ulong stm32_clk_get_rate(struct clk *clk) struct stm32_rcc_regs *regs = priv->base; u32 sysclk = 0; u32 vco; - u16 pllm, plln, pllp; + u32 sdmmcxsel_bit; + u16 pllm, plln, pllp, pllq; if ((readl(®s->cfgr) & RCC_CFGR_SWS_MASK) == RCC_CFGR_SWS_PLL) { @@ -334,6 +370,8 @@ static ulong stm32_clk_get_rate(struct clk *clk) >> RCC_PLLCFGR_PLLN_SHIFT); pllp = ((((readl(®s->pllcfgr) & RCC_PLLCFGR_PLLP_MASK) >> RCC_PLLCFGR_PLLP_SHIFT) + 1) << 1); + pllq = ((readl(®s->pllcfgr) & RCC_PLLCFGR_PLLQ_MASK) + >> RCC_PLLCFGR_PLLQ_SHIFT); vco = (priv->hse_rate / pllm) * plln; sysclk = vco / pllp; } else { @@ -366,25 +404,30 @@ static ulong stm32_clk_get_rate(struct clk *clk) /* APB2 CLOCK */ case STM32F7_APB2_CLOCK(TIM1) ... STM32F7_APB2_CLOCK(LTDC): + switch (clk->id) { /* * particular case for SDMMC1 and SDMMC2 : * 48Mhz source clock can be from main PLL or from - * SAI PLL + * PLLSAIP */ - switch (clk->id) { case STM32F7_APB2_CLOCK(SDMMC1): - if (readl(®s->dckcfgr2) & RCC_DCKCFGRX_SDMMC1SEL) - /* System clock is selected as SDMMC1 clock */ - return sysclk; - else - return stm32_clk_pll48clk_rate(priv, vco); - break; case STM32F7_APB2_CLOCK(SDMMC2): - if (readl(®s->dckcfgr2) & RCC_DCKCFGR2_SDMMC2SEL) - /* System clock is selected as SDMMC2 clock */ + if (clk->id == STM32F7_APB2_CLOCK(SDMMC1)) + sdmmcxsel_bit = RCC_DCKCFGRX_SDMMC1SEL; + else + sdmmcxsel_bit = RCC_DCKCFGR2_SDMMC2SEL; + + if (readl(®s->dckcfgr2) & sdmmcxsel_bit) + /* System clock is selected as SDMMC1 clock */ return sysclk; + /* + * 48 MHz can be generated by either PLLSAIP + * or by PLLQ depending of CK48MSEL bit of RCC_DCKCFGR + */ + if (stm32_clk_get_ck48msel(priv)) + return stm32_clk_get_pllsai_rate(priv, PLLSAIP); else - return stm32_clk_pll48clk_rate(priv, vco); + return (vco / pllq); break; /* For timer clock, an additionnal prescaler is used*/ |