From 30ca4b1efe07c18765763f251901974c3d0f1973 Mon Sep 17 00:00:00 2001 From: Bai Ping Date: Wed, 21 Jun 2017 14:35:28 +0800 Subject: [PATCH] MLK-15133-01 driver: clk: Enhance the frac&sccg pll code on i.mx8mq 1. Fix coding typo. 2. Add PLL lock check and fix the reload of divfi and divff for frac pll. Signed-off-by: Bai Ping --- drivers/clk/imx/clk-frac-pll.c | 62 +++++++++++++++++++++++++--------- drivers/clk/imx/clk-sccg-pll.c | 16 ++++----- 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/drivers/clk/imx/clk-frac-pll.c b/drivers/clk/imx/clk-frac-pll.c index 7916f341a4fb..0711e1c60504 100644 --- a/drivers/clk/imx/clk-frac-pll.c +++ b/drivers/clk/imx/clk-frac-pll.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "clk.h" @@ -22,8 +23,8 @@ #define PLL_LOCK_STATUS (0x1 << 31) #define PLL_CLKE 21 #define PLL_PD 19 - -#define PLL_NEWDIV_VAL 12 +#define PLL_NEWDIV_VAL (1 << 12) +#define PLL_NEWDIV_ACK (1 << 11) #define PLL_FRAC_DIV_MASK 0xffffff #define PLL_INT_DIV_MASK 0x7f #define PLL_FRAC_DENOM 0x1000000 @@ -33,24 +34,39 @@ struct clk_frac_pll { void __iomem *base; }; -#define to_clk_frac_pll(_hw) container_of(_hw, struct clk_frac_pll, hw); +#define to_clk_frac_pll(_hw) container_of(_hw, struct clk_frac_pll, hw) + +static int clk_wait_lock(struct clk_frac_pll *pll) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(10); + + /* Wait for PLL to lock */ + do { + if (readl_relaxed(pll->base) & PLL_LOCK_STATUS) + break; + if (time_after(jiffies, timeout)) + break; + } while (1); + + return readl_relaxed(pll->base) & PLL_LOCK_STATUS ? 0 : -ETIMEDOUT; +} static int clk_pll_prepare(struct clk_hw *hw) { - u32 val; struct clk_frac_pll *pll = to_clk_frac_pll(hw); + u32 val; val = readl_relaxed(pll->base + PLL_CFG0); val &= ~(1 << PLL_PD); writel_relaxed(val, pll->base + PLL_CFG0); - return 0; + return clk_wait_lock(pll); } static void clk_pll_unprepare(struct clk_hw *hw) { - u32 val; struct clk_frac_pll *pll = to_clk_frac_pll(hw); + u32 val; val = readl_relaxed(pll->base + PLL_CFG0); val |= (1 << PLL_PD); @@ -59,8 +75,8 @@ static void clk_pll_unprepare(struct clk_hw *hw) static int clk_pll_is_prepared(struct clk_hw *hw) { - u32 val; struct clk_frac_pll *pll = to_clk_frac_pll(hw); + u32 val; val = readl_relaxed(pll->base + PLL_CFG0); return (val & (1 << PLL_PD)) ? 0 : 1; @@ -69,9 +85,9 @@ static int clk_pll_is_prepared(struct clk_hw *hw) static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { + struct clk_frac_pll *pll = to_clk_frac_pll(hw); u32 val, divff, divfi, divq; u64 temp64; - struct clk_frac_pll *pll = to_clk_frac_pll(hw); val = readl_relaxed(pll->base + PLL_CFG0); divq = ((val & 0x1f) + 1) * 2; @@ -79,7 +95,6 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, divff = (val >> 7) & PLL_FRAC_DIV_MASK; divfi = (val & PLL_INT_DIV_MASK); - pr_debug("divq:%d, divfi:%d, divff:%d\n", divq, divfi, divff); temp64 = (u64)parent_rate * 8; temp64 *= (divff + 1); do_div(temp64, PLL_FRAC_DENOM); @@ -96,6 +111,7 @@ static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate = *prate; parent_rate *= 8; + rate *= 2; divfi = rate / parent_rate; temp64 = (u64)(rate - divfi * parent_rate); temp64 *= PLL_FRAC_DENOM; @@ -106,7 +122,7 @@ static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, temp64 *= divff; do_div(temp64, PLL_FRAC_DENOM); - return parent_rate * divfi + (unsigned long)temp64; + return (parent_rate * divfi + (unsigned long)temp64) / 2; } /* @@ -120,13 +136,17 @@ static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - u32 val, divfi, divff; struct clk_frac_pll *pll = to_clk_frac_pll(hw); + u32 val, divfi, divff; + u64 temp64; - parent_rate *= 4; + parent_rate *= 8; + rate *= 2; divfi = rate / parent_rate; - divff = rate - parent_rate * divfi; - divff *= (PLL_FRAC_DENOM / parent_rate); + temp64 = (u64) (rate - divfi * parent_rate); + temp64 *= PLL_FRAC_DENOM; + do_div(temp64, parent_rate); + divff = temp64; val = readl_relaxed(pll->base + PLL_CFG1); val &= ~((PLL_FRAC_DIV_MASK << 7) | (PLL_INT_DIV_MASK)); @@ -137,11 +157,21 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, val &= ~0x1f; writel_relaxed(val, pll->base + PLL_CFG0); + /* Set the NEV_DIV_VAL to reload the DIVFI and DIVFF */ + val = readl_relaxed(pll->base + PLL_CFG0); + val |= PLL_NEWDIV_VAL; + writel_relaxed(val, pll->base + PLL_CFG0); + + /* wait for the new divfi and divff reload successfully */ + while (!(readl_relaxed(pll->base + PLL_CFG0) & PLL_NEWDIV_ACK)) + ; + /* clear the NEV_DIV_VAL */ val = readl_relaxed(pll->base + PLL_CFG0); - val |= (1 << PLL_NEWDIV_VAL); + val &= ~PLL_NEWDIV_VAL; writel_relaxed(val, pll->base + PLL_CFG0); - return 0; + /* wait for PLL is locked */ + return clk_wait_lock(pll); } static const struct clk_ops clk_frac_pll_ops = { diff --git a/drivers/clk/imx/clk-sccg-pll.c b/drivers/clk/imx/clk-sccg-pll.c index b56ff48ba8dc..9f550a9662a1 100644 --- a/drivers/clk/imx/clk-sccg-pll.c +++ b/drivers/clk/imx/clk-sccg-pll.c @@ -32,12 +32,12 @@ struct clk_sccg_pll { void __iomem *base; }; -#define to_clk_sccg_pll(_hw) container_of(_hw, struct clk_sccg_pll, hw); +#define to_clk_sccg_pll(_hw) container_of(_hw, struct clk_sccg_pll, hw) static int clk_pll1_is_prepared(struct clk_hw *hw) { - u32 val; struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); + u32 val; val = readl_relaxed(pll->base + PLL_CFG0); return (val & (1 << PLL_PD)) ? 0 : 1; @@ -46,8 +46,8 @@ static int clk_pll1_is_prepared(struct clk_hw *hw) static unsigned long clk_pll1_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - u32 val, divf; struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); + u32 val, divf; val = readl_relaxed(pll->base + PLL_CFG2); divf = (val >> PLL_DIVF1_SHIFT) & PLL_DIVF_MASK; @@ -58,8 +58,8 @@ static unsigned long clk_pll1_recalc_rate(struct clk_hw *hw, static long clk_pll1_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { - u32 div; unsigned long parent_rate = *prate; + u32 div; div = rate / (parent_rate * 2); @@ -69,9 +69,9 @@ static long clk_pll1_round_rate(struct clk_hw *hw, unsigned long rate, static int clk_pll1_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { + struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); u32 val; u32 divf; - struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); divf = rate / (parent_rate * 2); @@ -86,8 +86,8 @@ static int clk_pll1_set_rate(struct clk_hw *hw, unsigned long rate, static int clk_pll1_prepare(struct clk_hw *hw) { - u32 val; struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); + u32 val; val = readl_relaxed(pll->base); val &= ~(1 << PLL_PD); @@ -100,8 +100,8 @@ static int clk_pll1_prepare(struct clk_hw *hw) static void clk_pll1_unprepare(struct clk_hw *hw) { - u32 val; struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); + u32 val; val = readl_relaxed(pll->base); val |= (1 << PLL_PD); @@ -111,8 +111,8 @@ static void clk_pll1_unprepare(struct clk_hw *hw) static unsigned long clk_pll2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - u32 val, divf; struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); + u32 val, divf; val = readl_relaxed(pll->base + PLL_CFG2); divf = (val >> PLL_DIVF2_SHIFT) & PLL_DIVF_MASK; -- 2.17.1