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