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