rjw | 1f88458 | 2022-01-06 17:20:42 +0800 | [diff] [blame^] | 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_IGNORE_UNUSED, 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 | } |