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
|
||||
config TEGRA_CLK_EMC
|
||||
def_bool y
|
||||
depends on TEGRA124_EMC
|
||||
|
||||
config CLK_TEGRA_BPMP
|
||||
def_bool y
|
||||
depends on TEGRA_BPMP
|
||||
|
@ -13,8 +13,8 @@ obj-y += clk-super.o
|
||||
obj-y += clk-tegra-audio.o
|
||||
obj-y += clk-tegra-periph.o
|
||||
obj-y += clk-tegra-fixed.o
|
||||
obj-y += clk-tegra-super-cclk.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-emc.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_124_SOC) += clk-tegra124.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-y += cvb.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-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);
|
||||
|
||||
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);
|
||||
|
||||
if (state && pll->params->defaults_set && pll->params->dyn_ramp &&
|
||||
(cfg->m == old_cfg.m) && (cfg->p == old_cfg.p)) {
|
||||
ret = pll->params->dyn_ramp(pll, cfg);
|
||||
if (!ret)
|
||||
return 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
done:
|
||||
if (state && pll->params->post_rate_change)
|
||||
pll->params->post_rate_change();
|
||||
|
||||
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,
|
||||
.freq_table = pll_x_freq_table,
|
||||
.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 = {
|
||||
@ -702,9 +704,10 @@ static void tegra20_super_clk_init(void)
|
||||
struct clk *clk;
|
||||
|
||||
/* 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,
|
||||
clk_base + CCLK_BURST_POLICY, 0, 4, 0, 0, NULL);
|
||||
clk_base + CCLK_BURST_POLICY, TEGRA20_SUPER_CLK,
|
||||
NULL);
|
||||
clks[TEGRA20_CLK_CCLK] = clk;
|
||||
|
||||
/* 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_SDMMC2 0x154
|
||||
#define CLK_SOURCE_SDMMC4 0x164
|
||||
#define CLK_SOURCE_EMC_DLL 0x664
|
||||
|
||||
#define PLLC_BASE 0x80
|
||||
#define PLLC_OUT 0x84
|
||||
@ -227,6 +228,10 @@
|
||||
#define RST_DFLL_DVCO 0x2f4
|
||||
#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_CLR 0x2ac
|
||||
#define CPU_SOFTRST_CTRL 0x380
|
||||
@ -314,12 +319,6 @@ static unsigned long tegra210_input_freq[] = {
|
||||
[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 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);
|
||||
|
||||
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)
|
||||
{
|
||||
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_uartc_8] = { .dt_id = TEGRA210_CLK_UARTC, .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_bsev] = { .dt_id = TEGRA210_CLK_BSEV, .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 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[] = {
|
||||
/*
|
||||
* 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 =
|
||||
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)
|
||||
{
|
||||
struct clk *clk;
|
||||
@ -3035,22 +3076,19 @@ static __init void tegra210_periph_clk_init(void __iomem *clk_base,
|
||||
periph_clk_enb_refcnt);
|
||||
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 */
|
||||
clk = tegra_clk_register_periph("la", la_parents,
|
||||
ARRAY_SIZE(la_parents), &tegra210_la, clk_base,
|
||||
CLK_SOURCE_LA, 0);
|
||||
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 */
|
||||
clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX,
|
||||
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);
|
||||
|
||||
/* 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,
|
||||
@ -3153,6 +3198,17 @@ static void __init tegra210_pll_init(void __iomem *clk_base,
|
||||
clk_register_clkdev(clk, "pll_m_ud", NULL);
|
||||
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 */
|
||||
if (!tegra210_init_pllu()) {
|
||||
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);
|
||||
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,
|
||||
tegra210_audio_plls,
|
||||
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,
|
||||
.flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_DCCON |
|
||||
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 = {
|
||||
@ -926,11 +928,11 @@ static void __init tegra30_super_clk_init(void)
|
||||
clk_register_clkdev(clk, "pll_p_out4_cclkg", NULL);
|
||||
|
||||
/* 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),
|
||||
CLK_SET_RATE_PARENT,
|
||||
clk_base + CCLKG_BURST_POLICY,
|
||||
0, 4, 0, 0, NULL);
|
||||
0, NULL);
|
||||
clks[TEGRA30_CLK_CCLK_G] = clk;
|
||||
|
||||
/*
|
||||
|
@ -266,6 +266,10 @@ struct tegra_clk_pll;
|
||||
* disabled.
|
||||
* @dyn_ramp: Callback which can be used to define a custom
|
||||
* 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:
|
||||
* 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);
|
||||
int (*dyn_ramp)(struct tegra_clk_pll *pll,
|
||||
struct tegra_clk_pll_freq_table *cfg);
|
||||
int (*pre_rate_change)(void);
|
||||
void (*post_rate_change)(void);
|
||||
};
|
||||
|
||||
#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
|
||||
* that this is LP cluster clock.
|
||||
* 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
|
||||
* to configure additional bit PLLP_OUT_CPU in the clock registers.
|
||||
* super mux parent using PLLP branches. To use PLLP branches to CPU, need
|
||||
* 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 clk_hw hw;
|
||||
@ -748,6 +756,7 @@ struct tegra_clk_super_mux {
|
||||
|
||||
#define TEGRA_DIVIDER_2 BIT(0)
|
||||
#define TEGRA210_CPU_CLK BIT(1)
|
||||
#define TEGRA20_SUPER_CLK BIT(2)
|
||||
|
||||
extern const struct clk_ops tegra_clk_super_ops;
|
||||
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,
|
||||
unsigned long flags, void __iomem *reg, u8 clk_super_flags,
|
||||
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
|
||||
@ -866,7 +881,7 @@ void tegra_super_clk_gen5_init(void __iomem *clk_base,
|
||||
void __iomem *pmc_base, struct tegra_clk *tegra_clks,
|
||||
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,
|
||||
spinlock_t *lock);
|
||||
#else
|
||||
@ -907,4 +922,7 @@ void tegra_clk_periph_resume(void);
|
||||
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 *tegra210_clk_register_emc(struct device_node *np,
|
||||
void __iomem *regs);
|
||||
|
||||
#endif /* TEGRA_CLK_H */
|
||||
|
@ -351,14 +351,14 @@
|
||||
#define TEGRA210_CLK_PLL_P_OUT_XUSB 317
|
||||
#define TEGRA210_CLK_XUSB_SSP_SRC 318
|
||||
#define TEGRA210_CLK_PLL_RE_OUT1 319
|
||||
/* 320 */
|
||||
/* 321 */
|
||||
#define TEGRA210_CLK_PLL_MB_UD 320
|
||||
#define TEGRA210_CLK_PLL_P_UD 321
|
||||
#define TEGRA210_CLK_ISP 322
|
||||
#define TEGRA210_CLK_PLL_A_OUT_ADSP 323
|
||||
#define TEGRA210_CLK_PLL_A_OUT0_OUT_ADSP 324
|
||||
/* 325 */
|
||||
#define TEGRA210_CLK_OSC 326
|
||||
/* 327 */
|
||||
#define TEGRA210_CLK_CSI_TPG 327
|
||||
/* 328 */
|
||||
/* 329 */
|
||||
/* 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_out_iddq(void);
|
||||
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;
|
||||
|
||||
@ -143,4 +146,28 @@ void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb,
|
||||
void *cb_arg);
|
||||
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_ */
|
||||
|
Loading…
Reference in New Issue
Block a user