Commit c60037f0 authored by Stephen Boyd's avatar Stephen Boyd

Merge tag 'for-5.8-clk' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux into clk-tegra

Pull Tegra clk driver updates from Thierry Reding:

These are a couple of changes to implement EMC frequency scaling on
Tegra210, CPU frequency scaling on Tegra20 and Tegra30 as well as a
special clock gate for the CSI test pattern generator on Tegra210.

* tag 'for-5.8-clk' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux:
  clk: tegra: Add Tegra210 CSI TPG clock gate
  clk: tegra30: Use custom CCLK implementation
  clk: tegra20: Use custom CCLK implementation
  clk: tegra: cclk: Add helpers for handling PLLX rate changes
  clk: tegra: pll: Add pre/post rate-change hooks
  clk: tegra: Add custom CCLK implementation
  clk: tegra: Remove the old emc_mux clock for Tegra210
  clk: tegra: Implement Tegra210 EMC clock
  clk: tegra: Export functions for EMC clock scaling
  clk: tegra: Add PLLP_UD and PLLMB_UD for Tegra210
  clk: tegra: Rename Tegra124 EMC clock source file
  dt-bindings: clock: tegra: Add clock ID for CSI TPG clock
parents 8f3d9f35 dec39632
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
config TEGRA_CLK_EMC
def_bool y
depends on TEGRA124_EMC
config CLK_TEGRA_BPMP config CLK_TEGRA_BPMP
def_bool y def_bool y
depends on TEGRA_BPMP depends on TEGRA_BPMP
......
...@@ -13,8 +13,8 @@ obj-y += clk-super.o ...@@ -13,8 +13,8 @@ obj-y += clk-super.o
obj-y += clk-tegra-audio.o obj-y += clk-tegra-audio.o
obj-y += clk-tegra-periph.o obj-y += clk-tegra-periph.o
obj-y += clk-tegra-fixed.o obj-y += clk-tegra-fixed.o
obj-y += clk-tegra-super-cclk.o
obj-y += clk-tegra-super-gen4.o obj-y += clk-tegra-super-gen4.o
obj-$(CONFIG_TEGRA_CLK_EMC) += clk-emc.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20-emc.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20-emc.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o
...@@ -22,8 +22,10 @@ obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra20-emc.o ...@@ -22,8 +22,10 @@ obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra20-emc.o
obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o
obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o
obj-$(CONFIG_TEGRA_CLK_DFLL) += clk-tegra124-dfll-fcpu.o obj-$(CONFIG_TEGRA_CLK_DFLL) += clk-tegra124-dfll-fcpu.o
obj-$(CONFIG_TEGRA124_EMC) += clk-tegra124-emc.o
obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o
obj-y += cvb.o obj-y += cvb.o
obj-$(CONFIG_ARCH_TEGRA_210_SOC) += clk-tegra210.o obj-$(CONFIG_ARCH_TEGRA_210_SOC) += clk-tegra210.o
obj-$(CONFIG_ARCH_TEGRA_210_SOC) += clk-tegra210-emc.o
obj-$(CONFIG_CLK_TEGRA_BPMP) += clk-bpmp.o obj-$(CONFIG_CLK_TEGRA_BPMP) += clk-bpmp.o
obj-y += clk-utils.o obj-y += clk-utils.o
...@@ -744,13 +744,19 @@ static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, ...@@ -744,13 +744,19 @@ static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg,
state = clk_pll_is_enabled(hw); state = clk_pll_is_enabled(hw);
if (state && pll->params->pre_rate_change) {
ret = pll->params->pre_rate_change();
if (WARN_ON(ret))
return ret;
}
_get_pll_mnp(pll, &old_cfg); _get_pll_mnp(pll, &old_cfg);
if (state && pll->params->defaults_set && pll->params->dyn_ramp && if (state && pll->params->defaults_set && pll->params->dyn_ramp &&
(cfg->m == old_cfg.m) && (cfg->p == old_cfg.p)) { (cfg->m == old_cfg.m) && (cfg->p == old_cfg.p)) {
ret = pll->params->dyn_ramp(pll, cfg); ret = pll->params->dyn_ramp(pll, cfg);
if (!ret) if (!ret)
return 0; goto done;
} }
if (state) { if (state) {
...@@ -772,6 +778,10 @@ static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, ...@@ -772,6 +778,10 @@ static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg,
pll_clk_start_ss(pll); pll_clk_start_ss(pll);
} }
done:
if (state && pll->params->post_rate_change)
pll->params->post_rate_change();
return ret; return ret;
} }
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Based on clk-super.c
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
*
* Based on older tegra20-cpufreq driver by Colin Cross <ccross@google.com>
* Copyright (C) 2010 Google, Inc.
*
* Author: Dmitry Osipenko <digetx@gmail.com>
* Copyright (C) 2019 GRATE-DRIVER project
*/
#include <linux/bits.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/types.h>
#include "clk.h"
#define PLLP_INDEX 4
#define PLLX_INDEX 8
#define SUPER_CDIV_ENB BIT(31)
static struct tegra_clk_super_mux *cclk_super;
static bool cclk_on_pllx;
static u8 cclk_super_get_parent(struct clk_hw *hw)
{
return tegra_clk_super_ops.get_parent(hw);
}
static int cclk_super_set_parent(struct clk_hw *hw, u8 index)
{
return tegra_clk_super_ops.set_parent(hw, index);
}
static int cclk_super_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
return tegra_clk_super_ops.set_rate(hw, rate, parent_rate);
}
static unsigned long cclk_super_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
if (cclk_super_get_parent(hw) == PLLX_INDEX)
return parent_rate;
return tegra_clk_super_ops.recalc_rate(hw, parent_rate);
}
static int cclk_super_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_hw *pllp_hw = clk_hw_get_parent_by_index(hw, PLLP_INDEX);
struct clk_hw *pllx_hw = clk_hw_get_parent_by_index(hw, PLLX_INDEX);
struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
unsigned long pllp_rate;
long rate = req->rate;
if (WARN_ON_ONCE(!pllp_hw || !pllx_hw))
return -EINVAL;
/*
* Switch parent to PLLP for all CCLK rates that are suitable for PLLP.
* PLLX will be disabled in this case, saving some power.
*/
pllp_rate = clk_hw_get_rate(pllp_hw);
if (rate <= pllp_rate) {
if (super->flags & TEGRA20_SUPER_CLK)
rate = pllp_rate;
else
rate = tegra_clk_super_ops.round_rate(hw, rate,
&pllp_rate);
req->best_parent_rate = pllp_rate;
req->best_parent_hw = pllp_hw;
req->rate = rate;
} else {
rate = clk_hw_round_rate(pllx_hw, rate);
req->best_parent_rate = rate;
req->best_parent_hw = pllx_hw;
req->rate = rate;
}
if (WARN_ON_ONCE(rate <= 0))
return -EINVAL;
return 0;
}
static const struct clk_ops tegra_cclk_super_ops = {
.get_parent = cclk_super_get_parent,
.set_parent = cclk_super_set_parent,
.set_rate = cclk_super_set_rate,
.recalc_rate = cclk_super_recalc_rate,
.determine_rate = cclk_super_determine_rate,
};
static const struct clk_ops tegra_cclk_super_mux_ops = {
.get_parent = cclk_super_get_parent,
.set_parent = cclk_super_set_parent,
.determine_rate = cclk_super_determine_rate,
};
struct clk *tegra_clk_register_super_cclk(const char *name,
const char * const *parent_names, u8 num_parents,
unsigned long flags, void __iomem *reg, u8 clk_super_flags,
spinlock_t *lock)
{
struct tegra_clk_super_mux *super;
struct clk *clk;
struct clk_init_data init;
u32 val;
if (WARN_ON(cclk_super))
return ERR_PTR(-EBUSY);
super = kzalloc(sizeof(*super), GFP_KERNEL);
if (!super)
return ERR_PTR(-ENOMEM);
init.name = name;
init.flags = flags;
init.parent_names = parent_names;
init.num_parents = num_parents;
super->reg = reg;
super->lock = lock;
super->width = 4;
super->flags = clk_super_flags;
super->hw.init = &init;
if (super->flags & TEGRA20_SUPER_CLK) {
init.ops = &tegra_cclk_super_mux_ops;
} else {
init.ops = &tegra_cclk_super_ops;
super->frac_div.reg = reg + 4;
super->frac_div.shift = 16;
super->frac_div.width = 8;
super->frac_div.frac_width = 1;
super->frac_div.lock = lock;
super->div_ops = &tegra_clk_frac_div_ops;
}
/*
* Tegra30+ has the following CPUG clock topology:
*
* +---+ +-------+ +-+ +-+ +-+
* PLLP+->+ +->+DIVIDER+->+0| +-------->+0| ------------->+0|
* | | +-------+ | | | +---+ | | | | |
* PLLC+->+MUX| | +->+ | S | | +->+ | +->+CPU
* ... | | | | | | K | | | | +-------+ | |
* PLLX+->+-->+------------>+1| +->+ I +->+1| +->+ DIV2 +->+1|
* +---+ +++ | P | +++ |SKIPPER| +++
* ^ | P | ^ +-------+ ^
* | | E | | |
* PLLX_SEL+--+ | R | | OVERHEAT+--+
* +---+ |
* |
* SUPER_CDIV_ENB+--+
*
* Tegra20 is similar, but simpler. It doesn't have the divider and
* thermal DIV2 skipper.
*
* At least for now we're not going to use clock-skipper, hence let's
* ensure that it is disabled.
*/
val = readl_relaxed(reg + 4);
val &= ~SUPER_CDIV_ENB;
writel_relaxed(val, reg + 4);
clk = clk_register(NULL, &super->hw);
if (IS_ERR(clk))
kfree(super);
else
cclk_super = super;
return clk;
}
int tegra_cclk_pre_pllx_rate_change(void)
{
if (IS_ERR_OR_NULL(cclk_super))
return -EINVAL;
if (cclk_super_get_parent(&cclk_super->hw) == PLLX_INDEX)
cclk_on_pllx = true;
else
cclk_on_pllx = false;
/*
* CPU needs to be temporarily re-parented away from PLLX if PLLX
* changes its rate. PLLP is a safe parent for CPU on all Tegra SoCs.
*/
if (cclk_on_pllx)
cclk_super_set_parent(&cclk_super->hw, PLLP_INDEX);
return 0;
}
void tegra_cclk_post_pllx_rate_change(void)
{
if (cclk_on_pllx)
cclk_super_set_parent(&cclk_super->hw, PLLX_INDEX);
}
...@@ -391,6 +391,8 @@ static struct tegra_clk_pll_params pll_x_params = { ...@@ -391,6 +391,8 @@ static struct tegra_clk_pll_params pll_x_params = {
.lock_delay = 300, .lock_delay = 300,
.freq_table = pll_x_freq_table, .freq_table = pll_x_freq_table,
.flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_HAS_LOCK_ENABLE, .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_HAS_LOCK_ENABLE,
.pre_rate_change = tegra_cclk_pre_pllx_rate_change,
.post_rate_change = tegra_cclk_post_pllx_rate_change,
}; };
static struct tegra_clk_pll_params pll_e_params = { static struct tegra_clk_pll_params pll_e_params = {
...@@ -702,9 +704,10 @@ static void tegra20_super_clk_init(void) ...@@ -702,9 +704,10 @@ static void tegra20_super_clk_init(void)
struct clk *clk; struct clk *clk;
/* CCLK */ /* CCLK */
clk = tegra_clk_register_super_mux("cclk", cclk_parents, clk = tegra_clk_register_super_cclk("cclk", cclk_parents,
ARRAY_SIZE(cclk_parents), CLK_SET_RATE_PARENT, ARRAY_SIZE(cclk_parents), CLK_SET_RATE_PARENT,
clk_base + CCLK_BURST_POLICY, 0, 4, 0, 0, NULL); clk_base + CCLK_BURST_POLICY, TEGRA20_SUPER_CLK,
NULL);
clks[TEGRA20_CLK_CCLK] = clk; clks[TEGRA20_CLK_CCLK] = clk;
/* SCLK */ /* SCLK */
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2015-2020, NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clk/tegra.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/slab.h>
#define CLK_SOURCE_EMC 0x19c
#define CLK_SOURCE_EMC_2X_CLK_SRC GENMASK(31, 29)
#define CLK_SOURCE_EMC_MC_EMC_SAME_FREQ BIT(16)
#define CLK_SOURCE_EMC_2X_CLK_DIVISOR GENMASK(7, 0)
#define CLK_SRC_PLLM 0
#define CLK_SRC_PLLC 1
#define CLK_SRC_PLLP 2
#define CLK_SRC_CLK_M 3
#define CLK_SRC_PLLM_UD 4
#define CLK_SRC_PLLMB_UD 5
#define CLK_SRC_PLLMB 6
#define CLK_SRC_PLLP_UD 7
struct tegra210_clk_emc {
struct clk_hw hw;
void __iomem *regs;
struct tegra210_clk_emc_provider *provider;
struct clk *parents[8];
};
static inline struct tegra210_clk_emc *
to_tegra210_clk_emc(struct clk_hw *hw)
{
return container_of(hw, struct tegra210_clk_emc, hw);
}
static const char *tegra210_clk_emc_parents[] = {
"pll_m", "pll_c", "pll_p", "clk_m", "pll_m_ud", "pll_mb_ud",
"pll_mb", "pll_p_ud",
};
static u8 tegra210_clk_emc_get_parent(struct clk_hw *hw)
{
struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
u32 value;
u8 src;
value = readl_relaxed(emc->regs + CLK_SOURCE_EMC);
src = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, value);
return src;
}
static unsigned long tegra210_clk_emc_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
u32 value, div;
/*
* CCF assumes that neither the parent nor its rate will change during
* ->set_rate(), so the parent rate passed in here was cached from the
* parent before the ->set_rate() call.
*
* This can lead to wrong results being reported for the EMC clock if
* the parent and/or parent rate have changed as part of the EMC rate
* change sequence. Fix this by overriding the parent clock with what
* we know to be the correct value after the rate change.
*/
parent_rate = clk_hw_get_rate(clk_hw_get_parent(hw));
value = readl_relaxed(emc->regs + CLK_SOURCE_EMC);
div = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_DIVISOR, value);
div += 2;
return DIV_ROUND_UP(parent_rate * 2, div);
}
static long tegra210_clk_emc_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
struct tegra210_clk_emc_provider *provider = emc->provider;
unsigned int i;
if (!provider || !provider->configs || provider->num_configs == 0)
return clk_hw_get_rate(hw);
for (i = 0; i < provider->num_configs; i++) {
if (provider->configs[i].rate >= rate)
return provider->configs[i].rate;
}
return provider->configs[i - 1].rate;
}
static struct clk *tegra210_clk_emc_find_parent(struct tegra210_clk_emc *emc,
u8 index)
{
struct clk_hw *parent = clk_hw_get_parent_by_index(&emc->hw, index);
const char *name = clk_hw_get_name(parent);
/* XXX implement cache? */
return __clk_lookup(name);
}
static int tegra210_clk_emc_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
struct tegra210_clk_emc_provider *provider = emc->provider;
struct tegra210_clk_emc_config *config;
struct device *dev = provider->dev;
struct clk_hw *old, *new, *parent;
u8 old_idx, new_idx, index;
struct clk *clk;
unsigned int i;
int err;
if (!provider || !provider->configs || provider->num_configs == 0)
return -EINVAL;
for (i = 0; i < provider->num_configs; i++) {
if (provider->configs[i].rate >= rate) {
config = &provider->configs[i];
break;
}
}
if (i == provider->num_configs)
config = &provider->configs[i - 1];
old_idx = tegra210_clk_emc_get_parent(hw);
new_idx = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, config->value);
old = clk_hw_get_parent_by_index(hw, old_idx);
new = clk_hw_get_parent_by_index(hw, new_idx);
/* if the rate has changed... */
if (config->parent_rate != clk_hw_get_rate(old)) {
/* ... but the clock source remains the same ... */
if (new_idx == old_idx) {
/* ... switch to the alternative clock source. */
switch (new_idx) {
case CLK_SRC_PLLM:
new_idx = CLK_SRC_PLLMB;
break;
case CLK_SRC_PLLM_UD:
new_idx = CLK_SRC_PLLMB_UD;
break;
case CLK_SRC_PLLMB_UD:
new_idx = CLK_SRC_PLLM_UD;
break;
case CLK_SRC_PLLMB:
new_idx = CLK_SRC_PLLM;
break;
}
/*
* This should never happen because we can't deal with
* it.
*/
if (WARN_ON(new_idx == old_idx))
return -EINVAL;
new = clk_hw_get_parent_by_index(hw, new_idx);
}
index = new_idx;
parent = new;
} else {
index = old_idx;
parent = old;
}
clk = tegra210_clk_emc_find_parent(emc, index);
if (IS_ERR(clk)) {
err = PTR_ERR(clk);
dev_err(dev, "failed to get parent clock for index %u: %d\n",
index, err);
return err;
}
/* set the new parent clock to the required rate */
if (clk_get_rate(clk) != config->parent_rate) {
err = clk_set_rate(clk, config->parent_rate);
if (err < 0) {
dev_err(dev, "failed to set rate %lu Hz for %pC: %d\n",
config->parent_rate, clk, err);
return err;
}
}
/* enable the new parent clock */
if (parent != old) {
err = clk_prepare_enable(clk);
if (err < 0) {
dev_err(dev, "failed to enable parent clock %pC: %d\n",
clk, err);
return err;
}
}
/* update the EMC source configuration to reflect the new parent */
config->value &= ~CLK_SOURCE_EMC_2X_CLK_SRC;
config->value |= FIELD_PREP(CLK_SOURCE_EMC_2X_CLK_SRC, index);
/*
* Finally, switch the EMC programming with both old and new parent
* clocks enabled.
*/
err = provider->set_rate(dev, config);
if (err < 0) {
dev_err(dev, "failed to set EMC rate to %lu Hz: %d\n", rate,
err);
/*
* If we're unable to switch to the new EMC frequency, we no
* longer need the new parent to be enabled.
*/
if (parent != old)
clk_disable_unprepare(clk);
return err;
}
/* reparent to new parent clock and disable the old parent clock */
if (parent != old) {
clk = tegra210_clk_emc_find_parent(emc, old_idx);
if (IS_ERR(clk)) {
err = PTR_ERR(clk);
dev_err(dev,
"failed to get parent clock for index %u: %d\n",
old_idx, err);
return err;
}
clk_hw_reparent(hw, parent);
clk_disable_unprepare(clk);
}
return err;
}
static const struct clk_ops tegra210_clk_emc_ops = {
.get_parent = tegra210_clk_emc_get_parent,
.recalc_rate = tegra210_clk_emc_recalc_rate,
.round_rate = tegra210_clk_emc_round_rate,
.set_rate = tegra210_clk_emc_set_rate,
};
struct clk *tegra210_clk_register_emc(struct device_node *np,
void __iomem *regs)
{
struct tegra210_clk_emc *emc;
struct clk_init_data init;
struct clk *clk;
emc = kzalloc(sizeof(*emc), GFP_KERNEL);
if (!emc)
return ERR_PTR(-ENOMEM);
emc->regs = regs;
init.name = "emc";
init.ops = &tegra210_clk_emc_ops;
init.flags = CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE;
init.parent_names = tegra210_clk_emc_parents;
init.num_parents = ARRAY_SIZE(tegra210_clk_emc_parents);
emc->hw.init = &init;
clk = clk_register(NULL, &emc->hw);
if (IS_ERR(clk)) {
kfree(emc);
return clk;
}
return clk;
}
int tegra210_clk_emc_attach(struct clk *clk,
struct tegra210_clk_emc_provider *provider)
{
struct clk_hw *hw = __clk_get_hw(clk);
struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
struct device *dev = provider->dev;
unsigned int i;
int err;
if (!try_module_get(provider->owner))
return -ENODEV;
for (i = 0; i < provider->num_configs; i++) {
struct tegra210_clk_emc_config *config = &provider->configs[i];
struct clk_hw *parent;
bool same_freq;
u8 div, src;
div = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_DIVISOR, config->value);
src = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, config->value);
/* do basic sanity checking on the EMC timings */
if (div & 0x1) {
dev_err(dev, "invalid odd divider %u for rate %lu Hz\n",
div, config->rate);
err = -EINVAL;
goto put;
}
same_freq = config->value & CLK_SOURCE_EMC_MC_EMC_SAME_FREQ;
if (same_freq != config->same_freq) {
dev_err(dev,
"ambiguous EMC to MC ratio for rate %lu Hz\n",
config->rate);
err = -EINVAL;
goto put;
}
parent = clk_hw_get_parent_by_index(hw, src);
config->parent = src;
if (src == CLK_SRC_PLLM || src == CLK_SRC_PLLM_UD) {
config->parent_rate = config->rate * (1 + div / 2);
} else {
unsigned long rate = config->rate * (1 + div / 2);
config->parent_rate = clk_hw_get_rate(parent);
if (config->parent_rate != rate) {
dev_err(dev,
"rate %lu Hz does not match input\n",
config->rate);
err = -EINVAL;
goto put;
}
}
}
emc->provider = provider;
return 0;
put:
module_put(provider->owner);
return err;
}
EXPORT_SYMBOL_GPL(tegra210_clk_emc_attach);
void tegra210_clk_emc_detach(struct clk *clk)
{
struct tegra210_clk_emc *emc = to_tegra210_clk_emc(__clk_get_hw(clk));
module_put(emc->provider->owner);
emc->provider = NULL;
}
EXPORT_SYMBOL_GPL(tegra210_clk_emc_detach);
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#define CLK_SOURCE_LA 0x1f8 #define CLK_SOURCE_LA 0x1f8
#define CLK_SOURCE_SDMMC2 0x154 #define CLK_SOURCE_SDMMC2 0x154
#define CLK_SOURCE_SDMMC4 0x164 #define CLK_SOURCE_SDMMC4 0x164
#define CLK_SOURCE_EMC_DLL 0x664
#define PLLC_BASE 0x80 #define PLLC_BASE 0x80
#define PLLC_OUT 0x84 #define PLLC_OUT 0x84
...@@ -227,6 +228,10 @@ ...@@ -227,6 +228,10 @@
#define RST_DFLL_DVCO 0x2f4 #define RST_DFLL_DVCO 0x2f4
#define DVFS_DFLL_RESET_SHIFT 0 #define DVFS_DFLL_RESET_SHIFT 0
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_X_SET 0x284
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_X_CLR 0x288
#define CLK_OUT_ENB_X_CLK_ENB_EMC_DLL BIT(14)
#define CLK_RST_CONTROLLER_RST_DEV_Y_SET 0x2a8 #define CLK_RST_CONTROLLER_RST_DEV_Y_SET 0x2a8
#define CLK_RST_CONTROLLER_RST_DEV_Y_CLR 0x2ac #define CLK_RST_CONTROLLER_RST_DEV_Y_CLR 0x2ac
#define CPU_SOFTRST_CTRL 0x380 #define CPU_SOFTRST_CTRL 0x380
...@@ -314,12 +319,6 @@ static unsigned long tegra210_input_freq[] = { ...@@ -314,12 +319,6 @@ static unsigned long tegra210_input_freq[] = {
[8] = 12000000, [8] = 12000000,
}; };
static const char *mux_pllmcp_clkm[] = {
"pll_m", "pll_c", "pll_p", "clk_m", "pll_m_ud", "pll_mb", "pll_mb",
"pll_p",
};
#define mux_pllmcp_clkm_idx NULL
#define PLL_ENABLE (1 << 30) #define PLL_ENABLE (1 << 30)
#define PLLCX_MISC1_IDDQ (1 << 27) #define PLLCX_MISC1_IDDQ (1 << 27)
...@@ -555,6 +554,27 @@ void tegra210_set_sata_pll_seq_sw(bool state) ...@@ -555,6 +554,27 @@ void tegra210_set_sata_pll_seq_sw(bool state)
} }
EXPORT_SYMBOL_GPL(tegra210_set_sata_pll_seq_sw); EXPORT_SYMBOL_GPL(tegra210_set_sata_pll_seq_sw);
void tegra210_clk_emc_dll_enable(bool flag)
{
u32 offset = flag ? CLK_RST_CONTROLLER_CLK_OUT_ENB_X_SET :
CLK_RST_CONTROLLER_CLK_OUT_ENB_X_CLR;
writel_relaxed(CLK_OUT_ENB_X_CLK_ENB_EMC_DLL, clk_base + offset);
}
EXPORT_SYMBOL_GPL(tegra210_clk_emc_dll_enable);
void tegra210_clk_emc_dll_update_setting(u32 emc_dll_src_value)
{
writel_relaxed(emc_dll_src_value, clk_base + CLK_SOURCE_EMC_DLL);
}
EXPORT_SYMBOL_GPL(tegra210_clk_emc_dll_update_setting);
void tegra210_clk_emc_update_setting(u32 emc_src_value)
{
writel_relaxed(emc_src_value, clk_base + CLK_SOURCE_EMC);
}
EXPORT_SYMBOL_GPL(tegra210_clk_emc_update_setting);
static void tegra210_generic_mbist_war(struct tegra210_domain_mbist_war *mbist) static void tegra210_generic_mbist_war(struct tegra210_domain_mbist_war *mbist)
{ {
u32 val; u32 val;
...@@ -2310,7 +2330,6 @@ static struct tegra_clk tegra210_clks[tegra_clk_max] __initdata = { ...@@ -2310,7 +2330,6 @@ static struct tegra_clk tegra210_clks[tegra_clk_max] __initdata = {
[tegra_clk_i2c2] = { .dt_id = TEGRA210_CLK_I2C2, .present = true }, [tegra_clk_i2c2] = { .dt_id = TEGRA210_CLK_I2C2, .present = true },
[tegra_clk_uartc_8] = { .dt_id = TEGRA210_CLK_UARTC, .present = true }, [tegra_clk_uartc_8] = { .dt_id = TEGRA210_CLK_UARTC, .present = true },
[tegra_clk_mipi_cal] = { .dt_id = TEGRA210_CLK_MIPI_CAL, .present = true }, [tegra_clk_mipi_cal] = { .dt_id = TEGRA210_CLK_MIPI_CAL, .present = true },
[tegra_clk_emc] = { .dt_id = TEGRA210_CLK_EMC, .present = true },
[tegra_clk_usb2] = { .dt_id = TEGRA210_CLK_USB2, .present = true }, [tegra_clk_usb2] = { .dt_id = TEGRA210_CLK_USB2, .present = true },
[tegra_clk_bsev] = { .dt_id = TEGRA210_CLK_BSEV, .present = true }, [tegra_clk_bsev] = { .dt_id = TEGRA210_CLK_BSEV, .present = true },
[tegra_clk_uartd_8] = { .dt_id = TEGRA210_CLK_UARTD, .present = true }, [tegra_clk_uartd_8] = { .dt_id = TEGRA210_CLK_UARTD, .present = true },
...@@ -2953,6 +2972,27 @@ static const char * const sor1_parents[] = { ...@@ -2953,6 +2972,27 @@ static const char * const sor1_parents[] = {
static u32 sor1_parents_idx[] = { 0, 2, 5, 6 }; static u32 sor1_parents_idx[] = { 0, 2, 5, 6 };
static const struct clk_div_table mc_div_table_tegra210[] = {
{ .val = 0, .div = 2 },
{ .val = 1, .div = 4 },
{ .val = 2, .div = 1 },
{ .val = 3, .div = 2 },
{ .val = 0, .div = 0 },
};
static void tegra210_clk_register_mc(const char *name,
const char *parent_name)
{
struct clk *clk;
clk = clk_register_divider_table(NULL, name, parent_name,
CLK_IS_CRITICAL,
clk_base + CLK_SOURCE_EMC,
15, 2, CLK_DIVIDER_READ_ONLY,
mc_div_table_tegra210, &emc_lock);
clks[TEGRA210_CLK_MC] = clk;
}
static const char * const sor1_out_parents[] = { static const char * const sor1_out_parents[] = {
/* /*
* Bit 0 of the mux selects sor1_pad_clkout, irrespective of bit 1, so * Bit 0 of the mux selects sor1_pad_clkout, irrespective of bit 1, so
...@@ -2995,7 +3035,8 @@ static const char * const la_parents[] = { ...@@ -2995,7 +3035,8 @@ static const char * const la_parents[] = {
static struct tegra_clk_periph tegra210_la = static struct tegra_clk_periph tegra210_la =
TEGRA_CLK_PERIPH(29, 7, 9, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP, 76, 0, NULL, NULL); TEGRA_CLK_PERIPH(29, 7, 9, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP, 76, 0, NULL, NULL);
static __init void tegra210_periph_clk_init(void __iomem *clk_base, static __init void tegra210_periph_clk_init(struct device_node *np,
void __iomem *clk_base,
void __iomem *pmc_base) void __iomem *pmc_base)
{ {
struct clk *clk; struct clk *clk;
...@@ -3035,22 +3076,19 @@ static __init void tegra210_periph_clk_init(void __iomem *clk_base, ...@@ -3035,22 +3076,19 @@ static __init void tegra210_periph_clk_init(void __iomem *clk_base,
periph_clk_enb_refcnt); periph_clk_enb_refcnt);
clks[TEGRA210_CLK_DSIB] = clk; clks[TEGRA210_CLK_DSIB] = clk;
/* csi_tpg */
clk = clk_register_gate(NULL, "csi_tpg", "pll_d",
CLK_SET_RATE_PARENT, clk_base + PLLD_BASE,
23, 0, &pll_d_lock);
clk_register_clkdev(clk, "csi_tpg", NULL);
clks[TEGRA210_CLK_CSI_TPG] = clk;
/* la */ /* la */
clk = tegra_clk_register_periph("la", la_parents, clk = tegra_clk_register_periph("la", la_parents,
ARRAY_SIZE(la_parents), &tegra210_la, clk_base, ARRAY_SIZE(la_parents), &tegra210_la, clk_base,
CLK_SOURCE_LA, 0); CLK_SOURCE_LA, 0);
clks[TEGRA210_CLK_LA] = clk; clks[TEGRA210_CLK_LA] = clk;
/* emc mux */
clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
ARRAY_SIZE(mux_pllmcp_clkm), 0,
clk_base + CLK_SOURCE_EMC,
29, 3, 0, &emc_lock);
clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
&emc_lock);
clks[TEGRA210_CLK_MC] = clk;
/* cml0 */ /* cml0 */
clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX, clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX,
0, 0, &pll_e_lock); 0, 0, &pll_e_lock);
...@@ -3093,6 +3131,13 @@ static __init void tegra210_periph_clk_init(void __iomem *clk_base, ...@@ -3093,6 +3131,13 @@ static __init void tegra210_periph_clk_init(void __iomem *clk_base,
} }
tegra_periph_clk_init(clk_base, pmc_base, tegra210_clks, &pll_p_params); tegra_periph_clk_init(clk_base, pmc_base, tegra210_clks, &pll_p_params);
/* emc */
clk = tegra210_clk_register_emc(np, clk_base);
clks[TEGRA210_CLK_EMC] = clk;
/* mc */
tegra210_clk_register_mc("mc", "emc");
} }
static void __init tegra210_pll_init(void __iomem *clk_base, static void __init tegra210_pll_init(void __iomem *clk_base,
...@@ -3153,6 +3198,17 @@ static void __init tegra210_pll_init(void __iomem *clk_base, ...@@ -3153,6 +3198,17 @@ static void __init tegra210_pll_init(void __iomem *clk_base,
clk_register_clkdev(clk, "pll_m_ud", NULL); clk_register_clkdev(clk, "pll_m_ud", NULL);
clks[TEGRA210_CLK_PLL_M_UD] = clk; clks[TEGRA210_CLK_PLL_M_UD] = clk;
/* PLLMB_UD */
clk = clk_register_fixed_factor(NULL, "pll_mb_ud", "pll_mb",
CLK_SET_RATE_PARENT, 1, 1);
clk_register_clkdev(clk, "pll_mb_ud", NULL);
clks[TEGRA210_CLK_PLL_MB_UD] = clk;
/* PLLP_UD */
clk = clk_register_fixed_factor(NULL, "pll_p_ud", "pll_p",
0, 1, 1);
clks[TEGRA210_CLK_PLL_P_UD] = clk;
/* PLLU_VCO */ /* PLLU_VCO */
if (!tegra210_init_pllu()) { if (!tegra210_init_pllu()) {
clk = clk_register_fixed_rate(NULL, "pll_u_vco", "pll_ref", 0, clk = clk_register_fixed_rate(NULL, "pll_u_vco", "pll_ref", 0,
...@@ -3680,7 +3736,7 @@ static void __init tegra210_clock_init(struct device_node *np) ...@@ -3680,7 +3736,7 @@ static void __init tegra210_clock_init(struct device_node *np)
tegra_fixed_clk_init(tegra210_clks); tegra_fixed_clk_init(tegra210_clks);
tegra210_pll_init(clk_base, pmc_base); tegra210_pll_init(clk_base, pmc_base);
tegra210_periph_clk_init(clk_base, pmc_base); tegra210_periph_clk_init(np, clk_base, pmc_base);
tegra_audio_clk_init(clk_base, pmc_base, tegra210_clks, tegra_audio_clk_init(clk_base, pmc_base, tegra210_clks,
tegra210_audio_plls, tegra210_audio_plls,
ARRAY_SIZE(tegra210_audio_plls), 24576000); ARRAY_SIZE(tegra210_audio_plls), 24576000);
......
...@@ -499,6 +499,8 @@ static struct tegra_clk_pll_params pll_x_params __ro_after_init = { ...@@ -499,6 +499,8 @@ static struct tegra_clk_pll_params pll_x_params __ro_after_init = {
.freq_table = pll_x_freq_table, .freq_table = pll_x_freq_table,
.flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_DCCON | .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_DCCON |
TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE, TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE,
.pre_rate_change = tegra_cclk_pre_pllx_rate_change,
.post_rate_change = tegra_cclk_post_pllx_rate_change,
}; };
static struct tegra_clk_pll_params pll_e_params __ro_after_init = { static struct tegra_clk_pll_params pll_e_params __ro_after_init = {
...@@ -926,11 +928,11 @@ static void __init tegra30_super_clk_init(void) ...@@ -926,11 +928,11 @@ static void __init tegra30_super_clk_init(void)
clk_register_clkdev(clk, "pll_p_out4_cclkg", NULL); clk_register_clkdev(clk, "pll_p_out4_cclkg", NULL);
/* CCLKG */ /* CCLKG */
clk = tegra_clk_register_super_mux("cclk_g", cclk_g_parents, clk = tegra_clk_register_super_cclk("cclk_g", cclk_g_parents,
ARRAY_SIZE(cclk_g_parents), ARRAY_SIZE(cclk_g_parents),
CLK_SET_RATE_PARENT, CLK_SET_RATE_PARENT,
clk_base + CCLKG_BURST_POLICY, clk_base + CCLKG_BURST_POLICY,
0, 4, 0, 0, NULL); 0, NULL);
clks[TEGRA30_CLK_CCLK_G] = clk; clks[TEGRA30_CLK_CCLK_G] = clk;
/* /*
......
...@@ -266,6 +266,10 @@ struct tegra_clk_pll; ...@@ -266,6 +266,10 @@ struct tegra_clk_pll;
* disabled. * disabled.
* @dyn_ramp: Callback which can be used to define a custom * @dyn_ramp: Callback which can be used to define a custom
* dynamic ramp function for a given PLL. * dynamic ramp function for a given PLL.
* @pre_rate_change: Callback which is invoked just before changing
* PLL's rate.
* @post_rate_change: Callback which is invoked right after changing
* PLL's rate.
* *
* Flags: * Flags:
* TEGRA_PLL_USE_LOCK - This flag indicated to use lock bits for * TEGRA_PLL_USE_LOCK - This flag indicated to use lock bits for
...@@ -342,6 +346,8 @@ struct tegra_clk_pll_params { ...@@ -342,6 +346,8 @@ struct tegra_clk_pll_params {
void (*set_defaults)(struct tegra_clk_pll *pll); void (*set_defaults)(struct tegra_clk_pll *pll);
int (*dyn_ramp)(struct tegra_clk_pll *pll, int (*dyn_ramp)(struct tegra_clk_pll *pll,
struct tegra_clk_pll_freq_table *cfg); struct tegra_clk_pll_freq_table *cfg);
int (*pre_rate_change)(void);
void (*post_rate_change)(void);
}; };
#define TEGRA_PLL_USE_LOCK BIT(0) #define TEGRA_PLL_USE_LOCK BIT(0)
...@@ -729,8 +735,10 @@ struct clk *tegra_clk_register_periph_data(void __iomem *clk_base, ...@@ -729,8 +735,10 @@ struct clk *tegra_clk_register_periph_data(void __iomem *clk_base,
* TEGRA_DIVIDER_2 - LP cluster has additional divider. This flag indicates * TEGRA_DIVIDER_2 - LP cluster has additional divider. This flag indicates
* that this is LP cluster clock. * that this is LP cluster clock.
* TEGRA210_CPU_CLK - This flag is used to identify CPU cluster for gen5 * TEGRA210_CPU_CLK - This flag is used to identify CPU cluster for gen5
* super mux parent using PLLP branches. To use PLLP branches to CPU, need * super mux parent using PLLP branches. To use PLLP branches to CPU, need
* to configure additional bit PLLP_OUT_CPU in the clock registers. * to configure additional bit PLLP_OUT_CPU in the clock registers.
* TEGRA20_SUPER_CLK - Tegra20 doesn't have a dedicated divider for Super
* clocks, it only has a clock-skipper.
*/ */
struct tegra_clk_super_mux { struct tegra_clk_super_mux {
struct clk_hw hw; struct clk_hw hw;
...@@ -748,6 +756,7 @@ struct tegra_clk_super_mux { ...@@ -748,6 +756,7 @@ struct tegra_clk_super_mux {
#define TEGRA_DIVIDER_2 BIT(0) #define TEGRA_DIVIDER_2 BIT(0)
#define TEGRA210_CPU_CLK BIT(1) #define TEGRA210_CPU_CLK BIT(1)
#define TEGRA20_SUPER_CLK BIT(2)
extern const struct clk_ops tegra_clk_super_ops; extern const struct clk_ops tegra_clk_super_ops;
struct clk *tegra_clk_register_super_mux(const char *name, struct clk *tegra_clk_register_super_mux(const char *name,
...@@ -758,6 +767,12 @@ struct clk *tegra_clk_register_super_clk(const char *name, ...@@ -758,6 +767,12 @@ struct clk *tegra_clk_register_super_clk(const char *name,
const char * const *parent_names, u8 num_parents, const char * const *parent_names, u8 num_parents,
unsigned long flags, void __iomem *reg, u8 clk_super_flags, unsigned long flags, void __iomem *reg, u8 clk_super_flags,
spinlock_t *lock); spinlock_t *lock);
struct clk *tegra_clk_register_super_cclk(const char *name,
const char * const *parent_names, u8 num_parents,
unsigned long flags, void __iomem *reg, u8 clk_super_flags,
spinlock_t *lock);
int tegra_cclk_pre_pllx_rate_change(void);
void tegra_cclk_post_pllx_rate_change(void);
/** /**
* struct tegra_sdmmc_mux - switch divider with Low Jitter inputs for SDMMC * struct tegra_sdmmc_mux - switch divider with Low Jitter inputs for SDMMC
...@@ -866,7 +881,7 @@ void tegra_super_clk_gen5_init(void __iomem *clk_base, ...@@ -866,7 +881,7 @@ void tegra_super_clk_gen5_init(void __iomem *clk_base,
void __iomem *pmc_base, struct tegra_clk *tegra_clks, void __iomem *pmc_base, struct tegra_clk *tegra_clks,
struct tegra_clk_pll_params *pll_params); struct tegra_clk_pll_params *pll_params);
#ifdef CONFIG_TEGRA_CLK_EMC #ifdef CONFIG_TEGRA124_EMC
struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np, struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np,
spinlock_t *lock); spinlock_t *lock);
#else #else
...@@ -907,4 +922,7 @@ void tegra_clk_periph_resume(void); ...@@ -907,4 +922,7 @@ void tegra_clk_periph_resume(void);
bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw); bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw);
struct clk *tegra20_clk_register_emc(void __iomem *ioaddr, bool low_jitter); struct clk *tegra20_clk_register_emc(void __iomem *ioaddr, bool low_jitter);
struct clk *tegra210_clk_register_emc(struct device_node *np,
void __iomem *regs);
#endif /* TEGRA_CLK_H */ #endif /* TEGRA_CLK_H */
...@@ -351,14 +351,14 @@ ...@@ -351,14 +351,14 @@
#define TEGRA210_CLK_PLL_P_OUT_XUSB 317 #define TEGRA210_CLK_PLL_P_OUT_XUSB 317
#define TEGRA210_CLK_XUSB_SSP_SRC 318 #define TEGRA210_CLK_XUSB_SSP_SRC 318
#define TEGRA210_CLK_PLL_RE_OUT1 319 #define TEGRA210_CLK_PLL_RE_OUT1 319
/* 320 */ #define TEGRA210_CLK_PLL_MB_UD 320
/* 321 */ #define TEGRA210_CLK_PLL_P_UD 321
#define TEGRA210_CLK_ISP 322 #define TEGRA210_CLK_ISP 322
#define TEGRA210_CLK_PLL_A_OUT_ADSP 323 #define TEGRA210_CLK_PLL_A_OUT_ADSP 323
#define TEGRA210_CLK_PLL_A_OUT0_OUT_ADSP 324 #define TEGRA210_CLK_PLL_A_OUT0_OUT_ADSP 324
/* 325 */ /* 325 */
#define TEGRA210_CLK_OSC 326 #define TEGRA210_CLK_OSC 326
/* 327 */ #define TEGRA210_CLK_CSI_TPG 327
/* 328 */ /* 328 */
/* 329 */ /* 329 */
/* 330 */ /* 330 */
......
...@@ -131,6 +131,9 @@ extern void tegra210_set_sata_pll_seq_sw(bool state); ...@@ -131,6 +131,9 @@ extern void tegra210_set_sata_pll_seq_sw(bool state);
extern void tegra210_put_utmipll_in_iddq(void); extern void tegra210_put_utmipll_in_iddq(void);
extern void tegra210_put_utmipll_out_iddq(void); extern void tegra210_put_utmipll_out_iddq(void);
extern int tegra210_clk_handle_mbist_war(unsigned int id); extern int tegra210_clk_handle_mbist_war(unsigned int id);
extern void tegra210_clk_emc_dll_enable(bool flag);
extern void tegra210_clk_emc_dll_update_setting(u32 emc_dll_src_value);
extern void tegra210_clk_emc_update_setting(u32 emc_src_value);
struct clk; struct clk;
...@@ -143,4 +146,28 @@ void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb, ...@@ -143,4 +146,28 @@ void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb,
void *cb_arg); void *cb_arg);
int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same); int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same);
struct tegra210_clk_emc_config {
unsigned long rate;
bool same_freq;
u32 value;
unsigned long parent_rate;
u8 parent;
};
struct tegra210_clk_emc_provider {
struct module *owner;
struct device *dev;
struct tegra210_clk_emc_config *configs;
unsigned int num_configs;
int (*set_rate)(struct device *dev,
const struct tegra210_clk_emc_config *config);
};
int tegra210_clk_emc_attach(struct clk *clk,
struct tegra210_clk_emc_provider *provider);
void tegra210_clk_emc_detach(struct clk *clk);
#endif /* __LINUX_CLK_TEGRA_H_ */ #endif /* __LINUX_CLK_TEGRA_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment