Skip to content

Commit 1641567

Browse files
digetxthierryreding
authored andcommitted
clk: tegra: Add custom CCLK implementation
CCLK stands for "CPU Clock", CPU core is running off CCLK. CCLK supports multiple parents, it has internal clock divider and a clock skipper. PLLX is the main CCLK parent that provides clock rates above 1GHz and it has special property such that the CCLK's internal divider is set into bypass mode when PLLX is selected as a parent for CCLK. This patch forks generic Super Clock into CCLK implementation which takes into account all CCLK specifics. The proper CCLK implementation is needed by the upcoming Tegra20 CPUFreq driver update that will allow to utilize the generic cpufreq-dt driver by moving intermediate clock selection into the clock driver. Note that technically this patch could be squashed into clk-super.c, but it is cleaner to have a separate source file. Also note that currently all CCLKLP bits are left in the clk-super.c and only CCLKG is supported by clk-tegra-super-cclk. It shouldn't be difficult to move the CCLKLP bits, but CCLKLP is not used by anything in kernel and thus better not to touch it for now. Acked-by: Peter De Schrijver <[email protected]> Tested-by: Peter Geis <[email protected]> Tested-by: Marcel Ziswiler <[email protected]> Tested-by: Jasper Korten <[email protected]> Tested-by: David Heidelberg <[email protected]> Tested-by: Nicolas Chauvet <[email protected]> Signed-off-by: Dmitry Osipenko <[email protected]> Signed-off-by: Thierry Reding <[email protected]>
1 parent 1d3e3c4 commit 1641567

File tree

3 files changed

+188
-2
lines changed

3 files changed

+188
-2
lines changed

drivers/clk/tegra/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ 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
1718
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o
1819
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20-emc.o
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
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 u8 cclk_super_get_parent(struct clk_hw *hw)
29+
{
30+
return tegra_clk_super_ops.get_parent(hw);
31+
}
32+
33+
static int cclk_super_set_parent(struct clk_hw *hw, u8 index)
34+
{
35+
return tegra_clk_super_ops.set_parent(hw, index);
36+
}
37+
38+
static int cclk_super_set_rate(struct clk_hw *hw, unsigned long rate,
39+
unsigned long parent_rate)
40+
{
41+
return tegra_clk_super_ops.set_rate(hw, rate, parent_rate);
42+
}
43+
44+
static unsigned long cclk_super_recalc_rate(struct clk_hw *hw,
45+
unsigned long parent_rate)
46+
{
47+
if (cclk_super_get_parent(hw) == PLLX_INDEX)
48+
return parent_rate;
49+
50+
return tegra_clk_super_ops.recalc_rate(hw, parent_rate);
51+
}
52+
53+
static int cclk_super_determine_rate(struct clk_hw *hw,
54+
struct clk_rate_request *req)
55+
{
56+
struct clk_hw *pllp_hw = clk_hw_get_parent_by_index(hw, PLLP_INDEX);
57+
struct clk_hw *pllx_hw = clk_hw_get_parent_by_index(hw, PLLX_INDEX);
58+
struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
59+
unsigned long pllp_rate;
60+
long rate = req->rate;
61+
62+
if (WARN_ON_ONCE(!pllp_hw || !pllx_hw))
63+
return -EINVAL;
64+
65+
/*
66+
* Switch parent to PLLP for all CCLK rates that are suitable for PLLP.
67+
* PLLX will be disabled in this case, saving some power.
68+
*/
69+
pllp_rate = clk_hw_get_rate(pllp_hw);
70+
71+
if (rate <= pllp_rate) {
72+
if (super->flags & TEGRA20_SUPER_CLK)
73+
rate = pllp_rate;
74+
else
75+
rate = tegra_clk_super_ops.round_rate(hw, rate,
76+
&pllp_rate);
77+
78+
req->best_parent_rate = pllp_rate;
79+
req->best_parent_hw = pllp_hw;
80+
req->rate = rate;
81+
} else {
82+
rate = clk_hw_round_rate(pllx_hw, rate);
83+
req->best_parent_rate = rate;
84+
req->best_parent_hw = pllx_hw;
85+
req->rate = rate;
86+
}
87+
88+
if (WARN_ON_ONCE(rate <= 0))
89+
return -EINVAL;
90+
91+
return 0;
92+
}
93+
94+
static const struct clk_ops tegra_cclk_super_ops = {
95+
.get_parent = cclk_super_get_parent,
96+
.set_parent = cclk_super_set_parent,
97+
.set_rate = cclk_super_set_rate,
98+
.recalc_rate = cclk_super_recalc_rate,
99+
.determine_rate = cclk_super_determine_rate,
100+
};
101+
102+
static const struct clk_ops tegra_cclk_super_mux_ops = {
103+
.get_parent = cclk_super_get_parent,
104+
.set_parent = cclk_super_set_parent,
105+
.determine_rate = cclk_super_determine_rate,
106+
};
107+
108+
struct clk *tegra_clk_register_super_cclk(const char *name,
109+
const char * const *parent_names, u8 num_parents,
110+
unsigned long flags, void __iomem *reg, u8 clk_super_flags,
111+
spinlock_t *lock)
112+
{
113+
struct tegra_clk_super_mux *super;
114+
struct clk *clk;
115+
struct clk_init_data init;
116+
u32 val;
117+
118+
super = kzalloc(sizeof(*super), GFP_KERNEL);
119+
if (!super)
120+
return ERR_PTR(-ENOMEM);
121+
122+
init.name = name;
123+
init.flags = flags;
124+
init.parent_names = parent_names;
125+
init.num_parents = num_parents;
126+
127+
super->reg = reg;
128+
super->lock = lock;
129+
super->width = 4;
130+
super->flags = clk_super_flags;
131+
super->hw.init = &init;
132+
133+
if (super->flags & TEGRA20_SUPER_CLK) {
134+
init.ops = &tegra_cclk_super_mux_ops;
135+
} else {
136+
init.ops = &tegra_cclk_super_ops;
137+
138+
super->frac_div.reg = reg + 4;
139+
super->frac_div.shift = 16;
140+
super->frac_div.width = 8;
141+
super->frac_div.frac_width = 1;
142+
super->frac_div.lock = lock;
143+
super->div_ops = &tegra_clk_frac_div_ops;
144+
}
145+
146+
/*
147+
* Tegra30+ has the following CPUG clock topology:
148+
*
149+
* +---+ +-------+ +-+ +-+ +-+
150+
* PLLP+->+ +->+DIVIDER+->+0| +-------->+0| ------------->+0|
151+
* | | +-------+ | | | +---+ | | | | |
152+
* PLLC+->+MUX| | +->+ | S | | +->+ | +->+CPU
153+
* ... | | | | | | K | | | | +-------+ | |
154+
* PLLX+->+-->+------------>+1| +->+ I +->+1| +->+ DIV2 +->+1|
155+
* +---+ +++ | P | +++ |SKIPPER| +++
156+
* ^ | P | ^ +-------+ ^
157+
* | | E | | |
158+
* PLLX_SEL+--+ | R | | OVERHEAT+--+
159+
* +---+ |
160+
* |
161+
* SUPER_CDIV_ENB+--+
162+
*
163+
* Tegra20 is similar, but simpler. It doesn't have the divider and
164+
* thermal DIV2 skipper.
165+
*
166+
* At least for now we're not going to use clock-skipper, hence let's
167+
* ensure that it is disabled.
168+
*/
169+
val = readl_relaxed(reg + 4);
170+
val &= ~SUPER_CDIV_ENB;
171+
writel_relaxed(val, reg + 4);
172+
173+
clk = clk_register(NULL, &super->hw);
174+
if (IS_ERR(clk))
175+
kfree(super);
176+
177+
return clk;
178+
}

