clk: clk_stm32f: Fix stm32_clk_get_rate() for timer
For timer clock, an additionnal prescaler is used which was
not taken into account previously.
Signed-off-by: Patrice Chotard <patrice.chotard@st.com>
diff --git a/drivers/clk/clk_stm32f.c b/drivers/clk/clk_stm32f.c
index 926b249..bde3160 100644
--- a/drivers/clk/clk_stm32f.c
+++ b/drivers/clk/clk_stm32f.c
@@ -63,6 +63,7 @@
#define RCC_PLLSAICFGR_PLLSAIQ_4 BIT(26)
#define RCC_PLLSAICFGR_PLLSAIR_2 BIT(29)
+#define RCC_DCKCFGRX_TIMPRE BIT(24)
#define RCC_DCKCFGRX_CK48MSEL BIT(27)
#define RCC_DCKCFGRX_SDMMC1SEL BIT(28)
#define RCC_DCKCFGR2_SDMMC2SEL BIT(29)
@@ -260,21 +261,88 @@
return sysclk / pllq;
}
-static unsigned long stm32_clk_get_rate(struct clk *clk)
+static bool stm32_get_timpre(struct stm32_clk *priv)
{
- struct stm32_clk *priv = dev_get_priv(clk->dev);
struct stm32_rcc_regs *regs = priv->base;
- u32 sysclk = 0;
- u32 shift = 0;
- u16 pllm, plln, pllp;
+ u32 val;
+
+ if (priv->info.v2) /*stm32f7 case */
+ val = readl(®s->dckcfgr2);
+ else
+ val = readl(®s->dckcfgr);
+ /* get timer prescaler */
+ return !!(val & RCC_DCKCFGRX_TIMPRE);
+}
+
+static u32 stm32_get_hclk_rate(struct stm32_rcc_regs *regs, u32 sysclk)
+{
+ u8 shift;
/* Prescaler table lookups for clock computation */
u8 ahb_psc_table[16] = {
0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9
};
+
+ shift = ahb_psc_table[(
+ (readl(®s->cfgr) & RCC_CFGR_AHB_PSC_MASK)
+ >> RCC_CFGR_HPRE_SHIFT)];
+
+ return sysclk >> shift;
+};
+
+static u8 stm32_get_apb_shift(struct stm32_rcc_regs *regs, enum apb apb)
+{
+ /* Prescaler table lookups for clock computation */
u8 apb_psc_table[8] = {
0, 0, 0, 0, 1, 2, 3, 4
};
+ if (apb == APB1)
+ return apb_psc_table[(
+ (readl(®s->cfgr) & RCC_CFGR_APB1_PSC_MASK)
+ >> RCC_CFGR_PPRE1_SHIFT)];
+ else /* APB2 */
+ return apb_psc_table[(
+ (readl(®s->cfgr) & RCC_CFGR_APB2_PSC_MASK)
+ >> RCC_CFGR_PPRE2_SHIFT)];
+};
+
+static u32 stm32_get_timer_rate(struct stm32_clk *priv, u32 sysclk,
+ enum apb apb)
+{
+ struct stm32_rcc_regs *regs = priv->base;
+ u8 shift = stm32_get_apb_shift(regs, apb);
+
+ if (stm32_get_timpre(priv))
+ /*
+ * if APB prescaler is configured to a
+ * division factor of 1, 2 or 4
+ */
+ switch (shift) {
+ case 0:
+ case 1:
+ case 2:
+ return stm32_get_hclk_rate(regs, sysclk);
+ default:
+ return (sysclk >> shift) * 4;
+ }
+ else
+ /*
+ * if APB prescaler is configured to a
+ * division factor of 1
+ */
+ if (shift == 0)
+ return sysclk;
+ else
+ return (sysclk >> shift) * 2;
+};
+
+static ulong stm32_clk_get_rate(struct clk *clk)
+{
+ struct stm32_clk *priv = dev_get_priv(clk->dev);
+ struct stm32_rcc_regs *regs = priv->base;
+ u32 sysclk = 0;
+ u16 pllm, plln, pllp;
+
if ((readl(®s->cfgr) & RCC_CFGR_SWS_MASK) ==
RCC_CFGR_SWS_PLL) {
pllm = (readl(®s->pllcfgr) & RCC_PLLCFGR_PLLM_MASK);
@@ -293,16 +361,24 @@
* AHB1, AHB2 and AHB3
*/
case STM32F7_AHB1_CLOCK(GPIOA) ... STM32F7_AHB3_CLOCK(QSPI):
- shift = ahb_psc_table[(
- (readl(®s->cfgr) & RCC_CFGR_AHB_PSC_MASK)
- >> RCC_CFGR_HPRE_SHIFT)];
- return sysclk >>= shift;
+ return stm32_get_hclk_rate(regs, sysclk);
/* APB1 CLOCK */
case STM32F7_APB1_CLOCK(TIM2) ... STM32F7_APB1_CLOCK(UART8):
- shift = apb_psc_table[(
- (readl(®s->cfgr) & RCC_CFGR_APB1_PSC_MASK)
- >> RCC_CFGR_PPRE1_SHIFT)];
- return sysclk >>= shift;
+ /* For timer clock, an additionnal prescaler is used*/
+ switch (clk->id) {
+ case STM32F7_APB1_CLOCK(TIM2):
+ case STM32F7_APB1_CLOCK(TIM3):
+ case STM32F7_APB1_CLOCK(TIM4):
+ case STM32F7_APB1_CLOCK(TIM5):
+ case STM32F7_APB1_CLOCK(TIM6):
+ case STM32F7_APB1_CLOCK(TIM7):
+ case STM32F7_APB1_CLOCK(TIM12):
+ case STM32F7_APB1_CLOCK(TIM13):
+ case STM32F7_APB1_CLOCK(TIM14):
+ return stm32_get_timer_rate(priv, sysclk, APB1);
+ }
+ return (sysclk >> stm32_get_apb_shift(regs, APB1));
+
/* APB2 CLOCK */
case STM32F7_APB2_CLOCK(TIM1) ... STM32F7_APB2_CLOCK(LTDC):
/*
@@ -325,12 +401,18 @@
else
return stm32_clk_pll48clk_rate(priv, sysclk);
break;
+
+ /* For timer clock, an additionnal prescaler is used*/
+ case STM32F7_APB2_CLOCK(TIM1):
+ case STM32F7_APB2_CLOCK(TIM8):
+ case STM32F7_APB2_CLOCK(TIM9):
+ case STM32F7_APB2_CLOCK(TIM10):
+ case STM32F7_APB2_CLOCK(TIM11):
+ return stm32_get_timer_rate(priv, sysclk, APB2);
+ break;
}
+ return (sysclk >> stm32_get_apb_shift(regs, APB2));
- shift = apb_psc_table[(
- (readl(®s->cfgr) & RCC_CFGR_APB2_PSC_MASK)
- >> RCC_CFGR_PPRE2_SHIFT)];
- return sysclk >>= shift;
default:
pr_err("clock index %ld out of range\n", clk->id);
return -EINVAL;