1 /* 2 * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms and conditions of the GNU General Public License, 6 * version 2, as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope it will be useful, but WITHOUT 9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 * more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16 17 #include <linux/io.h> 18 #include <linux/clk-provider.h> 19 #include <linux/of.h> 20 #include <linux/of_address.h> 21 #include <linux/delay.h> 22 #include <linux/export.h> 23 #include <linux/clk/tegra.h> 24 25 #include "clk.h" 26 #include "clk-id.h" 27 28 #define PLLX_BASE 0xe0 29 #define PLLX_MISC 0xe4 30 #define PLLX_MISC2 0x514 31 #define PLLX_MISC3 0x518 32 33 #define CCLKG_BURST_POLICY 0x368 34 #define CCLKLP_BURST_POLICY 0x370 35 #define SCLK_BURST_POLICY 0x028 36 #define SYSTEM_CLK_RATE 0x030 37 #define SCLK_DIVIDER 0x2c 38 39 static DEFINE_SPINLOCK(sysrate_lock); 40 41 enum tegra_super_gen { 42 gen4 = 4, 43 gen5, 44 }; 45 46 struct tegra_super_gen_info { 47 enum tegra_super_gen gen; 48 const char **sclk_parents; 49 const char **cclk_g_parents; 50 const char **cclk_lp_parents; 51 int num_sclk_parents; 52 int num_cclk_g_parents; 53 int num_cclk_lp_parents; 54 }; 55 56 static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4", 57 "pll_p", "pll_p_out2", "unused", 58 "clk_32k", "pll_m_out1" }; 59 60 static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", 61 "pll_p", "pll_p_out4", "unused", 62 "unused", "pll_x", "unused", "unused", 63 "unused", "unused", "unused", "unused", 64 "dfllCPU_out" }; 65 66 static const char *cclk_lp_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", 67 "pll_p", "pll_p_out4", "unused", 68 "unused", "pll_x", "pll_x_out0" }; 69 70 static const struct tegra_super_gen_info tegra_super_gen_info_gen4 = { 71 .gen = gen4, 72 .sclk_parents = sclk_parents, 73 .cclk_g_parents = cclk_g_parents, 74 .cclk_lp_parents = cclk_lp_parents, 75 .num_sclk_parents = ARRAY_SIZE(sclk_parents), 76 .num_cclk_g_parents = ARRAY_SIZE(cclk_g_parents), 77 .num_cclk_lp_parents = ARRAY_SIZE(cclk_lp_parents), 78 }; 79 80 static const char *sclk_parents_gen5[] = { "clk_m", "pll_c_out1", "pll_c4_out3", 81 "pll_p", "pll_p_out2", "pll_c4_out1", 82 "clk_32k", "pll_c4_out2" }; 83 84 static const char *cclk_g_parents_gen5[] = { "clk_m", "unused", "clk_32k", "unused", 85 "pll_p", "pll_p_out4", "unused", 86 "unused", "pll_x", "unused", "unused", 87 "unused", "unused", "unused", "unused", 88 "dfllCPU_out" }; 89 90 static const char *cclk_lp_parents_gen5[] = { "clk_m", "unused", "clk_32k", "unused", 91 "pll_p", "pll_p_out4", "unused", 92 "unused", "pll_x", "unused", "unused", 93 "unused", "unused", "unused", "unused", 94 "dfllCPU_out" }; 95 96 static const struct tegra_super_gen_info tegra_super_gen_info_gen5 = { 97 .gen = gen5, 98 .sclk_parents = sclk_parents_gen5, 99 .cclk_g_parents = cclk_g_parents_gen5, 100 .cclk_lp_parents = cclk_lp_parents_gen5, 101 .num_sclk_parents = ARRAY_SIZE(sclk_parents_gen5), 102 .num_cclk_g_parents = ARRAY_SIZE(cclk_g_parents_gen5), 103 .num_cclk_lp_parents = ARRAY_SIZE(cclk_lp_parents_gen5), 104 }; 105 106 static void __init tegra_sclk_init(void __iomem *clk_base, 107 struct tegra_clk *tegra_clks, 108 const struct tegra_super_gen_info *gen_info) 109 { 110 struct clk *clk; 111 struct clk **dt_clk; 112 113 /* SCLK_MUX */ 114 dt_clk = tegra_lookup_dt_id(tegra_clk_sclk_mux, tegra_clks); 115 if (dt_clk) { 116 clk = tegra_clk_register_super_mux("sclk_mux", 117 gen_info->sclk_parents, 118 gen_info->num_sclk_parents, 119 CLK_SET_RATE_PARENT, 120 clk_base + SCLK_BURST_POLICY, 121 0, 4, 0, 0, NULL); 122 *dt_clk = clk; 123 124 125 /* SCLK */ 126 dt_clk = tegra_lookup_dt_id(tegra_clk_sclk, tegra_clks); 127 if (dt_clk) { 128 clk = clk_register_divider(NULL, "sclk", "sclk_mux", 129 CLK_IS_CRITICAL, 130 clk_base + SCLK_DIVIDER, 0, 8, 131 0, &sysrate_lock); 132 *dt_clk = clk; 133 } 134 } else { 135 /* SCLK */ 136 dt_clk = tegra_lookup_dt_id(tegra_clk_sclk, tegra_clks); 137 if (dt_clk) { 138 clk = tegra_clk_register_super_mux("sclk", 139 gen_info->sclk_parents, 140 gen_info->num_sclk_parents, 141 CLK_SET_RATE_PARENT | 142 CLK_IS_CRITICAL, 143 clk_base + SCLK_BURST_POLICY, 144 0, 4, 0, 0, NULL); 145 *dt_clk = clk; 146 } 147 } 148 149 /* HCLK */ 150 dt_clk = tegra_lookup_dt_id(tegra_clk_hclk, tegra_clks); 151 if (dt_clk) { 152 clk = clk_register_divider(NULL, "hclk_div", "sclk", 0, 153 clk_base + SYSTEM_CLK_RATE, 4, 2, 0, 154 &sysrate_lock); 155 clk = clk_register_gate(NULL, "hclk", "hclk_div", 156 CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 157 clk_base + SYSTEM_CLK_RATE, 158 7, CLK_GATE_SET_TO_DISABLE, &sysrate_lock); 159 *dt_clk = clk; 160 } 161 162 /* PCLK */ 163 dt_clk = tegra_lookup_dt_id(tegra_clk_pclk, tegra_clks); 164 if (!dt_clk) 165 return; 166 167 clk = clk_register_divider(NULL, "pclk_div", "hclk", 0, 168 clk_base + SYSTEM_CLK_RATE, 0, 2, 0, 169 &sysrate_lock); 170 clk = clk_register_gate(NULL, "pclk", "pclk_div", CLK_SET_RATE_PARENT | 171 CLK_IS_CRITICAL, clk_base + SYSTEM_CLK_RATE, 172 3, CLK_GATE_SET_TO_DISABLE, &sysrate_lock); 173 *dt_clk = clk; 174 } 175 176 static void __init tegra_super_clk_init(void __iomem *clk_base, 177 void __iomem *pmc_base, 178 struct tegra_clk *tegra_clks, 179 struct tegra_clk_pll_params *params, 180 const struct tegra_super_gen_info *gen_info) 181 { 182 struct clk *clk; 183 struct clk **dt_clk; 184 185 /* CCLKG */ 186 dt_clk = tegra_lookup_dt_id(tegra_clk_cclk_g, tegra_clks); 187 if (dt_clk) { 188 if (gen_info->gen == gen5) { 189 clk = tegra_clk_register_super_mux("cclk_g", 190 gen_info->cclk_g_parents, 191 gen_info->num_cclk_g_parents, 192 CLK_SET_RATE_PARENT, 193 clk_base + CCLKG_BURST_POLICY, 194 0, 4, 8, 0, NULL); 195 } else { 196 clk = tegra_clk_register_super_mux("cclk_g", 197 gen_info->cclk_g_parents, 198 gen_info->num_cclk_g_parents, 199 CLK_SET_RATE_PARENT, 200 clk_base + CCLKG_BURST_POLICY, 201 0, 4, 0, 0, NULL); 202 } 203 *dt_clk = clk; 204 } 205 206 /* CCLKLP */ 207 dt_clk = tegra_lookup_dt_id(tegra_clk_cclk_lp, tegra_clks); 208 if (dt_clk) { 209 if (gen_info->gen == gen5) { 210 clk = tegra_clk_register_super_mux("cclk_lp", 211 gen_info->cclk_lp_parents, 212 gen_info->num_cclk_lp_parents, 213 CLK_SET_RATE_PARENT, 214 clk_base + CCLKLP_BURST_POLICY, 215 0, 4, 8, 0, NULL); 216 } else { 217 clk = tegra_clk_register_super_mux("cclk_lp", 218 gen_info->cclk_lp_parents, 219 gen_info->num_cclk_lp_parents, 220 CLK_SET_RATE_PARENT, 221 clk_base + CCLKLP_BURST_POLICY, 222 TEGRA_DIVIDER_2, 4, 8, 9, NULL); 223 } 224 *dt_clk = clk; 225 } 226 227 tegra_sclk_init(clk_base, tegra_clks, gen_info); 228 229 #if defined(CONFIG_ARCH_TEGRA_114_SOC) || \ 230 defined(CONFIG_ARCH_TEGRA_124_SOC) || \ 231 defined(CONFIG_ARCH_TEGRA_210_SOC) 232 /* PLLX */ 233 dt_clk = tegra_lookup_dt_id(tegra_clk_pll_x, tegra_clks); 234 if (!dt_clk) 235 return; 236 237 #if defined(CONFIG_ARCH_TEGRA_210_SOC) 238 if (gen_info->gen == gen5) 239 clk = tegra_clk_register_pllc_tegra210("pll_x", "pll_ref", 240 clk_base, pmc_base, CLK_IGNORE_UNUSED, params, NULL); 241 else 242 #endif 243 clk = tegra_clk_register_pllxc("pll_x", "pll_ref", clk_base, 244 pmc_base, CLK_IGNORE_UNUSED, params, NULL); 245 246 *dt_clk = clk; 247 248 /* PLLX_OUT0 */ 249 250 dt_clk = tegra_lookup_dt_id(tegra_clk_pll_x_out0, tegra_clks); 251 if (!dt_clk) 252 return; 253 clk = clk_register_fixed_factor(NULL, "pll_x_out0", "pll_x", 254 CLK_SET_RATE_PARENT, 1, 2); 255 *dt_clk = clk; 256 #endif 257 } 258 259 void __init tegra_super_clk_gen4_init(void __iomem *clk_base, 260 void __iomem *pmc_base, 261 struct tegra_clk *tegra_clks, 262 struct tegra_clk_pll_params *params) 263 { 264 tegra_super_clk_init(clk_base, pmc_base, tegra_clks, params, 265 &tegra_super_gen_info_gen4); 266 } 267 268 void __init tegra_super_clk_gen5_init(void __iomem *clk_base, 269 void __iomem *pmc_base, 270 struct tegra_clk *tegra_clks, 271 struct tegra_clk_pll_params *params) 272 { 273 tegra_super_clk_init(clk_base, pmc_base, tegra_clks, params, 274 &tegra_super_gen_info_gen5); 275 } 276