clk: tegra: Changes for v5.8-rc1
Thise 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. -----BEGIN PGP SIGNATURE----- iQJHBAABCAAxFiEEiOrDCAFJzPfAjcif3SOs138+s6EFAl6+oicTHHRyZWRpbmdA bnZpZGlhLmNvbQAKCRDdI6zXfz6zoRS/EACWjzWi16nSNqxn26otDEgAQhSkIOTF Su9PspCtlll/mZJXTLziXDifv3JqGruGJa43BGWAzUkQFrlazqPNDYShNaI7ovp5 LY42L8TgDdUC2xLVilvz9+5t2v1k1PgEm60NYoF6/ocA5QH6MXhXYk5T7PAHkfo3 BvsWFB0gZejlqTq6ZCI/Vo4BfqAF93fKb04bxv+Hd27gT3GMKznsyybRJMxovtU8 OYTVDUWVizrwaEXHISnmjQJ7EROP5z8dR/pQe/0nzlyBNcRFNo94el6roUyO/t/y goBoMNFPb33geG9zOLUYVIbwQcDV+cNWxa5av9EhTh2BvfUtTe5AA5uMrzHkHfNJ FgPcehY2ThKv7mYYLZ0yuL9VX0YLCBy87tIrpOKJ5Q/Mu6BJ7L0clTTG5016EKF9 yaimphx6UQZLwNu2H1BUtgvMBI2MsqQCJQCbfrdXpG9mNsigjKKX77KbckgfBNrj FsNKssQSegPEhZ72ulFIavZ2Oy6AwgZ7Lk/Y30MEyzFha5CuylM6riEk3HQq2YWk 41O0JtHY78TTE7jvSQDzjsyeP63VVtA3XdEMIFMbsqpFh0kl4aHJU8TfPSD2BDlz za9HhKW9FHSXKvJ/gG49IApAz0Wq2bDLb5ZoM2ip7wKi4yz63cncd4AgoLEdZTBF RurdLZVqLb+NTQ== =Sz0T -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
c60037f0d7
@ -1,8 +1,4 @@
|
|||||||
# 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
|
|||||||
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
|
|||||||
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,
|
|||||||
|
|
||||||
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,
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
212
drivers/clk/tegra/clk-tegra-super-cclk.c
Normal file
212
drivers/clk/tegra/clk-tegra-super-cclk.c
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
// 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 = {
|
|||||||
.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)
|
|||||||
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 */
|
||||||
|
369
drivers/clk/tegra/clk-tegra210-emc.c
Normal file
369
drivers/clk/tegra/clk-tegra210-emc.c
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
// 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 @@
|
|||||||
#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 @@
|
|||||||
#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[] = {
|
|||||||
[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)
|
|||||||
}
|
}
|
||||||
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 = {
|
|||||||
[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[] = {
|
|||||||
|
|
||||||
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[] = {
|
|||||||
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,
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|||||||
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)
|
|||||||
|
|
||||||
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 = {
|
|||||||
.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)
|
|||||||
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;
|
|||||||
* 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 {
|
|||||||
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,
|
|||||||
* 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 {
|
|||||||
|
|
||||||
#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,
|
|||||||
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,
|
|||||||
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);
|
|||||||
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 @@
|
|||||||
#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);
|
|||||||
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,
|
|||||||
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_ */
|
||||||
|
Loading…
Reference in New Issue
Block a user