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", 0, 129 clk_base + SCLK_DIVIDER, 0, 8, 130 0, &sysrate_lock); 131 *dt_clk = clk; 132 } 133 } else { 134 /* SCLK */ 135 dt_clk = tegra_lookup_dt_id(tegra_clk_sclk, tegra_clks); 136 if (dt_clk) { 137 clk = tegra_clk_register_super_mux("sclk", 138 gen_info->sclk_parents, 139 gen_info->num_sclk_parents, 140 CLK_SET_RATE_PARENT, 141 clk_base + SCLK_BURST_POLICY, 142 0, 4, 0, 0, NULL); 143 *dt_clk = clk; 144 } 145 } 146 147 /* HCLK */ 148 dt_clk = tegra_lookup_dt_id(tegra_clk_hclk, tegra_clks); 149 if (dt_clk) { 150 clk = clk_register_divider(NULL, "hclk_div", "sclk", 0, 151 clk_base + SYSTEM_CLK_RATE, 4, 2, 0, 152 &sysrate_lock); 153 clk = clk_register_gate(NULL, "hclk", "hclk_div", 154 CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 155 clk_base + SYSTEM_CLK_RATE, 156 7, CLK_GATE_SET_TO_DISABLE, &sysrate_lock); 157 *dt_clk = clk; 158 } 159 160 /* PCLK */ 161 dt_clk = tegra_lookup_dt_id(tegra_clk_pclk, tegra_clks); 162 if (!dt_clk) 163 return; 164 165 clk = clk_register_divider(NULL, "pclk_div", "hclk", 0, 166 clk_base + SYSTEM_CLK_RATE, 0, 2, 0, 167 &sysrate_lock); 168 clk = clk_register_gate(NULL, "pclk", "pclk_div", CLK_SET_RATE_PARENT | 169 CLK_IS_CRITICAL, clk_base + SYSTEM_CLK_RATE, 170 3, CLK_GATE_SET_TO_DISABLE, &sysrate_lock); 171 *dt_clk = clk; 172 } 173 174 static void __init tegra_super_clk_init(void __iomem *clk_base, 175 void __iomem *pmc_base, 176 struct tegra_clk *tegra_clks, 177 struct tegra_clk_pll_params *params, 178 const struct tegra_super_gen_info *gen_info) 179 { 180 struct clk *clk; 181 struct clk **dt_clk; 182 183 /* CCLKG */ 184 dt_clk = tegra_lookup_dt_id(tegra_clk_cclk_g, tegra_clks); 185 if (dt_clk) { 186 if (gen_info->gen == gen5) { 187 clk = tegra_clk_register_super_mux("cclk_g", 188 gen_info->cclk_g_parents, 189 gen_info->num_cclk_g_parents, 190 CLK_SET_RATE_PARENT, 191 clk_base + CCLKG_BURST_POLICY, 192 0, 4, 8, 0, NULL); 193 } else { 194 clk = tegra_clk_register_super_mux("cclk_g", 195 gen_info->cclk_g_parents, 196 gen_info->num_cclk_g_parents, 197 CLK_SET_RATE_PARENT, 198 clk_base + CCLKG_BURST_POLICY, 199 0, 4, 0, 0, NULL); 200 } 201 *dt_clk = clk; 202 } 203 204 /* CCLKLP */ 205 dt_clk = tegra_lookup_dt_id(tegra_clk_cclk_lp, tegra_clks); 206 if (dt_clk) { 207 if (gen_info->gen == gen5) { 208 clk = tegra_clk_register_super_mux("cclk_lp", 209 gen_info->cclk_lp_parents, 210 gen_info->num_cclk_lp_parents, 211 CLK_SET_RATE_PARENT, 212 clk_base + CCLKLP_BURST_POLICY, 213 0, 4, 8, 0, NULL); 214 } else { 215 clk = tegra_clk_register_super_mux("cclk_lp", 216 gen_info->cclk_lp_parents, 217 gen_info->num_cclk_lp_parents, 218 CLK_SET_RATE_PARENT, 219 clk_base + CCLKLP_BURST_POLICY, 220 TEGRA_DIVIDER_2, 4, 8, 9, NULL); 221 } 222 *dt_clk = clk; 223 } 224 225 tegra_sclk_init(clk_base, tegra_clks, gen_info); 226 227 #if defined(CONFIG_ARCH_TEGRA_114_SOC) || \ 228 defined(CONFIG_ARCH_TEGRA_124_SOC) || \ 229 defined(CONFIG_ARCH_TEGRA_210_SOC) 230 /* PLLX */ 231 dt_clk = tegra_lookup_dt_id(tegra_clk_pll_x, tegra_clks); 232 if (!dt_clk) 233 return; 234 235 #if defined(CONFIG_ARCH_TEGRA_210_SOC) 236 if (gen_info->gen == gen5) 237 clk = tegra_clk_register_pllc_tegra210("pll_x", "pll_ref", 238 clk_base, pmc_base, CLK_IGNORE_UNUSED, params, NULL); 239 else 240 #endif 241 clk = tegra_clk_register_pllxc("pll_x", "pll_ref", clk_base, 242 pmc_base, CLK_IGNORE_UNUSED, params, NULL); 243 244 *dt_clk = clk; 245 246 /* PLLX_OUT0 */ 247 248 dt_clk = tegra_lookup_dt_id(tegra_clk_pll_x_out0, tegra_clks); 249 if (!dt_clk) 250 return; 251 clk = clk_register_fixed_factor(NULL, "pll_x_out0", "pll_x", 252 CLK_SET_RATE_PARENT, 1, 2); 253 *dt_clk = clk; 254 #endif 255 } 256 257 void __init tegra_super_clk_gen4_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_gen4); 264 } 265 266 void __init tegra_super_clk_gen5_init(void __iomem *clk_base, 267 void __iomem *pmc_base, 268 struct tegra_clk *tegra_clks, 269 struct tegra_clk_pll_params *params) 270 { 271 tegra_super_clk_init(clk_base, pmc_base, tegra_clks, params, 272 &tegra_super_gen_info_gen5); 273 } 274