123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Based on drivers/clk/tegra/clk-emc.c
- * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
- *
- * Author: Dmitry Osipenko <[email protected]>
- * Copyright (C) 2019 GRATE-DRIVER project
- */
- #define pr_fmt(fmt) "tegra-emc-clk: " fmt
- #include <linux/bits.h>
- #include <linux/clk-provider.h>
- #include <linux/clk/tegra.h>
- #include <linux/err.h>
- #include <linux/export.h>
- #include <linux/io.h>
- #include <linux/kernel.h>
- #include <linux/slab.h>
- #include "clk.h"
- #define CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK GENMASK(7, 0)
- #define CLK_SOURCE_EMC_2X_CLK_SRC_MASK GENMASK(31, 30)
- #define CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT 30
- #define MC_EMC_SAME_FREQ BIT(16)
- #define USE_PLLM_UD BIT(29)
- #define EMC_SRC_PLL_M 0
- #define EMC_SRC_PLL_C 1
- #define EMC_SRC_PLL_P 2
- #define EMC_SRC_CLK_M 3
- static const char * const emc_parent_clk_names[] = {
- "pll_m", "pll_c", "pll_p", "clk_m",
- };
- struct tegra_clk_emc {
- struct clk_hw hw;
- void __iomem *reg;
- bool mc_same_freq;
- bool want_low_jitter;
- tegra20_clk_emc_round_cb *round_cb;
- void *cb_arg;
- };
- static inline struct tegra_clk_emc *to_tegra_clk_emc(struct clk_hw *hw)
- {
- return container_of(hw, struct tegra_clk_emc, hw);
- }
- static unsigned long emc_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
- {
- struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
- u32 val, div;
- val = readl_relaxed(emc->reg);
- div = val & CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
- return DIV_ROUND_UP(parent_rate * 2, div + 2);
- }
- static u8 emc_get_parent(struct clk_hw *hw)
- {
- struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
- return readl_relaxed(emc->reg) >> CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
- }
- static int emc_set_parent(struct clk_hw *hw, u8 index)
- {
- struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
- u32 val, div;
- val = readl_relaxed(emc->reg);
- val &= ~CLK_SOURCE_EMC_2X_CLK_SRC_MASK;
- val |= index << CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
- div = val & CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
- if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
- val |= USE_PLLM_UD;
- else
- val &= ~USE_PLLM_UD;
- if (emc->mc_same_freq)
- val |= MC_EMC_SAME_FREQ;
- else
- val &= ~MC_EMC_SAME_FREQ;
- writel_relaxed(val, emc->reg);
- fence_udelay(1, emc->reg);
- return 0;
- }
- static int emc_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
- {
- struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
- unsigned int index;
- u32 val, div;
- div = div_frac_get(rate, parent_rate, 8, 1, 0);
- val = readl_relaxed(emc->reg);
- val &= ~CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
- val |= div;
- index = val >> CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
- if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
- val |= USE_PLLM_UD;
- else
- val &= ~USE_PLLM_UD;
- if (emc->mc_same_freq)
- val |= MC_EMC_SAME_FREQ;
- else
- val &= ~MC_EMC_SAME_FREQ;
- writel_relaxed(val, emc->reg);
- fence_udelay(1, emc->reg);
- return 0;
- }
- static int emc_set_rate_and_parent(struct clk_hw *hw,
- unsigned long rate,
- unsigned long parent_rate,
- u8 index)
- {
- struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
- u32 val, div;
- div = div_frac_get(rate, parent_rate, 8, 1, 0);
- val = readl_relaxed(emc->reg);
- val &= ~CLK_SOURCE_EMC_2X_CLK_SRC_MASK;
- val |= index << CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
- val &= ~CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
- val |= div;
- if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
- val |= USE_PLLM_UD;
- else
- val &= ~USE_PLLM_UD;
- if (emc->mc_same_freq)
- val |= MC_EMC_SAME_FREQ;
- else
- val &= ~MC_EMC_SAME_FREQ;
- writel_relaxed(val, emc->reg);
- fence_udelay(1, emc->reg);
- return 0;
- }
- static int emc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
- {
- struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
- struct clk_hw *parent_hw;
- unsigned long divided_rate;
- unsigned long parent_rate;
- unsigned int i;
- long emc_rate;
- int div;
- emc_rate = emc->round_cb(req->rate, req->min_rate, req->max_rate,
- emc->cb_arg);
- if (emc_rate < 0)
- return emc_rate;
- for (i = 0; i < ARRAY_SIZE(emc_parent_clk_names); i++) {
- parent_hw = clk_hw_get_parent_by_index(hw, i);
- if (req->best_parent_hw == parent_hw)
- parent_rate = req->best_parent_rate;
- else
- parent_rate = clk_hw_get_rate(parent_hw);
- if (emc_rate > parent_rate)
- continue;
- div = div_frac_get(emc_rate, parent_rate, 8, 1, 0);
- divided_rate = DIV_ROUND_UP(parent_rate * 2, div + 2);
- if (divided_rate != emc_rate)
- continue;
- req->best_parent_rate = parent_rate;
- req->best_parent_hw = parent_hw;
- req->rate = emc_rate;
- break;
- }
- if (i == ARRAY_SIZE(emc_parent_clk_names)) {
- pr_err_once("can't find parent for rate %lu emc_rate %lu\n",
- req->rate, emc_rate);
- return -EINVAL;
- }
- return 0;
- }
- static const struct clk_ops tegra_clk_emc_ops = {
- .recalc_rate = emc_recalc_rate,
- .get_parent = emc_get_parent,
- .set_parent = emc_set_parent,
- .set_rate = emc_set_rate,
- .set_rate_and_parent = emc_set_rate_and_parent,
- .determine_rate = emc_determine_rate,
- };
- void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb,
- void *cb_arg)
- {
- struct clk *clk = __clk_lookup("emc");
- struct tegra_clk_emc *emc;
- struct clk_hw *hw;
- if (clk) {
- hw = __clk_get_hw(clk);
- emc = to_tegra_clk_emc(hw);
- emc->round_cb = round_cb;
- emc->cb_arg = cb_arg;
- }
- }
- EXPORT_SYMBOL_GPL(tegra20_clk_set_emc_round_callback);
- bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw)
- {
- return to_tegra_clk_emc(emc_hw)->round_cb != NULL;
- }
- struct clk *tegra20_clk_register_emc(void __iomem *ioaddr, bool low_jitter)
- {
- struct tegra_clk_emc *emc;
- struct clk_init_data init;
- struct clk *clk;
- emc = kzalloc(sizeof(*emc), GFP_KERNEL);
- if (!emc)
- return NULL;
- /*
- * EMC stands for External Memory Controller.
- *
- * We don't want EMC clock to be disabled ever by gating its
- * parent and whatnot because system is busted immediately in that
- * case, hence the clock is marked as critical.
- */
- init.name = "emc";
- init.ops = &tegra_clk_emc_ops;
- init.flags = CLK_IS_CRITICAL;
- init.parent_names = emc_parent_clk_names;
- init.num_parents = ARRAY_SIZE(emc_parent_clk_names);
- emc->reg = ioaddr;
- emc->hw.init = &init;
- emc->want_low_jitter = low_jitter;
- clk = clk_register(NULL, &emc->hw);
- if (IS_ERR(clk)) {
- kfree(emc);
- return NULL;
- }
- return clk;
- }
- int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same)
- {
- struct tegra_clk_emc *emc;
- struct clk_hw *hw;
- if (!emc_clk)
- return -EINVAL;
- hw = __clk_get_hw(emc_clk);
- emc = to_tegra_clk_emc(hw);
- emc->mc_same_freq = same;
- return 0;
- }
- EXPORT_SYMBOL_GPL(tegra20_clk_prepare_emc_mc_same_freq);
|