Skip to content

Commit c60037f

Browse files
committed
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
2 parents 8f3d9f3 + dec3963 commit c60037f

File tree

12 files changed

+730
-35
lines changed

12 files changed

+730
-35
lines changed

drivers/clk/tegra/Kconfig

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
# SPDX-License-Identifier: GPL-2.0-only
2-
config TEGRA_CLK_EMC
3-
def_bool y
4-
depends on TEGRA124_EMC
5-
62
config CLK_TEGRA_BPMP
73
def_bool y
84
depends on TEGRA_BPMP

drivers/clk/tegra/Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,19 @@ obj-y += clk-super.o
1313
obj-y += clk-tegra-audio.o
1414
obj-y += clk-tegra-periph.o
1515
obj-y += clk-tegra-fixed.o
16+
obj-y += clk-tegra-super-cclk.o
1617
obj-y += clk-tegra-super-gen4.o
17-
obj-$(CONFIG_TEGRA_CLK_EMC) += clk-emc.o
1818
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o
1919
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20-emc.o
2020
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o
2121
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra20-emc.o
2222
obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o
2323
obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o
2424
obj-$(CONFIG_TEGRA_CLK_DFLL) += clk-tegra124-dfll-fcpu.o
25+
obj-$(CONFIG_TEGRA124_EMC) += clk-tegra124-emc.o
2526
obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o
2627
obj-y += cvb.o
2728
obj-$(CONFIG_ARCH_TEGRA_210_SOC) += clk-tegra210.o
29+
obj-$(CONFIG_ARCH_TEGRA_210_SOC) += clk-tegra210-emc.o
2830
obj-$(CONFIG_CLK_TEGRA_BPMP) += clk-bpmp.o
2931
obj-y += clk-utils.o

drivers/clk/tegra/clk-pll.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -744,13 +744,19 @@ static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg,
744744

745745
state = clk_pll_is_enabled(hw);
746746

747+
if (state && pll->params->pre_rate_change) {
748+
ret = pll->params->pre_rate_change();
749+
if (WARN_ON(ret))
750+
return ret;
751+
}
752+
747753
_get_pll_mnp(pll, &old_cfg);
748754

749755
if (state && pll->params->defaults_set && pll->params->dyn_ramp &&
750756
(cfg->m == old_cfg.m) && (cfg->p == old_cfg.p)) {
751757
ret = pll->params->dyn_ramp(pll, cfg);
752758
if (!ret)
753-
return 0;
759+
goto done;
754760
}
755761

756762
if (state) {
@@ -772,6 +778,10 @@ static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg,
772778
pll_clk_start_ss(pll);
773779
}
774780

