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 CLK_CTL_OFFSET 0x44 80 #define CLK_CTL_AUTO_CLK_SEL BIT(8) 81 #define ALT_PLL_OFFSET 0x100 82 #define SSSCTL_OFFSET 0x160 83 #define PSCTL_OFFSET 0x164 84 85 #define PMUX_MASK 0x3 86 #define MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK GENMASK(5, 4) 87 #define MUX_AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL \ 88 FIELD_PREP(MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK, 0x03) 89 90 static const u8 prim_pll_regs[PLL_OFF_MAX_REGS] = { 91 [PLL_OFF_L_VAL] = 0x04, 92 [PLL_OFF_ALPHA_VAL] = 0x08, 93 [PLL_OFF_USER_CTL] = 0x10, 94 [PLL_OFF_CONFIG_CTL] = 0x18, 95 [PLL_OFF_CONFIG_CTL_U] = 0x1c, 96 [PLL_OFF_TEST_CTL] = 0x20, 97 [PLL_OFF_TEST_CTL_U] = 0x24, 98 [PLL_OFF_STATUS] = 0x28, 99 }; 100 101 static const u8 alt_pll_regs[PLL_OFF_MAX_REGS] = { 102 [PLL_OFF_L_VAL] = 0x04, 103 [PLL_OFF_ALPHA_VAL] = 0x08, 104 [PLL_OFF_USER_CTL] = 0x10, 105 [PLL_OFF_CONFIG_CTL] = 0x18, 106 [PLL_OFF_TEST_CTL] = 0x20, 107 [PLL_OFF_STATUS] = 0x28, 108 }; 109 110 /* PLLs */ 111 112 static const struct alpha_pll_config hfpll_config = { 113 .l = 54, 114 .config_ctl_val = 0x200d4828, 115 .config_ctl_hi_val = 0x006, 116 .test_ctl_val = 0x1c000000, 117 .test_ctl_hi_val = 0x00004000, 118 .pre_div_mask = BIT(12), 119 .post_div_mask = 0x3 << 8, 120 .post_div_val = 0x1 << 8, 121 .main_output_mask = BIT(0), 122 .early_output_mask = BIT(3), 123 }; 124 125 static const struct clk_parent_data pll_parent[] = { 126 { .fw_name = "xo" }, 127 }; 128 129 static struct clk_alpha_pll pwrcl_pll = { 130 .offset = PWRCL_REG_OFFSET, 131 .regs = prim_pll_regs, 132 .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, 133 .clkr.hw.init = &(struct clk_init_data){ 134 .name = "pwrcl_pll", 135 .parent_data = pll_parent, 136 .num_parents = ARRAY_SIZE(pll_parent), 137 .ops = &clk_alpha_pll_hwfsm_ops, 138 }, 139 }; 140 141 static struct clk_alpha_pll perfcl_pll = { 142 .offset = PERFCL_REG_OFFSET, 143 .regs = prim_pll_regs, 144 .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, 145 .clkr.hw.init = &(struct clk_init_data){ 146 .name = "perfcl_pll", 147 .parent_data = pll_parent, 148 .num_parents = ARRAY_SIZE(pll_parent), 149 .ops = &clk_alpha_pll_hwfsm_ops, 150 }, 151 }; 152 153 static struct clk_fixed_factor pwrcl_pll_postdiv = { 154 .mult = 1, 155 .div = 2, 156 .hw.init = &(struct clk_init_data){ 157 .name = "pwrcl_pll_postdiv", 158 .parent_data = &(const struct clk_parent_data){ 159 .hw = &pwrcl_pll.clkr.hw 160 }, 161 .num_parents = 1, 162 .ops = &clk_fixed_factor_ops, 163 .flags = CLK_SET_RATE_PARENT, 164 }, 165 }; 166 167 static struct clk_fixed_factor perfcl_pll_postdiv = { 168 .mult = 1, 169 .div = 2, 170 .hw.init = &(struct clk_init_data){ 171 .name = "perfcl_pll_postdiv", 172 .parent_data = &(const struct clk_parent_data){ 173 .hw = &perfcl_pll.clkr.hw 174 }, 175 .num_parents = 1, 176 .ops = &clk_fixed_factor_ops, 177 .flags = CLK_SET_RATE_PARENT, 178 }, 179 }; 180 181 static struct clk_fixed_factor perfcl_pll_acd = { 182 .mult = 1, 183 .div = 1, 184 .hw.init = &(struct clk_init_data){ 185 .name = "perfcl_pll_acd", 186 .parent_data = &(const struct clk_parent_data){ 187 .hw = &perfcl_pll.clkr.hw 188 }, 189 .num_parents = 1, 190 .ops = &clk_fixed_factor_ops, 191 .flags = CLK_SET_RATE_PARENT, 192 }, 193 }; 194 195 static struct clk_fixed_factor pwrcl_pll_acd = { 196 .mult = 1, 197 .div = 1, 198 .hw.init = &(struct clk_init_data){ 199 .name = "pwrcl_pll_acd", 200 .parent_data = &(const struct clk_parent_data){ 201 .hw = &pwrcl_pll.clkr.hw 202 }, 203 .num_parents = 1, 204 .ops = &clk_fixed_factor_ops, 205 .flags = CLK_SET_RATE_PARENT, 206 }, 207 }; 208 209 static const struct pll_vco alt_pll_vco_modes[] = { 210 VCO(3, 250000000, 500000000), 211 VCO(2, 500000000, 750000000), 212 VCO(1, 750000000, 1000000000), 213 VCO(0, 1000000000, 2150400000), 214 }; 215 216 static const struct alpha_pll_config altpll_config = { 217 .l = 16, 218 .vco_val = 0x3 << 20, 219 .vco_mask = 0x3 << 20, 220 .config_ctl_val = 0x4001051b, 221 .post_div_mask = 0x3 << 8, 222 .post_div_val = 0x1 << 8, 223 .main_output_mask = BIT(0), 224 .early_output_mask = BIT(3), 225 }; 226 227 static struct clk_alpha_pll pwrcl_alt_pll = { 228 .offset = PWRCL_REG_OFFSET + ALT_PLL_OFFSET, 229 .regs = alt_pll_regs, 230 .vco_table = alt_pll_vco_modes, 231 .num_vco = ARRAY_SIZE(alt_pll_vco_modes), 232 .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, 233 .clkr.hw.init = &(struct clk_init_data) { 234 .name = "pwrcl_alt_pll", 235 .parent_data = pll_parent, 236 .num_parents = ARRAY_SIZE(pll_parent), 237 .ops = &clk_alpha_pll_hwfsm_ops, 238 }, 239 }; 240 241 static struct clk_alpha_pll perfcl_alt_pll = { 242 .offset = PERFCL_REG_OFFSET + ALT_PLL_OFFSET, 243 .regs = alt_pll_regs, 244 .vco_table = alt_pll_vco_modes, 245 .num_vco = ARRAY_SIZE(alt_pll_vco_modes), 246 .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, 247 .clkr.hw.init = &(struct clk_init_data) { 248 .name = "perfcl_alt_pll", 249 .parent_data = pll_parent, 250 .num_parents = ARRAY_SIZE(pll_parent), 251 .ops = &clk_alpha_pll_hwfsm_ops, 252 }, 253 }; 254 255 struct clk_cpu_8996_pmux { 256 u32 reg; 257 struct notifier_block nb; 258 struct clk_regmap clkr; 259 }; 260 261 static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, 262 void *data); 263 264 #define to_clk_cpu_8996_pmux_nb(_nb) \ 265 container_of(_nb, struct clk_cpu_8996_pmux, nb) 266 267 static inline struct clk_cpu_8996_pmux *to_clk_cpu_8996_pmux_hw(struct clk_hw *hw) 268 { 269 return container_of(to_clk_regmap(hw), struct clk_cpu_8996_pmux, clkr); 270 } 271 272 static u8 clk_cpu_8996_pmux_get_parent(struct clk_hw *hw) 273 { 274 struct clk_regmap *clkr = to_clk_regmap(hw); 275 struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_hw(hw); 276 u32 val; 277 278 regmap_read(clkr->regmap, cpuclk->reg, &val); 279 280 return FIELD_GET(PMUX_MASK, val); 281 } 282 283 static int clk_cpu_8996_pmux_set_parent(struct clk_hw *hw, u8 index) 284 { 285 struct clk_regmap *clkr = to_clk_regmap(hw); 286 struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_hw(hw); 287 u32 val; 288 289 val = FIELD_PREP(PMUX_MASK, index); 290 291 return regmap_update_bits(clkr->regmap, cpuclk->reg, PMUX_MASK, val); 292 } 293 294 static int clk_cpu_8996_pmux_determine_rate(struct clk_hw *hw, 295 struct clk_rate_request *req) 296 { 297 struct clk_hw *parent; 298 299 if (req->rate < (DIV_2_THRESHOLD / 2)) 300 return -EINVAL; 301 302 if (req->rate < DIV_2_THRESHOLD) 303 parent = clk_hw_get_parent_by_index(hw, SMUX_INDEX); 304 else 305 parent = clk_hw_get_parent_by_index(hw, ACD_INDEX); 306 if (!parent) 307 return -EINVAL; 308 309 req->best_parent_rate = clk_hw_round_rate(parent, req->rate); 310 req->best_parent_hw = parent; 311 312 return 0; 313 } 314 315 static const struct clk_ops clk_cpu_8996_pmux_ops = { 316 .set_parent = clk_cpu_8996_pmux_set_parent, 317 .get_parent = clk_cpu_8996_pmux_get_parent, 318 .determine_rate = clk_cpu_8996_pmux_determine_rate, 319 }; 320 321 static const struct parent_map smux_parent_map[] = { 322 { .cfg = 0, }, /* xo */ 323 { .cfg = 1, }, /* pll */ 324 { .cfg = 3, }, /* sys_apcs_aux */ 325 }; 326 327 static const struct clk_parent_data pwrcl_smux_parents[] = { 328 { .fw_name = "xo" }, 329 { .hw = &pwrcl_pll_postdiv.hw }, 330 { .fw_name = "sys_apcs_aux" }, 331 }; 332 333 static const struct clk_parent_data perfcl_smux_parents[] = { 334 { .fw_name = "xo" }, 335 { .hw = &perfcl_pll_postdiv.hw }, 336 { .fw_name = "sys_apcs_aux" }, 337 }; 338 339 static struct clk_regmap_mux pwrcl_smux = { 340 .reg = PWRCL_REG_OFFSET + MUX_OFFSET, 341 .shift = 2, 342 .width = 2, 343 .parent_map = smux_parent_map, 344 .clkr.hw.init = &(struct clk_init_data) { 345 .name = "pwrcl_smux", 346 .parent_data = pwrcl_smux_parents, 347 .num_parents = ARRAY_SIZE(pwrcl_smux_parents), 348 .ops = &clk_regmap_mux_closest_ops, 349 .flags = CLK_SET_RATE_PARENT, 350 }, 351 }; 352 353 static struct clk_regmap_mux perfcl_smux = { 354 .reg = PERFCL_REG_OFFSET + MUX_OFFSET, 355 .shift = 2, 356 .width = 2, 357 .parent_map = smux_parent_map, 358 .clkr.hw.init = &(struct clk_init_data) { 359 .name = "perfcl_smux", 360 .parent_data = perfcl_smux_parents, 361 .num_parents = ARRAY_SIZE(perfcl_smux_parents), 362 .ops = &clk_regmap_mux_closest_ops, 363 .flags = CLK_SET_RATE_PARENT, 364 }, 365 }; 366 367 static const struct clk_hw *pwrcl_pmux_parents[] = { 368 [SMUX_INDEX] = &pwrcl_smux.clkr.hw, 369 [PLL_INDEX] = &pwrcl_pll.clkr.hw, 370 [ACD_INDEX] = &pwrcl_pll_acd.hw, 371 [ALT_INDEX] = &pwrcl_alt_pll.clkr.hw, 372 }; 373 374 static const struct clk_hw *perfcl_pmux_parents[] = { 375 [SMUX_INDEX] = &perfcl_smux.clkr.hw, 376 [PLL_INDEX] = &perfcl_pll.clkr.hw, 377 [ACD_INDEX] = &perfcl_pll_acd.hw, 378 [ALT_INDEX] = &perfcl_alt_pll.clkr.hw, 379 }; 380 381 static struct clk_cpu_8996_pmux pwrcl_pmux = { 382 .reg = PWRCL_REG_OFFSET + MUX_OFFSET, 383 .nb.notifier_call = cpu_clk_notifier_cb, 384 .clkr.hw.init = &(struct clk_init_data) { 385 .name = "pwrcl_pmux", 386 .parent_hws = pwrcl_pmux_parents, 387 .num_parents = ARRAY_SIZE(pwrcl_pmux_parents), 388 .ops = &clk_cpu_8996_pmux_ops, 389 /* CPU clock is critical and should never be gated */ 390 .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 391 }, 392 }; 393 394 static struct clk_cpu_8996_pmux perfcl_pmux = { 395 .reg = PERFCL_REG_OFFSET + MUX_OFFSET, 396 .nb.notifier_call = cpu_clk_notifier_cb, 397 .clkr.hw.init = &(struct clk_init_data) { 398 .name = "perfcl_pmux", 399 .parent_hws = perfcl_pmux_parents, 400 .num_parents = ARRAY_SIZE(perfcl_pmux_parents), 401 .ops = &clk_cpu_8996_pmux_ops, 402 /* CPU clock is critical and should never be gated */ 403 .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 404 }, 405 }; 406 407 static const struct regmap_config cpu_msm8996_regmap_config = { 408 .reg_bits = 32, 409 .reg_stride = 4, 410 .val_bits = 32, 411 .max_register = 0x80210, 412 .fast_io = true, 413 .val_format_endian = REGMAP_ENDIAN_LITTLE, 414 }; 415 416 static struct clk_hw *cpu_msm8996_hw_clks[] = { 417 &pwrcl_pll_postdiv.hw, 418 &perfcl_pll_postdiv.hw, 419 &pwrcl_pll_acd.hw, 420 &perfcl_pll_acd.hw, 421 }; 422 423 static struct clk_regmap *cpu_msm8996_clks[] = { 424 &pwrcl_pll.clkr, 425 &perfcl_pll.clkr, 426 &pwrcl_alt_pll.clkr, 427 &perfcl_alt_pll.clkr, 428 &pwrcl_smux.clkr, 429 &perfcl_smux.clkr, 430 &pwrcl_pmux.clkr, 431 &perfcl_pmux.clkr, 432 }; 433 434 static void qcom_cpu_clk_msm8996_acd_init(struct regmap *regmap); 435 436 static int qcom_cpu_clk_msm8996_register_clks(struct device *dev, 437 struct regmap *regmap) 438 { 439 int i, ret; 440 441 /* Select GPLL0 for 300MHz for both clusters */ 442 regmap_write(regmap, PERFCL_REG_OFFSET + MUX_OFFSET, 0xc); 443 regmap_write(regmap, PWRCL_REG_OFFSET + MUX_OFFSET, 0xc); 444 445 /* Ensure write goes through before PLLs are reconfigured */ 446 udelay(5); 447 448 /* Set the auto clock sel always-on source to GPLL0/2 (300MHz) */ 449 regmap_update_bits(regmap, PWRCL_REG_OFFSET + MUX_OFFSET, 450 MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK, 451 MUX_AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL); 452 regmap_update_bits(regmap, PERFCL_REG_OFFSET + MUX_OFFSET, 453 MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK, 454 MUX_AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL); 455 456 clk_alpha_pll_configure(&pwrcl_pll, regmap, &hfpll_config); 457 clk_alpha_pll_configure(&perfcl_pll, regmap, &hfpll_config); 458 clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config); 459 clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config); 460 461 /* Wait for PLL(s) to lock */ 462 udelay(50); 463 464 /* Enable auto clock selection for both clusters */ 465 regmap_update_bits(regmap, PWRCL_REG_OFFSET + CLK_CTL_OFFSET, 466 CLK_CTL_AUTO_CLK_SEL, CLK_CTL_AUTO_CLK_SEL); 467 regmap_update_bits(regmap, PERFCL_REG_OFFSET + CLK_CTL_OFFSET, 468 CLK_CTL_AUTO_CLK_SEL, CLK_CTL_AUTO_CLK_SEL); 469 470 /* Ensure write goes through before muxes are switched */ 471 udelay(5); 472 473 qcom_cpu_clk_msm8996_acd_init(regmap); 474 475 /* Pulse swallower and soft-start settings */ 476 regmap_write(regmap, PWRCL_REG_OFFSET + PSCTL_OFFSET, 0x00030005); 477 regmap_write(regmap, PERFCL_REG_OFFSET + PSCTL_OFFSET, 0x00030005); 478 479 /* Switch clusters to use the ACD leg */ 480 regmap_write(regmap, PWRCL_REG_OFFSET + MUX_OFFSET, 0x32); 481 regmap_write(regmap, PERFCL_REG_OFFSET + MUX_OFFSET, 0x32); 482 483 for (i = 0; i < ARRAY_SIZE(cpu_msm8996_hw_clks); i++) { 484 ret = devm_clk_hw_register(dev, cpu_msm8996_hw_clks[i]); 485 if (ret) 486 return ret; 487 } 488 489 for (i = 0; i < ARRAY_SIZE(cpu_msm8996_clks); i++) { 490 ret = devm_clk_register_regmap(dev, cpu_msm8996_clks[i]); 491 if (ret) 492 return ret; 493 } 494 495 /* Enable alt PLLs */ 496 clk_prepare_enable(pwrcl_alt_pll.clkr.hw.clk); 497 clk_prepare_enable(perfcl_alt_pll.clkr.hw.clk); 498 499 devm_clk_notifier_register(dev, pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb); 500 devm_clk_notifier_register(dev, perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb); 501 502 return ret; 503 } 504 505 #define CPU_CLUSTER_AFFINITY_MASK 0xf00 506 #define PWRCL_AFFINITY_MASK 0x000 507 #define PERFCL_AFFINITY_MASK 0x100 508 509 #define L2ACDCR_REG 0x580ULL 510 #define L2ACDTD_REG 0x581ULL 511 #define L2ACDDVMRC_REG 0x584ULL 512 #define L2ACDSSCR_REG 0x589ULL 513 514 static DEFINE_SPINLOCK(qcom_clk_acd_lock); 515 516 static void qcom_cpu_clk_msm8996_acd_init(struct regmap *regmap) 517 { 518 u64 hwid; 519 u32 val; 520 unsigned long flags; 521 522 spin_lock_irqsave(&qcom_clk_acd_lock, flags); 523 524 val = kryo_l2_get_indirect_reg(L2ACDTD_REG); 525 if (val == 0x00006a11) 526 goto out; 527 528 kryo_l2_set_indirect_reg(L2ACDTD_REG, 0x00006a11); 529 kryo_l2_set_indirect_reg(L2ACDDVMRC_REG, 0x000e0f0f); 530 kryo_l2_set_indirect_reg(L2ACDSSCR_REG, 0x00000601); 531 532 kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd); 533 534 hwid = read_cpuid_mpidr(); 535 if ((hwid & CPU_CLUSTER_AFFINITY_MASK) == PWRCL_AFFINITY_MASK) 536 regmap_write(regmap, PWRCL_REG_OFFSET + SSSCTL_OFFSET, 0xf); 537 else 538 regmap_write(regmap, PERFCL_REG_OFFSET + SSSCTL_OFFSET, 0xf); 539 540 out: 541 spin_unlock_irqrestore(&qcom_clk_acd_lock, flags); 542 } 543 544 static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, 545 void *data) 546 { 547 struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_nb(nb); 548 struct clk_notifier_data *cnd = data; 549 550 switch (event) { 551 case PRE_RATE_CHANGE: 552 qcom_cpu_clk_msm8996_acd_init(cpuclk->clkr.regmap); 553 554 /* 555 * Avoid overvolting. clk_core_set_rate_nolock() walks from top 556 * to bottom, so it will change the rate of the PLL before 557 * chaging the parent of PMUX. This can result in pmux getting 558 * clocked twice the expected rate. 559 * 560 * Manually switch to PLL/2 here. 561 */ 562 if (cnd->new_rate < DIV_2_THRESHOLD && 563 cnd->old_rate > DIV_2_THRESHOLD) 564 clk_cpu_8996_pmux_set_parent(&cpuclk->clkr.hw, SMUX_INDEX); 565 566 break; 567 case ABORT_RATE_CHANGE: 568 /* Revert manual change */ 569 if (cnd->new_rate < DIV_2_THRESHOLD && 570 cnd->old_rate > DIV_2_THRESHOLD) 571 clk_cpu_8996_pmux_set_parent(&cpuclk->clkr.hw, ACD_INDEX); 572 break; 573 default: 574 break; 575 } 576 577 return NOTIFY_OK; 578 }; 579 580 static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev) 581 { 582 static void __iomem *base; 583 struct regmap *regmap; 584 struct clk_hw_onecell_data *data; 585 struct device *dev = &pdev->dev; 586 int ret; 587 588 data = devm_kzalloc(dev, struct_size(data, hws, 2), GFP_KERNEL); 589 if (!data) 590 return -ENOMEM; 591 592 base = devm_platform_ioremap_resource(pdev, 0); 593 if (IS_ERR(base)) 594 return PTR_ERR(base); 595 596 regmap = devm_regmap_init_mmio(dev, base, &cpu_msm8996_regmap_config); 597 if (IS_ERR(regmap)) 598 return PTR_ERR(regmap); 599 600 ret = qcom_cpu_clk_msm8996_register_clks(dev, regmap); 601 if (ret) 602 return ret; 603 604 data->hws[0] = &pwrcl_pmux.clkr.hw; 605 data->hws[1] = &perfcl_pmux.clkr.hw; 606 data->num = 2; 607 608 return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data); 609 } 610 611 static const struct of_device_id qcom_cpu_clk_msm8996_match_table[] = { 612 { .compatible = "qcom,msm8996-apcc" }, 613 {} 614 }; 615 MODULE_DEVICE_TABLE(of, qcom_cpu_clk_msm8996_match_table); 616 617 static struct platform_driver qcom_cpu_clk_msm8996_driver = { 618 .probe = qcom_cpu_clk_msm8996_driver_probe, 619 .driver = { 620 .name = "qcom-msm8996-apcc", 621 .of_match_table = qcom_cpu_clk_msm8996_match_table, 622 }, 623 }; 624 module_platform_driver(qcom_cpu_clk_msm8996_driver); 625 626 MODULE_DESCRIPTION("QCOM MSM8996 CPU Clock Driver"); 627 MODULE_LICENSE("GPL v2"); 628