307 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2017, Intel Corporation4 */5#include <linux/slab.h>6#include <linux/clk-provider.h>7#include <linux/io.h>8 9#include "stratix10-clk.h"10#include "clk.h"11 12/* Clock Manager offsets */13#define CLK_MGR_PLL_CLK_SRC_SHIFT 1614#define CLK_MGR_PLL_CLK_SRC_MASK 0x315 16/* PLL Clock enable bits */17#define SOCFPGA_PLL_POWER 018#define SOCFPGA_PLL_RESET_MASK 0x219#define SOCFPGA_PLL_REFDIV_MASK 0x00003F0020#define SOCFPGA_PLL_REFDIV_SHIFT 821#define SOCFPGA_PLL_AREFDIV_MASK 0x00000F0022#define SOCFPGA_PLL_DREFDIV_MASK 0x0000300023#define SOCFPGA_PLL_DREFDIV_SHIFT 1224#define SOCFPGA_PLL_MDIV_MASK 0xFF00000025#define SOCFPGA_PLL_MDIV_SHIFT 2426#define SOCFPGA_AGILEX_PLL_MDIV_MASK 0x000003FF27#define SWCTRLBTCLKSEL_MASK 0x20028#define SWCTRLBTCLKSEL_SHIFT 929 30#define SOCFPGA_N5X_PLLDIV_FDIV_MASK GENMASK(16, 8)31#define SOCFPGA_N5X_PLLDIV_FDIV_SHIFT 832#define SOCFPGA_N5X_PLLDIV_RDIV_MASK GENMASK(5, 0)33#define SOCFPGA_N5X_PLLDIV_QDIV_MASK GENMASK(26, 24)34#define SOCFPGA_N5X_PLLDIV_QDIV_SHIFT 2435 36#define SOCFPGA_BOOT_CLK "boot_clk"37 38#define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw)39 40static unsigned long n5x_clk_pll_recalc_rate(struct clk_hw *hwclk,41 unsigned long parent_rate)42{43 struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);44 unsigned long fdiv, reg, rdiv, qdiv;45 u32 power = 1;46 47 /* read VCO1 reg for numerator and denominator */48 reg = readl(socfpgaclk->hw.reg + 0x8);49 fdiv = (reg & SOCFPGA_N5X_PLLDIV_FDIV_MASK) >> SOCFPGA_N5X_PLLDIV_FDIV_SHIFT;50 rdiv = (reg & SOCFPGA_N5X_PLLDIV_RDIV_MASK);51 qdiv = (reg & SOCFPGA_N5X_PLLDIV_QDIV_MASK) >> SOCFPGA_N5X_PLLDIV_QDIV_SHIFT;52 53 while (qdiv) {54 power *= 2;55 qdiv--;56 }57 58 return ((parent_rate * 2 * (fdiv + 1)) / ((rdiv + 1) * power));59}60 61static unsigned long agilex_clk_pll_recalc_rate(struct clk_hw *hwclk,62 unsigned long parent_rate)63{64 struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);65 unsigned long arefdiv, reg, mdiv;66 unsigned long long vco_freq;67 68 /* read VCO1 reg for numerator and denominator */69 reg = readl(socfpgaclk->hw.reg);70 arefdiv = (reg & SOCFPGA_PLL_AREFDIV_MASK) >> SOCFPGA_PLL_REFDIV_SHIFT;71 72 vco_freq = (unsigned long long)parent_rate / arefdiv;73 74 /* Read mdiv and fdiv from the fdbck register */75 reg = readl(socfpgaclk->hw.reg + 0x24);76 mdiv = reg & SOCFPGA_AGILEX_PLL_MDIV_MASK;77 78 vco_freq = (unsigned long long)vco_freq * mdiv;79 return (unsigned long)vco_freq;80}81 82static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,83 unsigned long parent_rate)84{85 struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);86 unsigned long mdiv;87 unsigned long refdiv;88 unsigned long reg;89 unsigned long long vco_freq;90 91 /* read VCO1 reg for numerator and denominator */92 reg = readl(socfpgaclk->hw.reg);93 refdiv = (reg & SOCFPGA_PLL_REFDIV_MASK) >> SOCFPGA_PLL_REFDIV_SHIFT;94 95 vco_freq = parent_rate;96 do_div(vco_freq, refdiv);97 98 /* Read mdiv and fdiv from the fdbck register */99 reg = readl(socfpgaclk->hw.reg + 0x4);100 mdiv = (reg & SOCFPGA_PLL_MDIV_MASK) >> SOCFPGA_PLL_MDIV_SHIFT;101 vco_freq = (unsigned long long)vco_freq * (mdiv + 6);102 103 return (unsigned long)vco_freq;104}105 106static unsigned long clk_boot_clk_recalc_rate(struct clk_hw *hwclk,107 unsigned long parent_rate)108{109 struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);110 u32 div;111 112 div = ((readl(socfpgaclk->hw.reg) &113 SWCTRLBTCLKSEL_MASK) >>114 SWCTRLBTCLKSEL_SHIFT);115 div += 1;116 return parent_rate / div;117}118 119 120static u8 clk_pll_get_parent(struct clk_hw *hwclk)121{122 struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);123 u32 pll_src;124 125 pll_src = readl(socfpgaclk->hw.reg);126 return (pll_src >> CLK_MGR_PLL_CLK_SRC_SHIFT) &127 CLK_MGR_PLL_CLK_SRC_MASK;128}129 130static u8 clk_boot_get_parent(struct clk_hw *hwclk)131{132 struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);133 u32 pll_src;134 135 pll_src = readl(socfpgaclk->hw.reg);136 return (pll_src >> SWCTRLBTCLKSEL_SHIFT) &137 SWCTRLBTCLKSEL_MASK;138}139 140static int clk_pll_prepare(struct clk_hw *hwclk)141{142 struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);143 u32 reg;144 145 /* Bring PLL out of reset */146 reg = readl(socfpgaclk->hw.reg);147 reg |= SOCFPGA_PLL_RESET_MASK;148 writel(reg, socfpgaclk->hw.reg);149 150 return 0;151}152 153static int n5x_clk_pll_prepare(struct clk_hw *hwclk)154{155 struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);156 u32 reg;157 158 /* Bring PLL out of reset */159 reg = readl(socfpgaclk->hw.reg + 0x4);160 reg |= SOCFPGA_PLL_RESET_MASK;161 writel(reg, socfpgaclk->hw.reg + 0x4);162 163 return 0;164}165 166static const struct clk_ops n5x_clk_pll_ops = {167 .recalc_rate = n5x_clk_pll_recalc_rate,168 .get_parent = clk_pll_get_parent,169 .prepare = n5x_clk_pll_prepare,170};171 172static const struct clk_ops agilex_clk_pll_ops = {173 .recalc_rate = agilex_clk_pll_recalc_rate,174 .get_parent = clk_pll_get_parent,175 .prepare = clk_pll_prepare,176};177 178static const struct clk_ops clk_pll_ops = {179 .recalc_rate = clk_pll_recalc_rate,180 .get_parent = clk_pll_get_parent,181 .prepare = clk_pll_prepare,182};183 184static const struct clk_ops clk_boot_ops = {185 .recalc_rate = clk_boot_clk_recalc_rate,186 .get_parent = clk_boot_get_parent,187 .prepare = clk_pll_prepare,188};189 190struct clk_hw *s10_register_pll(const struct stratix10_pll_clock *clks,191 void __iomem *reg)192{193 struct clk_hw *hw_clk;194 struct socfpga_pll *pll_clk;195 struct clk_init_data init;196 const char *name = clks->name;197 int ret;198 199 pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);200 if (WARN_ON(!pll_clk))201 return NULL;202 203 pll_clk->hw.reg = reg + clks->offset;204 205 if (streq(name, SOCFPGA_BOOT_CLK))206 init.ops = &clk_boot_ops;207 else208 init.ops = &clk_pll_ops;209 210 init.name = name;211 init.flags = clks->flags;212 213 init.num_parents = clks->num_parents;214 init.parent_names = NULL;215 init.parent_data = clks->parent_data;216 pll_clk->hw.hw.init = &init;217 218 pll_clk->hw.bit_idx = SOCFPGA_PLL_POWER;219 220 hw_clk = &pll_clk->hw.hw;221 222 ret = clk_hw_register(NULL, hw_clk);223 if (ret) {224 kfree(pll_clk);225 return ERR_PTR(ret);226 }227 return hw_clk;228}229 230struct clk_hw *agilex_register_pll(const struct stratix10_pll_clock *clks,231 void __iomem *reg)232{233 struct clk_hw *hw_clk;234 struct socfpga_pll *pll_clk;235 struct clk_init_data init;236 const char *name = clks->name;237 int ret;238 239 pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);240 if (WARN_ON(!pll_clk))241 return NULL;242 243 pll_clk->hw.reg = reg + clks->offset;244 245 if (streq(name, SOCFPGA_BOOT_CLK))246 init.ops = &clk_boot_ops;247 else248 init.ops = &agilex_clk_pll_ops;249 250 init.name = name;251 init.flags = clks->flags;252 253 init.num_parents = clks->num_parents;254 init.parent_names = NULL;255 init.parent_data = clks->parent_data;256 pll_clk->hw.hw.init = &init;257 258 pll_clk->hw.bit_idx = SOCFPGA_PLL_POWER;259 hw_clk = &pll_clk->hw.hw;260 261 ret = clk_hw_register(NULL, hw_clk);262 if (ret) {263 kfree(pll_clk);264 return ERR_PTR(ret);265 }266 return hw_clk;267}268 269struct clk_hw *n5x_register_pll(const struct stratix10_pll_clock *clks,270 void __iomem *reg)271{272 struct clk_hw *hw_clk;273 struct socfpga_pll *pll_clk;274 struct clk_init_data init;275 const char *name = clks->name;276 int ret;277 278 pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);279 if (WARN_ON(!pll_clk))280 return NULL;281 282 pll_clk->hw.reg = reg + clks->offset;283 284 if (streq(name, SOCFPGA_BOOT_CLK))285 init.ops = &clk_boot_ops;286 else287 init.ops = &n5x_clk_pll_ops;288 289 init.name = name;290 init.flags = clks->flags;291 292 init.num_parents = clks->num_parents;293 init.parent_names = NULL;294 init.parent_data = clks->parent_data;295 pll_clk->hw.hw.init = &init;296 297 pll_clk->hw.bit_idx = SOCFPGA_PLL_POWER;298 hw_clk = &pll_clk->hw.hw;299 300 ret = clk_hw_register(NULL, hw_clk);301 if (ret) {302 kfree(pll_clk);303 return ERR_PTR(ret);304 }305 return hw_clk;306}307