0001
0002
0003
0004
0005
0006 #include <linux/io.h>
0007 #include <linux/clk-provider.h>
0008 #include <linux/of.h>
0009 #include <linux/of_address.h>
0010 #include <linux/delay.h>
0011 #include <linux/export.h>
0012 #include <linux/clk/tegra.h>
0013
0014 #include "clk.h"
0015 #include "clk-id.h"
0016
0017 #define PLLX_BASE 0xe0
0018 #define PLLX_MISC 0xe4
0019 #define PLLX_MISC2 0x514
0020 #define PLLX_MISC3 0x518
0021
0022 #define CCLKG_BURST_POLICY 0x368
0023 #define CCLKLP_BURST_POLICY 0x370
0024 #define SCLK_BURST_POLICY 0x028
0025 #define SYSTEM_CLK_RATE 0x030
0026 #define SCLK_DIVIDER 0x2c
0027
0028 static DEFINE_SPINLOCK(sysrate_lock);
0029
0030 enum tegra_super_gen {
0031 gen4 = 4,
0032 gen5,
0033 };
0034
0035 struct tegra_super_gen_info {
0036 enum tegra_super_gen gen;
0037 const char **sclk_parents;
0038 const char **cclk_g_parents;
0039 const char **cclk_lp_parents;
0040 int num_sclk_parents;
0041 int num_cclk_g_parents;
0042 int num_cclk_lp_parents;
0043 };
0044
0045 static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4",
0046 "pll_p", "pll_p_out2", "unused",
0047 "clk_32k", "pll_m_out1" };
0048
0049 static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
0050 "pll_p", "pll_p_out4", "unused",
0051 "unused", "pll_x", "unused", "unused",
0052 "unused", "unused", "unused", "unused",
0053 "dfllCPU_out" };
0054
0055 static const char *cclk_lp_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
0056 "pll_p", "pll_p_out4", "unused",
0057 "unused", "pll_x", "pll_x_out0" };
0058
0059 static const struct tegra_super_gen_info tegra_super_gen_info_gen4 = {
0060 .gen = gen4,
0061 .sclk_parents = sclk_parents,
0062 .cclk_g_parents = cclk_g_parents,
0063 .cclk_lp_parents = cclk_lp_parents,
0064 .num_sclk_parents = ARRAY_SIZE(sclk_parents),
0065 .num_cclk_g_parents = ARRAY_SIZE(cclk_g_parents),
0066 .num_cclk_lp_parents = ARRAY_SIZE(cclk_lp_parents),
0067 };
0068
0069 static const char *sclk_parents_gen5[] = { "clk_m", "pll_c_out1", "pll_c4_out3",
0070 "pll_p", "pll_p_out2", "pll_c4_out1",
0071 "clk_32k", "pll_c4_out2" };
0072
0073 static const char *cclk_g_parents_gen5[] = { "clk_m", "unused", "clk_32k", "unused",
0074 "pll_p", "pll_p_out4", "unused",
0075 "unused", "pll_x", "unused", "unused",
0076 "unused", "unused", "unused", "unused",
0077 "dfllCPU_out" };
0078
0079 static const char *cclk_lp_parents_gen5[] = { "clk_m", "unused", "clk_32k", "unused",
0080 "pll_p", "pll_p_out4", "unused",
0081 "unused", "pll_x", "unused", "unused",
0082 "unused", "unused", "unused", "unused",
0083 "dfllCPU_out" };
0084
0085 static const struct tegra_super_gen_info tegra_super_gen_info_gen5 = {
0086 .gen = gen5,
0087 .sclk_parents = sclk_parents_gen5,
0088 .cclk_g_parents = cclk_g_parents_gen5,
0089 .cclk_lp_parents = cclk_lp_parents_gen5,
0090 .num_sclk_parents = ARRAY_SIZE(sclk_parents_gen5),
0091 .num_cclk_g_parents = ARRAY_SIZE(cclk_g_parents_gen5),
0092 .num_cclk_lp_parents = ARRAY_SIZE(cclk_lp_parents_gen5),
0093 };
0094
0095 static void __init tegra_sclk_init(void __iomem *clk_base,
0096 struct tegra_clk *tegra_clks,
0097 const struct tegra_super_gen_info *gen_info)
0098 {
0099 struct clk *clk;
0100 struct clk **dt_clk;
0101
0102
0103 dt_clk = tegra_lookup_dt_id(tegra_clk_sclk_mux, tegra_clks);
0104 if (dt_clk) {
0105 clk = tegra_clk_register_super_mux("sclk_mux",
0106 gen_info->sclk_parents,
0107 gen_info->num_sclk_parents,
0108 CLK_SET_RATE_PARENT,
0109 clk_base + SCLK_BURST_POLICY,
0110 0, 4, 0, 0, NULL);
0111 *dt_clk = clk;
0112
0113
0114
0115 dt_clk = tegra_lookup_dt_id(tegra_clk_sclk, tegra_clks);
0116 if (dt_clk) {
0117 clk = clk_register_divider(NULL, "sclk", "sclk_mux",
0118 CLK_IS_CRITICAL,
0119 clk_base + SCLK_DIVIDER, 0, 8,
0120 0, &sysrate_lock);
0121 *dt_clk = clk;
0122 }
0123 } else {
0124
0125 dt_clk = tegra_lookup_dt_id(tegra_clk_sclk, tegra_clks);
0126 if (dt_clk) {
0127 clk = tegra_clk_register_super_mux("sclk",
0128 gen_info->sclk_parents,
0129 gen_info->num_sclk_parents,
0130 CLK_SET_RATE_PARENT |
0131 CLK_IS_CRITICAL,
0132 clk_base + SCLK_BURST_POLICY,
0133 0, 4, 0, 0, NULL);
0134 *dt_clk = clk;
0135 }
0136 }
0137
0138
0139 dt_clk = tegra_lookup_dt_id(tegra_clk_hclk, tegra_clks);
0140 if (dt_clk) {
0141 clk = clk_register_divider(NULL, "hclk_div", "sclk", 0,
0142 clk_base + SYSTEM_CLK_RATE, 4, 2, 0,
0143 &sysrate_lock);
0144 clk = clk_register_gate(NULL, "hclk", "hclk_div",
0145 CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
0146 clk_base + SYSTEM_CLK_RATE,
0147 7, CLK_GATE_SET_TO_DISABLE, &sysrate_lock);
0148 *dt_clk = clk;
0149 }
0150
0151
0152 dt_clk = tegra_lookup_dt_id(tegra_clk_pclk, tegra_clks);
0153 if (!dt_clk)
0154 return;
0155
0156 clk = clk_register_divider(NULL, "pclk_div", "hclk", 0,
0157 clk_base + SYSTEM_CLK_RATE, 0, 2, 0,
0158 &sysrate_lock);
0159 clk = clk_register_gate(NULL, "pclk", "pclk_div", CLK_SET_RATE_PARENT |
0160 CLK_IS_CRITICAL, clk_base + SYSTEM_CLK_RATE,
0161 3, CLK_GATE_SET_TO_DISABLE, &sysrate_lock);
0162 *dt_clk = clk;
0163 }
0164
0165 static void __init tegra_super_clk_init(void __iomem *clk_base,
0166 void __iomem *pmc_base,
0167 struct tegra_clk *tegra_clks,
0168 struct tegra_clk_pll_params *params,
0169 const struct tegra_super_gen_info *gen_info)
0170 {
0171 struct clk *clk;
0172 struct clk **dt_clk;
0173
0174
0175 dt_clk = tegra_lookup_dt_id(tegra_clk_cclk_g, tegra_clks);
0176 if (dt_clk) {
0177 if (gen_info->gen == gen5) {
0178 clk = tegra_clk_register_super_mux("cclk_g",
0179 gen_info->cclk_g_parents,
0180 gen_info->num_cclk_g_parents,
0181 CLK_SET_RATE_PARENT,
0182 clk_base + CCLKG_BURST_POLICY,
0183 TEGRA210_CPU_CLK, 4, 8, 0, NULL);
0184 } else {
0185 clk = tegra_clk_register_super_mux("cclk_g",
0186 gen_info->cclk_g_parents,
0187 gen_info->num_cclk_g_parents,
0188 CLK_SET_RATE_PARENT,
0189 clk_base + CCLKG_BURST_POLICY,
0190 0, 4, 0, 0, NULL);
0191 }
0192 *dt_clk = clk;
0193 }
0194
0195
0196 dt_clk = tegra_lookup_dt_id(tegra_clk_cclk_lp, tegra_clks);
0197 if (dt_clk) {
0198 if (gen_info->gen == gen5) {
0199
0200
0201
0202
0203
0204 clk = tegra_clk_register_super_mux("cclk_lp",
0205 gen_info->cclk_lp_parents,
0206 gen_info->num_cclk_lp_parents,
0207 CLK_SET_RATE_PARENT,
0208 clk_base + CCLKLP_BURST_POLICY,
0209 0, 4, 8, 0, NULL);
0210 } else {
0211 clk = tegra_clk_register_super_mux("cclk_lp",
0212 gen_info->cclk_lp_parents,
0213 gen_info->num_cclk_lp_parents,
0214 CLK_SET_RATE_PARENT,
0215 clk_base + CCLKLP_BURST_POLICY,
0216 TEGRA_DIVIDER_2, 4, 8, 9, NULL);
0217 }
0218 *dt_clk = clk;
0219 }
0220
0221 tegra_sclk_init(clk_base, tegra_clks, gen_info);
0222
0223 #if defined(CONFIG_ARCH_TEGRA_114_SOC) || \
0224 defined(CONFIG_ARCH_TEGRA_124_SOC) || \
0225 defined(CONFIG_ARCH_TEGRA_210_SOC)
0226
0227 dt_clk = tegra_lookup_dt_id(tegra_clk_pll_x, tegra_clks);
0228 if (!dt_clk)
0229 return;
0230
0231 #if defined(CONFIG_ARCH_TEGRA_210_SOC)
0232 if (gen_info->gen == gen5)
0233 clk = tegra_clk_register_pllc_tegra210("pll_x", "pll_ref",
0234 clk_base, pmc_base, CLK_IGNORE_UNUSED, params, NULL);
0235 else
0236 #endif
0237 clk = tegra_clk_register_pllxc("pll_x", "pll_ref", clk_base,
0238 pmc_base, CLK_IGNORE_UNUSED, params, NULL);
0239
0240 *dt_clk = clk;
0241
0242
0243
0244 dt_clk = tegra_lookup_dt_id(tegra_clk_pll_x_out0, tegra_clks);
0245 if (!dt_clk)
0246 return;
0247 clk = clk_register_fixed_factor(NULL, "pll_x_out0", "pll_x",
0248 CLK_SET_RATE_PARENT, 1, 2);
0249 *dt_clk = clk;
0250 #endif
0251 }
0252
0253 void __init tegra_super_clk_gen4_init(void __iomem *clk_base,
0254 void __iomem *pmc_base,
0255 struct tegra_clk *tegra_clks,
0256 struct tegra_clk_pll_params *params)
0257 {
0258 tegra_super_clk_init(clk_base, pmc_base, tegra_clks, params,
0259 &tegra_super_gen_info_gen4);
0260 }
0261
0262 void __init tegra_super_clk_gen5_init(void __iomem *clk_base,
0263 void __iomem *pmc_base,
0264 struct tegra_clk *tegra_clks,
0265 struct tegra_clk_pll_params *params)
0266 {
0267 tegra_super_clk_init(clk_base, pmc_base, tegra_clks, params,
0268 &tegra_super_gen_info_gen5);
0269 }