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