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