|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | +/* |
| 3 | + * Copyright 2021 NXP |
| 4 | + */ |
| 5 | + |
| 6 | +#include <linux/bitfield.h> |
| 7 | +#include <linux/clk-provider.h> |
| 8 | +#include <linux/err.h> |
| 9 | +#include <linux/export.h> |
| 10 | +#include <linux/io.h> |
| 11 | +#include <linux/iopoll.h> |
| 12 | +#include <linux/slab.h> |
| 13 | +#include <asm/div64.h> |
| 14 | + |
| 15 | +#include "clk.h" |
| 16 | + |
| 17 | +#define PLL_CTRL 0x0 |
| 18 | +#define CLKMUX_BYPASS BIT(2) |
| 19 | +#define CLKMUX_EN BIT(1) |
| 20 | +#define POWERUP_MASK BIT(0) |
| 21 | + |
| 22 | +#define PLL_ANA_PRG 0x10 |
| 23 | +#define PLL_SPREAD_SPECTRUM 0x30 |
| 24 | + |
| 25 | +#define PLL_NUMERATOR 0x40 |
| 26 | +#define PLL_MFN_MASK GENMASK(31, 2) |
| 27 | + |
| 28 | +#define PLL_DENOMINATOR 0x50 |
| 29 | +#define PLL_MFD_MASK GENMASK(29, 0) |
| 30 | + |
| 31 | +#define PLL_DIV 0x60 |
| 32 | +#define PLL_MFI_MASK GENMASK(24, 16) |
| 33 | +#define PLL_RDIV_MASK GENMASK(15, 13) |
| 34 | +#define PLL_ODIV_MASK GENMASK(7, 0) |
| 35 | + |
| 36 | +#define PLL_DFS_CTRL(x) (0x70 + (x) * 0x10) |
| 37 | + |
| 38 | +#define PLL_STATUS 0xF0 |
| 39 | +#define LOCK_STATUS BIT(0) |
| 40 | + |
| 41 | +#define DFS_STATUS 0xF4 |
| 42 | + |
| 43 | +#define LOCK_TIMEOUT_US 200 |
| 44 | + |
| 45 | +#define PLL_FRACN_GP(_rate, _mfi, _mfn, _mfd, _rdiv, _odiv) \ |
| 46 | + { \ |
| 47 | + .rate = (_rate), \ |
| 48 | + .mfi = (_mfi), \ |
| 49 | + .mfn = (_mfn), \ |
| 50 | + .mfd = (_mfd), \ |
| 51 | + .rdiv = (_rdiv), \ |
| 52 | + .odiv = (_odiv), \ |
| 53 | + } |
| 54 | + |
| 55 | +struct clk_fracn_gppll { |
| 56 | + struct clk_hw hw; |
| 57 | + void __iomem *base; |
| 58 | + const struct imx_fracn_gppll_rate_table *rate_table; |
| 59 | + int rate_count; |
| 60 | +}; |
| 61 | + |
| 62 | +/* |
| 63 | + * Fvco = Fref * (MFI + MFN / MFD) |
| 64 | + * Fout = Fvco / (rdiv * odiv) |
| 65 | + */ |
| 66 | +static const struct imx_fracn_gppll_rate_table fracn_tbl[] = { |
| 67 | + PLL_FRACN_GP(650000000U, 81, 0, 0, 0, 3), |
| 68 | + PLL_FRACN_GP(594000000U, 198, 0, 0, 0, 8), |
| 69 | + PLL_FRACN_GP(560000000U, 70, 0, 0, 0, 3), |
| 70 | + PLL_FRACN_GP(400000000U, 50, 0, 0, 0, 3), |
| 71 | + PLL_FRACN_GP(393216000U, 81, 92, 100, 0, 5) |
| 72 | +}; |
| 73 | + |
| 74 | +struct imx_fracn_gppll_clk imx_fracn_gppll = { |
| 75 | + .rate_table = fracn_tbl, |
| 76 | + .rate_count = ARRAY_SIZE(fracn_tbl), |
| 77 | +}; |
| 78 | +EXPORT_SYMBOL_GPL(imx_fracn_gppll); |
| 79 | + |
| 80 | +static inline struct clk_fracn_gppll *to_clk_fracn_gppll(struct clk_hw *hw) |
| 81 | +{ |
| 82 | + return container_of(hw, struct clk_fracn_gppll, hw); |
| 83 | +} |
| 84 | + |
| 85 | +static const struct imx_fracn_gppll_rate_table * |
| 86 | +imx_get_pll_settings(struct clk_fracn_gppll *pll, unsigned long rate) |
| 87 | +{ |
| 88 | + const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table; |
| 89 | + int i; |
| 90 | + |
| 91 | + for (i = 0; i < pll->rate_count; i++) |
| 92 | + if (rate == rate_table[i].rate) |
| 93 | + return &rate_table[i]; |
| 94 | + |
| 95 | + return NULL; |
| 96 | +} |
| 97 | + |
| 98 | +static long clk_fracn_gppll_round_rate(struct clk_hw *hw, unsigned long rate, |
| 99 | + unsigned long *prate) |
| 100 | +{ |
| 101 | + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw); |
| 102 | + const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table; |
| 103 | + int i; |
| 104 | + |
| 105 | + /* Assuming rate_table is in descending order */ |
| 106 | + for (i = 0; i < pll->rate_count; i++) |
| 107 | + if (rate >= rate_table[i].rate) |
| 108 | + return rate_table[i].rate; |
| 109 | + |
| 110 | + /* return minimum supported value */ |
| 111 | + return rate_table[pll->rate_count - 1].rate; |
| 112 | +} |
| 113 | + |
| 114 | +static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) |
| 115 | +{ |
| 116 | + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw); |
| 117 | + const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table; |
| 118 | + u32 pll_numerator, pll_denominator, pll_div; |
| 119 | + u32 mfi, mfn, mfd, rdiv, odiv; |
| 120 | + u64 fvco = parent_rate; |
| 121 | + long rate = 0; |
| 122 | + int i; |
| 123 | + |
| 124 | + pll_numerator = readl_relaxed(pll->base + PLL_NUMERATOR); |
| 125 | + mfn = FIELD_GET(PLL_MFN_MASK, pll_numerator); |
| 126 | + |
| 127 | + pll_denominator = readl_relaxed(pll->base + PLL_DENOMINATOR); |
| 128 | + mfd = FIELD_GET(PLL_MFD_MASK, pll_denominator); |
| 129 | + |
| 130 | + pll_div = readl_relaxed(pll->base + PLL_DIV); |
| 131 | + mfi = FIELD_GET(PLL_MFI_MASK, pll_div); |
| 132 | + |
| 133 | + rdiv = FIELD_GET(PLL_RDIV_MASK, pll_div); |
| 134 | + rdiv = rdiv + 1; |
| 135 | + odiv = FIELD_GET(PLL_ODIV_MASK, pll_div); |
| 136 | + switch (odiv) { |
| 137 | + case 0: |
| 138 | + odiv = 2; |
| 139 | + break; |
| 140 | + case 1: |
| 141 | + odiv = 3; |
| 142 | + break; |
| 143 | + default: |
| 144 | + break; |
| 145 | + } |
| 146 | + |
| 147 | + /* |
| 148 | + * Sometimes, the recalculated rate has deviation due to |
| 149 | + * the frac part. So find the accurate pll rate from the table |
| 150 | + * first, if no match rate in the table, use the rate calculated |
| 151 | + * from the equation below. |
| 152 | + */ |
| 153 | + for (i = 0; i < pll->rate_count; i++) { |
| 154 | + if (rate_table[i].mfn == mfn && rate_table[i].mfi == mfi && |
| 155 | + rate_table[i].mfd == mfd && rate_table[i].rdiv == rdiv && |
| 156 | + rate_table[i].odiv == odiv) |
| 157 | + rate = rate_table[i].rate; |
| 158 | + } |
| 159 | + |
| 160 | + if (rate) |
| 161 | + return (unsigned long)rate; |
| 162 | + |
| 163 | + /* Fvco = Fref * (MFI + MFN / MFD) */ |
| 164 | + fvco = fvco * mfi * mfd + fvco * mfn; |
| 165 | + do_div(fvco, mfd * rdiv * odiv); |
| 166 | + |
| 167 | + return (unsigned long)fvco; |
| 168 | +} |
| 169 | + |
| 170 | +static int clk_fracn_gppll_wait_lock(struct clk_fracn_gppll *pll) |
| 171 | +{ |
| 172 | + u32 val; |
| 173 | + |
| 174 | + return readl_poll_timeout(pll->base + PLL_STATUS, val, |
| 175 | + val & LOCK_STATUS, 0, LOCK_TIMEOUT_US); |
| 176 | +} |
| 177 | + |
| 178 | +static int clk_fracn_gppll_set_rate(struct clk_hw *hw, unsigned long drate, |
| 179 | + unsigned long prate) |
| 180 | +{ |
| 181 | + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw); |
| 182 | + const struct imx_fracn_gppll_rate_table *rate; |
| 183 | + u32 tmp, pll_div, ana_mfn; |
| 184 | + int ret; |
| 185 | + |
| 186 | + rate = imx_get_pll_settings(pll, drate); |
| 187 | + |
| 188 | + /* Disable output */ |
| 189 | + tmp = readl_relaxed(pll->base + PLL_CTRL); |
| 190 | + tmp &= ~CLKMUX_EN; |
| 191 | + writel_relaxed(tmp, pll->base + PLL_CTRL); |
| 192 | + |
| 193 | + /* Power Down */ |
| 194 | + tmp &= ~POWERUP_MASK; |
| 195 | + writel_relaxed(tmp, pll->base + PLL_CTRL); |
| 196 | + |
| 197 | + /* Disable BYPASS */ |
| 198 | + tmp &= ~CLKMUX_BYPASS; |
| 199 | + writel_relaxed(tmp, pll->base + PLL_CTRL); |
| 200 | + |
| 201 | + pll_div = FIELD_PREP(PLL_RDIV_MASK, rate->rdiv) | rate->odiv | |
| 202 | + FIELD_PREP(PLL_MFI_MASK, rate->mfi); |
| 203 | + writel_relaxed(pll_div, pll->base + PLL_DIV); |
| 204 | + writel_relaxed(rate->mfd, pll->base + PLL_DENOMINATOR); |
| 205 | + writel_relaxed(FIELD_PREP(PLL_MFN_MASK, rate->mfn), pll->base + PLL_NUMERATOR); |
| 206 | + |
| 207 | + /* Wait for 5us according to fracn mode pll doc */ |
| 208 | + udelay(5); |
| 209 | + |
| 210 | + /* Enable Powerup */ |
| 211 | + tmp |= POWERUP_MASK; |
| 212 | + writel_relaxed(tmp, pll->base + PLL_CTRL); |
| 213 | + |
| 214 | + /* Wait Lock */ |
| 215 | + ret = clk_fracn_gppll_wait_lock(pll); |
| 216 | + if (ret) |
| 217 | + return ret; |
| 218 | + |
| 219 | + /* Enable output */ |
| 220 | + tmp |= CLKMUX_EN; |
| 221 | + writel_relaxed(tmp, pll->base + PLL_CTRL); |
| 222 | + |
| 223 | + ana_mfn = readl_relaxed(pll->base + PLL_STATUS); |
| 224 | + ana_mfn = FIELD_GET(PLL_MFN_MASK, ana_mfn); |
| 225 | + |
| 226 | + WARN(ana_mfn != rate->mfn, "ana_mfn != rate->mfn\n"); |
| 227 | + |
| 228 | + return 0; |
| 229 | +} |
| 230 | + |
| 231 | +static int clk_fracn_gppll_prepare(struct clk_hw *hw) |
| 232 | +{ |
| 233 | + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw); |
| 234 | + u32 val; |
| 235 | + int ret; |
| 236 | + |
| 237 | + val = readl_relaxed(pll->base + PLL_CTRL); |
| 238 | + if (val & POWERUP_MASK) |
| 239 | + return 0; |
| 240 | + |
| 241 | + val |= CLKMUX_BYPASS; |
| 242 | + writel_relaxed(val, pll->base + PLL_CTRL); |
| 243 | + |
| 244 | + val |= POWERUP_MASK; |
| 245 | + writel_relaxed(val, pll->base + PLL_CTRL); |
| 246 | + |
| 247 | + val |= CLKMUX_EN; |
| 248 | + writel_relaxed(val, pll->base + PLL_CTRL); |
| 249 | + |
| 250 | + ret = clk_fracn_gppll_wait_lock(pll); |
| 251 | + if (ret) |
| 252 | + return ret; |
| 253 | + |
| 254 | + val &= ~CLKMUX_BYPASS; |
| 255 | + writel_relaxed(val, pll->base + PLL_CTRL); |
| 256 | + |
| 257 | + return 0; |
| 258 | +} |
| 259 | + |
| 260 | +static int clk_fracn_gppll_is_prepared(struct clk_hw *hw) |
| 261 | +{ |
| 262 | + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw); |
| 263 | + u32 val; |
| 264 | + |
| 265 | + val = readl_relaxed(pll->base + PLL_CTRL); |
| 266 | + |
| 267 | + return (val & POWERUP_MASK) ? 1 : 0; |
| 268 | +} |
| 269 | + |
| 270 | +static void clk_fracn_gppll_unprepare(struct clk_hw *hw) |
| 271 | +{ |
| 272 | + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw); |
| 273 | + u32 val; |
| 274 | + |
| 275 | + val = readl_relaxed(pll->base + PLL_CTRL); |
| 276 | + val &= ~POWERUP_MASK; |
| 277 | + writel_relaxed(val, pll->base + PLL_CTRL); |
| 278 | +} |
| 279 | + |
| 280 | +static const struct clk_ops clk_fracn_gppll_ops = { |
| 281 | + .prepare = clk_fracn_gppll_prepare, |
| 282 | + .unprepare = clk_fracn_gppll_unprepare, |
| 283 | + .is_prepared = clk_fracn_gppll_is_prepared, |
| 284 | + .recalc_rate = clk_fracn_gppll_recalc_rate, |
| 285 | + .round_rate = clk_fracn_gppll_round_rate, |
| 286 | + .set_rate = clk_fracn_gppll_set_rate, |
| 287 | +}; |
| 288 | + |
| 289 | +struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base, |
| 290 | + const struct imx_fracn_gppll_clk *pll_clk) |
| 291 | +{ |
| 292 | + struct clk_fracn_gppll *pll; |
| 293 | + struct clk_hw *hw; |
| 294 | + struct clk_init_data init; |
| 295 | + int ret; |
| 296 | + |
| 297 | + pll = kzalloc(sizeof(*pll), GFP_KERNEL); |
| 298 | + if (!pll) |
| 299 | + return ERR_PTR(-ENOMEM); |
| 300 | + |
| 301 | + init.name = name; |
| 302 | + init.flags = pll_clk->flags; |
| 303 | + init.parent_names = &parent_name; |
| 304 | + init.num_parents = 1; |
| 305 | + init.ops = &clk_fracn_gppll_ops; |
| 306 | + |
| 307 | + pll->base = base; |
| 308 | + pll->hw.init = &init; |
| 309 | + pll->rate_table = pll_clk->rate_table; |
| 310 | + pll->rate_count = pll_clk->rate_count; |
| 311 | + |
| 312 | + hw = &pll->hw; |
| 313 | + |
| 314 | + ret = clk_hw_register(NULL, hw); |
| 315 | + if (ret) { |
| 316 | + pr_err("%s: failed to register pll %s %d\n", __func__, name, ret); |
| 317 | + kfree(pll); |
| 318 | + return ERR_PTR(ret); |
| 319 | + } |
| 320 | + |
| 321 | + return hw; |
| 322 | +} |
| 323 | +EXPORT_SYMBOL_GPL(imx_clk_fracn_gppll); |
0 commit comments