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