clk-cpu-8996.c (14686B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) 2020, The Linux Foundation. All rights reserved. 4 */ 5 6/* 7 * Each of the CPU clusters (Power and Perf) on msm8996 are 8 * clocked via 2 PLLs, a primary and alternate. There are also 9 * 2 Mux'es, a primary and secondary all connected together 10 * as shown below 11 * 12 * +-------+ 13 * XO | | 14 * +------------------>0 | 15 * | | 16 * PLL/2 | SMUX +----+ 17 * +------->1 | | 18 * | | | | 19 * | +-------+ | +-------+ 20 * | +---->0 | 21 * | | | 22 * +---------------+ | +----------->1 | CPU clk 23 * |Primary PLL +----+ PLL_EARLY | | +------> 24 * | +------+-----------+ +------>2 PMUX | 25 * +---------------+ | | | | 26 * | +------+ | +-->3 | 27 * +--^+ ACD +-----+ | +-------+ 28 * +---------------+ +------+ | 29 * |Alt PLL | | 30 * | +---------------------------+ 31 * +---------------+ PLL_EARLY 32 * 33 * The primary PLL is what drives the CPU clk, except for times 34 * when we are reprogramming the PLL itself (for rate changes) when 35 * we temporarily switch to an alternate PLL. 36 * 37 * The primary PLL operates on a single VCO range, between 600MHz 38 * and 3GHz. However the CPUs do support OPPs with frequencies 39 * between 300MHz and 600MHz. In order to support running the CPUs 40 * at those frequencies we end up having to lock the PLL at twice 41 * the rate and drive the CPU clk via the PLL/2 output and SMUX. 42 * 43 * So for frequencies above 600MHz we follow the following path 44 * Primary PLL --> PLL_EARLY --> PMUX(1) --> CPU clk 45 * and for frequencies between 300MHz and 600MHz we follow 46 * Primary PLL --> PLL/2 --> SMUX(1) --> PMUX(0) --> CPU clk 47 * 48 * ACD stands for Adaptive Clock Distribution and is used to 49 * detect voltage droops. 50 */ 51 52#include <linux/clk.h> 53#include <linux/clk-provider.h> 54#include <linux/io.h> 55#include <linux/module.h> 56#include <linux/platform_device.h> 57#include <linux/regmap.h> 58#include <soc/qcom/kryo-l2-accessors.h> 59 60#include "clk-alpha-pll.h" 61#include "clk-regmap.h" 62 63enum _pmux_input { 64 DIV_2_INDEX = 0, 65 PLL_INDEX, 66 ACD_INDEX, 67 ALT_INDEX, 68 NUM_OF_PMUX_INPUTS 69}; 70 71#define DIV_2_THRESHOLD 600000000 72#define PWRCL_REG_OFFSET 0x0 73#define PERFCL_REG_OFFSET 0x80000 74#define MUX_OFFSET 0x40 75#define ALT_PLL_OFFSET 0x100 76#define SSSCTL_OFFSET 0x160 77 78static const u8 prim_pll_regs[PLL_OFF_MAX_REGS] = { 79 [PLL_OFF_L_VAL] = 0x04, 80 [PLL_OFF_ALPHA_VAL] = 0x08, 81 [PLL_OFF_USER_CTL] = 0x10, 82 [PLL_OFF_CONFIG_CTL] = 0x18, 83 [PLL_OFF_CONFIG_CTL_U] = 0x1c, 84 [PLL_OFF_TEST_CTL] = 0x20, 85 [PLL_OFF_TEST_CTL_U] = 0x24, 86 [PLL_OFF_STATUS] = 0x28, 87}; 88 89static const u8 alt_pll_regs[PLL_OFF_MAX_REGS] = { 90 [PLL_OFF_L_VAL] = 0x04, 91 [PLL_OFF_ALPHA_VAL] = 0x08, 92 [PLL_OFF_ALPHA_VAL_U] = 0x0c, 93 [PLL_OFF_USER_CTL] = 0x10, 94 [PLL_OFF_USER_CTL_U] = 0x14, 95 [PLL_OFF_CONFIG_CTL] = 0x18, 96 [PLL_OFF_TEST_CTL] = 0x20, 97 [PLL_OFF_TEST_CTL_U] = 0x24, 98 [PLL_OFF_STATUS] = 0x28, 99}; 100 101/* PLLs */ 102 103static const struct alpha_pll_config hfpll_config = { 104 .l = 60, 105 .config_ctl_val = 0x200d4aa8, 106 .config_ctl_hi_val = 0x006, 107 .pre_div_mask = BIT(12), 108 .post_div_mask = 0x3 << 8, 109 .post_div_val = 0x1 << 8, 110 .main_output_mask = BIT(0), 111 .early_output_mask = BIT(3), 112}; 113 114static struct clk_alpha_pll perfcl_pll = { 115 .offset = PERFCL_REG_OFFSET, 116 .regs = prim_pll_regs, 117 .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, 118 .clkr.hw.init = &(struct clk_init_data){ 119 .name = "perfcl_pll", 120 .parent_names = (const char *[]){ "xo" }, 121 .num_parents = 1, 122 .ops = &clk_alpha_pll_huayra_ops, 123 }, 124}; 125 126static struct clk_alpha_pll pwrcl_pll = { 127 .offset = PWRCL_REG_OFFSET, 128 .regs = prim_pll_regs, 129 .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, 130 .clkr.hw.init = &(struct clk_init_data){ 131 .name = "pwrcl_pll", 132 .parent_names = (const char *[]){ "xo" }, 133 .num_parents = 1, 134 .ops = &clk_alpha_pll_huayra_ops, 135 }, 136}; 137 138static const struct pll_vco alt_pll_vco_modes[] = { 139 VCO(3, 250000000, 500000000), 140 VCO(2, 500000000, 750000000), 141 VCO(1, 750000000, 1000000000), 142 VCO(0, 1000000000, 2150400000), 143}; 144 145static const struct alpha_pll_config altpll_config = { 146 .l = 16, 147 .vco_val = 0x3 << 20, 148 .vco_mask = 0x3 << 20, 149 .config_ctl_val = 0x4001051b, 150 .post_div_mask = 0x3 << 8, 151 .post_div_val = 0x1 << 8, 152 .main_output_mask = BIT(0), 153 .early_output_mask = BIT(3), 154}; 155 156static struct clk_alpha_pll perfcl_alt_pll = { 157 .offset = PERFCL_REG_OFFSET + ALT_PLL_OFFSET, 158 .regs = alt_pll_regs, 159 .vco_table = alt_pll_vco_modes, 160 .num_vco = ARRAY_SIZE(alt_pll_vco_modes), 161 .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, 162 .clkr.hw.init = &(struct clk_init_data) { 163 .name = "perfcl_alt_pll", 164 .parent_names = (const char *[]){ "xo" }, 165 .num_parents = 1, 166 .ops = &clk_alpha_pll_hwfsm_ops, 167 }, 168}; 169 170static struct clk_alpha_pll pwrcl_alt_pll = { 171 .offset = PWRCL_REG_OFFSET + ALT_PLL_OFFSET, 172 .regs = alt_pll_regs, 173 .vco_table = alt_pll_vco_modes, 174 .num_vco = ARRAY_SIZE(alt_pll_vco_modes), 175 .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, 176 .clkr.hw.init = &(struct clk_init_data) { 177 .name = "pwrcl_alt_pll", 178 .parent_names = (const char *[]){ "xo" }, 179 .num_parents = 1, 180 .ops = &clk_alpha_pll_hwfsm_ops, 181 }, 182}; 183 184struct clk_cpu_8996_mux { 185 u32 reg; 186 u8 shift; 187 u8 width; 188 struct notifier_block nb; 189 struct clk_hw *pll; 190 struct clk_hw *pll_div_2; 191 struct clk_regmap clkr; 192}; 193 194static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, 195 void *data); 196 197#define to_clk_cpu_8996_mux_nb(_nb) \ 198 container_of(_nb, struct clk_cpu_8996_mux, nb) 199 200static inline struct clk_cpu_8996_mux *to_clk_cpu_8996_mux_hw(struct clk_hw *hw) 201{ 202 return container_of(to_clk_regmap(hw), struct clk_cpu_8996_mux, clkr); 203} 204 205static u8 clk_cpu_8996_mux_get_parent(struct clk_hw *hw) 206{ 207 struct clk_regmap *clkr = to_clk_regmap(hw); 208 struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw); 209 u32 mask = GENMASK(cpuclk->width - 1, 0); 210 u32 val; 211 212 regmap_read(clkr->regmap, cpuclk->reg, &val); 213 val >>= cpuclk->shift; 214 215 return val & mask; 216} 217 218static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index) 219{ 220 struct clk_regmap *clkr = to_clk_regmap(hw); 221 struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw); 222 u32 mask = GENMASK(cpuclk->width + cpuclk->shift - 1, cpuclk->shift); 223 u32 val; 224 225 val = index; 226 val <<= cpuclk->shift; 227 228 return regmap_update_bits(clkr->regmap, cpuclk->reg, mask, val); 229} 230 231static int clk_cpu_8996_mux_determine_rate(struct clk_hw *hw, 232 struct clk_rate_request *req) 233{ 234 struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw); 235 struct clk_hw *parent = cpuclk->pll; 236 237 if (cpuclk->pll_div_2 && req->rate < DIV_2_THRESHOLD) { 238 if (req->rate < (DIV_2_THRESHOLD / 2)) 239 return -EINVAL; 240 241 parent = cpuclk->pll_div_2; 242 } 243 244 req->best_parent_rate = clk_hw_round_rate(parent, req->rate); 245 req->best_parent_hw = parent; 246 247 return 0; 248} 249 250static const struct clk_ops clk_cpu_8996_mux_ops = { 251 .set_parent = clk_cpu_8996_mux_set_parent, 252 .get_parent = clk_cpu_8996_mux_get_parent, 253 .determine_rate = clk_cpu_8996_mux_determine_rate, 254}; 255 256static struct clk_cpu_8996_mux pwrcl_smux = { 257 .reg = PWRCL_REG_OFFSET + MUX_OFFSET, 258 .shift = 2, 259 .width = 2, 260 .clkr.hw.init = &(struct clk_init_data) { 261 .name = "pwrcl_smux", 262 .parent_names = (const char *[]){ 263 "xo", 264 "pwrcl_pll_main", 265 }, 266 .num_parents = 2, 267 .ops = &clk_cpu_8996_mux_ops, 268 .flags = CLK_SET_RATE_PARENT, 269 }, 270}; 271 272static struct clk_cpu_8996_mux perfcl_smux = { 273 .reg = PERFCL_REG_OFFSET + MUX_OFFSET, 274 .shift = 2, 275 .width = 2, 276 .clkr.hw.init = &(struct clk_init_data) { 277 .name = "perfcl_smux", 278 .parent_names = (const char *[]){ 279 "xo", 280 "perfcl_pll_main", 281 }, 282 .num_parents = 2, 283 .ops = &clk_cpu_8996_mux_ops, 284 .flags = CLK_SET_RATE_PARENT, 285 }, 286}; 287 288static struct clk_cpu_8996_mux pwrcl_pmux = { 289 .reg = PWRCL_REG_OFFSET + MUX_OFFSET, 290 .shift = 0, 291 .width = 2, 292 .pll = &pwrcl_pll.clkr.hw, 293 .pll_div_2 = &pwrcl_smux.clkr.hw, 294 .nb.notifier_call = cpu_clk_notifier_cb, 295 .clkr.hw.init = &(struct clk_init_data) { 296 .name = "pwrcl_pmux", 297 .parent_names = (const char *[]){ 298 "pwrcl_smux", 299 "pwrcl_pll", 300 "pwrcl_pll_acd", 301 "pwrcl_alt_pll", 302 }, 303 .num_parents = 4, 304 .ops = &clk_cpu_8996_mux_ops, 305 /* CPU clock is critical and should never be gated */ 306 .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 307 }, 308}; 309 310static struct clk_cpu_8996_mux perfcl_pmux = { 311 .reg = PERFCL_REG_OFFSET + MUX_OFFSET, 312 .shift = 0, 313 .width = 2, 314 .pll = &perfcl_pll.clkr.hw, 315 .pll_div_2 = &perfcl_smux.clkr.hw, 316 .nb.notifier_call = cpu_clk_notifier_cb, 317 .clkr.hw.init = &(struct clk_init_data) { 318 .name = "perfcl_pmux", 319 .parent_names = (const char *[]){ 320 "perfcl_smux", 321 "perfcl_pll", 322 "perfcl_pll_acd", 323 "perfcl_alt_pll", 324 }, 325 .num_parents = 4, 326 .ops = &clk_cpu_8996_mux_ops, 327 /* CPU clock is critical and should never be gated */ 328 .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 329 }, 330}; 331 332static const struct regmap_config cpu_msm8996_regmap_config = { 333 .reg_bits = 32, 334 .reg_stride = 4, 335 .val_bits = 32, 336 .max_register = 0x80210, 337 .fast_io = true, 338 .val_format_endian = REGMAP_ENDIAN_LITTLE, 339}; 340 341static struct clk_regmap *cpu_msm8996_clks[] = { 342 &perfcl_pll.clkr, 343 &pwrcl_pll.clkr, 344 &perfcl_alt_pll.clkr, 345 &pwrcl_alt_pll.clkr, 346 &perfcl_smux.clkr, 347 &pwrcl_smux.clkr, 348 &perfcl_pmux.clkr, 349 &pwrcl_pmux.clkr, 350}; 351 352static int qcom_cpu_clk_msm8996_register_clks(struct device *dev, 353 struct regmap *regmap) 354{ 355 int i, ret; 356 357 perfcl_smux.pll = clk_hw_register_fixed_factor(dev, "perfcl_pll_main", 358 "perfcl_pll", 359 CLK_SET_RATE_PARENT, 360 1, 2); 361 if (IS_ERR(perfcl_smux.pll)) { 362 dev_err(dev, "Failed to initialize perfcl_pll_main\n"); 363 return PTR_ERR(perfcl_smux.pll); 364 } 365 366 pwrcl_smux.pll = clk_hw_register_fixed_factor(dev, "pwrcl_pll_main", 367 "pwrcl_pll", 368 CLK_SET_RATE_PARENT, 369 1, 2); 370 if (IS_ERR(pwrcl_smux.pll)) { 371 dev_err(dev, "Failed to initialize pwrcl_pll_main\n"); 372 clk_hw_unregister(perfcl_smux.pll); 373 return PTR_ERR(pwrcl_smux.pll); 374 } 375 376 for (i = 0; i < ARRAY_SIZE(cpu_msm8996_clks); i++) { 377 ret = devm_clk_register_regmap(dev, cpu_msm8996_clks[i]); 378 if (ret) { 379 clk_hw_unregister(perfcl_smux.pll); 380 clk_hw_unregister(pwrcl_smux.pll); 381 return ret; 382 } 383 } 384 385 clk_alpha_pll_configure(&perfcl_pll, regmap, &hfpll_config); 386 clk_alpha_pll_configure(&pwrcl_pll, regmap, &hfpll_config); 387 clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config); 388 clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config); 389 390 /* Enable alt PLLs */ 391 clk_prepare_enable(pwrcl_alt_pll.clkr.hw.clk); 392 clk_prepare_enable(perfcl_alt_pll.clkr.hw.clk); 393 394 clk_notifier_register(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb); 395 clk_notifier_register(perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb); 396 397 return ret; 398} 399 400static int qcom_cpu_clk_msm8996_unregister_clks(void) 401{ 402 int ret = 0; 403 404 ret = clk_notifier_unregister(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb); 405 if (ret) 406 return ret; 407 408 ret = clk_notifier_unregister(perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb); 409 if (ret) 410 return ret; 411 412 clk_hw_unregister(perfcl_smux.pll); 413 clk_hw_unregister(pwrcl_smux.pll); 414 415 return 0; 416} 417 418#define CPU_AFINITY_MASK 0xFFF 419#define PWRCL_CPU_REG_MASK 0x3 420#define PERFCL_CPU_REG_MASK 0x103 421 422#define L2ACDCR_REG 0x580ULL 423#define L2ACDTD_REG 0x581ULL 424#define L2ACDDVMRC_REG 0x584ULL 425#define L2ACDSSCR_REG 0x589ULL 426 427static DEFINE_SPINLOCK(qcom_clk_acd_lock); 428static void __iomem *base; 429 430static void qcom_cpu_clk_msm8996_acd_init(void __iomem *base) 431{ 432 u64 hwid; 433 unsigned long flags; 434 435 spin_lock_irqsave(&qcom_clk_acd_lock, flags); 436 437 hwid = read_cpuid_mpidr() & CPU_AFINITY_MASK; 438 439 kryo_l2_set_indirect_reg(L2ACDTD_REG, 0x00006a11); 440 kryo_l2_set_indirect_reg(L2ACDDVMRC_REG, 0x000e0f0f); 441 kryo_l2_set_indirect_reg(L2ACDSSCR_REG, 0x00000601); 442 443 if (PWRCL_CPU_REG_MASK == (hwid | PWRCL_CPU_REG_MASK)) { 444 writel(0xf, base + PWRCL_REG_OFFSET + SSSCTL_OFFSET); 445 kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd); 446 } 447 448 if (PERFCL_CPU_REG_MASK == (hwid | PERFCL_CPU_REG_MASK)) { 449 kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd); 450 writel(0xf, base + PERFCL_REG_OFFSET + SSSCTL_OFFSET); 451 } 452 453 spin_unlock_irqrestore(&qcom_clk_acd_lock, flags); 454} 455 456static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, 457 void *data) 458{ 459 struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_nb(nb); 460 struct clk_notifier_data *cnd = data; 461 int ret; 462 463 switch (event) { 464 case PRE_RATE_CHANGE: 465 ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, ALT_INDEX); 466 qcom_cpu_clk_msm8996_acd_init(base); 467 break; 468 case POST_RATE_CHANGE: 469 if (cnd->new_rate < DIV_2_THRESHOLD) 470 ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, 471 DIV_2_INDEX); 472 else 473 ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, 474 ACD_INDEX); 475 break; 476 default: 477 ret = 0; 478 break; 479 } 480 481 return notifier_from_errno(ret); 482}; 483 484static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev) 485{ 486 struct regmap *regmap; 487 struct clk_hw_onecell_data *data; 488 struct device *dev = &pdev->dev; 489 int ret; 490 491 data = devm_kzalloc(dev, struct_size(data, hws, 2), GFP_KERNEL); 492 if (!data) 493 return -ENOMEM; 494 495 base = devm_platform_ioremap_resource(pdev, 0); 496 if (IS_ERR(base)) 497 return PTR_ERR(base); 498 499 regmap = devm_regmap_init_mmio(dev, base, &cpu_msm8996_regmap_config); 500 if (IS_ERR(regmap)) 501 return PTR_ERR(regmap); 502 503 ret = qcom_cpu_clk_msm8996_register_clks(dev, regmap); 504 if (ret) 505 return ret; 506 507 qcom_cpu_clk_msm8996_acd_init(base); 508 509 data->hws[0] = &pwrcl_pmux.clkr.hw; 510 data->hws[1] = &perfcl_pmux.clkr.hw; 511 data->num = 2; 512 513 return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data); 514} 515 516static int qcom_cpu_clk_msm8996_driver_remove(struct platform_device *pdev) 517{ 518 return qcom_cpu_clk_msm8996_unregister_clks(); 519} 520 521static const struct of_device_id qcom_cpu_clk_msm8996_match_table[] = { 522 { .compatible = "qcom,msm8996-apcc" }, 523 {} 524}; 525MODULE_DEVICE_TABLE(of, qcom_cpu_clk_msm8996_match_table); 526 527static struct platform_driver qcom_cpu_clk_msm8996_driver = { 528 .probe = qcom_cpu_clk_msm8996_driver_probe, 529 .remove = qcom_cpu_clk_msm8996_driver_remove, 530 .driver = { 531 .name = "qcom-msm8996-apcc", 532 .of_match_table = qcom_cpu_clk_msm8996_match_table, 533 }, 534}; 535module_platform_driver(qcom_cpu_clk_msm8996_driver); 536 537MODULE_DESCRIPTION("QCOM MSM8996 CPU Clock Driver"); 538MODULE_LICENSE("GPL v2");