diff options
author | Patrice Chotard <patrice.chotard@st.com> | 2018-02-07 12:44:47 +0300 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2018-03-14 04:45:37 +0300 |
commit | b43679482b06d476d908033b176f64c156f69b5e (patch) | |
tree | 8f658cdb20d049fa55a7925d2583cb8f020891e0 /drivers/clk | |
parent | 61803a95a1e702275afe372422a97e94c655fd9d (diff) | |
download | u-boot-b43679482b06d476d908033b176f64c156f69b5e.tar.xz |
clk: clk_stm32h7: Fix stm32_clk_get_rate() for timer
For timer clock, an additional prescaler is used which
was not taken into account previously.
Signed-off-by: Patrice Chotard <patrice.chotard@st.com>
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/clk_stm32h7.c | 102 |
1 files changed, 90 insertions, 12 deletions
diff --git a/drivers/clk/clk_stm32h7.c b/drivers/clk/clk_stm32h7.c index c9594d405a..92db71431e 100644 --- a/drivers/clk/clk_stm32h7.c +++ b/drivers/clk/clk_stm32h7.c @@ -35,6 +35,7 @@ DECLARE_GLOBAL_DATA_PTR; #define RCC_CFGR_SW_CSI 1 #define RCC_CFGR_SW_HSE 2 #define RCC_CFGR_SW_PLL1 3 +#define RCC_CFGR_TIMPRE BIT(15) #define RCC_PLLCKSELR_PLLSRC_HSI 0 #define RCC_PLLCKSELR_PLLSRC_CSI 1 @@ -339,6 +340,11 @@ struct pll_psc sys_pll_psc = { .divr = 2, }; +enum apb { + APB1, + APB2, +}; + int configure_clocks(struct udevice *dev) { struct stm32_clk *priv = dev_get_priv(dev); @@ -562,6 +568,67 @@ static u32 stm32_get_PLL1_rate(struct stm32_rcc_regs *regs, return -EINVAL; } +static u32 stm32_get_apb_psc(struct stm32_rcc_regs *regs, enum apb apb) +{ + u16 prescaler_table[8] = {2, 4, 8, 16, 64, 128, 256, 512}; + u32 d2cfgr = readl(®s->d2cfgr); + + if (apb == APB1) { + if (d2cfgr & RCC_D2CFGR_D2PPRE1_DIVIDED) + /* get D2 domain APB1 prescaler */ + return prescaler_table[ + ((d2cfgr & RCC_D2CFGR_D2PPRE1_DIVIDER) + >> RCC_D2CFGR_D2PPRE1_SHIFT)]; + } else { /* APB2 */ + if (d2cfgr & RCC_D2CFGR_D2PPRE2_DIVIDED) + /* get D2 domain APB2 prescaler */ + return prescaler_table[ + ((d2cfgr & RCC_D2CFGR_D2PPRE2_DIVIDER) + >> RCC_D2CFGR_D2PPRE2_SHIFT)]; + } + + return 1; +}; + +static u32 stm32_get_timer_rate(struct stm32_clk *priv, u32 sysclk, + enum apb apb) +{ + struct stm32_rcc_regs *regs = priv->rcc_base; +u32 psc = stm32_get_apb_psc(regs, apb); + + if (readl(®s->cfgr) & RCC_CFGR_TIMPRE) + /* + * if APB prescaler is configured to a + * division factor of 1, 2 or 4 + */ + switch (psc) { + case 1: + case 2: + case 4: + return sysclk; + case 8: + return sysclk / 2; + case 16: + return sysclk / 4; + default: + pr_err("unexpected prescaler value (%d)\n", psc); + return 0; + } + else + switch (psc) { + case 1: + return sysclk; + case 2: + case 4: + case 8: + case 16: + return sysclk / psc; + default: + pr_err("unexpected prescaler value (%d)\n", psc); + return 0; + } +}; + static ulong stm32_clk_get_rate(struct clk *clk) { struct stm32_clk *priv = dev_get_priv(clk->dev); @@ -660,31 +727,42 @@ static ulong stm32_clk_get_rate(struct clk *clk) case RCC_APB1LENR: case RCC_APB1HENR: - if (d1cfgr & RCC_D2CFGR_D2PPRE1_DIVIDED) { - /* get D2 domain APB1 prescaler */ - idx = (d1cfgr & RCC_D2CFGR_D2PPRE1_DIVIDER) >> - RCC_D2CFGR_D2PPRE1_SHIFT; - sysclk = sysclk / prescaler_table[idx]; + /* special case for GPT timers */ + switch (clk->id) { + case TIM14_CK: + case TIM13_CK: + case TIM12_CK: + case TIM7_CK: + case TIM6_CK: + case TIM5_CK: + case TIM4_CK: + case TIM3_CK: + case TIM2_CK: + return stm32_get_timer_rate(priv, sysclk, APB1); } debug("%s system clock: freq after APB1 prescaler = %ld\n", __func__, sysclk); - return sysclk; + return (sysclk / stm32_get_apb_psc(regs, APB1)); break; case RCC_APB2ENR: - if (d1cfgr & RCC_D2CFGR_D2PPRE2_DIVIDED) { - /* get D2 domain APB1 prescaler */ - idx = (d1cfgr & RCC_D2CFGR_D2PPRE2_DIVIDER) >> - RCC_D2CFGR_D2PPRE2_SHIFT; - sysclk = sysclk / prescaler_table[idx]; + /* special case for timers */ + switch (clk->id) { + case TIM17_CK: + case TIM16_CK: + case TIM15_CK: + case TIM8_CK: + case TIM1_CK: + return stm32_get_timer_rate(priv, sysclk, APB2); } debug("%s system clock: freq after APB2 prescaler = %ld\n", __func__, sysclk); - return sysclk; + return (sysclk / stm32_get_apb_psc(regs, APB2)); + break; default: |