781+
done:
782+
if (state && pll->params->post_rate_change)
783+
pll->params->post_rate_change();
784+
775785
return ret;
776786
}
777787

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Based on clk-super.c
4+
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
5+
*
6+
* Based on older tegra20-cpufreq driver by Colin Cross <[email protected]>
7+
* Copyright (C) 2010 Google, Inc.
8+
*
9+
* Author: Dmitry Osipenko <[email protected]>
10+
* Copyright (C) 2019 GRATE-DRIVER project
11+
*/
12+
13+
#include <linux/bits.h>
14+
#include <linux/clk-provider.h>
15+
#include <linux/err.h>
16+
#include <linux/io.h>
17+
#include <linux/kernel.h>
18+
#include <linux/slab.h>
19+
#include <linux/types.h>
20+
21+
#include "clk.h"
22+
23+
#define PLLP_INDEX 4
24+
#define PLLX_INDEX 8
25+
26+
#define SUPER_CDIV_ENB BIT(31)
27+
28+
static struct tegra_clk_super_mux *cclk_super;
29+
static bool cclk_on_pllx;
30+
31+
static u8 cclk_super_get_parent(struct clk_hw *hw)
32+
{
33+
return tegra_clk_super_ops.get_parent(hw);
34+
}
35+
36+
static int cclk_super_set_parent(struct clk_hw *hw, u8 index)
37+
{
38+
return tegra_clk_super_ops.set_parent(hw, index);
39+
}
40+
41+
static int cclk_super_set_rate(struct clk_hw *hw, unsigned long rate,
42+
unsigned long parent_rate)
43+
{
44+
return tegra_clk_super_ops.set_rate(hw, rate, parent_rate);
45+
}
46+
47+
static unsigned long cclk_super_recalc_rate(struct clk_hw *hw,
48+
unsigned long parent_rate)
49+
{
50+
if (cclk_super_get_parent(hw) == PLLX_INDEX)
51+
return parent_rate;
52+
53+
return tegra_clk_super_ops.recalc_rate(hw, parent_rate);
54+
}
55+
56+
static int cclk_super_determine_rate(struct clk_hw *hw,
57+
struct clk_rate_request *req)
58+
{
59+
struct clk_hw *pllp_hw = clk_hw_get_parent_by_index(hw, PLLP_INDEX);
60+
struct clk_hw *pllx_hw = clk_hw_get_parent_by_index(hw, PLLX_INDEX);
61+
struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
62+
unsigned long pllp_rate;
63+
long rate = req->rate;
64+
65+
if (WARN_ON_ONCE(!pllp_hw || !pllx_hw))
66+
return -EINVAL;
67+
68+
/*
69+
* Switch parent to PLLP for all CCLK rates that are suitable for PLLP.
70+
* PLLX will be disabled in this case, saving some power.
71+
*/
72+
pllp_rate = clk_hw_get_rate(pllp_hw);
73+
74+
if (rate <= pllp_rate) {
75+
if (super->flags & TEGRA20_SUPER_CLK)
76+
rate = pllp_rate;
77+
else
78+
rate = tegra_clk_super_ops.round_rate(hw, rate,
79+
&pllp_rate);
80+
81+
req->best_parent_rate = pllp_rate;
82+
req->best_parent_hw = pllp_hw;
83+
req->rate = rate;
84+
} else {
85+
rate = clk_hw_round_rate(pllx_hw, rate);
86+
req->best_parent_rate = rate;
87+
req->best_parent_hw = pllx_hw;
88+
req->rate = rate;
89+
}
90+
91+
if (WARN_ON_ONCE(rate <= 0))
92+
return -EINVAL;
93+
94+
return 0;
95+
}
96+
97+
static const struct clk_ops tegra_cclk_super_ops = {
98+
.get_parent = cclk_super_get_parent,
99+
.set_parent = cclk_super_set_parent,
100+
.set_rate = cclk_super_set_rate,
101+
.recalc_rate = cclk_super_recalc_rate,
102+
.determine_rate = cclk_super_determine_rate,
103+
};
104+
105+
static const struct clk_ops tegra_cclk_super_mux_ops = {
106+
.get_parent = cclk_super_get_parent,
107+
.set_parent = cclk_super_set_parent,
108+
.determine_rate = cclk_super_determine_rate,
109+
};
110+
111+
struct clk *tegra_clk_register_super_cclk(const char *name,
112+
const char * const *parent_names, u8 num_parents,
113+
unsigned long flags, void __iomem *reg, u8 clk_super_flags,
114+
spinlock_t *lock)
115+
{
116+
struct tegra_clk_super_mux *super;
117+
struct clk *clk;
118+
struct clk_init_data init;
119+
u32 val;
120+
121+
if (WARN_ON(cclk_super))
122+
return ERR_PTR(-EBUSY);
123+
124+
super = kzalloc(sizeof(*super), GFP_KERNEL);
125+
if (!super)
126+
return ERR_PTR(-ENOMEM);
127+
128+
init.name = name;
129+
init.flags = flags;
130+
init.parent_names = parent_names;
131+
init.num_parents = num_parents;
132+
133+
super->reg = reg;
134+
super->lock = lock;
135+
super->width = 4;
136+
super->flags = clk_super_flags;
137+
super->hw.init = &init;
138+
139+
if (super->flags & TEGRA20_SUPER_CLK) {
140+
init.ops = &tegra_cclk_super_mux_ops;
141+
} else {
142+
init.ops = &tegra_cclk_super_ops;
143+
144+
super->frac_div.reg = reg + 4;
145+
super->frac_div.shift = 16;
146+
super->frac_div.width = 8;
147+
super->frac_div.frac_width = 1;
148+
super->frac_div.lock = lock;
149+
super->div_ops = &tegra_clk_frac_div_ops;
150+
}
151+
152+
/*
153+
* Tegra30+ has the following CPUG clock topology:
154+
*
155+
* +---+ +-------+ +-+ +-+ +-+
156+
* PLLP+->+ +->+DIVIDER+->+0| +-------->+0| ------------->+0|
157+
* | | +-------+ | | | +---+ | | | | |
158+
* PLLC+->+MUX| | +->+ | S | | +->+ | +->+CPU
159+
* ... | | | | | | K | | | | +-------+ | |
160+
* PLLX+->+-->+------------>+1| +->+ I +->+1| +->+ DIV2 +->+1|
161+
* +---+ +++ | P | +++ |SKIPPER| +++
162+
* ^ | P | ^ +-------+ ^
163+
* | | E | | |
164+
* PLLX_SEL+--+ | R | | OVERHEAT+--+
165+
* +---+ |
166+
* |
167+
* SUPER_CDIV_ENB+--+
168+
*
169+
* Tegra20 is similar, but simpler. It doesn't have the divider and
170+
* thermal DIV2 skipper.
171+
*
172+
* At least for now we're not going to use clock-skipper, hence let's
173+
* ensure that it is disabled.
174+
*/
175+
val = readl_relaxed(reg + 4);
176+
val &= ~SUPER_CDIV_ENB;
177+
writel_relaxed(val, reg + 4);
178+
179+
clk = clk_register(NULL, &super->hw);
180+
if (IS_ERR(clk))
181+
kfree(super);
182+
else
183+
cclk_super = super;
184+
185+
return clk;
186+
}
187+
188+
int tegra_cclk_pre_pllx_rate_change(void)
189+
{
190+
if (IS_ERR_OR_NULL(cclk_super))
191+
return -EINVAL;
192+
193+
if (cclk_super_get_parent(&cclk_super->hw) == PLLX_INDEX)
194+
cclk_on_pllx = true;
195+
else
196+
cclk_on_pllx = false;
197+
198+
/*
199+
* CPU needs to be temporarily re-parented away from PLLX if PLLX
200+
* changes its rate. PLLP is a safe parent for CPU on all Tegra SoCs.
201+
*/
202+
if (cclk_on_pllx)
203+
cclk_super_set_parent(&cclk_super->hw, PLLP_INDEX);
204+
205+
return 0;
206+
}
207+
208+
void tegra_cclk_post_pllx_rate_change(void)
209+
{
210+
if (cclk_on_pllx)
211+
cclk_super_set_parent(&cclk_super->hw, PLLX_INDEX);
212+
}
File renamed without changes.

drivers/clk/tegra/clk-tegra20.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,8 @@ static struct tegra_clk_pll_params pll_x_params = {
391391
.lock_delay = 300,
392392
.freq_table = pll_x_freq_table,
393393
.flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_HAS_LOCK_ENABLE,
394+
.pre_rate_change = tegra_cclk_pre_pllx_rate_change,
395+
.post_rate_change = tegra_cclk_post_pllx_rate_change,
394396
};
395397

396398
static struct tegra_clk_pll_params pll_e_params = {
@@ -702,9 +704,10 @@ static void tegra20_super_clk_init(void)
702704
struct clk *clk;
703705

704706
/* CCLK */
705-
clk = tegra_clk_register_super_mux("cclk", cclk_parents,
707+
clk = tegra_clk_register_super_cclk("cclk", cclk_parents,
706708
ARRAY_SIZE(cclk_parents), CLK_SET_RATE_PARENT,
707-
clk_base + CCLK_BURST_POLICY, 0, 4, 0, 0, NULL);
709+
clk_base + CCLK_BURST_POLICY, TEGRA20_SUPER_CLK,
710+
NULL);
708711
clks[TEGRA20_CLK_CCLK] = clk;
709712

710713
/* SCLK */

0 commit comments

Comments
 (0)