Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 | // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2015-2020, NVIDIA CORPORATION. All rights reserved. */ #include <linux/bitfield.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/clk/tegra.h> #include <linux/device.h> #include <linux/module.h> #include <linux/io.h> #include <linux/slab.h> #include "clk.h" #define CLK_SOURCE_EMC 0x19c #define CLK_SOURCE_EMC_2X_CLK_SRC GENMASK(31, 29) #define CLK_SOURCE_EMC_MC_EMC_SAME_FREQ BIT(16) #define CLK_SOURCE_EMC_2X_CLK_DIVISOR GENMASK(7, 0) #define CLK_SRC_PLLM 0 #define CLK_SRC_PLLC 1 #define CLK_SRC_PLLP 2 #define CLK_SRC_CLK_M 3 #define CLK_SRC_PLLM_UD 4 #define CLK_SRC_PLLMB_UD 5 #define CLK_SRC_PLLMB 6 #define CLK_SRC_PLLP_UD 7 struct tegra210_clk_emc { struct clk_hw hw; void __iomem *regs; struct tegra210_clk_emc_provider *provider; struct clk *parents[8]; }; static inline struct tegra210_clk_emc * to_tegra210_clk_emc(struct clk_hw *hw) { return container_of(hw, struct tegra210_clk_emc, hw); } static const char *tegra210_clk_emc_parents[] = { "pll_m", "pll_c", "pll_p", "clk_m", "pll_m_ud", "pll_mb_ud", "pll_mb", "pll_p_ud", }; static u8 tegra210_clk_emc_get_parent(struct clk_hw *hw) { struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw); u32 value; u8 src; value = readl_relaxed(emc->regs + CLK_SOURCE_EMC); src = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, value); return src; } static unsigned long tegra210_clk_emc_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw); u32 value, div; /* * CCF assumes that neither the parent nor its rate will change during * ->set_rate(), so the parent rate passed in here was cached from the * parent before the ->set_rate() call. * * This can lead to wrong results being reported for the EMC clock if * the parent and/or parent rate have changed as part of the EMC rate * change sequence. Fix this by overriding the parent clock with what * we know to be the correct value after the rate change. */ parent_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); value = readl_relaxed(emc->regs + CLK_SOURCE_EMC); div = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_DIVISOR, value); div += 2; return DIV_ROUND_UP(parent_rate * 2, div); } static long tegra210_clk_emc_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw); struct tegra210_clk_emc_provider *provider = emc->provider; unsigned int i; if (!provider || !provider->configs || provider->num_configs == 0) return clk_hw_get_rate(hw); for (i = 0; i < provider->num_configs; i++) { if (provider->configs[i].rate >= rate) return provider->configs[i].rate; } return provider->configs[i - 1].rate; } static struct clk *tegra210_clk_emc_find_parent(struct tegra210_clk_emc *emc, u8 index) { struct clk_hw *parent = clk_hw_get_parent_by_index(&emc->hw, index); const char *name = clk_hw_get_name(parent); /* XXX implement cache? */ return __clk_lookup(name); } static int tegra210_clk_emc_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw); struct tegra210_clk_emc_provider *provider = emc->provider; struct tegra210_clk_emc_config *config; struct device *dev = provider->dev; struct clk_hw *old, *new, *parent; u8 old_idx, new_idx, index; struct clk *clk; unsigned int i; int err; if (!provider->configs || provider->num_configs == 0) return -EINVAL; for (i = 0; i < provider->num_configs; i++) { if (provider->configs[i].rate >= rate) { config = &provider->configs[i]; break; } } if (i == provider->num_configs) config = &provider->configs[i - 1]; old_idx = tegra210_clk_emc_get_parent(hw); new_idx = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, config->value); old = clk_hw_get_parent_by_index(hw, old_idx); new = clk_hw_get_parent_by_index(hw, new_idx); /* if the rate has changed... */ if (config->parent_rate != clk_hw_get_rate(old)) { /* ... but the clock source remains the same ... */ if (new_idx == old_idx) { /* ... switch to the alternative clock source. */ switch (new_idx) { case CLK_SRC_PLLM: new_idx = CLK_SRC_PLLMB; break; case CLK_SRC_PLLM_UD: new_idx = CLK_SRC_PLLMB_UD; break; case CLK_SRC_PLLMB_UD: new_idx = CLK_SRC_PLLM_UD; break; case CLK_SRC_PLLMB: new_idx = CLK_SRC_PLLM; break; } /* * This should never happen because we can't deal with * it. */ if (WARN_ON(new_idx == old_idx)) return -EINVAL; new = clk_hw_get_parent_by_index(hw, new_idx); } index = new_idx; parent = new; } else { index = old_idx; parent = old; } clk = tegra210_clk_emc_find_parent(emc, index); if (IS_ERR(clk)) { err = PTR_ERR(clk); dev_err(dev, "failed to get parent clock for index %u: %d\n", index, err); return err; } /* set the new parent clock to the required rate */ if (clk_get_rate(clk) != config->parent_rate) { err = clk_set_rate(clk, config->parent_rate); if (err < 0) { dev_err(dev, "failed to set rate %lu Hz for %pC: %d\n", config->parent_rate, clk, err); return err; } } /* enable the new parent clock */ if (parent != old) { err = clk_prepare_enable(clk); if (err < 0) { dev_err(dev, "failed to enable parent clock %pC: %d\n", clk, err); return err; } } /* update the EMC source configuration to reflect the new parent */ config->value &= ~CLK_SOURCE_EMC_2X_CLK_SRC; config->value |= FIELD_PREP(CLK_SOURCE_EMC_2X_CLK_SRC, index); /* * Finally, switch the EMC programming with both old and new parent * clocks enabled. */ err = provider->set_rate(dev, config); if (err < 0) { dev_err(dev, "failed to set EMC rate to %lu Hz: %d\n", rate, err); /* * If we're unable to switch to the new EMC frequency, we no * longer need the new parent to be enabled. */ if (parent != old) clk_disable_unprepare(clk); return err; } /* reparent to new parent clock and disable the old parent clock */ if (parent != old) { clk = tegra210_clk_emc_find_parent(emc, old_idx); if (IS_ERR(clk)) { err = PTR_ERR(clk); dev_err(dev, "failed to get parent clock for index %u: %d\n", old_idx, err); return err; } clk_hw_reparent(hw, parent); clk_disable_unprepare(clk); } return err; } static const struct clk_ops tegra210_clk_emc_ops = { .get_parent = tegra210_clk_emc_get_parent, .recalc_rate = tegra210_clk_emc_recalc_rate, .round_rate = tegra210_clk_emc_round_rate, .set_rate = tegra210_clk_emc_set_rate, }; struct clk *tegra210_clk_register_emc(struct device_node *np, void __iomem *regs) { struct tegra210_clk_emc *emc; struct clk_init_data init; struct clk *clk; emc = kzalloc(sizeof(*emc), GFP_KERNEL); if (!emc) return ERR_PTR(-ENOMEM); emc->regs = regs; init.name = "emc"; init.ops = &tegra210_clk_emc_ops; init.flags = CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE; init.parent_names = tegra210_clk_emc_parents; init.num_parents = ARRAY_SIZE(tegra210_clk_emc_parents); emc->hw.init = &init; clk = clk_register(NULL, &emc->hw); if (IS_ERR(clk)) { kfree(emc); return clk; } return clk; } int tegra210_clk_emc_attach(struct clk *clk, struct tegra210_clk_emc_provider *provider) { struct clk_hw *hw = __clk_get_hw(clk); struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw); struct device *dev = provider->dev; unsigned int i; int err; if (!try_module_get(provider->owner)) return -ENODEV; for (i = 0; i < provider->num_configs; i++) { struct tegra210_clk_emc_config *config = &provider->configs[i]; struct clk_hw *parent; bool same_freq; u8 div, src; div = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_DIVISOR, config->value); src = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, config->value); /* do basic sanity checking on the EMC timings */ if (div & 0x1) { dev_err(dev, "invalid odd divider %u for rate %lu Hz\n", div, config->rate); err = -EINVAL; goto put; } same_freq = config->value & CLK_SOURCE_EMC_MC_EMC_SAME_FREQ; if (same_freq != config->same_freq) { dev_err(dev, "ambiguous EMC to MC ratio for rate %lu Hz\n", config->rate); err = -EINVAL; goto put; } parent = clk_hw_get_parent_by_index(hw, src); config->parent = src; if (src == CLK_SRC_PLLM || src == CLK_SRC_PLLM_UD) { config->parent_rate = config->rate * (1 + div / 2); } else { unsigned long rate = config->rate * (1 + div / 2); config->parent_rate = clk_hw_get_rate(parent); if (config->parent_rate != rate) { dev_err(dev, "rate %lu Hz does not match input\n", config->rate); err = -EINVAL; goto put; } } } emc->provider = provider; return 0; put: module_put(provider->owner); return err; } EXPORT_SYMBOL_GPL(tegra210_clk_emc_attach); void tegra210_clk_emc_detach(struct clk *clk) { struct tegra210_clk_emc *emc = to_tegra210_clk_emc(__clk_get_hw(clk)); module_put(emc->provider->owner); emc->provider = NULL; } EXPORT_SYMBOL_GPL(tegra210_clk_emc_detach); |