Thomas Gleixner | 9952f69 | 2019-05-28 10:10:04 -0700 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-only |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 2 | /* |
| 3 | * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved. |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 4 | */ |
| 5 | |
| 6 | #include <linux/io.h> |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 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 |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 26 | #define SCLK_DIVIDER 0x2c |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 27 | |
| 28 | static DEFINE_SPINLOCK(sysrate_lock); |
| 29 | |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 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 | |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 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", |
Tuomas Tynkkynen | 79cf95c | 2015-05-13 17:58:43 +0300 | [diff] [blame] | 51 | "unused", "pll_x", "unused", "unused", |
| 52 | "unused", "unused", "unused", "unused", |
| 53 | "dfllCPU_out" }; |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 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 | |
Jon Hunter | 5a1d5ef | 2015-12-04 17:04:25 +0000 | [diff] [blame] | 59 | static const struct tegra_super_gen_info tegra_super_gen_info_gen4 = { |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 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 | |
Jon Hunter | 5a1d5ef | 2015-12-04 17:04:25 +0000 | [diff] [blame] | 85 | static const struct tegra_super_gen_info tegra_super_gen_info_gen5 = { |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 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 | |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 95 | static void __init tegra_sclk_init(void __iomem *clk_base, |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 96 | struct tegra_clk *tegra_clks, |
| 97 | const struct tegra_super_gen_info *gen_info) |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 98 | { |
| 99 | struct clk *clk; |
| 100 | struct clk **dt_clk; |
| 101 | |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 102 | /* SCLK_MUX */ |
| 103 | dt_clk = tegra_lookup_dt_id(tegra_clk_sclk_mux, tegra_clks); |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 104 | if (dt_clk) { |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 105 | clk = tegra_clk_register_super_mux("sclk_mux", |
| 106 | gen_info->sclk_parents, |
| 107 | gen_info->num_sclk_parents, |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 108 | CLK_SET_RATE_PARENT, |
| 109 | clk_base + SCLK_BURST_POLICY, |
| 110 | 0, 4, 0, 0, NULL); |
| 111 | *dt_clk = clk; |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 112 | |
| 113 | |
| 114 | /* SCLK */ |
| 115 | dt_clk = tegra_lookup_dt_id(tegra_clk_sclk, tegra_clks); |
| 116 | if (dt_clk) { |
Dmitry Osipenko | 2dcabf0 | 2018-01-10 16:59:42 +0300 | [diff] [blame] | 117 | clk = clk_register_divider(NULL, "sclk", "sclk_mux", |
| 118 | CLK_IS_CRITICAL, |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 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, |
Dmitry Osipenko | 2dcabf0 | 2018-01-10 16:59:42 +0300 | [diff] [blame] | 130 | CLK_SET_RATE_PARENT | |
| 131 | CLK_IS_CRITICAL, |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 132 | clk_base + SCLK_BURST_POLICY, |
| 133 | 0, 4, 0, 0, NULL); |
| 134 | *dt_clk = clk; |
| 135 | } |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 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", |
Dmitry Osipenko | 2dcabf0 | 2018-01-10 16:59:42 +0300 | [diff] [blame] | 145 | CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 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 | |
Jon Hunter | 109eba2 | 2017-10-23 12:12:52 +0100 | [diff] [blame] | 160 | CLK_IS_CRITICAL, clk_base + SYSTEM_CLK_RATE, |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 161 | 3, CLK_GATE_SET_TO_DISABLE, &sysrate_lock); |
| 162 | *dt_clk = clk; |
| 163 | } |
| 164 | |
Jon Hunter | 5a1d5ef | 2015-12-04 17:04:25 +0000 | [diff] [blame] | 165 | static void __init tegra_super_clk_init(void __iomem *clk_base, |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 166 | void __iomem *pmc_base, |
| 167 | struct tegra_clk *tegra_clks, |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 168 | struct tegra_clk_pll_params *params, |
| 169 | const struct tegra_super_gen_info *gen_info) |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 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) { |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 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, |
Sowjanya Komatineni | 68a14a5 | 2019-08-16 12:41:54 -0700 | [diff] [blame] | 183 | TEGRA210_CPU_CLK, 4, 8, 0, NULL); |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 184 | } else { |
| 185 | clk = tegra_clk_register_super_mux("cclk_g", |
| 186 | gen_info->cclk_g_parents, |
| 187 | gen_info->num_cclk_g_parents, |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 188 | CLK_SET_RATE_PARENT, |
| 189 | clk_base + CCLKG_BURST_POLICY, |
| 190 | 0, 4, 0, 0, NULL); |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 191 | } |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 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) { |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 198 | if (gen_info->gen == gen5) { |
Sowjanya Komatineni | 68a14a5 | 2019-08-16 12:41:54 -0700 | [diff] [blame] | 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 | */ |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 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, |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 214 | CLK_SET_RATE_PARENT, |
| 215 | clk_base + CCLKLP_BURST_POLICY, |
Andrew Bresticker | 88b4bd7 | 2013-12-26 16:44:26 -0800 | [diff] [blame] | 216 | TEGRA_DIVIDER_2, 4, 8, 9, NULL); |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 217 | } |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 218 | *dt_clk = clk; |
| 219 | } |
| 220 | |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 221 | tegra_sclk_init(clk_base, tegra_clks, gen_info); |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 222 | |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 223 | #if defined(CONFIG_ARCH_TEGRA_114_SOC) || \ |
| 224 | defined(CONFIG_ARCH_TEGRA_124_SOC) || \ |
| 225 | defined(CONFIG_ARCH_TEGRA_210_SOC) |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 226 | /* PLLX */ |
| 227 | dt_clk = tegra_lookup_dt_id(tegra_clk_pll_x, tegra_clks); |
| 228 | if (!dt_clk) |
| 229 | return; |
| 230 | |
Alex Frid | ac99afe | 2017-07-25 13:34:11 +0300 | [diff] [blame] | 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 | |
Peter De Schrijver | a7c8485 | 2013-09-03 15:46:01 +0300 | [diff] [blame] | 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 | |
Bill Huang | 139fd30 | 2015-06-18 17:28:35 -0400 | [diff] [blame] | 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 | } |