Commit | Line | Data |
---|---|---|
9952f691 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
a7c8485a PDS |
2 | /* |
3 | * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved. | |
a7c8485a PDS |
4 | */ |
5 | ||
6 | #include <linux/io.h> | |
a7c8485a PDS |
7 | #include <linux/clk-provider.h> |
8 | #include <linux/of.h> | |
9 | #include <linux/of_address.h> | |
10 | #include <linux/delay.h> | |
11 | #include <linux/export.h> | |
12 | #include <linux/clk/tegra.h> | |
13 | ||
14 | #include "clk.h" | |
15 | #include "clk-id.h" | |
16 | ||
17 | #define PLLX_BASE 0xe0 | |
18 | #define PLLX_MISC 0xe4 | |
19 | #define PLLX_MISC2 0x514 | |
20 | #define PLLX_MISC3 0x518 | |
21 | ||
22 | #define CCLKG_BURST_POLICY 0x368 | |
23 | #define CCLKLP_BURST_POLICY 0x370 | |
24 | #define SCLK_BURST_POLICY 0x028 | |
25 | #define SYSTEM_CLK_RATE 0x030 | |
139fd309 | 26 | #define SCLK_DIVIDER 0x2c |
a7c8485a PDS |
27 | |
28 | static DEFINE_SPINLOCK(sysrate_lock); | |
29 | ||
139fd309 BH |
30 | enum tegra_super_gen { |
31 | gen4 = 4, | |
32 | gen5, | |
33 | }; | |
34 | ||
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; | |
40 | int num_sclk_parents; | |
41 | int num_cclk_g_parents; | |
42 | int num_cclk_lp_parents; | |
43 | }; | |
44 | ||
a7c8485a PDS |
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" }; | |
48 | ||
49 | static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", | |
50 | "pll_p", "pll_p_out4", "unused", | |
79cf95c7 TT |
51 | "unused", "pll_x", "unused", "unused", |
52 | "unused", "unused", "unused", "unused", | |
53 | "dfllCPU_out" }; | |
a7c8485a PDS |
54 | |
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" }; | |
58 | ||
5a1d5eff | 59 | static const struct tegra_super_gen_info tegra_super_gen_info_gen4 = { |
139fd309 BH |
60 | .gen = 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), | |
67 | }; | |
68 | ||
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" }; | |
72 | ||
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", | |
77 | "dfllCPU_out" }; | |
78 | ||
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", | |
83 | "dfllCPU_out" }; | |
84 | ||
5a1d5eff | 85 | static const struct tegra_super_gen_info tegra_super_gen_info_gen5 = { |
139fd309 BH |
86 | .gen = 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), | |
93 | }; | |
94 | ||
a7c8485a | 95 | static void __init tegra_sclk_init(void __iomem *clk_base, |
139fd309 BH |
96 | struct tegra_clk *tegra_clks, |
97 | const struct tegra_super_gen_info *gen_info) | |
a7c8485a PDS |
98 | { |
99 | struct clk *clk; | |
100 | struct clk **dt_clk; | |
101 | ||
139fd309 BH |
102 | /* SCLK_MUX */ |
103 | dt_clk = tegra_lookup_dt_id(tegra_clk_sclk_mux, tegra_clks); | |
a7c8485a | 104 | if (dt_clk) { |
139fd309 BH |
105 | clk = tegra_clk_register_super_mux("sclk_mux", |
106 | gen_info->sclk_parents, | |
107 | gen_info->num_sclk_parents, | |
a7c8485a PDS |
108 | CLK_SET_RATE_PARENT, |
109 | clk_base + SCLK_BURST_POLICY, | |
110 | 0, 4, 0, 0, NULL); | |
111 | *dt_clk = clk; | |
139fd309 BH |
112 | |
113 | ||
114 | /* SCLK */ | |
115 | dt_clk = tegra_lookup_dt_id(tegra_clk_sclk, tegra_clks); | |
116 | if (dt_clk) { | |
2dcabf05 DO |
117 | clk = clk_register_divider(NULL, "sclk", "sclk_mux", |
118 | CLK_IS_CRITICAL, | |
139fd309 BH |
119 | clk_base + SCLK_DIVIDER, 0, 8, |
120 | 0, &sysrate_lock); | |
121 | *dt_clk = clk; | |
122 | } | |
123 | } else { | |
124 | /* SCLK */ | |
125 | dt_clk = tegra_lookup_dt_id(tegra_clk_sclk, tegra_clks); | |
126 | if (dt_clk) { | |
127 | clk = tegra_clk_register_super_mux("sclk", | |
128 | gen_info->sclk_parents, | |
129 | gen_info->num_sclk_parents, | |
2dcabf05 DO |
130 | CLK_SET_RATE_PARENT | |
131 | CLK_IS_CRITICAL, | |
139fd309 BH |
132 | clk_base + SCLK_BURST_POLICY, |
133 | 0, 4, 0, 0, NULL); | |
134 | *dt_clk = clk; | |
135 | } | |
a7c8485a PDS |
136 | } |
137 | ||
138 | /* HCLK */ | |
139 | dt_clk = tegra_lookup_dt_id(tegra_clk_hclk, tegra_clks); | |
140 | if (dt_clk) { | |
141 | clk = clk_register_divider(NULL, "hclk_div", "sclk", 0, | |
142 | clk_base + SYSTEM_CLK_RATE, 4, 2, 0, | |
143 | &sysrate_lock); | |
144 | clk = clk_register_gate(NULL, "hclk", "hclk_div", | |
2dcabf05 | 145 | CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, |
a7c8485a PDS |
146 | clk_base + SYSTEM_CLK_RATE, |
147 | 7, CLK_GATE_SET_TO_DISABLE, &sysrate_lock); | |
148 | *dt_clk = clk; | |
149 | } | |
150 | ||
151 | /* PCLK */ | |
152 | dt_clk = tegra_lookup_dt_id(tegra_clk_pclk, tegra_clks); | |
153 | if (!dt_clk) | |
154 | return; | |
155 | ||
156 | clk = clk_register_divider(NULL, "pclk_div", "hclk", 0, | |
157 | clk_base + SYSTEM_CLK_RATE, 0, 2, 0, | |
158 | &sysrate_lock); | |
159 | clk = clk_register_gate(NULL, "pclk", "pclk_div", CLK_SET_RATE_PARENT | | |
109eba2e | 160 | CLK_IS_CRITICAL, clk_base + SYSTEM_CLK_RATE, |
a7c8485a PDS |
161 | 3, CLK_GATE_SET_TO_DISABLE, &sysrate_lock); |
162 | *dt_clk = clk; | |
163 | } | |
164 | ||
5a1d5eff | 165 | static void __init tegra_super_clk_init(void __iomem *clk_base, |
a7c8485a PDS |
166 | void __iomem *pmc_base, |
167 | struct tegra_clk *tegra_clks, | |
139fd309 BH |
168 | struct tegra_clk_pll_params *params, |
169 | const struct tegra_super_gen_info *gen_info) | |
a7c8485a PDS |
170 | { |
171 | struct clk *clk; | |
172 | struct clk **dt_clk; | |
173 | ||
174 | /* CCLKG */ | |
175 | dt_clk = tegra_lookup_dt_id(tegra_clk_cclk_g, tegra_clks); | |
176 | if (dt_clk) { | |
139fd309 BH |
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, | |
181 | CLK_SET_RATE_PARENT, | |
182 | clk_base + CCLKG_BURST_POLICY, | |
68a14a56 | 183 | TEGRA210_CPU_CLK, 4, 8, 0, NULL); |
139fd309 BH |
184 | } else { |
185 | clk = tegra_clk_register_super_mux("cclk_g", | |
186 | gen_info->cclk_g_parents, | |
187 | gen_info->num_cclk_g_parents, | |
a7c8485a PDS |
188 | CLK_SET_RATE_PARENT, |
189 | clk_base + CCLKG_BURST_POLICY, | |
190 | 0, 4, 0, 0, NULL); | |
139fd309 | 191 | } |
a7c8485a PDS |
192 | *dt_clk = clk; |
193 | } | |
194 | ||
195 | /* CCLKLP */ | |
196 | dt_clk = tegra_lookup_dt_id(tegra_clk_cclk_lp, tegra_clks); | |
197 | if (dt_clk) { | |
139fd309 | 198 | if (gen_info->gen == gen5) { |
68a14a56 SK |
199 | /* |
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. | |
203 | */ | |
139fd309 BH |
204 | clk = tegra_clk_register_super_mux("cclk_lp", |
205 | gen_info->cclk_lp_parents, | |
206 | gen_info->num_cclk_lp_parents, | |
207 | CLK_SET_RATE_PARENT, | |
208 | clk_base + CCLKLP_BURST_POLICY, | |
209 | 0, 4, 8, 0, NULL); | |
210 | } else { | |
211 | clk = tegra_clk_register_super_mux("cclk_lp", | |
212 | gen_info->cclk_lp_parents, | |
213 | gen_info->num_cclk_lp_parents, | |
a7c8485a PDS |
214 | CLK_SET_RATE_PARENT, |
215 | clk_base + CCLKLP_BURST_POLICY, | |
88b4bd70 | 216 | TEGRA_DIVIDER_2, 4, 8, 9, NULL); |
139fd309 | 217 | } |
a7c8485a PDS |
218 | *dt_clk = clk; |
219 | } | |
220 | ||
139fd309 | 221 | tegra_sclk_init(clk_base, tegra_clks, gen_info); |
a7c8485a | 222 | |
139fd309 BH |
223 | #if defined(CONFIG_ARCH_TEGRA_114_SOC) || \ |
224 | defined(CONFIG_ARCH_TEGRA_124_SOC) || \ | |
225 | defined(CONFIG_ARCH_TEGRA_210_SOC) | |
a7c8485a PDS |
226 | /* PLLX */ |
227 | dt_clk = tegra_lookup_dt_id(tegra_clk_pll_x, tegra_clks); | |
228 | if (!dt_clk) | |
229 | return; | |
230 | ||
ac99afe5 AF |
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); | |
235 | else | |
236 | #endif | |
237 | clk = tegra_clk_register_pllxc("pll_x", "pll_ref", clk_base, | |
238 | pmc_base, CLK_IGNORE_UNUSED, params, NULL); | |
239 | ||
a7c8485a PDS |
240 | *dt_clk = clk; |
241 | ||
242 | /* PLLX_OUT0 */ | |
243 | ||
244 | dt_clk = tegra_lookup_dt_id(tegra_clk_pll_x_out0, tegra_clks); | |
245 | if (!dt_clk) | |
246 | return; | |
247 | clk = clk_register_fixed_factor(NULL, "pll_x_out0", "pll_x", | |
248 | CLK_SET_RATE_PARENT, 1, 2); | |
249 | *dt_clk = clk; | |
250 | #endif | |
251 | } | |
252 | ||
139fd309 BH |
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) | |
257 | { | |
258 | tegra_super_clk_init(clk_base, pmc_base, tegra_clks, params, | |
259 | &tegra_super_gen_info_gen4); | |
260 | } | |
261 | ||
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) | |
266 | { | |
267 | tegra_super_clk_init(clk_base, pmc_base, tegra_clks, params, | |
268 | &tegra_super_gen_info_gen5); | |
269 | } |