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 79*9daaaaaaSDmitry Baryshkov #define CLK_CTL_OFFSET 0x44 80*9daaaaaaSDmitry Baryshkov #define CLK_CTL_AUTO_CLK_SEL BIT(8) 8103e342dcSLoic Poulain #define ALT_PLL_OFFSET 0x100 8203e342dcSLoic Poulain #define SSSCTL_OFFSET 0x160 83*9daaaaaaSDmitry Baryshkov #define PSCTL_OFFSET 0x164 8403e342dcSLoic Poulain 85f9ea0f59SDmitry Baryshkov #define PMUX_MASK 0x3 86*9daaaaaaSDmitry Baryshkov #define MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK GENMASK(5, 4) 87*9daaaaaaSDmitry Baryshkov #define MUX_AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL \ 88*9daaaaaaSDmitry Baryshkov FIELD_PREP(MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK, 0x03) 89f9ea0f59SDmitry Baryshkov 9003e342dcSLoic Poulain static const u8 prim_pll_regs[PLL_OFF_MAX_REGS] = { 9103e342dcSLoic Poulain [PLL_OFF_L_VAL] = 0x04, 9203e342dcSLoic Poulain [PLL_OFF_ALPHA_VAL] = 0x08, 9303e342dcSLoic Poulain [PLL_OFF_USER_CTL] = 0x10, 9403e342dcSLoic Poulain [PLL_OFF_CONFIG_CTL] = 0x18, 9503e342dcSLoic Poulain [PLL_OFF_CONFIG_CTL_U] = 0x1c, 9603e342dcSLoic Poulain [PLL_OFF_TEST_CTL] = 0x20, 9703e342dcSLoic Poulain [PLL_OFF_TEST_CTL_U] = 0x24, 9803e342dcSLoic Poulain [PLL_OFF_STATUS] = 0x28, 9903e342dcSLoic Poulain }; 10003e342dcSLoic Poulain 10103e342dcSLoic Poulain static const u8 alt_pll_regs[PLL_OFF_MAX_REGS] = { 10203e342dcSLoic Poulain [PLL_OFF_L_VAL] = 0x04, 10303e342dcSLoic Poulain [PLL_OFF_ALPHA_VAL] = 0x08, 10403e342dcSLoic Poulain [PLL_OFF_USER_CTL] = 0x10, 10503e342dcSLoic Poulain [PLL_OFF_CONFIG_CTL] = 0x18, 10603e342dcSLoic Poulain [PLL_OFF_TEST_CTL] = 0x20, 10703e342dcSLoic Poulain [PLL_OFF_STATUS] = 0x28, 10803e342dcSLoic Poulain }; 10903e342dcSLoic Poulain 11003e342dcSLoic Poulain /* PLLs */ 11103e342dcSLoic Poulain 11203e342dcSLoic Poulain static const struct alpha_pll_config hfpll_config = { 113be4e65d1SDmitry Baryshkov .l = 54, 1144953610bSDmitry Baryshkov .config_ctl_val = 0x200d4828, 11503e342dcSLoic Poulain .config_ctl_hi_val = 0x006, 1164953610bSDmitry Baryshkov .test_ctl_val = 0x1c000000, 1174953610bSDmitry Baryshkov .test_ctl_hi_val = 0x00004000, 11803e342dcSLoic Poulain .pre_div_mask = BIT(12), 11903e342dcSLoic Poulain .post_div_mask = 0x3 << 8, 12003e342dcSLoic Poulain .post_div_val = 0x1 << 8, 12103e342dcSLoic Poulain .main_output_mask = BIT(0), 12203e342dcSLoic Poulain .early_output_mask = BIT(3), 12303e342dcSLoic Poulain }; 12403e342dcSLoic Poulain 125da5daae8SYassine Oudjana static const struct clk_parent_data pll_parent[] = { 126da5daae8SYassine Oudjana { .fw_name = "xo" }, 127da5daae8SYassine Oudjana }; 128da5daae8SYassine Oudjana 12903e342dcSLoic Poulain static struct clk_alpha_pll pwrcl_pll = { 13003e342dcSLoic Poulain .offset = PWRCL_REG_OFFSET, 13103e342dcSLoic Poulain .regs = prim_pll_regs, 13203e342dcSLoic Poulain .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, 13303e342dcSLoic Poulain .clkr.hw.init = &(struct clk_init_data){ 13403e342dcSLoic Poulain .name = "pwrcl_pll", 135da5daae8SYassine Oudjana .parent_data = pll_parent, 136da5daae8SYassine Oudjana .num_parents = ARRAY_SIZE(pll_parent), 137682c6a45SDmitry Baryshkov .ops = &clk_alpha_pll_hwfsm_ops, 13803e342dcSLoic Poulain }, 13903e342dcSLoic Poulain }; 14003e342dcSLoic Poulain 141382139bfSYassine Oudjana static struct clk_alpha_pll perfcl_pll = { 142382139bfSYassine Oudjana .offset = PERFCL_REG_OFFSET, 143382139bfSYassine Oudjana .regs = prim_pll_regs, 144382139bfSYassine Oudjana .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, 145382139bfSYassine Oudjana .clkr.hw.init = &(struct clk_init_data){ 146382139bfSYassine Oudjana .name = "perfcl_pll", 147da5daae8SYassine Oudjana .parent_data = pll_parent, 148da5daae8SYassine Oudjana .num_parents = ARRAY_SIZE(pll_parent), 149682c6a45SDmitry Baryshkov .ops = &clk_alpha_pll_hwfsm_ops, 150382139bfSYassine Oudjana }, 151382139bfSYassine Oudjana }; 152382139bfSYassine Oudjana 153de37e021SYassine Oudjana static struct clk_fixed_factor pwrcl_pll_postdiv = { 154de37e021SYassine Oudjana .mult = 1, 155de37e021SYassine Oudjana .div = 2, 156de37e021SYassine Oudjana .hw.init = &(struct clk_init_data){ 157de37e021SYassine Oudjana .name = "pwrcl_pll_postdiv", 158de37e021SYassine Oudjana .parent_data = &(const struct clk_parent_data){ 159de37e021SYassine Oudjana .hw = &pwrcl_pll.clkr.hw 160de37e021SYassine Oudjana }, 161de37e021SYassine Oudjana .num_parents = 1, 162de37e021SYassine Oudjana .ops = &clk_fixed_factor_ops, 163de37e021SYassine Oudjana .flags = CLK_SET_RATE_PARENT, 164de37e021SYassine Oudjana }, 165de37e021SYassine Oudjana }; 166de37e021SYassine Oudjana 167de37e021SYassine Oudjana static struct clk_fixed_factor perfcl_pll_postdiv = { 168de37e021SYassine Oudjana .mult = 1, 169de37e021SYassine Oudjana .div = 2, 170de37e021SYassine Oudjana .hw.init = &(struct clk_init_data){ 171de37e021SYassine Oudjana .name = "perfcl_pll_postdiv", 172de37e021SYassine Oudjana .parent_data = &(const struct clk_parent_data){ 173de37e021SYassine Oudjana .hw = &perfcl_pll.clkr.hw 174de37e021SYassine Oudjana }, 175de37e021SYassine Oudjana .num_parents = 1, 176de37e021SYassine Oudjana .ops = &clk_fixed_factor_ops, 177de37e021SYassine Oudjana .flags = CLK_SET_RATE_PARENT, 178de37e021SYassine Oudjana }, 179de37e021SYassine Oudjana }; 180de37e021SYassine Oudjana 181f1e3fcc4SDmitry Baryshkov static struct clk_fixed_factor perfcl_pll_acd = { 182f1e3fcc4SDmitry Baryshkov .mult = 1, 183f1e3fcc4SDmitry Baryshkov .div = 1, 184f1e3fcc4SDmitry Baryshkov .hw.init = &(struct clk_init_data){ 185f1e3fcc4SDmitry Baryshkov .name = "perfcl_pll_acd", 186f1e3fcc4SDmitry Baryshkov .parent_data = &(const struct clk_parent_data){ 187f1e3fcc4SDmitry Baryshkov .hw = &perfcl_pll.clkr.hw 188f1e3fcc4SDmitry Baryshkov }, 189f1e3fcc4SDmitry Baryshkov .num_parents = 1, 190f1e3fcc4SDmitry Baryshkov .ops = &clk_fixed_factor_ops, 191f1e3fcc4SDmitry Baryshkov .flags = CLK_SET_RATE_PARENT, 192f1e3fcc4SDmitry Baryshkov }, 193f1e3fcc4SDmitry Baryshkov }; 194f1e3fcc4SDmitry Baryshkov 195f1e3fcc4SDmitry Baryshkov static struct clk_fixed_factor pwrcl_pll_acd = { 196f1e3fcc4SDmitry Baryshkov .mult = 1, 197f1e3fcc4SDmitry Baryshkov .div = 1, 198f1e3fcc4SDmitry Baryshkov .hw.init = &(struct clk_init_data){ 199f1e3fcc4SDmitry Baryshkov .name = "pwrcl_pll_acd", 200f1e3fcc4SDmitry Baryshkov .parent_data = &(const struct clk_parent_data){ 201f1e3fcc4SDmitry Baryshkov .hw = &pwrcl_pll.clkr.hw 202f1e3fcc4SDmitry Baryshkov }, 203f1e3fcc4SDmitry Baryshkov .num_parents = 1, 204f1e3fcc4SDmitry Baryshkov .ops = &clk_fixed_factor_ops, 205f1e3fcc4SDmitry Baryshkov .flags = CLK_SET_RATE_PARENT, 206f1e3fcc4SDmitry Baryshkov }, 207f1e3fcc4SDmitry Baryshkov }; 208f1e3fcc4SDmitry Baryshkov 20903e342dcSLoic Poulain static const struct pll_vco alt_pll_vco_modes[] = { 21003e342dcSLoic Poulain VCO(3, 250000000, 500000000), 21103e342dcSLoic Poulain VCO(2, 500000000, 750000000), 21203e342dcSLoic Poulain VCO(1, 750000000, 1000000000), 21303e342dcSLoic Poulain VCO(0, 1000000000, 2150400000), 21403e342dcSLoic Poulain }; 21503e342dcSLoic Poulain 21603e342dcSLoic Poulain static const struct alpha_pll_config altpll_config = { 21703e342dcSLoic Poulain .l = 16, 21803e342dcSLoic Poulain .vco_val = 0x3 << 20, 21903e342dcSLoic Poulain .vco_mask = 0x3 << 20, 22003e342dcSLoic Poulain .config_ctl_val = 0x4001051b, 22103e342dcSLoic Poulain .post_div_mask = 0x3 << 8, 22203e342dcSLoic Poulain .post_div_val = 0x1 << 8, 22303e342dcSLoic Poulain .main_output_mask = BIT(0), 22403e342dcSLoic Poulain .early_output_mask = BIT(3), 22503e342dcSLoic Poulain }; 22603e342dcSLoic Poulain 22703e342dcSLoic Poulain static struct clk_alpha_pll pwrcl_alt_pll = { 22803e342dcSLoic Poulain .offset = PWRCL_REG_OFFSET + ALT_PLL_OFFSET, 22903e342dcSLoic Poulain .regs = alt_pll_regs, 23003e342dcSLoic Poulain .vco_table = alt_pll_vco_modes, 23103e342dcSLoic Poulain .num_vco = ARRAY_SIZE(alt_pll_vco_modes), 23203e342dcSLoic Poulain .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, 23303e342dcSLoic Poulain .clkr.hw.init = &(struct clk_init_data) { 23403e342dcSLoic Poulain .name = "pwrcl_alt_pll", 235da5daae8SYassine Oudjana .parent_data = pll_parent, 236da5daae8SYassine Oudjana .num_parents = ARRAY_SIZE(pll_parent), 23703e342dcSLoic Poulain .ops = &clk_alpha_pll_hwfsm_ops, 23803e342dcSLoic Poulain }, 23903e342dcSLoic Poulain }; 24003e342dcSLoic Poulain 241382139bfSYassine Oudjana static struct clk_alpha_pll perfcl_alt_pll = { 242382139bfSYassine Oudjana .offset = PERFCL_REG_OFFSET + ALT_PLL_OFFSET, 243382139bfSYassine Oudjana .regs = alt_pll_regs, 244382139bfSYassine Oudjana .vco_table = alt_pll_vco_modes, 245382139bfSYassine Oudjana .num_vco = ARRAY_SIZE(alt_pll_vco_modes), 246382139bfSYassine Oudjana .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, 247382139bfSYassine Oudjana .clkr.hw.init = &(struct clk_init_data) { 248382139bfSYassine Oudjana .name = "perfcl_alt_pll", 249da5daae8SYassine Oudjana .parent_data = pll_parent, 250da5daae8SYassine Oudjana .num_parents = ARRAY_SIZE(pll_parent), 251382139bfSYassine Oudjana .ops = &clk_alpha_pll_hwfsm_ops, 252382139bfSYassine Oudjana }, 253382139bfSYassine Oudjana }; 254382139bfSYassine Oudjana 2559a9f5f9aSYassine Oudjana struct clk_cpu_8996_pmux { 25603e342dcSLoic Poulain u32 reg; 25703e342dcSLoic Poulain struct notifier_block nb; 25803e342dcSLoic Poulain struct clk_regmap clkr; 25903e342dcSLoic Poulain }; 26003e342dcSLoic Poulain 26103e342dcSLoic Poulain static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, 26203e342dcSLoic Poulain void *data); 26303e342dcSLoic Poulain 2649a9f5f9aSYassine Oudjana #define to_clk_cpu_8996_pmux_nb(_nb) \ 2659a9f5f9aSYassine Oudjana container_of(_nb, struct clk_cpu_8996_pmux, nb) 26603e342dcSLoic Poulain 2679a9f5f9aSYassine Oudjana static inline struct clk_cpu_8996_pmux *to_clk_cpu_8996_pmux_hw(struct clk_hw *hw) 26803e342dcSLoic Poulain { 2699a9f5f9aSYassine Oudjana return container_of(to_clk_regmap(hw), struct clk_cpu_8996_pmux, clkr); 27003e342dcSLoic Poulain } 27103e342dcSLoic Poulain 2729a9f5f9aSYassine Oudjana static u8 clk_cpu_8996_pmux_get_parent(struct clk_hw *hw) 27303e342dcSLoic Poulain { 27403e342dcSLoic Poulain struct clk_regmap *clkr = to_clk_regmap(hw); 2759a9f5f9aSYassine Oudjana struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_hw(hw); 27603e342dcSLoic Poulain u32 val; 27703e342dcSLoic Poulain 27803e342dcSLoic Poulain regmap_read(clkr->regmap, cpuclk->reg, &val); 27903e342dcSLoic Poulain 280f9ea0f59SDmitry Baryshkov return FIELD_GET(PMUX_MASK, val); 28103e342dcSLoic Poulain } 28203e342dcSLoic Poulain 2839a9f5f9aSYassine Oudjana static int clk_cpu_8996_pmux_set_parent(struct clk_hw *hw, u8 index) 28403e342dcSLoic Poulain { 28503e342dcSLoic Poulain struct clk_regmap *clkr = to_clk_regmap(hw); 2869a9f5f9aSYassine Oudjana struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_hw(hw); 28703e342dcSLoic Poulain u32 val; 28803e342dcSLoic Poulain 289f9ea0f59SDmitry Baryshkov val = FIELD_PREP(PMUX_MASK, index); 29003e342dcSLoic Poulain 291f9ea0f59SDmitry Baryshkov return regmap_update_bits(clkr->regmap, cpuclk->reg, PMUX_MASK, val); 29203e342dcSLoic Poulain } 29303e342dcSLoic Poulain 2949a9f5f9aSYassine Oudjana static int clk_cpu_8996_pmux_determine_rate(struct clk_hw *hw, 29503e342dcSLoic Poulain struct clk_rate_request *req) 29603e342dcSLoic Poulain { 297f387d1c4SDmitry Baryshkov struct clk_hw *parent; 29803e342dcSLoic Poulain 29903e342dcSLoic Poulain if (req->rate < (DIV_2_THRESHOLD / 2)) 30003e342dcSLoic Poulain return -EINVAL; 30103e342dcSLoic Poulain 302f387d1c4SDmitry Baryshkov if (req->rate < DIV_2_THRESHOLD) 303f387d1c4SDmitry Baryshkov parent = clk_hw_get_parent_by_index(hw, SMUX_INDEX); 304f387d1c4SDmitry Baryshkov else 305f387d1c4SDmitry Baryshkov parent = clk_hw_get_parent_by_index(hw, ACD_INDEX); 306f387d1c4SDmitry Baryshkov if (!parent) 307f387d1c4SDmitry Baryshkov return -EINVAL; 30803e342dcSLoic Poulain 30903e342dcSLoic Poulain req->best_parent_rate = clk_hw_round_rate(parent, req->rate); 31003e342dcSLoic Poulain req->best_parent_hw = parent; 31103e342dcSLoic Poulain 31203e342dcSLoic Poulain return 0; 31303e342dcSLoic Poulain } 31403e342dcSLoic Poulain 3159a9f5f9aSYassine Oudjana static const struct clk_ops clk_cpu_8996_pmux_ops = { 3169a9f5f9aSYassine Oudjana .set_parent = clk_cpu_8996_pmux_set_parent, 3179a9f5f9aSYassine Oudjana .get_parent = clk_cpu_8996_pmux_get_parent, 3189a9f5f9aSYassine Oudjana .determine_rate = clk_cpu_8996_pmux_determine_rate, 31903e342dcSLoic Poulain }; 32003e342dcSLoic Poulain 321fe8a5005SDmitry Baryshkov static const struct parent_map smux_parent_map[] = { 322fe8a5005SDmitry Baryshkov { .cfg = 0, }, /* xo */ 323fe8a5005SDmitry Baryshkov { .cfg = 1, }, /* pll */ 324fe8a5005SDmitry Baryshkov { .cfg = 3, }, /* sys_apcs_aux */ 325fe8a5005SDmitry Baryshkov }; 326fe8a5005SDmitry Baryshkov 327da5daae8SYassine Oudjana static const struct clk_parent_data pwrcl_smux_parents[] = { 328da5daae8SYassine Oudjana { .fw_name = "xo" }, 329da5daae8SYassine Oudjana { .hw = &pwrcl_pll_postdiv.hw }, 330fe8a5005SDmitry Baryshkov { .fw_name = "sys_apcs_aux" }, 331da5daae8SYassine Oudjana }; 332da5daae8SYassine Oudjana 333da5daae8SYassine Oudjana static const struct clk_parent_data perfcl_smux_parents[] = { 334da5daae8SYassine Oudjana { .fw_name = "xo" }, 335da5daae8SYassine Oudjana { .hw = &perfcl_pll_postdiv.hw }, 336fe8a5005SDmitry Baryshkov { .fw_name = "sys_apcs_aux" }, 337da5daae8SYassine Oudjana }; 338da5daae8SYassine Oudjana 3399a9f5f9aSYassine Oudjana static struct clk_regmap_mux pwrcl_smux = { 34003e342dcSLoic Poulain .reg = PWRCL_REG_OFFSET + MUX_OFFSET, 34103e342dcSLoic Poulain .shift = 2, 34203e342dcSLoic Poulain .width = 2, 343fe8a5005SDmitry Baryshkov .parent_map = smux_parent_map, 34403e342dcSLoic Poulain .clkr.hw.init = &(struct clk_init_data) { 34503e342dcSLoic Poulain .name = "pwrcl_smux", 346da5daae8SYassine Oudjana .parent_data = pwrcl_smux_parents, 347da5daae8SYassine Oudjana .num_parents = ARRAY_SIZE(pwrcl_smux_parents), 3489a9f5f9aSYassine Oudjana .ops = &clk_regmap_mux_closest_ops, 34903e342dcSLoic Poulain .flags = CLK_SET_RATE_PARENT, 35003e342dcSLoic Poulain }, 35103e342dcSLoic Poulain }; 35203e342dcSLoic Poulain 3539a9f5f9aSYassine Oudjana static struct clk_regmap_mux perfcl_smux = { 35403e342dcSLoic Poulain .reg = PERFCL_REG_OFFSET + MUX_OFFSET, 35503e342dcSLoic Poulain .shift = 2, 35603e342dcSLoic Poulain .width = 2, 357fe8a5005SDmitry Baryshkov .parent_map = smux_parent_map, 35803e342dcSLoic Poulain .clkr.hw.init = &(struct clk_init_data) { 35903e342dcSLoic Poulain .name = "perfcl_smux", 360da5daae8SYassine Oudjana .parent_data = perfcl_smux_parents, 361da5daae8SYassine Oudjana .num_parents = ARRAY_SIZE(perfcl_smux_parents), 3629a9f5f9aSYassine Oudjana .ops = &clk_regmap_mux_closest_ops, 36303e342dcSLoic Poulain .flags = CLK_SET_RATE_PARENT, 36403e342dcSLoic Poulain }, 36503e342dcSLoic Poulain }; 36603e342dcSLoic Poulain 367da5daae8SYassine Oudjana static const struct clk_hw *pwrcl_pmux_parents[] = { 368da5daae8SYassine Oudjana [SMUX_INDEX] = &pwrcl_smux.clkr.hw, 369da5daae8SYassine Oudjana [PLL_INDEX] = &pwrcl_pll.clkr.hw, 370f1e3fcc4SDmitry Baryshkov [ACD_INDEX] = &pwrcl_pll_acd.hw, 371da5daae8SYassine Oudjana [ALT_INDEX] = &pwrcl_alt_pll.clkr.hw, 372da5daae8SYassine Oudjana }; 373da5daae8SYassine Oudjana 374da5daae8SYassine Oudjana static const struct clk_hw *perfcl_pmux_parents[] = { 375da5daae8SYassine Oudjana [SMUX_INDEX] = &perfcl_smux.clkr.hw, 376da5daae8SYassine Oudjana [PLL_INDEX] = &perfcl_pll.clkr.hw, 377f1e3fcc4SDmitry Baryshkov [ACD_INDEX] = &perfcl_pll_acd.hw, 378da5daae8SYassine Oudjana [ALT_INDEX] = &perfcl_alt_pll.clkr.hw, 379da5daae8SYassine Oudjana }; 380da5daae8SYassine Oudjana 3819a9f5f9aSYassine Oudjana static struct clk_cpu_8996_pmux pwrcl_pmux = { 38203e342dcSLoic Poulain .reg = PWRCL_REG_OFFSET + MUX_OFFSET, 38303e342dcSLoic Poulain .nb.notifier_call = cpu_clk_notifier_cb, 38403e342dcSLoic Poulain .clkr.hw.init = &(struct clk_init_data) { 38503e342dcSLoic Poulain .name = "pwrcl_pmux", 386da5daae8SYassine Oudjana .parent_hws = pwrcl_pmux_parents, 387da5daae8SYassine Oudjana .num_parents = ARRAY_SIZE(pwrcl_pmux_parents), 3889a9f5f9aSYassine Oudjana .ops = &clk_cpu_8996_pmux_ops, 38903e342dcSLoic Poulain /* CPU clock is critical and should never be gated */ 39003e342dcSLoic Poulain .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 39103e342dcSLoic Poulain }, 39203e342dcSLoic Poulain }; 39303e342dcSLoic Poulain 3949a9f5f9aSYassine Oudjana static struct clk_cpu_8996_pmux perfcl_pmux = { 39503e342dcSLoic Poulain .reg = PERFCL_REG_OFFSET + MUX_OFFSET, 39603e342dcSLoic Poulain .nb.notifier_call = cpu_clk_notifier_cb, 39703e342dcSLoic Poulain .clkr.hw.init = &(struct clk_init_data) { 39803e342dcSLoic Poulain .name = "perfcl_pmux", 399da5daae8SYassine Oudjana .parent_hws = perfcl_pmux_parents, 400da5daae8SYassine Oudjana .num_parents = ARRAY_SIZE(perfcl_pmux_parents), 4019a9f5f9aSYassine Oudjana .ops = &clk_cpu_8996_pmux_ops, 40203e342dcSLoic Poulain /* CPU clock is critical and should never be gated */ 40303e342dcSLoic Poulain .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 40403e342dcSLoic Poulain }, 40503e342dcSLoic Poulain }; 40603e342dcSLoic Poulain 40703e342dcSLoic Poulain static const struct regmap_config cpu_msm8996_regmap_config = { 40803e342dcSLoic Poulain .reg_bits = 32, 40903e342dcSLoic Poulain .reg_stride = 4, 41003e342dcSLoic Poulain .val_bits = 32, 41103e342dcSLoic Poulain .max_register = 0x80210, 41203e342dcSLoic Poulain .fast_io = true, 41303e342dcSLoic Poulain .val_format_endian = REGMAP_ENDIAN_LITTLE, 41403e342dcSLoic Poulain }; 41503e342dcSLoic Poulain 416f1e3fcc4SDmitry Baryshkov static struct clk_hw *cpu_msm8996_hw_clks[] = { 417f1e3fcc4SDmitry Baryshkov &pwrcl_pll_postdiv.hw, 418f1e3fcc4SDmitry Baryshkov &perfcl_pll_postdiv.hw, 419f1e3fcc4SDmitry Baryshkov &pwrcl_pll_acd.hw, 420f1e3fcc4SDmitry Baryshkov &perfcl_pll_acd.hw, 421f1e3fcc4SDmitry Baryshkov }; 422f1e3fcc4SDmitry Baryshkov 4238607fa16SWei Yongjun static struct clk_regmap *cpu_msm8996_clks[] = { 42403e342dcSLoic Poulain &pwrcl_pll.clkr, 425382139bfSYassine Oudjana &perfcl_pll.clkr, 42603e342dcSLoic Poulain &pwrcl_alt_pll.clkr, 427382139bfSYassine Oudjana &perfcl_alt_pll.clkr, 42803e342dcSLoic Poulain &pwrcl_smux.clkr, 429382139bfSYassine Oudjana &perfcl_smux.clkr, 43003e342dcSLoic Poulain &pwrcl_pmux.clkr, 431382139bfSYassine Oudjana &perfcl_pmux.clkr, 43203e342dcSLoic Poulain }; 43303e342dcSLoic Poulain 434fa0bc05fSDmitry Baryshkov static void qcom_cpu_clk_msm8996_acd_init(struct regmap *regmap); 435fa0bc05fSDmitry Baryshkov 43603e342dcSLoic Poulain static int qcom_cpu_clk_msm8996_register_clks(struct device *dev, 43703e342dcSLoic Poulain struct regmap *regmap) 43803e342dcSLoic Poulain { 43903e342dcSLoic Poulain int i, ret; 44003e342dcSLoic Poulain 4416fb03dd0SDmitry Baryshkov /* Select GPLL0 for 300MHz for both clusters */ 4426fb03dd0SDmitry Baryshkov regmap_write(regmap, PERFCL_REG_OFFSET + MUX_OFFSET, 0xc); 4436fb03dd0SDmitry Baryshkov regmap_write(regmap, PWRCL_REG_OFFSET + MUX_OFFSET, 0xc); 4446fb03dd0SDmitry Baryshkov 4456fb03dd0SDmitry Baryshkov /* Ensure write goes through before PLLs are reconfigured */ 4466fb03dd0SDmitry Baryshkov udelay(5); 4476fb03dd0SDmitry Baryshkov 448*9daaaaaaSDmitry Baryshkov /* Set the auto clock sel always-on source to GPLL0/2 (300MHz) */ 449*9daaaaaaSDmitry Baryshkov regmap_update_bits(regmap, PWRCL_REG_OFFSET + MUX_OFFSET, 450*9daaaaaaSDmitry Baryshkov MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK, 451*9daaaaaaSDmitry Baryshkov MUX_AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL); 452*9daaaaaaSDmitry Baryshkov regmap_update_bits(regmap, PERFCL_REG_OFFSET + MUX_OFFSET, 453*9daaaaaaSDmitry Baryshkov MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK, 454*9daaaaaaSDmitry Baryshkov MUX_AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL); 455*9daaaaaaSDmitry Baryshkov 45661dc1a73SDmitry Baryshkov clk_alpha_pll_configure(&pwrcl_pll, regmap, &hfpll_config); 45761dc1a73SDmitry Baryshkov clk_alpha_pll_configure(&perfcl_pll, regmap, &hfpll_config); 45861dc1a73SDmitry Baryshkov clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config); 45961dc1a73SDmitry Baryshkov clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config); 46061dc1a73SDmitry Baryshkov 4616fb03dd0SDmitry Baryshkov /* Wait for PLL(s) to lock */ 4626fb03dd0SDmitry Baryshkov udelay(50); 4636fb03dd0SDmitry Baryshkov 464*9daaaaaaSDmitry Baryshkov /* Enable auto clock selection for both clusters */ 465*9daaaaaaSDmitry Baryshkov regmap_update_bits(regmap, PWRCL_REG_OFFSET + CLK_CTL_OFFSET, 466*9daaaaaaSDmitry Baryshkov CLK_CTL_AUTO_CLK_SEL, CLK_CTL_AUTO_CLK_SEL); 467*9daaaaaaSDmitry Baryshkov regmap_update_bits(regmap, PERFCL_REG_OFFSET + CLK_CTL_OFFSET, 468*9daaaaaaSDmitry Baryshkov CLK_CTL_AUTO_CLK_SEL, CLK_CTL_AUTO_CLK_SEL); 469*9daaaaaaSDmitry Baryshkov 470*9daaaaaaSDmitry Baryshkov /* Ensure write goes through before muxes are switched */ 471*9daaaaaaSDmitry Baryshkov udelay(5); 472*9daaaaaaSDmitry Baryshkov 473fa0bc05fSDmitry Baryshkov qcom_cpu_clk_msm8996_acd_init(regmap); 474fa0bc05fSDmitry Baryshkov 475*9daaaaaaSDmitry Baryshkov /* Pulse swallower and soft-start settings */ 476*9daaaaaaSDmitry Baryshkov regmap_write(regmap, PWRCL_REG_OFFSET + PSCTL_OFFSET, 0x00030005); 477*9daaaaaaSDmitry Baryshkov regmap_write(regmap, PERFCL_REG_OFFSET + PSCTL_OFFSET, 0x00030005); 478*9daaaaaaSDmitry Baryshkov 4796fb03dd0SDmitry Baryshkov /* Switch clusters to use the ACD leg */ 480*9daaaaaaSDmitry Baryshkov regmap_write(regmap, PWRCL_REG_OFFSET + MUX_OFFSET, 0x32); 481*9daaaaaaSDmitry Baryshkov regmap_write(regmap, PERFCL_REG_OFFSET + MUX_OFFSET, 0x32); 4826fb03dd0SDmitry Baryshkov 483f1e3fcc4SDmitry Baryshkov for (i = 0; i < ARRAY_SIZE(cpu_msm8996_hw_clks); i++) { 484f1e3fcc4SDmitry Baryshkov ret = devm_clk_hw_register(dev, cpu_msm8996_hw_clks[i]); 485f1e3fcc4SDmitry Baryshkov if (ret) 486de37e021SYassine Oudjana return ret; 48703e342dcSLoic Poulain } 48803e342dcSLoic Poulain 48903e342dcSLoic Poulain for (i = 0; i < ARRAY_SIZE(cpu_msm8996_clks); i++) { 49003e342dcSLoic Poulain ret = devm_clk_register_regmap(dev, cpu_msm8996_clks[i]); 491de37e021SYassine Oudjana if (ret) 49203e342dcSLoic Poulain return ret; 49303e342dcSLoic Poulain } 49403e342dcSLoic Poulain 49503e342dcSLoic Poulain /* Enable alt PLLs */ 49603e342dcSLoic Poulain clk_prepare_enable(pwrcl_alt_pll.clkr.hw.clk); 49703e342dcSLoic Poulain clk_prepare_enable(perfcl_alt_pll.clkr.hw.clk); 49803e342dcSLoic Poulain 499a808c784SDmitry Baryshkov devm_clk_notifier_register(dev, pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb); 500a808c784SDmitry Baryshkov devm_clk_notifier_register(dev, perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb); 50103e342dcSLoic Poulain 50203e342dcSLoic Poulain return ret; 50303e342dcSLoic Poulain } 50403e342dcSLoic Poulain 505495bc5a7SDmitry Baryshkov #define CPU_CLUSTER_AFFINITY_MASK 0xf00 506495bc5a7SDmitry Baryshkov #define PWRCL_AFFINITY_MASK 0x000 507495bc5a7SDmitry Baryshkov #define PERFCL_AFFINITY_MASK 0x100 50803e342dcSLoic Poulain 50903e342dcSLoic Poulain #define L2ACDCR_REG 0x580ULL 51003e342dcSLoic Poulain #define L2ACDTD_REG 0x581ULL 51103e342dcSLoic Poulain #define L2ACDDVMRC_REG 0x584ULL 51203e342dcSLoic Poulain #define L2ACDSSCR_REG 0x589ULL 51303e342dcSLoic Poulain 51403e342dcSLoic Poulain static DEFINE_SPINLOCK(qcom_clk_acd_lock); 51503e342dcSLoic Poulain 516fa0bc05fSDmitry Baryshkov static void qcom_cpu_clk_msm8996_acd_init(struct regmap *regmap) 51703e342dcSLoic Poulain { 51803e342dcSLoic Poulain u64 hwid; 51972537606SDmitry Baryshkov u32 val; 52003e342dcSLoic Poulain unsigned long flags; 52103e342dcSLoic Poulain 52203e342dcSLoic Poulain spin_lock_irqsave(&qcom_clk_acd_lock, flags); 52303e342dcSLoic Poulain 52472537606SDmitry Baryshkov val = kryo_l2_get_indirect_reg(L2ACDTD_REG); 52572537606SDmitry Baryshkov if (val == 0x00006a11) 52672537606SDmitry Baryshkov goto out; 52772537606SDmitry Baryshkov 52803e342dcSLoic Poulain kryo_l2_set_indirect_reg(L2ACDTD_REG, 0x00006a11); 52903e342dcSLoic Poulain kryo_l2_set_indirect_reg(L2ACDDVMRC_REG, 0x000e0f0f); 53003e342dcSLoic Poulain kryo_l2_set_indirect_reg(L2ACDSSCR_REG, 0x00000601); 53103e342dcSLoic Poulain 53203e342dcSLoic Poulain kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd); 53303e342dcSLoic Poulain 534495bc5a7SDmitry Baryshkov hwid = read_cpuid_mpidr(); 535495bc5a7SDmitry Baryshkov if ((hwid & CPU_CLUSTER_AFFINITY_MASK) == PWRCL_AFFINITY_MASK) 536495bc5a7SDmitry Baryshkov regmap_write(regmap, PWRCL_REG_OFFSET + SSSCTL_OFFSET, 0xf); 537495bc5a7SDmitry Baryshkov else 538fa0bc05fSDmitry Baryshkov regmap_write(regmap, PERFCL_REG_OFFSET + SSSCTL_OFFSET, 0xf); 53903e342dcSLoic Poulain 54072537606SDmitry Baryshkov out: 54103e342dcSLoic Poulain spin_unlock_irqrestore(&qcom_clk_acd_lock, flags); 54203e342dcSLoic Poulain } 54303e342dcSLoic Poulain 54403e342dcSLoic Poulain static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, 54503e342dcSLoic Poulain void *data) 54603e342dcSLoic Poulain { 5479a9f5f9aSYassine Oudjana struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_nb(nb); 54803e342dcSLoic Poulain struct clk_notifier_data *cnd = data; 54903e342dcSLoic Poulain 55003e342dcSLoic Poulain switch (event) { 55103e342dcSLoic Poulain case PRE_RATE_CHANGE: 552fa0bc05fSDmitry Baryshkov qcom_cpu_clk_msm8996_acd_init(cpuclk->clkr.regmap); 553b3b274bcSDmitry Baryshkov 554b3b274bcSDmitry Baryshkov /* 555b3b274bcSDmitry Baryshkov * Avoid overvolting. clk_core_set_rate_nolock() walks from top 556b3b274bcSDmitry Baryshkov * to bottom, so it will change the rate of the PLL before 557b3b274bcSDmitry Baryshkov * chaging the parent of PMUX. This can result in pmux getting 558b3b274bcSDmitry Baryshkov * clocked twice the expected rate. 559b3b274bcSDmitry Baryshkov * 560b3b274bcSDmitry Baryshkov * Manually switch to PLL/2 here. 561b3b274bcSDmitry Baryshkov */ 562b3b274bcSDmitry Baryshkov if (cnd->new_rate < DIV_2_THRESHOLD && 563b3b274bcSDmitry Baryshkov cnd->old_rate > DIV_2_THRESHOLD) 564b3b274bcSDmitry Baryshkov clk_cpu_8996_pmux_set_parent(&cpuclk->clkr.hw, SMUX_INDEX); 565b3b274bcSDmitry Baryshkov 56603e342dcSLoic Poulain break; 567b3b274bcSDmitry Baryshkov case ABORT_RATE_CHANGE: 568b3b274bcSDmitry Baryshkov /* Revert manual change */ 569b3b274bcSDmitry Baryshkov if (cnd->new_rate < DIV_2_THRESHOLD && 570b3b274bcSDmitry Baryshkov cnd->old_rate > DIV_2_THRESHOLD) 571b3b274bcSDmitry Baryshkov clk_cpu_8996_pmux_set_parent(&cpuclk->clkr.hw, ACD_INDEX); 57203e342dcSLoic Poulain break; 57303e342dcSLoic Poulain default: 57403e342dcSLoic Poulain break; 57503e342dcSLoic Poulain } 57603e342dcSLoic Poulain 577b3b274bcSDmitry Baryshkov return NOTIFY_OK; 57803e342dcSLoic Poulain }; 57903e342dcSLoic Poulain 58003e342dcSLoic Poulain static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev) 58103e342dcSLoic Poulain { 582fa0bc05fSDmitry Baryshkov static void __iomem *base; 58303e342dcSLoic Poulain struct regmap *regmap; 58403e342dcSLoic Poulain struct clk_hw_onecell_data *data; 58503e342dcSLoic Poulain struct device *dev = &pdev->dev; 58603e342dcSLoic Poulain int ret; 58703e342dcSLoic Poulain 58803e342dcSLoic Poulain data = devm_kzalloc(dev, struct_size(data, hws, 2), GFP_KERNEL); 58903e342dcSLoic Poulain if (!data) 59003e342dcSLoic Poulain return -ENOMEM; 59103e342dcSLoic Poulain 59203e342dcSLoic Poulain base = devm_platform_ioremap_resource(pdev, 0); 59303e342dcSLoic Poulain if (IS_ERR(base)) 59403e342dcSLoic Poulain return PTR_ERR(base); 59503e342dcSLoic Poulain 59603e342dcSLoic Poulain regmap = devm_regmap_init_mmio(dev, base, &cpu_msm8996_regmap_config); 59703e342dcSLoic Poulain if (IS_ERR(regmap)) 59803e342dcSLoic Poulain return PTR_ERR(regmap); 59903e342dcSLoic Poulain 60003e342dcSLoic Poulain ret = qcom_cpu_clk_msm8996_register_clks(dev, regmap); 60103e342dcSLoic Poulain if (ret) 60203e342dcSLoic Poulain return ret; 60303e342dcSLoic Poulain 60403e342dcSLoic Poulain data->hws[0] = &pwrcl_pmux.clkr.hw; 60503e342dcSLoic Poulain data->hws[1] = &perfcl_pmux.clkr.hw; 60603e342dcSLoic Poulain data->num = 2; 60703e342dcSLoic Poulain 60803e342dcSLoic Poulain return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data); 60903e342dcSLoic Poulain } 61003e342dcSLoic Poulain 61103e342dcSLoic Poulain static const struct of_device_id qcom_cpu_clk_msm8996_match_table[] = { 61203e342dcSLoic Poulain { .compatible = "qcom,msm8996-apcc" }, 61303e342dcSLoic Poulain {} 61403e342dcSLoic Poulain }; 61503e342dcSLoic Poulain MODULE_DEVICE_TABLE(of, qcom_cpu_clk_msm8996_match_table); 61603e342dcSLoic Poulain 61703e342dcSLoic Poulain static struct platform_driver qcom_cpu_clk_msm8996_driver = { 61803e342dcSLoic Poulain .probe = qcom_cpu_clk_msm8996_driver_probe, 61903e342dcSLoic Poulain .driver = { 62003e342dcSLoic Poulain .name = "qcom-msm8996-apcc", 62103e342dcSLoic Poulain .of_match_table = qcom_cpu_clk_msm8996_match_table, 62203e342dcSLoic Poulain }, 62303e342dcSLoic Poulain }; 62403e342dcSLoic Poulain module_platform_driver(qcom_cpu_clk_msm8996_driver); 62503e342dcSLoic Poulain 62603e342dcSLoic Poulain MODULE_DESCRIPTION("QCOM MSM8996 CPU Clock Driver"); 62703e342dcSLoic Poulain MODULE_LICENSE("GPL v2"); 628