1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved.
7 #include <linux/clk-provider.h>
9 #include <linux/of_address.h>
10 #include <linux/delay.h>
11 #include <linux/export.h>
12 #include <linux/clk/tegra.h>
17 #define PLLX_BASE 0xe0
18 #define PLLX_MISC 0xe4
19 #define PLLX_MISC2 0x514
20 #define PLLX_MISC3 0x518
22 #define CCLKG_BURST_POLICY 0x368
23 #define CCLKLP_BURST_POLICY 0x370
24 #define SCLK_BURST_POLICY 0x028
25 #define SYSTEM_CLK_RATE 0x030
26 #define SCLK_DIVIDER 0x2c
28 static DEFINE_SPINLOCK(sysrate_lock
);
30 enum tegra_super_gen
{
35 struct tegra_super_gen_info
{
36 enum tegra_super_gen gen
;
37 const char **sclk_parents
;
38 const char **cclk_g_parents
;
39 const char **cclk_lp_parents
;
41 int num_cclk_g_parents
;
42 int num_cclk_lp_parents
;
45 static const char *sclk_parents
[] = { "clk_m", "pll_c_out1", "pll_p_out4",
46 "pll_p", "pll_p_out2", "unused",
47 "clk_32k", "pll_m_out1" };
49 static const char *cclk_g_parents
[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
50 "pll_p", "pll_p_out4", "unused",
51 "unused", "pll_x", "unused", "unused",
52 "unused", "unused", "unused", "unused",
55 static const char *cclk_lp_parents
[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
56 "pll_p", "pll_p_out4", "unused",
57 "unused", "pll_x", "pll_x_out0" };
59 static const struct tegra_super_gen_info tegra_super_gen_info_gen4
= {
61 .sclk_parents
= sclk_parents
,
62 .cclk_g_parents
= cclk_g_parents
,
63 .cclk_lp_parents
= cclk_lp_parents
,
64 .num_sclk_parents
= ARRAY_SIZE(sclk_parents
),
65 .num_cclk_g_parents
= ARRAY_SIZE(cclk_g_parents
),
66 .num_cclk_lp_parents
= ARRAY_SIZE(cclk_lp_parents
),
69 static const char *sclk_parents_gen5
[] = { "clk_m", "pll_c_out1", "pll_c4_out3",
70 "pll_p", "pll_p_out2", "pll_c4_out1",
71 "clk_32k", "pll_c4_out2" };
73 static const char *cclk_g_parents_gen5
[] = { "clk_m", "unused", "clk_32k", "unused",
74 "pll_p", "pll_p_out4", "unused",
75 "unused", "pll_x", "unused", "unused",
76 "unused", "unused", "unused", "unused",
79 static const char *cclk_lp_parents_gen5
[] = { "clk_m", "unused", "clk_32k", "unused",
80 "pll_p", "pll_p_out4", "unused",
81 "unused", "pll_x", "unused", "unused",
82 "unused", "unused", "unused", "unused",
85 static const struct tegra_super_gen_info tegra_super_gen_info_gen5
= {
87 .sclk_parents
= sclk_parents_gen5
,
88 .cclk_g_parents
= cclk_g_parents_gen5
,
89 .cclk_lp_parents
= cclk_lp_parents_gen5
,
90 .num_sclk_parents
= ARRAY_SIZE(sclk_parents_gen5
),
91 .num_cclk_g_parents
= ARRAY_SIZE(cclk_g_parents_gen5
),
92 .num_cclk_lp_parents
= ARRAY_SIZE(cclk_lp_parents_gen5
),
95 static void __init
tegra_sclk_init(void __iomem
*clk_base
,
96 struct tegra_clk
*tegra_clks
,
97 const struct tegra_super_gen_info
*gen_info
)
103 dt_clk
= tegra_lookup_dt_id(tegra_clk_sclk_mux
, tegra_clks
);
105 clk
= tegra_clk_register_super_mux("sclk_mux",
106 gen_info
->sclk_parents
,
107 gen_info
->num_sclk_parents
,
109 clk_base
+ SCLK_BURST_POLICY
,
115 dt_clk
= tegra_lookup_dt_id(tegra_clk_sclk
, tegra_clks
);
117 clk
= clk_register_divider(NULL
, "sclk", "sclk_mux",
119 clk_base
+ SCLK_DIVIDER
, 0, 8,
125 dt_clk
= tegra_lookup_dt_id(tegra_clk_sclk
, tegra_clks
);
127 clk
= tegra_clk_register_super_mux("sclk",
128 gen_info
->sclk_parents
,
129 gen_info
->num_sclk_parents
,
130 CLK_SET_RATE_PARENT
|
132 clk_base
+ SCLK_BURST_POLICY
,
139 dt_clk
= tegra_lookup_dt_id(tegra_clk_hclk
, tegra_clks
);
141 clk
= clk_register_divider(NULL
, "hclk_div", "sclk", 0,
142 clk_base
+ SYSTEM_CLK_RATE
, 4, 2, 0,
144 clk
= clk_register_gate(NULL
, "hclk", "hclk_div",
145 CLK_SET_RATE_PARENT
| CLK_IS_CRITICAL
,
146 clk_base
+ SYSTEM_CLK_RATE
,
147 7, CLK_GATE_SET_TO_DISABLE
, &sysrate_lock
);
152 dt_clk
= tegra_lookup_dt_id(tegra_clk_pclk
, tegra_clks
);
156 clk
= clk_register_divider(NULL
, "pclk_div", "hclk", 0,
157 clk_base
+ SYSTEM_CLK_RATE
, 0, 2, 0,
159 clk
= clk_register_gate(NULL
, "pclk", "pclk_div", CLK_SET_RATE_PARENT
|
160 CLK_IS_CRITICAL
, clk_base
+ SYSTEM_CLK_RATE
,
161 3, CLK_GATE_SET_TO_DISABLE
, &sysrate_lock
);
165 static void __init
tegra_super_clk_init(void __iomem
*clk_base
,
166 void __iomem
*pmc_base
,
167 struct tegra_clk
*tegra_clks
,
168 struct tegra_clk_pll_params
*params
,
169 const struct tegra_super_gen_info
*gen_info
)
175 dt_clk
= tegra_lookup_dt_id(tegra_clk_cclk_g
, tegra_clks
);
177 if (gen_info
->gen
== gen5
) {
178 clk
= tegra_clk_register_super_mux("cclk_g",
179 gen_info
->cclk_g_parents
,
180 gen_info
->num_cclk_g_parents
,
182 clk_base
+ CCLKG_BURST_POLICY
,
183 TEGRA210_CPU_CLK
, 4, 8, 0, NULL
);
185 clk
= tegra_clk_register_super_mux("cclk_g",
186 gen_info
->cclk_g_parents
,
187 gen_info
->num_cclk_g_parents
,
189 clk_base
+ CCLKG_BURST_POLICY
,
196 dt_clk
= tegra_lookup_dt_id(tegra_clk_cclk_lp
, tegra_clks
);
198 if (gen_info
->gen
== gen5
) {
200 * TEGRA210_CPU_CLK flag is not needed for cclk_lp as
201 * cluster switching is not currently supported on
202 * Tegra210 and also cpu_lp is not used.
204 clk
= tegra_clk_register_super_mux("cclk_lp",
205 gen_info
->cclk_lp_parents
,
206 gen_info
->num_cclk_lp_parents
,
208 clk_base
+ CCLKLP_BURST_POLICY
,
211 clk
= tegra_clk_register_super_mux("cclk_lp",
212 gen_info
->cclk_lp_parents
,
213 gen_info
->num_cclk_lp_parents
,
215 clk_base
+ CCLKLP_BURST_POLICY
,
216 TEGRA_DIVIDER_2
, 4, 8, 9, NULL
);
221 tegra_sclk_init(clk_base
, tegra_clks
, gen_info
);
223 #if defined(CONFIG_ARCH_TEGRA_114_SOC) || \
224 defined(CONFIG_ARCH_TEGRA_124_SOC) || \
225 defined(CONFIG_ARCH_TEGRA_210_SOC)
227 dt_clk
= tegra_lookup_dt_id(tegra_clk_pll_x
, tegra_clks
);
231 #if defined(CONFIG_ARCH_TEGRA_210_SOC)
232 if (gen_info
->gen
== gen5
)
233 clk
= tegra_clk_register_pllc_tegra210("pll_x", "pll_ref",
234 clk_base
, pmc_base
, CLK_IGNORE_UNUSED
, params
, NULL
);
237 clk
= tegra_clk_register_pllxc("pll_x", "pll_ref", clk_base
,
238 pmc_base
, CLK_IGNORE_UNUSED
, params
, NULL
);
244 dt_clk
= tegra_lookup_dt_id(tegra_clk_pll_x_out0
, tegra_clks
);
247 clk
= clk_register_fixed_factor(NULL
, "pll_x_out0", "pll_x",
248 CLK_SET_RATE_PARENT
, 1, 2);
253 void __init
tegra_super_clk_gen4_init(void __iomem
*clk_base
,
254 void __iomem
*pmc_base
,
255 struct tegra_clk
*tegra_clks
,
256 struct tegra_clk_pll_params
*params
)
258 tegra_super_clk_init(clk_base
, pmc_base
, tegra_clks
, params
,
259 &tegra_super_gen_info_gen4
);
262 void __init
tegra_super_clk_gen5_init(void __iomem
*clk_base
,
263 void __iomem
*pmc_base
,
264 struct tegra_clk
*tegra_clks
,
265 struct tegra_clk_pll_params
*params
)
267 tegra_super_clk_init(clk_base
, pmc_base
, tegra_clks
, params
,
268 &tegra_super_gen_info_gen5
);