linux/drivers/clk/tegra/clk-tegra20-emc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Based on drivers/clk/tegra/clk-emc.c
   4 * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
   5 *
   6 * Author: Dmitry Osipenko <digetx@gmail.com>
   7 * Copyright (C) 2019 GRATE-DRIVER project
   8 */
   9
  10#define pr_fmt(fmt)     "tegra-emc-clk: " fmt
  11
  12#include <linux/bits.h>
  13#include <linux/clk-provider.h>
  14#include <linux/clk/tegra.h>
  15#include <linux/err.h>
  16#include <linux/export.h>
  17#include <linux/io.h>
  18#include <linux/kernel.h>
  19#include <linux/slab.h>
  20
  21#include "clk.h"
  22
  23#define CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK      GENMASK(7, 0)
  24#define CLK_SOURCE_EMC_2X_CLK_SRC_MASK          GENMASK(31, 30)
  25#define CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT         30
  26
  27#define MC_EMC_SAME_FREQ        BIT(16)
  28#define USE_PLLM_UD             BIT(29)
  29
  30#define EMC_SRC_PLL_M           0
  31#define EMC_SRC_PLL_C           1
  32#define EMC_SRC_PLL_P           2
  33#define EMC_SRC_CLK_M           3
  34
  35static const char * const emc_parent_clk_names[] = {
  36        "pll_m", "pll_c", "pll_p", "clk_m",
  37};
  38
  39struct tegra_clk_emc {
  40        struct clk_hw hw;
  41        void __iomem *reg;
  42        bool mc_same_freq;
  43        bool want_low_jitter;
  44
  45        tegra20_clk_emc_round_cb *round_cb;
  46        void *cb_arg;
  47};
  48
  49static inline struct tegra_clk_emc *to_tegra_clk_emc(struct clk_hw *hw)
  50{
  51        return container_of(hw, struct tegra_clk_emc, hw);
  52}
  53
  54static unsigned long emc_recalc_rate(struct clk_hw *hw,
  55                                     unsigned long parent_rate)
  56{
  57        struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
  58        u32 val, div;
  59
  60        val = readl_relaxed(emc->reg);
  61        div = val & CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
  62
  63        return DIV_ROUND_UP(parent_rate * 2, div + 2);
  64}
  65
  66static u8 emc_get_parent(struct clk_hw *hw)
  67{
  68        struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
  69
  70        return readl_relaxed(emc->reg) >> CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
  71}
  72
  73static int emc_set_parent(struct clk_hw *hw, u8 index)
  74{
  75        struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
  76        u32 val, div;
  77
  78        val = readl_relaxed(emc->reg);
  79        val &= ~CLK_SOURCE_EMC_2X_CLK_SRC_MASK;
  80        val |= index << CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
  81
  82        div = val & CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
  83
  84        if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
  85                val |= USE_PLLM_UD;
  86        else
  87                val &= ~USE_PLLM_UD;
  88
  89        if (emc->mc_same_freq)
  90                val |= MC_EMC_SAME_FREQ;
  91        else
  92                val &= ~MC_EMC_SAME_FREQ;
  93
  94        writel_relaxed(val, emc->reg);
  95
  96        fence_udelay(1, emc->reg);
  97
  98        return 0;
  99}
 100
 101static int emc_set_rate(struct clk_hw *hw, unsigned long rate,
 102                        unsigned long parent_rate)
 103{
 104        struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
 105        unsigned int index;
 106        u32 val, div;
 107
 108        div = div_frac_get(rate, parent_rate, 8, 1, 0);
 109
 110        val = readl_relaxed(emc->reg);
 111        val &= ~CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
 112        val |= div;
 113
 114        index = val >> CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
 115
 116        if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
 117                val |= USE_PLLM_UD;
 118        else
 119                val &= ~USE_PLLM_UD;
 120
 121        if (emc->mc_same_freq)
 122                val |= MC_EMC_SAME_FREQ;
 123        else
 124                val &= ~MC_EMC_SAME_FREQ;
 125
 126        writel_relaxed(val, emc->reg);
 127
 128        fence_udelay(1, emc->reg);
 129
 130        return 0;
 131}
 132
 133static int emc_set_rate_and_parent(struct clk_hw *hw,
 134                                   unsigned long rate,
 135                                   unsigned long parent_rate,
 136                                   u8 index)
 137{
 138        struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
 139        u32 val, div;
 140
 141        div = div_frac_get(rate, parent_rate, 8, 1, 0);
 142
 143        val = readl_relaxed(emc->reg);
 144
 145        val &= ~CLK_SOURCE_EMC_2X_CLK_SRC_MASK;
 146        val |= index << CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
 147
 148        val &= ~CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
 149        val |= div;
 150
 151        if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
 152                val |= USE_PLLM_UD;
 153        else
 154                val &= ~USE_PLLM_UD;
 155
 156        if (emc->mc_same_freq)
 157                val |= MC_EMC_SAME_FREQ;
 158        else
 159                val &= ~MC_EMC_SAME_FREQ;
 160
 161        writel_relaxed(val, emc->reg);
 162
 163        fence_udelay(1, emc->reg);
 164
 165        return 0;
 166}
 167
 168static int emc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
 169{
 170        struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
 171        struct clk_hw *parent_hw;
 172        unsigned long divided_rate;
 173        unsigned long parent_rate;
 174        unsigned int i;
 175        long emc_rate;
 176        int div;
 177
 178        emc_rate = emc->round_cb(req->rate, req->min_rate, req->max_rate,
 179                                 emc->cb_arg);
 180        if (emc_rate < 0)
 181                return emc_rate;
 182
 183        for (i = 0; i < ARRAY_SIZE(emc_parent_clk_names); i++) {
 184                parent_hw = clk_hw_get_parent_by_index(hw, i);
 185
 186                if (req->best_parent_hw == parent_hw)
 187                        parent_rate = req->best_parent_rate;
 188                else
 189                        parent_rate = clk_hw_get_rate(parent_hw);
 190
 191                if (emc_rate > parent_rate)
 192                        continue;
 193
 194                div = div_frac_get(emc_rate, parent_rate, 8, 1, 0);
 195                divided_rate = DIV_ROUND_UP(parent_rate * 2, div + 2);
 196
 197                if (divided_rate != emc_rate)
 198                        continue;
 199
 200                req->best_parent_rate = parent_rate;
 201                req->best_parent_hw = parent_hw;
 202                req->rate = emc_rate;
 203                break;
 204        }
 205
 206        if (i == ARRAY_SIZE(emc_parent_clk_names)) {
 207                pr_err_once("can't find parent for rate %lu emc_rate %lu\n",
 208                            req->rate, emc_rate);
 209                return -EINVAL;
 210        }
 211
 212        return 0;
 213}
 214
 215static const struct clk_ops tegra_clk_emc_ops = {
 216        .recalc_rate = emc_recalc_rate,
 217        .get_parent = emc_get_parent,
 218        .set_parent = emc_set_parent,
 219        .set_rate = emc_set_rate,
 220        .set_rate_and_parent = emc_set_rate_and_parent,
 221        .determine_rate = emc_determine_rate,
 222};
 223
 224void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb,
 225                                        void *cb_arg)
 226{
 227        struct clk *clk = __clk_lookup("emc");
 228        struct tegra_clk_emc *emc;
 229        struct clk_hw *hw;
 230
 231        if (clk) {
 232                hw = __clk_get_hw(clk);
 233                emc = to_tegra_clk_emc(hw);
 234
 235                emc->round_cb = round_cb;
 236                emc->cb_arg = cb_arg;
 237        }
 238}
 239EXPORT_SYMBOL_GPL(tegra20_clk_set_emc_round_callback);
 240
 241bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw)
 242{
 243        return to_tegra_clk_emc(emc_hw)->round_cb != NULL;
 244}
 245
 246struct clk *tegra20_clk_register_emc(void __iomem *ioaddr, bool low_jitter)
 247{
 248        struct tegra_clk_emc *emc;
 249        struct clk_init_data init;
 250        struct clk *clk;
 251
 252        emc = kzalloc(sizeof(*emc), GFP_KERNEL);
 253        if (!emc)
 254                return NULL;
 255
 256        /*
 257         * EMC stands for External Memory Controller.
 258         *
 259         * We don't want EMC clock to be disabled ever by gating its
 260         * parent and whatnot because system is busted immediately in that
 261         * case, hence the clock is marked as critical.
 262         */
 263        init.name = "emc";
 264        init.ops = &tegra_clk_emc_ops;
 265        init.flags = CLK_IS_CRITICAL;
 266        init.parent_names = emc_parent_clk_names;
 267        init.num_parents = ARRAY_SIZE(emc_parent_clk_names);
 268
 269        emc->reg = ioaddr;
 270        emc->hw.init = &init;
 271        emc->want_low_jitter = low_jitter;
 272
 273        clk = clk_register(NULL, &emc->hw);
 274        if (IS_ERR(clk)) {
 275                kfree(emc);
 276                return NULL;
 277        }
 278
 279        return clk;
 280}
 281
 282int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same)
 283{
 284        struct tegra_clk_emc *emc;
 285        struct clk_hw *hw;
 286
 287        if (!emc_clk)
 288                return -EINVAL;
 289
 290        hw = __clk_get_hw(emc_clk);
 291        emc = to_tegra_clk_emc(hw);
 292        emc->mc_same_freq = same;
 293
 294        return 0;
 295}
 296EXPORT_SYMBOL_GPL(tegra20_clk_prepare_emc_mc_same_freq);
 297