diff options
author | Kevin Cernekee <cernekee@chromium.org> | 2015-05-27 01:01:09 +0300 |
---|---|---|
committer | Stephen Boyd <sboyd@codeaurora.org> | 2015-06-04 22:43:39 +0300 |
commit | 17bfa3f7b3cd07e92b41ce7b5bea2dd8c8e2a8c3 (patch) | |
tree | c24c733c8b2d1eaffb88bfdcc2054669e73af989 /drivers/clk/pistachio | |
parent | e0b7a79524771ad368abefbbcbd73f130f8e500e (diff) | |
download | linux-17bfa3f7b3cd07e92b41ce7b5bea2dd8c8e2a8c3.tar.xz |
clk: pistachio: Add sanity checks on PLL configuration
When setting the PLL rates, check that:
- VCO is within range
- PFD is within range
- PLL is disabled when postdiv is changed
- postdiv2 <= postdiv1
Reviewed-by: Andrew Bresticker <abrestic@chromium.org>
Signed-off-by: Kevin Cernekee <cernekee@chromium.org>
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@imgtec.com>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Diffstat (limited to 'drivers/clk/pistachio')
-rw-r--r-- | drivers/clk/pistachio/clk-pll.c | 83 |
1 files changed, 79 insertions, 4 deletions
diff --git a/drivers/clk/pistachio/clk-pll.c b/drivers/clk/pistachio/clk-pll.c index f12d5207e478..e17dada0dd21 100644 --- a/drivers/clk/pistachio/clk-pll.c +++ b/drivers/clk/pistachio/clk-pll.c @@ -6,9 +6,12 @@ * version 2, as published by the Free Software Foundation. */ +#define pr_fmt(fmt) "%s: " fmt, __func__ + #include <linux/clk-provider.h> #include <linux/io.h> #include <linux/kernel.h> +#include <linux/printk.h> #include <linux/slab.h> #include "clk.h" @@ -50,6 +53,18 @@ #define PLL_CTRL4 0x10 #define PLL_FRAC_CTRL4_BYPASS BIT(28) +#define MIN_PFD 9600000UL +#define MIN_VCO_LA 400000000UL +#define MAX_VCO_LA 1600000000UL +#define MIN_VCO_FRAC_INT 600000000UL +#define MAX_VCO_FRAC_INT 1600000000UL +#define MIN_VCO_FRAC_FRAC 600000000UL +#define MAX_VCO_FRAC_FRAC 2400000000UL +#define MIN_OUTPUT_LA 8000000UL +#define MAX_OUTPUT_LA 1600000000UL +#define MIN_OUTPUT_FRAC 12000000UL +#define MAX_OUTPUT_FRAC 1600000000UL + struct pistachio_clk_pll { struct clk_hw hw; void __iomem *base; @@ -158,12 +173,29 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, struct pistachio_clk_pll *pll = to_pistachio_pll(hw); struct pistachio_pll_rate_table *params; int enabled = pll_gf40lp_frac_is_enabled(hw); - u32 val; + u32 val, vco, old_postdiv1, old_postdiv2; + const char *name = __clk_get_name(hw->clk); + + if (rate < MIN_OUTPUT_FRAC || rate > MAX_OUTPUT_FRAC) + return -EINVAL; params = pll_get_params(pll, parent_rate, rate); - if (!params) + if (!params || !params->refdiv) return -EINVAL; + vco = params->fref * params->fbdiv / params->refdiv; + if (vco < MIN_VCO_FRAC_FRAC || vco > MAX_VCO_FRAC_FRAC) + pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco, + MIN_VCO_FRAC_FRAC, MAX_VCO_FRAC_FRAC); + + val = params->fref / params->refdiv; + if (val < MIN_PFD) + pr_warn("%s: PFD %u is too low (min %lu)\n", + name, val, MIN_PFD); + if (val > vco / 16) + pr_warn("%s: PFD %u is too high (max %u)\n", + name, val, vco / 16); + val = pll_readl(pll, PLL_CTRL1); val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) | (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT)); @@ -172,6 +204,19 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, pll_writel(pll, val, PLL_CTRL1); val = pll_readl(pll, PLL_CTRL2); + + old_postdiv1 = (val >> PLL_FRAC_CTRL2_POSTDIV1_SHIFT) & + PLL_FRAC_CTRL2_POSTDIV1_MASK; + old_postdiv2 = (val >> PLL_FRAC_CTRL2_POSTDIV2_SHIFT) & + PLL_FRAC_CTRL2_POSTDIV2_MASK; + if (enabled && + (params->postdiv1 != old_postdiv1 || + params->postdiv2 != old_postdiv2)) + pr_warn("%s: changing postdiv while PLL is enabled\n", name); + + if (params->postdiv2 > params->postdiv1) + pr_warn("%s: postdiv2 should not exceed postdiv1\n", name); + val &= ~((PLL_FRAC_CTRL2_FRAC_MASK << PLL_FRAC_CTRL2_FRAC_SHIFT) | (PLL_FRAC_CTRL2_POSTDIV1_MASK << PLL_FRAC_CTRL2_POSTDIV1_SHIFT) | @@ -270,13 +315,43 @@ static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate, struct pistachio_clk_pll *pll = to_pistachio_pll(hw); struct pistachio_pll_rate_table *params; int enabled = pll_gf40lp_laint_is_enabled(hw); - u32 val; + u32 val, vco, old_postdiv1, old_postdiv2; + const char *name = __clk_get_name(hw->clk); + + if (rate < MIN_OUTPUT_LA || rate > MAX_OUTPUT_LA) + return -EINVAL; params = pll_get_params(pll, parent_rate, rate); - if (!params) + if (!params || !params->refdiv) return -EINVAL; + vco = params->fref * params->fbdiv / params->refdiv; + if (vco < MIN_VCO_LA || vco > MAX_VCO_LA) + pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco, + MIN_VCO_LA, MAX_VCO_LA); + + val = params->fref / params->refdiv; + if (val < MIN_PFD) + pr_warn("%s: PFD %u is too low (min %lu)\n", + name, val, MIN_PFD); + if (val > vco / 16) + pr_warn("%s: PFD %u is too high (max %u)\n", + name, val, vco / 16); + val = pll_readl(pll, PLL_CTRL1); + + old_postdiv1 = (val >> PLL_INT_CTRL1_POSTDIV1_SHIFT) & + PLL_INT_CTRL1_POSTDIV1_MASK; + old_postdiv2 = (val >> PLL_INT_CTRL1_POSTDIV2_SHIFT) & + PLL_INT_CTRL1_POSTDIV2_MASK; + if (enabled && + (params->postdiv1 != old_postdiv1 || + params->postdiv2 != old_postdiv2)) + pr_warn("%s: changing postdiv while PLL is enabled\n", name); + + if (params->postdiv2 > params->postdiv1) + pr_warn("%s: postdiv2 should not exceed postdiv1\n", name); + val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) | (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT) | (PLL_INT_CTRL1_POSTDIV1_MASK << PLL_INT_CTRL1_POSTDIV1_SHIFT) | |