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/bitfield.h> 53 #include <linux/clk.h> 54 #include <linux/clk-provider.h> 55 #include <linux/io.h> 56 #include <linux/module.h> 57 #include <linux/platform_device.h> 58 #include <linux/regmap.h> 59 #include <soc/qcom/kryo-l2-accessors.h> 60 61 #include "clk-alpha-pll.h" 62 #include "clk-regmap.h" 63 #include "clk-regmap-mux.h" 64 65 enum _pmux_input { 66 SMUX_INDEX = 0, 67 PLL_INDEX, 68 ACD_INDEX, 69 ALT_INDEX, 70 NUM_OF_PMUX_INPUTS 71 }; 72 73 #define DIV_2_THRESHOLD 600000000 74 #define PWRCL_REG_OFFSET 0x0 75 #define PERFCL_REG_OFFSET 0x80000 76 #define MUX_OFFSET 0x40 77 #define ALT_PLL_OFFSET 0x100 78 #define SSSCTL_OFFSET 0x160 79 80 #define PMUX_MASK 0x3 81 82 static const u8 prim_pll_regs[PLL_OFF_MAX_REGS] = { 83 [PLL_OFF_L_VAL] = 0x04, 84 [PLL_OFF_ALPHA_VAL] = 0x08, 85 [PLL_OFF_USER_CTL] = 0x10, 86 [PLL_OFF_CONFIG_CTL] = 0x18, 87 [PLL_OFF_CONFIG_CTL_U] = 0x1c, 88 [PLL_OFF_TEST_CTL] = 0x20, 89 [PLL_OFF_TEST_CTL_U] = 0x24, 90 [PLL_OFF_STATUS] = 0x28, 91 }; 92 93 static const u8 alt_pll_regs[PLL_OFF_MAX_REGS] = { 94 [PLL_OFF_L_VAL] = 0x04, 95 [PLL_OFF_ALPHA_VAL] = 0x08, 96 [PLL_OFF_USER_CTL] = 0x10, 97 [PLL_OFF_CONFIG_CTL] = 0x18, 98 [PLL_OFF_TEST_CTL] = 0x20, 99 [PLL_OFF_STATUS] = 0x28, 100 }; 101 102 /* PLLs */ 103 104 static const struct alpha_pll_config hfpll_config = { 105 .l = 60, 106 .config_ctl_val = 0x200d4828, 107 .config_ctl_hi_val = 0x006, 108 .test_ctl_val = 0x1c000000, 109 .test_ctl_hi_val = 0x00004000, 110 .pre_div_mask = BIT(12), 111 .post_div_mask = 0x3 << 8, 112 .post_div_val = 0x1 << 8, 113 .main_output_mask = BIT(0), 114 .early_output_mask = BIT(3), 115 }; 116 117 static const struct clk_parent_data pll_parent[] = { 118 { .fw_name = "xo" }, 119 }; 120 121 static struct clk_alpha_pll pwrcl_pll = { 122 .offset = PWRCL_REG_OFFSET, 123 .regs = prim_pll_regs, 124 .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, 125 .clkr.hw.init = &(struct clk_init_data){ 126 .name = "pwrcl_pll", 127 .parent_data = pll_parent, 128 .num_parents = ARRAY_SIZE(pll_parent), 129 .ops = &clk_alpha_pll_huayra_ops, 130 }, 131 }; 132 133 static struct clk_alpha_pll perfcl_pll = { 134 .offset = PERFCL_REG_OFFSET, 135 .regs = prim_pll_regs, 136 .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, 137 .clkr.hw.init = &(struct clk_init_data){ 138 .name = "perfcl_pll", 139 .parent_data = pll_parent, 140 .num_parents = ARRAY_SIZE(pll_parent), 141 .ops = &clk_alpha_pll_huayra_ops, 142 }, 143 }; 144 145 static struct clk_fixed_factor pwrcl_pll_postdiv = { 146 .mult = 1, 147 .div = 2, 148 .hw.init = &(struct clk_init_data){ 149 .name = "pwrcl_pll_postdiv", 150 .parent_data = &(const struct clk_parent_data){ 151 .hw = &pwrcl_pll.clkr.hw 152 }, 153 .num_parents = 1, 154 .ops = &clk_fixed_factor_ops, 155 .flags = CLK_SET_RATE_PARENT, 156 }, 157 }; 158 159 static struct clk_fixed_factor perfcl_pll_postdiv = { 160 .mult = 1, 161 .div = 2, 162 .hw.init = &(struct clk_init_data){ 163 .name = "perfcl_pll_postdiv", 164 .parent_data = &(const struct clk_parent_data){ 165 .hw = &perfcl_pll.clkr.hw 166 }, 167 .num_parents = 1, 168 .ops = &clk_fixed_factor_ops, 169 .flags = CLK_SET_RATE_PARENT, 170 }, 171 }; 172 173 static struct clk_fixed_factor perfcl_pll_acd = { 174 .mult = 1, 175 .div = 1, 176 .hw.init = &(struct clk_init_data){ 177 .name = "perfcl_pll_acd", 178 .parent_data = &(const struct clk_parent_data){ 179 .hw = &perfcl_pll.clkr.hw 180 }, 181 .num_parents = 1, 182 .ops = &clk_fixed_factor_ops, 183 .flags = CLK_SET_RATE_PARENT, 184 }, 185 }; 186 187 static struct clk_fixed_factor pwrcl_pll_acd = { 188 .mult = 1, 189 .div = 1, 190 .hw.init = &(struct clk_init_data){ 191 .name = "pwrcl_pll_acd", 192 .parent_data = &(const struct clk_parent_data){ 193 .hw = &pwrcl_pll.clkr.hw 194 }, 195 .num_parents = 1, 196 .ops = &clk_fixed_factor_ops, 197 .flags = CLK_SET_RATE_PARENT, 198 }, 199 }; 200 201 static const struct pll_vco alt_pll_vco_modes[] = { 202 VCO(3, 250000000, 500000000), 203 VCO(2, 500000000, 750000000), 204 VCO(1, 750000000, 1000000000), 205 VCO(0, 1000000000, 2150400000), 206 }; 207 208 static const struct alpha_pll_config altpll_config = { 209 .l = 16, 210 .vco_val = 0x3 << 20, 211 .vco_mask = 0x3 << 20, 212 .config_ctl_val = 0x4001051b, 213 .post_div_mask = 0x3 << 8, 214 .post_div_val = 0x1 << 8, 215 .main_output_mask = BIT(0), 216 .early_output_mask = BIT(3), 217 }; 218 219 static struct clk_alpha_pll pwrcl_alt_pll = { 220 .offset = PWRCL_REG_OFFSET + ALT_PLL_OFFSET, 221 .regs = alt_pll_regs, 222 .vco_table = alt_pll_vco_modes, 223 .num_vco = ARRAY_SIZE(alt_pll_vco_modes), 224 .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, 225 .clkr.hw.init = &(struct clk_init_data) { 226 .name = "pwrcl_alt_pll", 227 .parent_data = pll_parent, 228 .num_parents = ARRAY_SIZE(pll_parent), 229 .ops = &clk_alpha_pll_hwfsm_ops, 230 }, 231 }; 232 233 static struct clk_alpha_pll perfcl_alt_pll = { 234 .offset = PERFCL_REG_OFFSET + ALT_PLL_OFFSET, 235 .regs = alt_pll_regs, 236 .vco_table = alt_pll_vco_modes, 237 .num_vco = ARRAY_SIZE(alt_pll_vco_modes), 238 .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, 239 .clkr.hw.init = &(struct clk_init_data) { 240 .name = "perfcl_alt_pll", 241 .parent_data = pll_parent, 242 .num_parents = ARRAY_SIZE(pll_parent), 243 .ops = &clk_alpha_pll_hwfsm_ops, 244 }, 245 }; 246 247 struct clk_cpu_8996_pmux { 248 u32 reg; 249 struct notifier_block nb; 250 struct clk_regmap clkr; 251 }; 252 253 static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, 254 void *data); 255 256 #define to_clk_cpu_8996_pmux_nb(_nb) \ 257 container_of(_nb, struct clk_cpu_8996_pmux, nb) 258 259 static inline struct clk_cpu_8996_pmux *to_clk_cpu_8996_pmux_hw(struct clk_hw *hw) 260 { 261 return container_of(to_clk_regmap(hw), struct clk_cpu_8996_pmux, clkr); 262 } 263 264 static u8 clk_cpu_8996_pmux_get_parent(struct clk_hw *hw) 265 { 266 struct clk_regmap *clkr = to_clk_regmap(hw); 267 struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_hw(hw); 268 u32 val; 269 270 regmap_read(clkr->regmap, cpuclk->reg, &val); 271 272 return FIELD_GET(PMUX_MASK, val); 273 } 274 275 static int clk_cpu_8996_pmux_set_parent(struct clk_hw *hw, u8 index) 276 { 277 struct clk_regmap *clkr = to_clk_regmap(hw); 278 struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_hw(hw); 279 u32 val; 280 281 val = FIELD_PREP(PMUX_MASK, index); 282 283 return regmap_update_bits(clkr->regmap, cpuclk->reg, PMUX_MASK, val); 284 } 285 286 static int clk_cpu_8996_pmux_determine_rate(struct clk_hw *hw, 287 struct clk_rate_request *req) 288 { 289 struct clk_hw *parent; 290 291 if (req->rate < (DIV_2_THRESHOLD / 2)) 292 return -EINVAL; 293 294 if (req->rate < DIV_2_THRESHOLD) 295 parent = clk_hw_get_parent_by_index(hw, SMUX_INDEX); 296 else 297 parent = clk_hw_get_parent_by_index(hw, ACD_INDEX); 298 if (!parent) 299 return -EINVAL; 300 301 req->best_parent_rate = clk_hw_round_rate(parent, req->rate); 302 req->best_parent_hw = parent; 303 304 return 0; 305 } 306 307 static const struct clk_ops clk_cpu_8996_pmux_ops = { 308 .set_parent = clk_cpu_8996_pmux_set_parent, 309 .get_parent = clk_cpu_8996_pmux_get_parent, 310 .determine_rate = clk_cpu_8996_pmux_determine_rate, 311 }; 312 313 static const struct clk_parent_data pwrcl_smux_parents[] = { 314 { .fw_name = "xo" }, 315 { .hw = &pwrcl_pll_postdiv.hw }, 316 }; 317 318 static const struct clk_parent_data perfcl_smux_parents[] = { 319 { .fw_name = "xo" }, 320 { .hw = &perfcl_pll_postdiv.hw }, 321 }; 322 323 static struct clk_regmap_mux pwrcl_smux = { 324 .reg = PWRCL_REG_OFFSET + MUX_OFFSET, 325 .shift = 2, 326 .width = 2, 327 .clkr.hw.init = &(struct clk_init_data) { 328 .name = "pwrcl_smux", 329 .parent_data = pwrcl_smux_parents, 330 .num_parents = ARRAY_SIZE(pwrcl_smux_parents), 331 .ops = &clk_regmap_mux_closest_ops, 332 .flags = CLK_SET_RATE_PARENT, 333 }, 334 }; 335 336 static struct clk_regmap_mux perfcl_smux = { 337 .reg = PERFCL_REG_OFFSET + MUX_OFFSET, 338 .shift = 2, 339 .width = 2, 340 .clkr.hw.init = &(struct clk_init_data) { 341 .name = "perfcl_smux", 342 .parent_data = perfcl_smux_parents, 343 .num_parents = ARRAY_SIZE(perfcl_smux_parents), 344 .ops = &clk_regmap_mux_closest_ops, 345 .flags = CLK_SET_RATE_PARENT, 346 }, 347 }; 348 349 static const struct clk_hw *pwrcl_pmux_parents[] = { 350 [SMUX_INDEX] = &pwrcl_smux.clkr.hw, 351 [PLL_INDEX] = &pwrcl_pll.clkr.hw, 352 [ACD_INDEX] = &pwrcl_pll_acd.hw, 353 [ALT_INDEX] = &pwrcl_alt_pll.clkr.hw, 354 }; 355 356 static const struct clk_hw *perfcl_pmux_parents[] = { 357 [SMUX_INDEX] = &perfcl_smux.clkr.hw, 358 [PLL_INDEX] = &perfcl_pll.clkr.hw, 359 [ACD_INDEX] = &perfcl_pll_acd.hw, 360 [ALT_INDEX] = &perfcl_alt_pll.clkr.hw, 361 }; 362 363 static struct clk_cpu_8996_pmux pwrcl_pmux = { 364 .reg = PWRCL_REG_OFFSET + MUX_OFFSET, 365 .nb.notifier_call = cpu_clk_notifier_cb, 366 .clkr.hw.init = &(struct clk_init_data) { 367 .name = "pwrcl_pmux", 368 .parent_hws = pwrcl_pmux_parents, 369 .num_parents = ARRAY_SIZE(pwrcl_pmux_parents), 370 .ops = &clk_cpu_8996_pmux_ops, 371 /* CPU clock is critical and should never be gated */ 372 .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 373 }, 374 }; 375 376 static struct clk_cpu_8996_pmux perfcl_pmux = { 377 .reg = PERFCL_REG_OFFSET + MUX_OFFSET, 378 .nb.notifier_call = cpu_clk_notifier_cb, 379 .clkr.hw.init = &(struct clk_init_data) { 380 .name = "perfcl_pmux", 381 .parent_hws = perfcl_pmux_parents, 382 .num_parents = ARRAY_SIZE(perfcl_pmux_parents), 383 .ops = &clk_cpu_8996_pmux_ops, 384 /* CPU clock is critical and should never be gated */ 385 .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 386 }, 387 }; 388 389 static const struct regmap_config cpu_msm8996_regmap_config = { 390 .reg_bits = 32, 391 .reg_stride = 4, 392 .val_bits = 32, 393 .max_register = 0x80210, 394 .fast_io = true, 395 .val_format_endian = REGMAP_ENDIAN_LITTLE, 396 }; 397 398 static struct clk_hw *cpu_msm8996_hw_clks[] = { 399 &pwrcl_pll_postdiv.hw, 400 &perfcl_pll_postdiv.hw, 401 &pwrcl_pll_acd.hw, 402 &perfcl_pll_acd.hw, 403 }; 404 405 static struct clk_regmap *cpu_msm8996_clks[] = { 406 &pwrcl_pll.clkr, 407 &perfcl_pll.clkr, 408 &pwrcl_alt_pll.clkr, 409 &perfcl_alt_pll.clkr, 410 &pwrcl_smux.clkr, 411 &perfcl_smux.clkr, 412 &pwrcl_pmux.clkr, 413 &perfcl_pmux.clkr, 414 }; 415 416 static int qcom_cpu_clk_msm8996_register_clks(struct device *dev, 417 struct regmap *regmap) 418 { 419 int i, ret; 420 421 for (i = 0; i < ARRAY_SIZE(cpu_msm8996_hw_clks); i++) { 422 ret = devm_clk_hw_register(dev, cpu_msm8996_hw_clks[i]); 423 if (ret) 424 return ret; 425 } 426 427 for (i = 0; i < ARRAY_SIZE(cpu_msm8996_clks); i++) { 428 ret = devm_clk_register_regmap(dev, cpu_msm8996_clks[i]); 429 if (ret) 430 return ret; 431 } 432 433 clk_alpha_pll_configure(&pwrcl_pll, regmap, &hfpll_config); 434 clk_alpha_pll_configure(&perfcl_pll, regmap, &hfpll_config); 435 clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config); 436 clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config); 437 438 /* Enable alt PLLs */ 439 clk_prepare_enable(pwrcl_alt_pll.clkr.hw.clk); 440 clk_prepare_enable(perfcl_alt_pll.clkr.hw.clk); 441 442 devm_clk_notifier_register(dev, pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb); 443 devm_clk_notifier_register(dev, perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb); 444 445 return ret; 446 } 447 448 #define CPU_AFINITY_MASK 0xFFF 449 #define PWRCL_CPU_REG_MASK 0x3 450 #define PERFCL_CPU_REG_MASK 0x103 451 452 #define L2ACDCR_REG 0x580ULL 453 #define L2ACDTD_REG 0x581ULL 454 #define L2ACDDVMRC_REG 0x584ULL 455 #define L2ACDSSCR_REG 0x589ULL 456 457 static DEFINE_SPINLOCK(qcom_clk_acd_lock); 458 static void __iomem *base; 459 460 static void qcom_cpu_clk_msm8996_acd_init(void __iomem *base) 461 { 462 u64 hwid; 463 unsigned long flags; 464 465 spin_lock_irqsave(&qcom_clk_acd_lock, flags); 466 467 hwid = read_cpuid_mpidr() & CPU_AFINITY_MASK; 468 469 kryo_l2_set_indirect_reg(L2ACDTD_REG, 0x00006a11); 470 kryo_l2_set_indirect_reg(L2ACDDVMRC_REG, 0x000e0f0f); 471 kryo_l2_set_indirect_reg(L2ACDSSCR_REG, 0x00000601); 472 473 if (PWRCL_CPU_REG_MASK == (hwid | PWRCL_CPU_REG_MASK)) { 474 writel(0xf, base + PWRCL_REG_OFFSET + SSSCTL_OFFSET); 475 kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd); 476 } 477 478 if (PERFCL_CPU_REG_MASK == (hwid | PERFCL_CPU_REG_MASK)) { 479 kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd); 480 writel(0xf, base + PERFCL_REG_OFFSET + SSSCTL_OFFSET); 481 } 482 483 spin_unlock_irqrestore(&qcom_clk_acd_lock, flags); 484 } 485 486 static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, 487 void *data) 488 { 489 struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_nb(nb); 490 struct clk_notifier_data *cnd = data; 491 int ret; 492 493 switch (event) { 494 case PRE_RATE_CHANGE: 495 ret = clk_cpu_8996_pmux_set_parent(&cpuclk->clkr.hw, ALT_INDEX); 496 qcom_cpu_clk_msm8996_acd_init(base); 497 break; 498 case POST_RATE_CHANGE: 499 if (cnd->new_rate < DIV_2_THRESHOLD) 500 ret = clk_cpu_8996_pmux_set_parent(&cpuclk->clkr.hw, 501 SMUX_INDEX); 502 else 503 ret = clk_cpu_8996_pmux_set_parent(&cpuclk->clkr.hw, 504 ACD_INDEX); 505 break; 506 default: 507 ret = 0; 508 break; 509 } 510 511 return notifier_from_errno(ret); 512 }; 513 514 static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev) 515 { 516 struct regmap *regmap; 517 struct clk_hw_onecell_data *data; 518 struct device *dev = &pdev->dev; 519 int ret; 520 521 data = devm_kzalloc(dev, struct_size(data, hws, 2), GFP_KERNEL); 522 if (!data) 523 return -ENOMEM; 524 525 base = devm_platform_ioremap_resource(pdev, 0); 526 if (IS_ERR(base)) 527 return PTR_ERR(base); 528 529 regmap = devm_regmap_init_mmio(dev, base, &cpu_msm8996_regmap_config); 530 if (IS_ERR(regmap)) 531 return PTR_ERR(regmap); 532 533 ret = qcom_cpu_clk_msm8996_register_clks(dev, regmap); 534 if (ret) 535 return ret; 536 537 qcom_cpu_clk_msm8996_acd_init(base); 538 539 data->hws[0] = &pwrcl_pmux.clkr.hw; 540 data->hws[1] = &perfcl_pmux.clkr.hw; 541 data->num = 2; 542 543 return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data); 544 } 545 546 static const struct of_device_id qcom_cpu_clk_msm8996_match_table[] = { 547 { .compatible = "qcom,msm8996-apcc" }, 548 {} 549 }; 550 MODULE_DEVICE_TABLE(of, qcom_cpu_clk_msm8996_match_table); 551 552 static struct platform_driver qcom_cpu_clk_msm8996_driver = { 553 .probe = qcom_cpu_clk_msm8996_driver_probe, 554 .driver = { 555 .name = "qcom-msm8996-apcc", 556 .of_match_table = qcom_cpu_clk_msm8996_match_table, 557 }, 558 }; 559 module_platform_driver(qcom_cpu_clk_msm8996_driver); 560 561 MODULE_DESCRIPTION("QCOM MSM8996 CPU Clock Driver"); 562 MODULE_LICENSE("GPL v2"); 563