drivers/clk/tegra/clk.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -729,8 +729,10 @@ struct clk *tegra_clk_register_periph_data(void __iomem *clk_base,
729729
* TEGRA_DIVIDER_2 - LP cluster has additional divider. This flag indicates
730730
* that this is LP cluster clock.
731731
* TEGRA210_CPU_CLK - This flag is used to identify CPU cluster for gen5
732-
* super mux parent using PLLP branches. To use PLLP branches to CPU, need
733-
* to configure additional bit PLLP_OUT_CPU in the clock registers.
732+
* super mux parent using PLLP branches. To use PLLP branches to CPU, need
733+
* to configure additional bit PLLP_OUT_CPU in the clock registers.
734+
* TEGRA20_SUPER_CLK - Tegra20 doesn't have a dedicated divider for Super
735+
* clocks, it only has a clock-skipper.
734736
*/
735737
struct tegra_clk_super_mux {
736738
struct clk_hw hw;
@@ -748,6 +750,7 @@ struct tegra_clk_super_mux {
748750

749751
#define TEGRA_DIVIDER_2 BIT(0)
750752
#define TEGRA210_CPU_CLK BIT(1)
753+
#define TEGRA20_SUPER_CLK BIT(2)
751754

752755
extern const struct clk_ops tegra_clk_super_ops;
753756
struct clk *tegra_clk_register_super_mux(const char *name,
@@ -758,6 +761,10 @@ struct clk *tegra_clk_register_super_clk(const char *name,
758761
const char * const *parent_names, u8 num_parents,
759762
unsigned long flags, void __iomem *reg, u8 clk_super_flags,
760763
spinlock_t *lock);
764+
struct clk *tegra_clk_register_super_cclk(const char *name,
765+
const char * const *parent_names, u8 num_parents,
766+
unsigned long flags, void __iomem *reg, u8 clk_super_flags,
767+
spinlock_t *lock);
761768

762769
/**
763770
* struct tegra_sdmmc_mux - switch divider with Low Jitter inputs for SDMMC

0 commit comments

Comments
 (0)