Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Kevin Cernekee and committed by
Stephen Boyd
17bfa3f7 e0b7a795

+80 -5
+80 -5
drivers/clk/pistachio/clk-pll.c
··· 6 6 * version 2, as published by the Free Software Foundation. 7 7 */ 8 8 9 + #define pr_fmt(fmt) "%s: " fmt, __func__ 10 + 9 11 #include <linux/clk-provider.h> 10 12 #include <linux/io.h> 11 13 #include <linux/kernel.h> 14 + #include <linux/printk.h> 12 15 #include <linux/slab.h> 13 16 14 17 #include "clk.h" ··· 52 49 53 50 #define PLL_CTRL4 0x10 54 51 #define PLL_FRAC_CTRL4_BYPASS BIT(28) 52 + 53 + #define MIN_PFD 9600000UL 54 + #define MIN_VCO_LA 400000000UL 55 + #define MAX_VCO_LA 1600000000UL 56 + #define MIN_VCO_FRAC_INT 600000000UL 57 + #define MAX_VCO_FRAC_INT 1600000000UL 58 + #define MIN_VCO_FRAC_FRAC 600000000UL 59 + #define MAX_VCO_FRAC_FRAC 2400000000UL 60 + #define MIN_OUTPUT_LA 8000000UL 61 + #define MAX_OUTPUT_LA 1600000000UL 62 + #define MIN_OUTPUT_FRAC 12000000UL 63 + #define MAX_OUTPUT_FRAC 1600000000UL 55 64 56 65 struct pistachio_clk_pll { 57 66 struct clk_hw hw; ··· 173 158 struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 174 159 struct pistachio_pll_rate_table *params; 175 160 int enabled = pll_gf40lp_frac_is_enabled(hw); 176 - u32 val; 161 + u32 val, vco, old_postdiv1, old_postdiv2; 162 + const char *name = __clk_get_name(hw->clk); 163 + 164 + if (rate < MIN_OUTPUT_FRAC || rate > MAX_OUTPUT_FRAC) 165 + return -EINVAL; 177 166 178 167 params = pll_get_params(pll, parent_rate, rate); 179 - if (!params) 168 + if (!params || !params->refdiv) 180 169 return -EINVAL; 170 + 171 + vco = params->fref * params->fbdiv / params->refdiv; 172 + if (vco < MIN_VCO_FRAC_FRAC || vco > MAX_VCO_FRAC_FRAC) 173 + pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco, 174 + MIN_VCO_FRAC_FRAC, MAX_VCO_FRAC_FRAC); 175 + 176 + val = params->fref / params->refdiv; 177 + if (val < MIN_PFD) 178 + pr_warn("%s: PFD %u is too low (min %lu)\n", 179 + name, val, MIN_PFD); 180 + if (val > vco / 16) 181 + pr_warn("%s: PFD %u is too high (max %u)\n", 182 + name, val, vco / 16); 181 183 182 184 val = pll_readl(pll, PLL_CTRL1); 183 185 val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) | ··· 204 172 pll_writel(pll, val, PLL_CTRL1); 205 173 206 174 val = pll_readl(pll, PLL_CTRL2); 175 + 176 + old_postdiv1 = (val >> PLL_FRAC_CTRL2_POSTDIV1_SHIFT) & 177 + PLL_FRAC_CTRL2_POSTDIV1_MASK; 178 + old_postdiv2 = (val >> PLL_FRAC_CTRL2_POSTDIV2_SHIFT) & 179 + PLL_FRAC_CTRL2_POSTDIV2_MASK; 180 + if (enabled && 181 + (params->postdiv1 != old_postdiv1 || 182 + params->postdiv2 != old_postdiv2)) 183 + pr_warn("%s: changing postdiv while PLL is enabled\n", name); 184 + 185 + if (params->postdiv2 > params->postdiv1) 186 + pr_warn("%s: postdiv2 should not exceed postdiv1\n", name); 187 + 207 188 val &= ~((PLL_FRAC_CTRL2_FRAC_MASK << PLL_FRAC_CTRL2_FRAC_SHIFT) | 208 189 (PLL_FRAC_CTRL2_POSTDIV1_MASK << 209 190 PLL_FRAC_CTRL2_POSTDIV1_SHIFT) | ··· 315 270 struct pistachio_clk_pll *pll = to_pistachio_pll(hw); 316 271 struct pistachio_pll_rate_table *params; 317 272 int enabled = pll_gf40lp_laint_is_enabled(hw); 318 - u32 val; 273 + u32 val, vco, old_postdiv1, old_postdiv2; 274 + const char *name = __clk_get_name(hw->clk); 319 275 320 - params = pll_get_params(pll, parent_rate, rate); 321 - if (!params) 276 + if (rate < MIN_OUTPUT_LA || rate > MAX_OUTPUT_LA) 322 277 return -EINVAL; 323 278 279 + params = pll_get_params(pll, parent_rate, rate); 280 + if (!params || !params->refdiv) 281 + return -EINVAL; 282 + 283 + vco = params->fref * params->fbdiv / params->refdiv; 284 + if (vco < MIN_VCO_LA || vco > MAX_VCO_LA) 285 + pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco, 286 + MIN_VCO_LA, MAX_VCO_LA); 287 + 288 + val = params->fref / params->refdiv; 289 + if (val < MIN_PFD) 290 + pr_warn("%s: PFD %u is too low (min %lu)\n", 291 + name, val, MIN_PFD); 292 + if (val > vco / 16) 293 + pr_warn("%s: PFD %u is too high (max %u)\n", 294 + name, val, vco / 16); 295 + 324 296 val = pll_readl(pll, PLL_CTRL1); 297 + 298 + old_postdiv1 = (val >> PLL_INT_CTRL1_POSTDIV1_SHIFT) & 299 + PLL_INT_CTRL1_POSTDIV1_MASK; 300 + old_postdiv2 = (val >> PLL_INT_CTRL1_POSTDIV2_SHIFT) & 301 + PLL_INT_CTRL1_POSTDIV2_MASK; 302 + if (enabled && 303 + (params->postdiv1 != old_postdiv1 || 304 + params->postdiv2 != old_postdiv2)) 305 + pr_warn("%s: changing postdiv while PLL is enabled\n", name); 306 + 307 + if (params->postdiv2 > params->postdiv1) 308 + pr_warn("%s: postdiv2 should not exceed postdiv1\n", name); 309 + 325 310 val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) | 326 311 (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT) | 327 312 (PLL_INT_CTRL1_POSTDIV1_MASK << PLL_INT_CTRL1_POSTDIV1_SHIFT) |