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 0, 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 clk = tegra_clk_register_super_mux("cclk_lp", 200 gen_info->cclk_lp_parents, 201 gen_info->num_cclk_lp_parents, 202 CLK_SET_RATE_PARENT, 203 clk_base + CCLKLP_BURST_POLICY, 204 0, 4, 8, 0, NULL); 205 } else { 206 clk = tegra_clk_register_super_mux("cclk_lp", 207 gen_info->cclk_lp_parents, 208 gen_info->num_cclk_lp_parents, 209 CLK_SET_RATE_PARENT, 210 clk_base + CCLKLP_BURST_POLICY, 211 TEGRA_DIVIDER_2, 4, 8, 9, NULL); 212 } 213 *dt_clk = clk; 214 } 215 216 tegra_sclk_init(clk_base, tegra_clks, gen_info); 217 218 #if defined(CONFIG_ARCH_TEGRA_114_SOC) || \ 219 defined(CONFIG_ARCH_TEGRA_124_SOC) || \ 220 defined(CONFIG_ARCH_TEGRA_210_SOC) 221 /* PLLX */ 222 dt_clk = tegra_lookup_dt_id(tegra_clk_pll_x, tegra_clks); 223 if (!dt_clk) 224 return; 225 226 #if defined(CONFIG_ARCH_TEGRA_210_SOC) 227 if (gen_info->gen == gen5) 228 clk = tegra_clk_register_pllc_tegra210("pll_x", "pll_ref", 229 clk_base, pmc_base, CLK_IGNORE_UNUSED, params, NULL); 230 else 231 #endif 232 clk = tegra_clk_register_pllxc("pll_x", "pll_ref", clk_base, 233 pmc_base, CLK_IGNORE_UNUSED, params, NULL); 234 235 *dt_clk = clk; 236 237 /* PLLX_OUT0 */ 238 239 dt_clk = tegra_lookup_dt_id(tegra_clk_pll_x_out0, tegra_clks); 240 if (!dt_clk) 241 return; 242 clk = clk_register_fixed_factor(NULL, "pll_x_out0", "pll_x", 243 CLK_SET_RATE_PARENT, 1, 2); 244 *dt_clk = clk; 245 #endif 246 } 247 248 void __init tegra_super_clk_gen4_init(void __iomem *clk_base, 249 void __iomem *pmc_base, 250 struct tegra_clk *tegra_clks, 251 struct tegra_clk_pll_params *params) 252 { 253 tegra_super_clk_init(clk_base, pmc_base, tegra_clks, params, 254 &tegra_super_gen_info_gen4); 255 } 256 257 void __init tegra_super_clk_gen5_init(void __iomem *clk_base, 258 void __iomem *pmc_base, 259 struct tegra_clk *tegra_clks, 260 struct tegra_clk_pll_params *params) 261 { 262 tegra_super_clk_init(clk_base, pmc_base, tegra_clks, params, 263 &tegra_super_gen_info_gen5); 264 } 265