1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved. 4 */ 5 6 #include <linux/io.h> 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 26 #define SCLK_DIVIDER 0x2c 27 28 static DEFINE_SPINLOCK(sysrate_lock); 29 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 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", 51 "unused", "pll_x", "unused", "unused", 52 "unused", "unused", "unused", "unused", 53 "dfllCPU_out" }; 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 59 static const struct tegra_super_gen_info tegra_super_gen_info_gen4 = { 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 85 static const struct tegra_super_gen_info tegra_super_gen_info_gen5 = { 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 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) 98 { 99 struct clk *clk; 100 struct clk **dt_clk; 101 102 /* SCLK_MUX */ 103 dt_clk = tegra_lookup_dt_id(tegra_clk_sclk_mux, tegra_clks); 104 if (dt_clk) { 105 clk = tegra_clk_register_super_mux("sclk_mux", 106 gen_info->sclk_parents, 107 gen_info->num_sclk_parents, 108 CLK_SET_RATE_PARENT, 109 clk_base + SCLK_BURST_POLICY, 110 0, 4, 0, 0, NULL); 111 *dt_clk = clk; 112 113 114 /* SCLK */ 115 dt_clk = tegra_lookup_dt_id(tegra_clk_sclk, tegra_clks); 116 if (dt_clk) { 117 clk = clk_register_divider(NULL, "sclk", "sclk_mux", 118 CLK_IS_CRITICAL, 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, 130 CLK_SET_RATE_PARENT | 131 CLK_IS_CRITICAL, 132 clk_base + SCLK_BURST_POLICY, 133 0, 4, 0, 0, NULL); 134 *dt_clk = clk; 135 } 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", 145 CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 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 | 160 CLK_IS_CRITICAL, clk_base + SYSTEM_CLK_RATE, 161 3, CLK_GATE_SET_TO_DISABLE, &sysrate_lock); 162 *dt_clk = clk; 163 } 164 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) 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) { 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, 183 TEGRA210_CPU_CLK, 4, 8, 0, NULL); 184 } else { 185 clk = tegra_clk_register_super_mux("cclk_g", 186 gen_info->cclk_g_parents, 187 gen_info->num_cclk_g_parents, 188 CLK_SET_RATE_PARENT, 189 clk_base + CCLKG_BURST_POLICY, 190 0, 4, 0, 0, NULL); 191 } 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) { 198 if (gen_info->gen == gen5) { 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 */ 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, 214 CLK_SET_RATE_PARENT, 215 clk_base + CCLKLP_BURST_POLICY, 216 TEGRA_DIVIDER_2, 4, 8, 9, NULL); 217 } 218 *dt_clk = clk; 219 } 220 221 tegra_sclk_init(clk_base, tegra_clks, gen_info); 222 223 #if defined(CONFIG_ARCH_TEGRA_114_SOC) || \ 224 defined(CONFIG_ARCH_TEGRA_124_SOC) || \ 225 defined(CONFIG_ARCH_TEGRA_210_SOC) 226 /* PLLX */ 227 dt_clk = tegra_lookup_dt_id(tegra_clk_pll_x, tegra_clks); 228 if (!dt_clk) 229 return; 230 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 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 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 } 270