Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2015-2020, NVIDIA CORPORATION.  All rights reserved.
0004  */
0005 
0006 #include <linux/bitfield.h>
0007 #include <linux/clk.h>
0008 #include <linux/clk-provider.h>
0009 #include <linux/clk/tegra.h>
0010 #include <linux/device.h>
0011 #include <linux/module.h>
0012 #include <linux/io.h>
0013 #include <linux/slab.h>
0014 
0015 #include "clk.h"
0016 
0017 #define CLK_SOURCE_EMC 0x19c
0018 #define  CLK_SOURCE_EMC_2X_CLK_SRC GENMASK(31, 29)
0019 #define  CLK_SOURCE_EMC_MC_EMC_SAME_FREQ BIT(16)
0020 #define  CLK_SOURCE_EMC_2X_CLK_DIVISOR GENMASK(7, 0)
0021 
0022 #define CLK_SRC_PLLM 0
0023 #define CLK_SRC_PLLC 1
0024 #define CLK_SRC_PLLP 2
0025 #define CLK_SRC_CLK_M 3
0026 #define CLK_SRC_PLLM_UD 4
0027 #define CLK_SRC_PLLMB_UD 5
0028 #define CLK_SRC_PLLMB 6
0029 #define CLK_SRC_PLLP_UD 7
0030 
0031 struct tegra210_clk_emc {
0032     struct clk_hw hw;
0033     void __iomem *regs;
0034 
0035     struct tegra210_clk_emc_provider *provider;
0036 
0037     struct clk *parents[8];
0038 };
0039 
0040 static inline struct tegra210_clk_emc *
0041 to_tegra210_clk_emc(struct clk_hw *hw)
0042 {
0043     return container_of(hw, struct tegra210_clk_emc, hw);
0044 }
0045 
0046 static const char *tegra210_clk_emc_parents[] = {
0047     "pll_m", "pll_c", "pll_p", "clk_m", "pll_m_ud", "pll_mb_ud",
0048     "pll_mb", "pll_p_ud",
0049 };
0050 
0051 static u8 tegra210_clk_emc_get_parent(struct clk_hw *hw)
0052 {
0053     struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
0054     u32 value;
0055     u8 src;
0056 
0057     value = readl_relaxed(emc->regs + CLK_SOURCE_EMC);
0058     src = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, value);
0059 
0060     return src;
0061 }
0062 
0063 static unsigned long tegra210_clk_emc_recalc_rate(struct clk_hw *hw,
0064                           unsigned long parent_rate)
0065 {
0066     struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
0067     u32 value, div;
0068 
0069     /*
0070      * CCF assumes that neither the parent nor its rate will change during
0071      * ->set_rate(), so the parent rate passed in here was cached from the
0072      * parent before the ->set_rate() call.
0073      *
0074      * This can lead to wrong results being reported for the EMC clock if
0075      * the parent and/or parent rate have changed as part of the EMC rate
0076      * change sequence. Fix this by overriding the parent clock with what
0077      * we know to be the correct value after the rate change.
0078      */
0079     parent_rate = clk_hw_get_rate(clk_hw_get_parent(hw));
0080 
0081     value = readl_relaxed(emc->regs + CLK_SOURCE_EMC);
0082 
0083     div = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_DIVISOR, value);
0084     div += 2;
0085 
0086     return DIV_ROUND_UP(parent_rate * 2, div);
0087 }
0088 
0089 static long tegra210_clk_emc_round_rate(struct clk_hw *hw, unsigned long rate,
0090                     unsigned long *prate)
0091 {
0092     struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
0093     struct tegra210_clk_emc_provider *provider = emc->provider;
0094     unsigned int i;
0095 
0096     if (!provider || !provider->configs || provider->num_configs == 0)
0097         return clk_hw_get_rate(hw);
0098 
0099     for (i = 0; i < provider->num_configs; i++) {
0100         if (provider->configs[i].rate >= rate)
0101             return provider->configs[i].rate;
0102     }
0103 
0104     return provider->configs[i - 1].rate;
0105 }
0106 
0107 static struct clk *tegra210_clk_emc_find_parent(struct tegra210_clk_emc *emc,
0108                         u8 index)
0109 {
0110     struct clk_hw *parent = clk_hw_get_parent_by_index(&emc->hw, index);
0111     const char *name = clk_hw_get_name(parent);
0112 
0113     /* XXX implement cache? */
0114 
0115     return __clk_lookup(name);
0116 }
0117 
0118 static int tegra210_clk_emc_set_rate(struct clk_hw *hw, unsigned long rate,
0119                      unsigned long parent_rate)
0120 {
0121     struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
0122     struct tegra210_clk_emc_provider *provider = emc->provider;
0123     struct tegra210_clk_emc_config *config;
0124     struct device *dev = provider->dev;
0125     struct clk_hw *old, *new, *parent;
0126     u8 old_idx, new_idx, index;
0127     struct clk *clk;
0128     unsigned int i;
0129     int err;
0130 
0131     if (!provider->configs || provider->num_configs == 0)
0132         return -EINVAL;
0133 
0134     for (i = 0; i < provider->num_configs; i++) {
0135         if (provider->configs[i].rate >= rate) {
0136             config = &provider->configs[i];
0137             break;
0138         }
0139     }
0140 
0141     if (i == provider->num_configs)
0142         config = &provider->configs[i - 1];
0143 
0144     old_idx = tegra210_clk_emc_get_parent(hw);
0145     new_idx = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, config->value);
0146 
0147     old = clk_hw_get_parent_by_index(hw, old_idx);
0148     new = clk_hw_get_parent_by_index(hw, new_idx);
0149 
0150     /* if the rate has changed... */
0151     if (config->parent_rate != clk_hw_get_rate(old)) {
0152         /* ... but the clock source remains the same ... */
0153         if (new_idx == old_idx) {
0154             /* ... switch to the alternative clock source. */
0155             switch (new_idx) {
0156             case CLK_SRC_PLLM:
0157                 new_idx = CLK_SRC_PLLMB;
0158                 break;
0159 
0160             case CLK_SRC_PLLM_UD:
0161                 new_idx = CLK_SRC_PLLMB_UD;
0162                 break;
0163 
0164             case CLK_SRC_PLLMB_UD:
0165                 new_idx = CLK_SRC_PLLM_UD;
0166                 break;
0167 
0168             case CLK_SRC_PLLMB:
0169                 new_idx = CLK_SRC_PLLM;
0170                 break;
0171             }
0172 
0173             /*
0174              * This should never happen because we can't deal with
0175              * it.
0176              */
0177             if (WARN_ON(new_idx == old_idx))
0178                 return -EINVAL;
0179 
0180             new = clk_hw_get_parent_by_index(hw, new_idx);
0181         }
0182 
0183         index = new_idx;
0184         parent = new;
0185     } else {
0186         index = old_idx;
0187         parent = old;
0188     }
0189 
0190     clk = tegra210_clk_emc_find_parent(emc, index);
0191     if (IS_ERR(clk)) {
0192         err = PTR_ERR(clk);
0193         dev_err(dev, "failed to get parent clock for index %u: %d\n",
0194             index, err);
0195         return err;
0196     }
0197 
0198     /* set the new parent clock to the required rate */
0199     if (clk_get_rate(clk) != config->parent_rate) {
0200         err = clk_set_rate(clk, config->parent_rate);
0201         if (err < 0) {
0202             dev_err(dev, "failed to set rate %lu Hz for %pC: %d\n",
0203                 config->parent_rate, clk, err);
0204             return err;
0205         }
0206     }
0207 
0208     /* enable the new parent clock */
0209     if (parent != old) {
0210         err = clk_prepare_enable(clk);
0211         if (err < 0) {
0212             dev_err(dev, "failed to enable parent clock %pC: %d\n",
0213                 clk, err);
0214             return err;
0215         }
0216     }
0217 
0218     /* update the EMC source configuration to reflect the new parent */
0219     config->value &= ~CLK_SOURCE_EMC_2X_CLK_SRC;
0220     config->value |= FIELD_PREP(CLK_SOURCE_EMC_2X_CLK_SRC, index);
0221 
0222     /*
0223      * Finally, switch the EMC programming with both old and new parent
0224      * clocks enabled.
0225      */
0226     err = provider->set_rate(dev, config);
0227     if (err < 0) {
0228         dev_err(dev, "failed to set EMC rate to %lu Hz: %d\n", rate,
0229             err);
0230 
0231         /*
0232          * If we're unable to switch to the new EMC frequency, we no
0233          * longer need the new parent to be enabled.
0234          */
0235         if (parent != old)
0236             clk_disable_unprepare(clk);
0237 
0238         return err;
0239     }
0240 
0241     /* reparent to new parent clock and disable the old parent clock */
0242     if (parent != old) {
0243         clk = tegra210_clk_emc_find_parent(emc, old_idx);
0244         if (IS_ERR(clk)) {
0245             err = PTR_ERR(clk);
0246             dev_err(dev,
0247                 "failed to get parent clock for index %u: %d\n",
0248                 old_idx, err);
0249             return err;
0250         }
0251 
0252         clk_hw_reparent(hw, parent);
0253         clk_disable_unprepare(clk);
0254     }
0255 
0256     return err;
0257 }
0258 
0259 static const struct clk_ops tegra210_clk_emc_ops = {
0260     .get_parent = tegra210_clk_emc_get_parent,
0261     .recalc_rate = tegra210_clk_emc_recalc_rate,
0262     .round_rate = tegra210_clk_emc_round_rate,
0263     .set_rate = tegra210_clk_emc_set_rate,
0264 };
0265 
0266 struct clk *tegra210_clk_register_emc(struct device_node *np,
0267                       void __iomem *regs)
0268 {
0269     struct tegra210_clk_emc *emc;
0270     struct clk_init_data init;
0271     struct clk *clk;
0272 
0273     emc = kzalloc(sizeof(*emc), GFP_KERNEL);
0274     if (!emc)
0275         return ERR_PTR(-ENOMEM);
0276 
0277     emc->regs = regs;
0278 
0279     init.name = "emc";
0280     init.ops = &tegra210_clk_emc_ops;
0281     init.flags = CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE;
0282     init.parent_names = tegra210_clk_emc_parents;
0283     init.num_parents = ARRAY_SIZE(tegra210_clk_emc_parents);
0284     emc->hw.init = &init;
0285 
0286     clk = clk_register(NULL, &emc->hw);
0287     if (IS_ERR(clk)) {
0288         kfree(emc);
0289         return clk;
0290     }
0291 
0292     return clk;
0293 }
0294 
0295 int tegra210_clk_emc_attach(struct clk *clk,
0296                 struct tegra210_clk_emc_provider *provider)
0297 {
0298     struct clk_hw *hw = __clk_get_hw(clk);
0299     struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
0300     struct device *dev = provider->dev;
0301     unsigned int i;
0302     int err;
0303 
0304     if (!try_module_get(provider->owner))
0305         return -ENODEV;
0306 
0307     for (i = 0; i < provider->num_configs; i++) {
0308         struct tegra210_clk_emc_config *config = &provider->configs[i];
0309         struct clk_hw *parent;
0310         bool same_freq;
0311         u8 div, src;
0312 
0313         div = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_DIVISOR, config->value);
0314         src = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, config->value);
0315 
0316         /* do basic sanity checking on the EMC timings */
0317         if (div & 0x1) {
0318             dev_err(dev, "invalid odd divider %u for rate %lu Hz\n",
0319                 div, config->rate);
0320             err = -EINVAL;
0321             goto put;
0322         }
0323 
0324         same_freq = config->value & CLK_SOURCE_EMC_MC_EMC_SAME_FREQ;
0325 
0326         if (same_freq != config->same_freq) {
0327             dev_err(dev,
0328                 "ambiguous EMC to MC ratio for rate %lu Hz\n",
0329                 config->rate);
0330             err = -EINVAL;
0331             goto put;
0332         }
0333 
0334         parent = clk_hw_get_parent_by_index(hw, src);
0335         config->parent = src;
0336 
0337         if (src == CLK_SRC_PLLM || src == CLK_SRC_PLLM_UD) {
0338             config->parent_rate = config->rate * (1 + div / 2);
0339         } else {
0340             unsigned long rate = config->rate * (1 + div / 2);
0341 
0342             config->parent_rate = clk_hw_get_rate(parent);
0343 
0344             if (config->parent_rate != rate) {
0345                 dev_err(dev,
0346                     "rate %lu Hz does not match input\n",
0347                     config->rate);
0348                 err = -EINVAL;
0349                 goto put;
0350             }
0351         }
0352     }
0353 
0354     emc->provider = provider;
0355 
0356     return 0;
0357 
0358 put:
0359     module_put(provider->owner);
0360     return err;
0361 }
0362 EXPORT_SYMBOL_GPL(tegra210_clk_emc_attach);
0363 
0364 void tegra210_clk_emc_detach(struct clk *clk)
0365 {
0366     struct tegra210_clk_emc *emc = to_tegra210_clk_emc(__clk_get_hw(clk));
0367 
0368     module_put(emc->provider->owner);
0369     emc->provider = NULL;
0370 }
0371 EXPORT_SYMBOL_GPL(tegra210_clk_emc_detach);