103e342dcSLoic Poulain // SPDX-License-Identifier: GPL-2.0 203e342dcSLoic Poulain /* 303e342dcSLoic Poulain * Copyright (c) 2020, The Linux Foundation. All rights reserved. 403e342dcSLoic Poulain */ 503e342dcSLoic Poulain 603e342dcSLoic Poulain /* 703e342dcSLoic Poulain * Each of the CPU clusters (Power and Perf) on msm8996 are 803e342dcSLoic Poulain * clocked via 2 PLLs, a primary and alternate. There are also 903e342dcSLoic Poulain * 2 Mux'es, a primary and secondary all connected together 1003e342dcSLoic Poulain * as shown below 1103e342dcSLoic Poulain * 1203e342dcSLoic Poulain * +-------+ 1303e342dcSLoic Poulain * XO | | 1403e342dcSLoic Poulain * +------------------>0 | 15fe8a5005SDmitry Baryshkov * SYS_APCS_AUX | | 16fe8a5005SDmitry Baryshkov * +------------------>3 | 1703e342dcSLoic Poulain * | | 1803e342dcSLoic Poulain * PLL/2 | SMUX +----+ 1903e342dcSLoic Poulain * +------->1 | | 2003e342dcSLoic Poulain * | | | | 2103e342dcSLoic Poulain * | +-------+ | +-------+ 2203e342dcSLoic Poulain * | +---->0 | 2303e342dcSLoic Poulain * | | | 2403e342dcSLoic Poulain * +---------------+ | +----------->1 | CPU clk 2503e342dcSLoic Poulain * |Primary PLL +----+ PLL_EARLY | | +------> 2603e342dcSLoic Poulain * | +------+-----------+ +------>2 PMUX | 2703e342dcSLoic Poulain * +---------------+ | | | | 2803e342dcSLoic Poulain * | +------+ | +-->3 | 2903e342dcSLoic Poulain * +--^+ ACD +-----+ | +-------+ 3003e342dcSLoic Poulain * +---------------+ +------+ | 3103e342dcSLoic Poulain * |Alt PLL | | 3203e342dcSLoic Poulain * | +---------------------------+ 3303e342dcSLoic Poulain * +---------------+ PLL_EARLY 3403e342dcSLoic Poulain * 3503e342dcSLoic Poulain * The primary PLL is what drives the CPU clk, except for times 3603e342dcSLoic Poulain * when we are reprogramming the PLL itself (for rate changes) when 3703e342dcSLoic Poulain * we temporarily switch to an alternate PLL. 3803e342dcSLoic Poulain * 3903e342dcSLoic Poulain * The primary PLL operates on a single VCO range, between 600MHz 4003e342dcSLoic Poulain * and 3GHz. However the CPUs do support OPPs with frequencies 4103e342dcSLoic Poulain * between 300MHz and 600MHz. In order to support running the CPUs 4203e342dcSLoic Poulain * at those frequencies we end up having to lock the PLL at twice 4303e342dcSLoic Poulain * the rate and drive the CPU clk via the PLL/2 output and SMUX. 4403e342dcSLoic Poulain * 4503e342dcSLoic Poulain * So for frequencies above 600MHz we follow the following path 4603e342dcSLoic Poulain * Primary PLL --> PLL_EARLY --> PMUX(1) --> CPU clk 4703e342dcSLoic Poulain * and for frequencies between 300MHz and 600MHz we follow 4803e342dcSLoic Poulain * Primary PLL --> PLL/2 --> SMUX(1) --> PMUX(0) --> CPU clk 4903e342dcSLoic Poulain * 5003e342dcSLoic Poulain * ACD stands for Adaptive Clock Distribution and is used to 5103e342dcSLoic Poulain * detect voltage droops. 5203e342dcSLoic Poulain */ 5303e342dcSLoic Poulain 54f9ea0f59SDmitry Baryshkov #include <linux/bitfield.h> 5503e342dcSLoic Poulain #include <linux/clk.h> 5603e342dcSLoic Poulain #include <linux/clk-provider.h> 5703e342dcSLoic Poulain #include <linux/io.h> 5803e342dcSLoic Poulain #include <linux/module.h> 5903e342dcSLoic Poulain #include <linux/platform_device.h> 6003e342dcSLoic Poulain #include <linux/regmap.h> 6103e342dcSLoic Poulain #include <soc/qcom/kryo-l2-accessors.h> 6203e342dcSLoic Poulain 6303e342dcSLoic Poulain #include "clk-alpha-pll.h" 6403e342dcSLoic Poulain #include "clk-regmap.h" 659a9f5f9aSYassine Oudjana #include "clk-regmap-mux.h" 6603e342dcSLoic Poulain 6703e342dcSLoic Poulain enum _pmux_input { 681ba0a3bbSYassine Oudjana SMUX_INDEX = 0, 6903e342dcSLoic Poulain PLL_INDEX, 7003e342dcSLoic Poulain ACD_INDEX, 7103e342dcSLoic Poulain ALT_INDEX, 7203e342dcSLoic Poulain NUM_OF_PMUX_INPUTS 7303e342dcSLoic Poulain }; 7403e342dcSLoic Poulain 7503e342dcSLoic Poulain #define DIV_2_THRESHOLD 600000000 7603e342dcSLoic Poulain #define PWRCL_REG_OFFSET 0x0 7703e342dcSLoic Poulain #define PERFCL_REG_OFFSET 0x80000 7803e342dcSLoic Poulain #define MUX_OFFSET 0x40 7903e342dcSLoic Poulain #define ALT_PLL_OFFSET 0x100 8003e342dcSLoic Poulain #define SSSCTL_OFFSET 0x160 8103e342dcSLoic Poulain 82f9ea0f59SDmitry Baryshkov #define PMUX_MASK 0x3 83f9ea0f59SDmitry Baryshkov 8403e342dcSLoic Poulain static const u8 prim_pll_regs[PLL_OFF_MAX_REGS] = { 8503e342dcSLoic Poulain [PLL_OFF_L_VAL] = 0x04, 8603e342dcSLoic Poulain [PLL_OFF_ALPHA_VAL] = 0x08, 8703e342dcSLoic Poulain [PLL_OFF_USER_CTL] = 0x10, 8803e342dcSLoic Poulain [PLL_OFF_CONFIG_CTL] = 0x18, 8903e342dcSLoic Poulain [PLL_OFF_CONFIG_CTL_U] = 0x1c, 9003e342dcSLoic Poulain [PLL_OFF_TEST_CTL] = 0x20, 9103e342dcSLoic Poulain [PLL_OFF_TEST_CTL_U] = 0x24, 9203e342dcSLoic Poulain [PLL_OFF_STATUS] = 0x28, 9303e342dcSLoic Poulain }; 9403e342dcSLoic Poulain 9503e342dcSLoic Poulain static const u8 alt_pll_regs[PLL_OFF_MAX_REGS] = { 9603e342dcSLoic Poulain [PLL_OFF_L_VAL] = 0x04, 9703e342dcSLoic Poulain [PLL_OFF_ALPHA_VAL] = 0x08, 9803e342dcSLoic Poulain [PLL_OFF_USER_CTL] = 0x10, 9903e342dcSLoic Poulain [PLL_OFF_CONFIG_CTL] = 0x18, 10003e342dcSLoic Poulain [PLL_OFF_TEST_CTL] = 0x20, 10103e342dcSLoic Poulain [PLL_OFF_STATUS] = 0x28, 10203e342dcSLoic Poulain }; 10303e342dcSLoic Poulain 10403e342dcSLoic Poulain /* PLLs */ 10503e342dcSLoic Poulain 10603e342dcSLoic Poulain static const struct alpha_pll_config hfpll_config = { 107be4e65d1SDmitry Baryshkov .l = 54, 1084953610bSDmitry Baryshkov .config_ctl_val = 0x200d4828, 10903e342dcSLoic Poulain .config_ctl_hi_val = 0x006, 1104953610bSDmitry Baryshkov .test_ctl_val = 0x1c000000, 1114953610bSDmitry Baryshkov .test_ctl_hi_val = 0x00004000, 11203e342dcSLoic Poulain .pre_div_mask = BIT(12), 11303e342dcSLoic Poulain .post_div_mask = 0x3 << 8, 11403e342dcSLoic Poulain .post_div_val = 0x1 << 8, 11503e342dcSLoic Poulain .main_output_mask = BIT(0), 11603e342dcSLoic Poulain .early_output_mask = BIT(3), 11703e342dcSLoic Poulain }; 11803e342dcSLoic Poulain 119da5daae8SYassine Oudjana static const struct clk_parent_data pll_parent[] = { 120da5daae8SYassine Oudjana { .fw_name = "xo" }, 121da5daae8SYassine Oudjana }; 122da5daae8SYassine Oudjana 12303e342dcSLoic Poulain static struct clk_alpha_pll pwrcl_pll = { 12403e342dcSLoic Poulain .offset = PWRCL_REG_OFFSET, 12503e342dcSLoic Poulain .regs = prim_pll_regs, 12603e342dcSLoic Poulain .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, 12703e342dcSLoic Poulain .clkr.hw.init = &(struct clk_init_data){ 12803e342dcSLoic Poulain .name = "pwrcl_pll", 129da5daae8SYassine Oudjana .parent_data = pll_parent, 130da5daae8SYassine Oudjana .num_parents = ARRAY_SIZE(pll_parent), 13103e342dcSLoic Poulain .ops = &clk_alpha_pll_huayra_ops, 13203e342dcSLoic Poulain }, 13303e342dcSLoic Poulain }; 13403e342dcSLoic Poulain 135382139bfSYassine Oudjana static struct clk_alpha_pll perfcl_pll = { 136382139bfSYassine Oudjana .offset = PERFCL_REG_OFFSET, 137382139bfSYassine Oudjana .regs = prim_pll_regs, 138382139bfSYassine Oudjana .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, 139382139bfSYassine Oudjana .clkr.hw.init = &(struct clk_init_data){ 140382139bfSYassine Oudjana .name = "perfcl_pll", 141da5daae8SYassine Oudjana .parent_data = pll_parent, 142da5daae8SYassine Oudjana .num_parents = ARRAY_SIZE(pll_parent), 143382139bfSYassine Oudjana .ops = &clk_alpha_pll_huayra_ops, 144382139bfSYassine Oudjana }, 145382139bfSYassine Oudjana }; 146382139bfSYassine Oudjana 147de37e021SYassine Oudjana static struct clk_fixed_factor pwrcl_pll_postdiv = { 148de37e021SYassine Oudjana .mult = 1, 149de37e021SYassine Oudjana .div = 2, 150de37e021SYassine Oudjana .hw.init = &(struct clk_init_data){ 151de37e021SYassine Oudjana .name = "pwrcl_pll_postdiv", 152de37e021SYassine Oudjana .parent_data = &(const struct clk_parent_data){ 153de37e021SYassine Oudjana .hw = &pwrcl_pll.clkr.hw 154de37e021SYassine Oudjana }, 155de37e021SYassine Oudjana .num_parents = 1, 156de37e021SYassine Oudjana .ops = &clk_fixed_factor_ops, 157de37e021SYassine Oudjana .flags = CLK_SET_RATE_PARENT, 158de37e021SYassine Oudjana }, 159de37e021SYassine Oudjana }; 160de37e021SYassine Oudjana 161de37e021SYassine Oudjana static struct clk_fixed_factor perfcl_pll_postdiv = { 162de37e021SYassine Oudjana .mult = 1, 163de37e021SYassine Oudjana .div = 2, 164de37e021SYassine Oudjana .hw.init = &(struct clk_init_data){ 165de37e021SYassine Oudjana .name = "perfcl_pll_postdiv", 166de37e021SYassine Oudjana .parent_data = &(const struct clk_parent_data){ 167de37e021SYassine Oudjana .hw = &perfcl_pll.clkr.hw 168de37e021SYassine Oudjana }, 169de37e021SYassine Oudjana .num_parents = 1, 170de37e021SYassine Oudjana .ops = &clk_fixed_factor_ops, 171de37e021SYassine Oudjana .flags = CLK_SET_RATE_PARENT, 172de37e021SYassine Oudjana }, 173de37e021SYassine Oudjana }; 174de37e021SYassine Oudjana 175f1e3fcc4SDmitry Baryshkov static struct clk_fixed_factor perfcl_pll_acd = { 176f1e3fcc4SDmitry Baryshkov .mult = 1, 177f1e3fcc4SDmitry Baryshkov .div = 1, 178f1e3fcc4SDmitry Baryshkov .hw.init = &(struct clk_init_data){ 179f1e3fcc4SDmitry Baryshkov .name = "perfcl_pll_acd", 180f1e3fcc4SDmitry Baryshkov .parent_data = &(const struct clk_parent_data){ 181f1e3fcc4SDmitry Baryshkov .hw = &perfcl_pll.clkr.hw 182f1e3fcc4SDmitry Baryshkov }, 183f1e3fcc4SDmitry Baryshkov .num_parents = 1, 184f1e3fcc4SDmitry Baryshkov .ops = &clk_fixed_factor_ops, 185f1e3fcc4SDmitry Baryshkov .flags = CLK_SET_RATE_PARENT, 186f1e3fcc4SDmitry Baryshkov }, 187f1e3fcc4SDmitry Baryshkov }; 188f1e3fcc4SDmitry Baryshkov 189f1e3fcc4SDmitry Baryshkov static struct clk_fixed_factor pwrcl_pll_acd = { 190f1e3fcc4SDmitry Baryshkov .mult = 1, 191f1e3fcc4SDmitry Baryshkov .div = 1, 192f1e3fcc4SDmitry Baryshkov .hw.init = &(struct clk_init_data){ 193f1e3fcc4SDmitry Baryshkov .name = "pwrcl_pll_acd", 194f1e3fcc4SDmitry Baryshkov .parent_data = &(const struct clk_parent_data){ 195f1e3fcc4SDmitry Baryshkov .hw = &pwrcl_pll.clkr.hw 196f1e3fcc4SDmitry Baryshkov }, 197f1e3fcc4SDmitry Baryshkov .num_parents = 1, 198f1e3fcc4SDmitry Baryshkov .ops = &clk_fixed_factor_ops, 199f1e3fcc4SDmitry Baryshkov .flags = CLK_SET_RATE_PARENT, 200f1e3fcc4SDmitry Baryshkov }, 201f1e3fcc4SDmitry Baryshkov }; 202f1e3fcc4SDmitry Baryshkov 20303e342dcSLoic Poulain static const struct pll_vco alt_pll_vco_modes[] = { 20403e342dcSLoic Poulain VCO(3, 250000000, 500000000), 20503e342dcSLoic Poulain VCO(2, 500000000, 750000000), 20603e342dcSLoic Poulain VCO(1, 750000000, 1000000000), 20703e342dcSLoic Poulain VCO(0, 1000000000, 2150400000), 20803e342dcSLoic Poulain }; 20903e342dcSLoic Poulain 21003e342dcSLoic Poulain static const struct alpha_pll_config altpll_config = { 21103e342dcSLoic Poulain .l = 16, 21203e342dcSLoic Poulain .vco_val = 0x3 << 20, 21303e342dcSLoic Poulain .vco_mask = 0x3 << 20, 21403e342dcSLoic Poulain .config_ctl_val = 0x4001051b, 21503e342dcSLoic Poulain .post_div_mask = 0x3 << 8, 21603e342dcSLoic Poulain .post_div_val = 0x1 << 8, 21703e342dcSLoic Poulain .main_output_mask = BIT(0), 21803e342dcSLoic Poulain .early_output_mask = BIT(3), 21903e342dcSLoic Poulain }; 22003e342dcSLoic Poulain 22103e342dcSLoic Poulain static struct clk_alpha_pll pwrcl_alt_pll = { 22203e342dcSLoic Poulain .offset = PWRCL_REG_OFFSET + ALT_PLL_OFFSET, 22303e342dcSLoic Poulain .regs = alt_pll_regs, 22403e342dcSLoic Poulain .vco_table = alt_pll_vco_modes, 22503e342dcSLoic Poulain .num_vco = ARRAY_SIZE(alt_pll_vco_modes), 22603e342dcSLoic Poulain .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, 22703e342dcSLoic Poulain .clkr.hw.init = &(struct clk_init_data) { 22803e342dcSLoic Poulain .name = "pwrcl_alt_pll", 229da5daae8SYassine Oudjana .parent_data = pll_parent, 230da5daae8SYassine Oudjana .num_parents = ARRAY_SIZE(pll_parent), 23103e342dcSLoic Poulain .ops = &clk_alpha_pll_hwfsm_ops, 23203e342dcSLoic Poulain }, 23303e342dcSLoic Poulain }; 23403e342dcSLoic Poulain 235382139bfSYassine Oudjana static struct clk_alpha_pll perfcl_alt_pll = { 236382139bfSYassine Oudjana .offset = PERFCL_REG_OFFSET + ALT_PLL_OFFSET, 237382139bfSYassine Oudjana .regs = alt_pll_regs, 238382139bfSYassine Oudjana .vco_table = alt_pll_vco_modes, 239382139bfSYassine Oudjana .num_vco = ARRAY_SIZE(alt_pll_vco_modes), 240382139bfSYassine Oudjana .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, 241382139bfSYassine Oudjana .clkr.hw.init = &(struct clk_init_data) { 242382139bfSYassine Oudjana .name = "perfcl_alt_pll", 243da5daae8SYassine Oudjana .parent_data = pll_parent, 244da5daae8SYassine Oudjana .num_parents = ARRAY_SIZE(pll_parent), 245382139bfSYassine Oudjana .ops = &clk_alpha_pll_hwfsm_ops, 246382139bfSYassine Oudjana }, 247382139bfSYassine Oudjana }; 248382139bfSYassine Oudjana 2499a9f5f9aSYassine Oudjana struct clk_cpu_8996_pmux { 25003e342dcSLoic Poulain u32 reg; 25103e342dcSLoic Poulain struct notifier_block nb; 25203e342dcSLoic Poulain struct clk_regmap clkr; 25303e342dcSLoic Poulain }; 25403e342dcSLoic Poulain 25503e342dcSLoic Poulain static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, 25603e342dcSLoic Poulain void *data); 25703e342dcSLoic Poulain 2589a9f5f9aSYassine Oudjana #define to_clk_cpu_8996_pmux_nb(_nb) \ 2599a9f5f9aSYassine Oudjana container_of(_nb, struct clk_cpu_8996_pmux, nb) 26003e342dcSLoic Poulain 2619a9f5f9aSYassine Oudjana static inline struct clk_cpu_8996_pmux *to_clk_cpu_8996_pmux_hw(struct clk_hw *hw) 26203e342dcSLoic Poulain { 2639a9f5f9aSYassine Oudjana return container_of(to_clk_regmap(hw), struct clk_cpu_8996_pmux, clkr); 26403e342dcSLoic Poulain } 26503e342dcSLoic Poulain 2669a9f5f9aSYassine Oudjana static u8 clk_cpu_8996_pmux_get_parent(struct clk_hw *hw) 26703e342dcSLoic Poulain { 26803e342dcSLoic Poulain struct clk_regmap *clkr = to_clk_regmap(hw); 2699a9f5f9aSYassine Oudjana struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_hw(hw); 27003e342dcSLoic Poulain u32 val; 27103e342dcSLoic Poulain 27203e342dcSLoic Poulain regmap_read(clkr->regmap, cpuclk->reg, &val); 27303e342dcSLoic Poulain 274f9ea0f59SDmitry Baryshkov return FIELD_GET(PMUX_MASK, val); 27503e342dcSLoic Poulain } 27603e342dcSLoic Poulain 2779a9f5f9aSYassine Oudjana static int clk_cpu_8996_pmux_set_parent(struct clk_hw *hw, u8 index) 27803e342dcSLoic Poulain { 27903e342dcSLoic Poulain struct clk_regmap *clkr = to_clk_regmap(hw); 2809a9f5f9aSYassine Oudjana struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_hw(hw); 28103e342dcSLoic Poulain u32 val; 28203e342dcSLoic Poulain 283f9ea0f59SDmitry Baryshkov val = FIELD_PREP(PMUX_MASK, index); 28403e342dcSLoic Poulain 285f9ea0f59SDmitry Baryshkov return regmap_update_bits(clkr->regmap, cpuclk->reg, PMUX_MASK, val); 28603e342dcSLoic Poulain } 28703e342dcSLoic Poulain 2889a9f5f9aSYassine Oudjana static int clk_cpu_8996_pmux_determine_rate(struct clk_hw *hw, 28903e342dcSLoic Poulain struct clk_rate_request *req) 29003e342dcSLoic Poulain { 291f387d1c4SDmitry Baryshkov struct clk_hw *parent; 29203e342dcSLoic Poulain 29303e342dcSLoic Poulain if (req->rate < (DIV_2_THRESHOLD / 2)) 29403e342dcSLoic Poulain return -EINVAL; 29503e342dcSLoic Poulain 296f387d1c4SDmitry Baryshkov if (req->rate < DIV_2_THRESHOLD) 297f387d1c4SDmitry Baryshkov parent = clk_hw_get_parent_by_index(hw, SMUX_INDEX); 298f387d1c4SDmitry Baryshkov else 299f387d1c4SDmitry Baryshkov parent = clk_hw_get_parent_by_index(hw, ACD_INDEX); 300f387d1c4SDmitry Baryshkov if (!parent) 301f387d1c4SDmitry Baryshkov return -EINVAL; 30203e342dcSLoic Poulain 30303e342dcSLoic Poulain req->best_parent_rate = clk_hw_round_rate(parent, req->rate); 30403e342dcSLoic Poulain req->best_parent_hw = parent; 30503e342dcSLoic Poulain 30603e342dcSLoic Poulain return 0; 30703e342dcSLoic Poulain } 30803e342dcSLoic Poulain 3099a9f5f9aSYassine Oudjana static const struct clk_ops clk_cpu_8996_pmux_ops = { 3109a9f5f9aSYassine Oudjana .set_parent = clk_cpu_8996_pmux_set_parent, 3119a9f5f9aSYassine Oudjana .get_parent = clk_cpu_8996_pmux_get_parent, 3129a9f5f9aSYassine Oudjana .determine_rate = clk_cpu_8996_pmux_determine_rate, 31303e342dcSLoic Poulain }; 31403e342dcSLoic Poulain 315fe8a5005SDmitry Baryshkov static const struct parent_map smux_parent_map[] = { 316fe8a5005SDmitry Baryshkov { .cfg = 0, }, /* xo */ 317fe8a5005SDmitry Baryshkov { .cfg = 1, }, /* pll */ 318fe8a5005SDmitry Baryshkov { .cfg = 3, }, /* sys_apcs_aux */ 319fe8a5005SDmitry Baryshkov }; 320fe8a5005SDmitry Baryshkov 321da5daae8SYassine Oudjana static const struct clk_parent_data pwrcl_smux_parents[] = { 322da5daae8SYassine Oudjana { .fw_name = "xo" }, 323da5daae8SYassine Oudjana { .hw = &pwrcl_pll_postdiv.hw }, 324fe8a5005SDmitry Baryshkov { .fw_name = "sys_apcs_aux" }, 325da5daae8SYassine Oudjana }; 326da5daae8SYassine Oudjana 327da5daae8SYassine Oudjana static const struct clk_parent_data perfcl_smux_parents[] = { 328da5daae8SYassine Oudjana { .fw_name = "xo" }, 329da5daae8SYassine Oudjana { .hw = &perfcl_pll_postdiv.hw }, 330fe8a5005SDmitry Baryshkov { .fw_name = "sys_apcs_aux" }, 331da5daae8SYassine Oudjana }; 332da5daae8SYassine Oudjana 3339a9f5f9aSYassine Oudjana static struct clk_regmap_mux pwrcl_smux = { 33403e342dcSLoic Poulain .reg = PWRCL_REG_OFFSET + MUX_OFFSET, 33503e342dcSLoic Poulain .shift = 2, 33603e342dcSLoic Poulain .width = 2, 337fe8a5005SDmitry Baryshkov .parent_map = smux_parent_map, 33803e342dcSLoic Poulain .clkr.hw.init = &(struct clk_init_data) { 33903e342dcSLoic Poulain .name = "pwrcl_smux", 340da5daae8SYassine Oudjana .parent_data = pwrcl_smux_parents, 341da5daae8SYassine Oudjana .num_parents = ARRAY_SIZE(pwrcl_smux_parents), 3429a9f5f9aSYassine Oudjana .ops = &clk_regmap_mux_closest_ops, 34303e342dcSLoic Poulain .flags = CLK_SET_RATE_PARENT, 34403e342dcSLoic Poulain }, 34503e342dcSLoic Poulain }; 34603e342dcSLoic Poulain 3479a9f5f9aSYassine Oudjana static struct clk_regmap_mux perfcl_smux = { 34803e342dcSLoic Poulain .reg = PERFCL_REG_OFFSET + MUX_OFFSET, 34903e342dcSLoic Poulain .shift = 2, 35003e342dcSLoic Poulain .width = 2, 351fe8a5005SDmitry Baryshkov .parent_map = smux_parent_map, 35203e342dcSLoic Poulain .clkr.hw.init = &(struct clk_init_data) { 35303e342dcSLoic Poulain .name = "perfcl_smux", 354da5daae8SYassine Oudjana .parent_data = perfcl_smux_parents, 355da5daae8SYassine Oudjana .num_parents = ARRAY_SIZE(perfcl_smux_parents), 3569a9f5f9aSYassine Oudjana .ops = &clk_regmap_mux_closest_ops, 35703e342dcSLoic Poulain .flags = CLK_SET_RATE_PARENT, 35803e342dcSLoic Poulain }, 35903e342dcSLoic Poulain }; 36003e342dcSLoic Poulain 361da5daae8SYassine Oudjana static const struct clk_hw *pwrcl_pmux_parents[] = { 362da5daae8SYassine Oudjana [SMUX_INDEX] = &pwrcl_smux.clkr.hw, 363da5daae8SYassine Oudjana [PLL_INDEX] = &pwrcl_pll.clkr.hw, 364f1e3fcc4SDmitry Baryshkov [ACD_INDEX] = &pwrcl_pll_acd.hw, 365da5daae8SYassine Oudjana [ALT_INDEX] = &pwrcl_alt_pll.clkr.hw, 366da5daae8SYassine Oudjana }; 367da5daae8SYassine Oudjana 368da5daae8SYassine Oudjana static const struct clk_hw *perfcl_pmux_parents[] = { 369da5daae8SYassine Oudjana [SMUX_INDEX] = &perfcl_smux.clkr.hw, 370da5daae8SYassine Oudjana [PLL_INDEX] = &perfcl_pll.clkr.hw, 371f1e3fcc4SDmitry Baryshkov [ACD_INDEX] = &perfcl_pll_acd.hw, 372da5daae8SYassine Oudjana [ALT_INDEX] = &perfcl_alt_pll.clkr.hw, 373da5daae8SYassine Oudjana }; 374da5daae8SYassine Oudjana 3759a9f5f9aSYassine Oudjana static struct clk_cpu_8996_pmux pwrcl_pmux = { 37603e342dcSLoic Poulain .reg = PWRCL_REG_OFFSET + MUX_OFFSET, 37703e342dcSLoic Poulain .nb.notifier_call = cpu_clk_notifier_cb, 37803e342dcSLoic Poulain .clkr.hw.init = &(struct clk_init_data) { 37903e342dcSLoic Poulain .name = "pwrcl_pmux", 380da5daae8SYassine Oudjana .parent_hws = pwrcl_pmux_parents, 381da5daae8SYassine Oudjana .num_parents = ARRAY_SIZE(pwrcl_pmux_parents), 3829a9f5f9aSYassine Oudjana .ops = &clk_cpu_8996_pmux_ops, 38303e342dcSLoic Poulain /* CPU clock is critical and should never be gated */ 38403e342dcSLoic Poulain .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 38503e342dcSLoic Poulain }, 38603e342dcSLoic Poulain }; 38703e342dcSLoic Poulain 3889a9f5f9aSYassine Oudjana static struct clk_cpu_8996_pmux perfcl_pmux = { 38903e342dcSLoic Poulain .reg = PERFCL_REG_OFFSET + MUX_OFFSET, 39003e342dcSLoic Poulain .nb.notifier_call = cpu_clk_notifier_cb, 39103e342dcSLoic Poulain .clkr.hw.init = &(struct clk_init_data) { 39203e342dcSLoic Poulain .name = "perfcl_pmux", 393da5daae8SYassine Oudjana .parent_hws = perfcl_pmux_parents, 394da5daae8SYassine Oudjana .num_parents = ARRAY_SIZE(perfcl_pmux_parents), 3959a9f5f9aSYassine Oudjana .ops = &clk_cpu_8996_pmux_ops, 39603e342dcSLoic Poulain /* CPU clock is critical and should never be gated */ 39703e342dcSLoic Poulain .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 39803e342dcSLoic Poulain }, 39903e342dcSLoic Poulain }; 40003e342dcSLoic Poulain 40103e342dcSLoic Poulain static const struct regmap_config cpu_msm8996_regmap_config = { 40203e342dcSLoic Poulain .reg_bits = 32, 40303e342dcSLoic Poulain .reg_stride = 4, 40403e342dcSLoic Poulain .val_bits = 32, 40503e342dcSLoic Poulain .max_register = 0x80210, 40603e342dcSLoic Poulain .fast_io = true, 40703e342dcSLoic Poulain .val_format_endian = REGMAP_ENDIAN_LITTLE, 40803e342dcSLoic Poulain }; 40903e342dcSLoic Poulain 410f1e3fcc4SDmitry Baryshkov static struct clk_hw *cpu_msm8996_hw_clks[] = { 411f1e3fcc4SDmitry Baryshkov &pwrcl_pll_postdiv.hw, 412f1e3fcc4SDmitry Baryshkov &perfcl_pll_postdiv.hw, 413f1e3fcc4SDmitry Baryshkov &pwrcl_pll_acd.hw, 414f1e3fcc4SDmitry Baryshkov &perfcl_pll_acd.hw, 415f1e3fcc4SDmitry Baryshkov }; 416f1e3fcc4SDmitry Baryshkov 4178607fa16SWei Yongjun static struct clk_regmap *cpu_msm8996_clks[] = { 41803e342dcSLoic Poulain &pwrcl_pll.clkr, 419382139bfSYassine Oudjana &perfcl_pll.clkr, 42003e342dcSLoic Poulain &pwrcl_alt_pll.clkr, 421382139bfSYassine Oudjana &perfcl_alt_pll.clkr, 42203e342dcSLoic Poulain &pwrcl_smux.clkr, 423382139bfSYassine Oudjana &perfcl_smux.clkr, 42403e342dcSLoic Poulain &pwrcl_pmux.clkr, 425382139bfSYassine Oudjana &perfcl_pmux.clkr, 42603e342dcSLoic Poulain }; 42703e342dcSLoic Poulain 42803e342dcSLoic Poulain static int qcom_cpu_clk_msm8996_register_clks(struct device *dev, 42903e342dcSLoic Poulain struct regmap *regmap) 43003e342dcSLoic Poulain { 43103e342dcSLoic Poulain int i, ret; 43203e342dcSLoic Poulain 433*61dc1a73SDmitry Baryshkov clk_alpha_pll_configure(&pwrcl_pll, regmap, &hfpll_config); 434*61dc1a73SDmitry Baryshkov clk_alpha_pll_configure(&perfcl_pll, regmap, &hfpll_config); 435*61dc1a73SDmitry Baryshkov clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config); 436*61dc1a73SDmitry Baryshkov clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config); 437*61dc1a73SDmitry Baryshkov 438f1e3fcc4SDmitry Baryshkov for (i = 0; i < ARRAY_SIZE(cpu_msm8996_hw_clks); i++) { 439f1e3fcc4SDmitry Baryshkov ret = devm_clk_hw_register(dev, cpu_msm8996_hw_clks[i]); 440f1e3fcc4SDmitry Baryshkov if (ret) 441de37e021SYassine Oudjana return ret; 44203e342dcSLoic Poulain } 44303e342dcSLoic Poulain 44403e342dcSLoic Poulain for (i = 0; i < ARRAY_SIZE(cpu_msm8996_clks); i++) { 44503e342dcSLoic Poulain ret = devm_clk_register_regmap(dev, cpu_msm8996_clks[i]); 446de37e021SYassine Oudjana if (ret) 44703e342dcSLoic Poulain return ret; 44803e342dcSLoic Poulain } 44903e342dcSLoic Poulain 45003e342dcSLoic Poulain /* Enable alt PLLs */ 45103e342dcSLoic Poulain clk_prepare_enable(pwrcl_alt_pll.clkr.hw.clk); 45203e342dcSLoic Poulain clk_prepare_enable(perfcl_alt_pll.clkr.hw.clk); 45303e342dcSLoic Poulain 454a808c784SDmitry Baryshkov devm_clk_notifier_register(dev, pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb); 455a808c784SDmitry Baryshkov devm_clk_notifier_register(dev, perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb); 45603e342dcSLoic Poulain 45703e342dcSLoic Poulain return ret; 45803e342dcSLoic Poulain } 45903e342dcSLoic Poulain 46003e342dcSLoic Poulain #define CPU_AFINITY_MASK 0xFFF 46103e342dcSLoic Poulain #define PWRCL_CPU_REG_MASK 0x3 46203e342dcSLoic Poulain #define PERFCL_CPU_REG_MASK 0x103 46303e342dcSLoic Poulain 46403e342dcSLoic Poulain #define L2ACDCR_REG 0x580ULL 46503e342dcSLoic Poulain #define L2ACDTD_REG 0x581ULL 46603e342dcSLoic Poulain #define L2ACDDVMRC_REG 0x584ULL 46703e342dcSLoic Poulain #define L2ACDSSCR_REG 0x589ULL 46803e342dcSLoic Poulain 46903e342dcSLoic Poulain static DEFINE_SPINLOCK(qcom_clk_acd_lock); 47003e342dcSLoic Poulain static void __iomem *base; 47103e342dcSLoic Poulain 47203e342dcSLoic Poulain static void qcom_cpu_clk_msm8996_acd_init(void __iomem *base) 47303e342dcSLoic Poulain { 47403e342dcSLoic Poulain u64 hwid; 47572537606SDmitry Baryshkov u32 val; 47603e342dcSLoic Poulain unsigned long flags; 47703e342dcSLoic Poulain 47803e342dcSLoic Poulain spin_lock_irqsave(&qcom_clk_acd_lock, flags); 47903e342dcSLoic Poulain 48072537606SDmitry Baryshkov val = kryo_l2_get_indirect_reg(L2ACDTD_REG); 48172537606SDmitry Baryshkov if (val == 0x00006a11) 48272537606SDmitry Baryshkov goto out; 48372537606SDmitry Baryshkov 48403e342dcSLoic Poulain hwid = read_cpuid_mpidr() & CPU_AFINITY_MASK; 48503e342dcSLoic Poulain 48603e342dcSLoic Poulain kryo_l2_set_indirect_reg(L2ACDTD_REG, 0x00006a11); 48703e342dcSLoic Poulain kryo_l2_set_indirect_reg(L2ACDDVMRC_REG, 0x000e0f0f); 48803e342dcSLoic Poulain kryo_l2_set_indirect_reg(L2ACDSSCR_REG, 0x00000601); 48903e342dcSLoic Poulain 49003e342dcSLoic Poulain if (PWRCL_CPU_REG_MASK == (hwid | PWRCL_CPU_REG_MASK)) { 49103e342dcSLoic Poulain writel(0xf, base + PWRCL_REG_OFFSET + SSSCTL_OFFSET); 49203e342dcSLoic Poulain kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd); 49303e342dcSLoic Poulain } 49403e342dcSLoic Poulain 49503e342dcSLoic Poulain if (PERFCL_CPU_REG_MASK == (hwid | PERFCL_CPU_REG_MASK)) { 49603e342dcSLoic Poulain kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd); 49703e342dcSLoic Poulain writel(0xf, base + PERFCL_REG_OFFSET + SSSCTL_OFFSET); 49803e342dcSLoic Poulain } 49903e342dcSLoic Poulain 50072537606SDmitry Baryshkov out: 50103e342dcSLoic Poulain spin_unlock_irqrestore(&qcom_clk_acd_lock, flags); 50203e342dcSLoic Poulain } 50303e342dcSLoic Poulain 50403e342dcSLoic Poulain static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, 50503e342dcSLoic Poulain void *data) 50603e342dcSLoic Poulain { 5079a9f5f9aSYassine Oudjana struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_nb(nb); 50803e342dcSLoic Poulain struct clk_notifier_data *cnd = data; 50903e342dcSLoic Poulain 51003e342dcSLoic Poulain switch (event) { 51103e342dcSLoic Poulain case PRE_RATE_CHANGE: 51203e342dcSLoic Poulain qcom_cpu_clk_msm8996_acd_init(base); 513b3b274bcSDmitry Baryshkov 514b3b274bcSDmitry Baryshkov /* 515b3b274bcSDmitry Baryshkov * Avoid overvolting. clk_core_set_rate_nolock() walks from top 516b3b274bcSDmitry Baryshkov * to bottom, so it will change the rate of the PLL before 517b3b274bcSDmitry Baryshkov * chaging the parent of PMUX. This can result in pmux getting 518b3b274bcSDmitry Baryshkov * clocked twice the expected rate. 519b3b274bcSDmitry Baryshkov * 520b3b274bcSDmitry Baryshkov * Manually switch to PLL/2 here. 521b3b274bcSDmitry Baryshkov */ 522b3b274bcSDmitry Baryshkov if (cnd->new_rate < DIV_2_THRESHOLD && 523b3b274bcSDmitry Baryshkov cnd->old_rate > DIV_2_THRESHOLD) 524b3b274bcSDmitry Baryshkov clk_cpu_8996_pmux_set_parent(&cpuclk->clkr.hw, SMUX_INDEX); 525b3b274bcSDmitry Baryshkov 52603e342dcSLoic Poulain break; 527b3b274bcSDmitry Baryshkov case ABORT_RATE_CHANGE: 528b3b274bcSDmitry Baryshkov /* Revert manual change */ 529b3b274bcSDmitry Baryshkov if (cnd->new_rate < DIV_2_THRESHOLD && 530b3b274bcSDmitry Baryshkov cnd->old_rate > DIV_2_THRESHOLD) 531b3b274bcSDmitry Baryshkov clk_cpu_8996_pmux_set_parent(&cpuclk->clkr.hw, ACD_INDEX); 53203e342dcSLoic Poulain break; 53303e342dcSLoic Poulain default: 53403e342dcSLoic Poulain break; 53503e342dcSLoic Poulain } 53603e342dcSLoic Poulain 537b3b274bcSDmitry Baryshkov return NOTIFY_OK; 53803e342dcSLoic Poulain }; 53903e342dcSLoic Poulain 54003e342dcSLoic Poulain static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev) 54103e342dcSLoic Poulain { 54203e342dcSLoic Poulain struct regmap *regmap; 54303e342dcSLoic Poulain struct clk_hw_onecell_data *data; 54403e342dcSLoic Poulain struct device *dev = &pdev->dev; 54503e342dcSLoic Poulain int ret; 54603e342dcSLoic Poulain 54703e342dcSLoic Poulain data = devm_kzalloc(dev, struct_size(data, hws, 2), GFP_KERNEL); 54803e342dcSLoic Poulain if (!data) 54903e342dcSLoic Poulain return -ENOMEM; 55003e342dcSLoic Poulain 55103e342dcSLoic Poulain base = devm_platform_ioremap_resource(pdev, 0); 55203e342dcSLoic Poulain if (IS_ERR(base)) 55303e342dcSLoic Poulain return PTR_ERR(base); 55403e342dcSLoic Poulain 55503e342dcSLoic Poulain regmap = devm_regmap_init_mmio(dev, base, &cpu_msm8996_regmap_config); 55603e342dcSLoic Poulain if (IS_ERR(regmap)) 55703e342dcSLoic Poulain return PTR_ERR(regmap); 55803e342dcSLoic Poulain 55903e342dcSLoic Poulain ret = qcom_cpu_clk_msm8996_register_clks(dev, regmap); 56003e342dcSLoic Poulain if (ret) 56103e342dcSLoic Poulain return ret; 56203e342dcSLoic Poulain 56303e342dcSLoic Poulain qcom_cpu_clk_msm8996_acd_init(base); 56403e342dcSLoic Poulain 56503e342dcSLoic Poulain data->hws[0] = &pwrcl_pmux.clkr.hw; 56603e342dcSLoic Poulain data->hws[1] = &perfcl_pmux.clkr.hw; 56703e342dcSLoic Poulain data->num = 2; 56803e342dcSLoic Poulain 56903e342dcSLoic Poulain return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data); 57003e342dcSLoic Poulain } 57103e342dcSLoic Poulain 57203e342dcSLoic Poulain static const struct of_device_id qcom_cpu_clk_msm8996_match_table[] = { 57303e342dcSLoic Poulain { .compatible = "qcom,msm8996-apcc" }, 57403e342dcSLoic Poulain {} 57503e342dcSLoic Poulain }; 57603e342dcSLoic Poulain MODULE_DEVICE_TABLE(of, qcom_cpu_clk_msm8996_match_table); 57703e342dcSLoic Poulain 57803e342dcSLoic Poulain static struct platform_driver qcom_cpu_clk_msm8996_driver = { 57903e342dcSLoic Poulain .probe = qcom_cpu_clk_msm8996_driver_probe, 58003e342dcSLoic Poulain .driver = { 58103e342dcSLoic Poulain .name = "qcom-msm8996-apcc", 58203e342dcSLoic Poulain .of_match_table = qcom_cpu_clk_msm8996_match_table, 58303e342dcSLoic Poulain }, 58403e342dcSLoic Poulain }; 58503e342dcSLoic Poulain module_platform_driver(qcom_cpu_clk_msm8996_driver); 58603e342dcSLoic Poulain 58703e342dcSLoic Poulain MODULE_DESCRIPTION("QCOM MSM8996 CPU Clock Driver"); 58803e342dcSLoic Poulain MODULE_LICENSE("GPL v2"); 589