162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2020, The Linux Foundation. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/* 762306a36Sopenharmony_ci * Each of the CPU clusters (Power and Perf) on msm8996 are 862306a36Sopenharmony_ci * clocked via 2 PLLs, a primary and alternate. There are also 962306a36Sopenharmony_ci * 2 Mux'es, a primary and secondary all connected together 1062306a36Sopenharmony_ci * as shown below 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * +-------+ 1362306a36Sopenharmony_ci * XO | | 1462306a36Sopenharmony_ci * +------------------>0 | 1562306a36Sopenharmony_ci * SYS_APCS_AUX | | 1662306a36Sopenharmony_ci * +------------------>3 | 1762306a36Sopenharmony_ci * | | 1862306a36Sopenharmony_ci * PLL/2 | SMUX +----+ 1962306a36Sopenharmony_ci * +------->1 | | 2062306a36Sopenharmony_ci * | | | | 2162306a36Sopenharmony_ci * | +-------+ | +-------+ 2262306a36Sopenharmony_ci * | +---->0 | 2362306a36Sopenharmony_ci * | | | 2462306a36Sopenharmony_ci * +---------------+ | +----------->1 | CPU clk 2562306a36Sopenharmony_ci * |Primary PLL +----+ PLL_EARLY | | +------> 2662306a36Sopenharmony_ci * | +------+-----------+ +------>2 PMUX | 2762306a36Sopenharmony_ci * +---------------+ | | | | 2862306a36Sopenharmony_ci * | +------+ | +-->3 | 2962306a36Sopenharmony_ci * +--^+ ACD +-----+ | +-------+ 3062306a36Sopenharmony_ci * +---------------+ +------+ | 3162306a36Sopenharmony_ci * |Alt PLL | | 3262306a36Sopenharmony_ci * | +---------------------------+ 3362306a36Sopenharmony_ci * +---------------+ PLL_EARLY 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * The primary PLL is what drives the CPU clk, except for times 3662306a36Sopenharmony_ci * when we are reprogramming the PLL itself (for rate changes) when 3762306a36Sopenharmony_ci * we temporarily switch to an alternate PLL. 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * The primary PLL operates on a single VCO range, between 600MHz 4062306a36Sopenharmony_ci * and 3GHz. However the CPUs do support OPPs with frequencies 4162306a36Sopenharmony_ci * between 300MHz and 600MHz. In order to support running the CPUs 4262306a36Sopenharmony_ci * at those frequencies we end up having to lock the PLL at twice 4362306a36Sopenharmony_ci * the rate and drive the CPU clk via the PLL/2 output and SMUX. 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * So for frequencies above 600MHz we follow the following path 4662306a36Sopenharmony_ci * Primary PLL --> PLL_EARLY --> PMUX(1) --> CPU clk 4762306a36Sopenharmony_ci * and for frequencies between 300MHz and 600MHz we follow 4862306a36Sopenharmony_ci * Primary PLL --> PLL/2 --> SMUX(1) --> PMUX(0) --> CPU clk 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * ACD stands for Adaptive Clock Distribution and is used to 5162306a36Sopenharmony_ci * detect voltage droops. 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#include <linux/bitfield.h> 5562306a36Sopenharmony_ci#include <linux/clk.h> 5662306a36Sopenharmony_ci#include <linux/clk-provider.h> 5762306a36Sopenharmony_ci#include <linux/io.h> 5862306a36Sopenharmony_ci#include <linux/module.h> 5962306a36Sopenharmony_ci#include <linux/platform_device.h> 6062306a36Sopenharmony_ci#include <linux/regmap.h> 6162306a36Sopenharmony_ci#include <soc/qcom/kryo-l2-accessors.h> 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#include <asm/cputype.h> 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#include "clk-alpha-pll.h" 6662306a36Sopenharmony_ci#include "clk-regmap.h" 6762306a36Sopenharmony_ci#include "clk-regmap-mux.h" 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cienum _pmux_input { 7062306a36Sopenharmony_ci SMUX_INDEX = 0, 7162306a36Sopenharmony_ci PLL_INDEX, 7262306a36Sopenharmony_ci ACD_INDEX, 7362306a36Sopenharmony_ci ALT_INDEX, 7462306a36Sopenharmony_ci NUM_OF_PMUX_INPUTS 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define DIV_2_THRESHOLD 600000000 7862306a36Sopenharmony_ci#define PWRCL_REG_OFFSET 0x0 7962306a36Sopenharmony_ci#define PERFCL_REG_OFFSET 0x80000 8062306a36Sopenharmony_ci#define MUX_OFFSET 0x40 8162306a36Sopenharmony_ci#define CLK_CTL_OFFSET 0x44 8262306a36Sopenharmony_ci#define CLK_CTL_AUTO_CLK_SEL BIT(8) 8362306a36Sopenharmony_ci#define ALT_PLL_OFFSET 0x100 8462306a36Sopenharmony_ci#define SSSCTL_OFFSET 0x160 8562306a36Sopenharmony_ci#define PSCTL_OFFSET 0x164 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define PMUX_MASK 0x3 8862306a36Sopenharmony_ci#define MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK GENMASK(5, 4) 8962306a36Sopenharmony_ci#define MUX_AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL \ 9062306a36Sopenharmony_ci FIELD_PREP(MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK, 0x03) 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic const u8 prim_pll_regs[PLL_OFF_MAX_REGS] = { 9362306a36Sopenharmony_ci [PLL_OFF_L_VAL] = 0x04, 9462306a36Sopenharmony_ci [PLL_OFF_ALPHA_VAL] = 0x08, 9562306a36Sopenharmony_ci [PLL_OFF_USER_CTL] = 0x10, 9662306a36Sopenharmony_ci [PLL_OFF_CONFIG_CTL] = 0x18, 9762306a36Sopenharmony_ci [PLL_OFF_CONFIG_CTL_U] = 0x1c, 9862306a36Sopenharmony_ci [PLL_OFF_TEST_CTL] = 0x20, 9962306a36Sopenharmony_ci [PLL_OFF_TEST_CTL_U] = 0x24, 10062306a36Sopenharmony_ci [PLL_OFF_STATUS] = 0x28, 10162306a36Sopenharmony_ci}; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic const u8 alt_pll_regs[PLL_OFF_MAX_REGS] = { 10462306a36Sopenharmony_ci [PLL_OFF_L_VAL] = 0x04, 10562306a36Sopenharmony_ci [PLL_OFF_ALPHA_VAL] = 0x08, 10662306a36Sopenharmony_ci [PLL_OFF_USER_CTL] = 0x10, 10762306a36Sopenharmony_ci [PLL_OFF_CONFIG_CTL] = 0x18, 10862306a36Sopenharmony_ci [PLL_OFF_TEST_CTL] = 0x20, 10962306a36Sopenharmony_ci [PLL_OFF_STATUS] = 0x28, 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* PLLs */ 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic const struct alpha_pll_config hfpll_config = { 11562306a36Sopenharmony_ci .l = 54, 11662306a36Sopenharmony_ci .config_ctl_val = 0x200d4828, 11762306a36Sopenharmony_ci .config_ctl_hi_val = 0x006, 11862306a36Sopenharmony_ci .test_ctl_val = 0x1c000000, 11962306a36Sopenharmony_ci .test_ctl_hi_val = 0x00004000, 12062306a36Sopenharmony_ci .pre_div_mask = BIT(12), 12162306a36Sopenharmony_ci .post_div_mask = 0x3 << 8, 12262306a36Sopenharmony_ci .post_div_val = 0x1 << 8, 12362306a36Sopenharmony_ci .main_output_mask = BIT(0), 12462306a36Sopenharmony_ci .early_output_mask = BIT(3), 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic const struct clk_parent_data pll_parent[] = { 12862306a36Sopenharmony_ci { .fw_name = "xo" }, 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic struct clk_alpha_pll pwrcl_pll = { 13262306a36Sopenharmony_ci .offset = PWRCL_REG_OFFSET, 13362306a36Sopenharmony_ci .regs = prim_pll_regs, 13462306a36Sopenharmony_ci .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, 13562306a36Sopenharmony_ci .clkr.hw.init = &(struct clk_init_data){ 13662306a36Sopenharmony_ci .name = "pwrcl_pll", 13762306a36Sopenharmony_ci .parent_data = pll_parent, 13862306a36Sopenharmony_ci .num_parents = ARRAY_SIZE(pll_parent), 13962306a36Sopenharmony_ci .ops = &clk_alpha_pll_hwfsm_ops, 14062306a36Sopenharmony_ci }, 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic struct clk_alpha_pll perfcl_pll = { 14462306a36Sopenharmony_ci .offset = PERFCL_REG_OFFSET, 14562306a36Sopenharmony_ci .regs = prim_pll_regs, 14662306a36Sopenharmony_ci .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, 14762306a36Sopenharmony_ci .clkr.hw.init = &(struct clk_init_data){ 14862306a36Sopenharmony_ci .name = "perfcl_pll", 14962306a36Sopenharmony_ci .parent_data = pll_parent, 15062306a36Sopenharmony_ci .num_parents = ARRAY_SIZE(pll_parent), 15162306a36Sopenharmony_ci .ops = &clk_alpha_pll_hwfsm_ops, 15262306a36Sopenharmony_ci }, 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic struct clk_fixed_factor pwrcl_pll_postdiv = { 15662306a36Sopenharmony_ci .mult = 1, 15762306a36Sopenharmony_ci .div = 2, 15862306a36Sopenharmony_ci .hw.init = &(struct clk_init_data){ 15962306a36Sopenharmony_ci .name = "pwrcl_pll_postdiv", 16062306a36Sopenharmony_ci .parent_data = &(const struct clk_parent_data){ 16162306a36Sopenharmony_ci .hw = &pwrcl_pll.clkr.hw 16262306a36Sopenharmony_ci }, 16362306a36Sopenharmony_ci .num_parents = 1, 16462306a36Sopenharmony_ci .ops = &clk_fixed_factor_ops, 16562306a36Sopenharmony_ci .flags = CLK_SET_RATE_PARENT, 16662306a36Sopenharmony_ci }, 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic struct clk_fixed_factor perfcl_pll_postdiv = { 17062306a36Sopenharmony_ci .mult = 1, 17162306a36Sopenharmony_ci .div = 2, 17262306a36Sopenharmony_ci .hw.init = &(struct clk_init_data){ 17362306a36Sopenharmony_ci .name = "perfcl_pll_postdiv", 17462306a36Sopenharmony_ci .parent_data = &(const struct clk_parent_data){ 17562306a36Sopenharmony_ci .hw = &perfcl_pll.clkr.hw 17662306a36Sopenharmony_ci }, 17762306a36Sopenharmony_ci .num_parents = 1, 17862306a36Sopenharmony_ci .ops = &clk_fixed_factor_ops, 17962306a36Sopenharmony_ci .flags = CLK_SET_RATE_PARENT, 18062306a36Sopenharmony_ci }, 18162306a36Sopenharmony_ci}; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic struct clk_fixed_factor perfcl_pll_acd = { 18462306a36Sopenharmony_ci .mult = 1, 18562306a36Sopenharmony_ci .div = 1, 18662306a36Sopenharmony_ci .hw.init = &(struct clk_init_data){ 18762306a36Sopenharmony_ci .name = "perfcl_pll_acd", 18862306a36Sopenharmony_ci .parent_data = &(const struct clk_parent_data){ 18962306a36Sopenharmony_ci .hw = &perfcl_pll.clkr.hw 19062306a36Sopenharmony_ci }, 19162306a36Sopenharmony_ci .num_parents = 1, 19262306a36Sopenharmony_ci .ops = &clk_fixed_factor_ops, 19362306a36Sopenharmony_ci .flags = CLK_SET_RATE_PARENT, 19462306a36Sopenharmony_ci }, 19562306a36Sopenharmony_ci}; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic struct clk_fixed_factor pwrcl_pll_acd = { 19862306a36Sopenharmony_ci .mult = 1, 19962306a36Sopenharmony_ci .div = 1, 20062306a36Sopenharmony_ci .hw.init = &(struct clk_init_data){ 20162306a36Sopenharmony_ci .name = "pwrcl_pll_acd", 20262306a36Sopenharmony_ci .parent_data = &(const struct clk_parent_data){ 20362306a36Sopenharmony_ci .hw = &pwrcl_pll.clkr.hw 20462306a36Sopenharmony_ci }, 20562306a36Sopenharmony_ci .num_parents = 1, 20662306a36Sopenharmony_ci .ops = &clk_fixed_factor_ops, 20762306a36Sopenharmony_ci .flags = CLK_SET_RATE_PARENT, 20862306a36Sopenharmony_ci }, 20962306a36Sopenharmony_ci}; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic const struct pll_vco alt_pll_vco_modes[] = { 21262306a36Sopenharmony_ci VCO(3, 250000000, 500000000), 21362306a36Sopenharmony_ci VCO(2, 500000000, 750000000), 21462306a36Sopenharmony_ci VCO(1, 750000000, 1000000000), 21562306a36Sopenharmony_ci VCO(0, 1000000000, 2150400000), 21662306a36Sopenharmony_ci}; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic const struct alpha_pll_config altpll_config = { 21962306a36Sopenharmony_ci .l = 16, 22062306a36Sopenharmony_ci .vco_val = 0x3 << 20, 22162306a36Sopenharmony_ci .vco_mask = 0x3 << 20, 22262306a36Sopenharmony_ci .config_ctl_val = 0x4001051b, 22362306a36Sopenharmony_ci .post_div_mask = 0x3 << 8, 22462306a36Sopenharmony_ci .post_div_val = 0x1 << 8, 22562306a36Sopenharmony_ci .main_output_mask = BIT(0), 22662306a36Sopenharmony_ci .early_output_mask = BIT(3), 22762306a36Sopenharmony_ci}; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic struct clk_alpha_pll pwrcl_alt_pll = { 23062306a36Sopenharmony_ci .offset = PWRCL_REG_OFFSET + ALT_PLL_OFFSET, 23162306a36Sopenharmony_ci .regs = alt_pll_regs, 23262306a36Sopenharmony_ci .vco_table = alt_pll_vco_modes, 23362306a36Sopenharmony_ci .num_vco = ARRAY_SIZE(alt_pll_vco_modes), 23462306a36Sopenharmony_ci .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, 23562306a36Sopenharmony_ci .clkr.hw.init = &(struct clk_init_data) { 23662306a36Sopenharmony_ci .name = "pwrcl_alt_pll", 23762306a36Sopenharmony_ci .parent_data = pll_parent, 23862306a36Sopenharmony_ci .num_parents = ARRAY_SIZE(pll_parent), 23962306a36Sopenharmony_ci .ops = &clk_alpha_pll_hwfsm_ops, 24062306a36Sopenharmony_ci }, 24162306a36Sopenharmony_ci}; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic struct clk_alpha_pll perfcl_alt_pll = { 24462306a36Sopenharmony_ci .offset = PERFCL_REG_OFFSET + ALT_PLL_OFFSET, 24562306a36Sopenharmony_ci .regs = alt_pll_regs, 24662306a36Sopenharmony_ci .vco_table = alt_pll_vco_modes, 24762306a36Sopenharmony_ci .num_vco = ARRAY_SIZE(alt_pll_vco_modes), 24862306a36Sopenharmony_ci .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, 24962306a36Sopenharmony_ci .clkr.hw.init = &(struct clk_init_data) { 25062306a36Sopenharmony_ci .name = "perfcl_alt_pll", 25162306a36Sopenharmony_ci .parent_data = pll_parent, 25262306a36Sopenharmony_ci .num_parents = ARRAY_SIZE(pll_parent), 25362306a36Sopenharmony_ci .ops = &clk_alpha_pll_hwfsm_ops, 25462306a36Sopenharmony_ci }, 25562306a36Sopenharmony_ci}; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistruct clk_cpu_8996_pmux { 25862306a36Sopenharmony_ci u32 reg; 25962306a36Sopenharmony_ci struct notifier_block nb; 26062306a36Sopenharmony_ci struct clk_regmap clkr; 26162306a36Sopenharmony_ci}; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, 26462306a36Sopenharmony_ci void *data); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci#define to_clk_cpu_8996_pmux_nb(_nb) \ 26762306a36Sopenharmony_ci container_of(_nb, struct clk_cpu_8996_pmux, nb) 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic inline struct clk_cpu_8996_pmux *to_clk_cpu_8996_pmux_hw(struct clk_hw *hw) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci return container_of(to_clk_regmap(hw), struct clk_cpu_8996_pmux, clkr); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic u8 clk_cpu_8996_pmux_get_parent(struct clk_hw *hw) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct clk_regmap *clkr = to_clk_regmap(hw); 27762306a36Sopenharmony_ci struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_hw(hw); 27862306a36Sopenharmony_ci u32 val; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci regmap_read(clkr->regmap, cpuclk->reg, &val); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return FIELD_GET(PMUX_MASK, val); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int clk_cpu_8996_pmux_set_parent(struct clk_hw *hw, u8 index) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct clk_regmap *clkr = to_clk_regmap(hw); 28862306a36Sopenharmony_ci struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_hw(hw); 28962306a36Sopenharmony_ci u32 val; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci val = FIELD_PREP(PMUX_MASK, index); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return regmap_update_bits(clkr->regmap, cpuclk->reg, PMUX_MASK, val); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic int clk_cpu_8996_pmux_determine_rate(struct clk_hw *hw, 29762306a36Sopenharmony_ci struct clk_rate_request *req) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct clk_hw *parent; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (req->rate < (DIV_2_THRESHOLD / 2)) 30262306a36Sopenharmony_ci return -EINVAL; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (req->rate < DIV_2_THRESHOLD) 30562306a36Sopenharmony_ci parent = clk_hw_get_parent_by_index(hw, SMUX_INDEX); 30662306a36Sopenharmony_ci else 30762306a36Sopenharmony_ci parent = clk_hw_get_parent_by_index(hw, ACD_INDEX); 30862306a36Sopenharmony_ci if (!parent) 30962306a36Sopenharmony_ci return -EINVAL; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci req->best_parent_rate = clk_hw_round_rate(parent, req->rate); 31262306a36Sopenharmony_ci req->best_parent_hw = parent; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return 0; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic const struct clk_ops clk_cpu_8996_pmux_ops = { 31862306a36Sopenharmony_ci .set_parent = clk_cpu_8996_pmux_set_parent, 31962306a36Sopenharmony_ci .get_parent = clk_cpu_8996_pmux_get_parent, 32062306a36Sopenharmony_ci .determine_rate = clk_cpu_8996_pmux_determine_rate, 32162306a36Sopenharmony_ci}; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic const struct parent_map smux_parent_map[] = { 32462306a36Sopenharmony_ci { .cfg = 0, }, /* xo */ 32562306a36Sopenharmony_ci { .cfg = 1, }, /* pll */ 32662306a36Sopenharmony_ci { .cfg = 3, }, /* sys_apcs_aux */ 32762306a36Sopenharmony_ci}; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic const struct clk_parent_data pwrcl_smux_parents[] = { 33062306a36Sopenharmony_ci { .fw_name = "xo" }, 33162306a36Sopenharmony_ci { .hw = &pwrcl_pll_postdiv.hw }, 33262306a36Sopenharmony_ci { .fw_name = "sys_apcs_aux" }, 33362306a36Sopenharmony_ci}; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic const struct clk_parent_data perfcl_smux_parents[] = { 33662306a36Sopenharmony_ci { .fw_name = "xo" }, 33762306a36Sopenharmony_ci { .hw = &perfcl_pll_postdiv.hw }, 33862306a36Sopenharmony_ci { .fw_name = "sys_apcs_aux" }, 33962306a36Sopenharmony_ci}; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic struct clk_regmap_mux pwrcl_smux = { 34262306a36Sopenharmony_ci .reg = PWRCL_REG_OFFSET + MUX_OFFSET, 34362306a36Sopenharmony_ci .shift = 2, 34462306a36Sopenharmony_ci .width = 2, 34562306a36Sopenharmony_ci .parent_map = smux_parent_map, 34662306a36Sopenharmony_ci .clkr.hw.init = &(struct clk_init_data) { 34762306a36Sopenharmony_ci .name = "pwrcl_smux", 34862306a36Sopenharmony_ci .parent_data = pwrcl_smux_parents, 34962306a36Sopenharmony_ci .num_parents = ARRAY_SIZE(pwrcl_smux_parents), 35062306a36Sopenharmony_ci .ops = &clk_regmap_mux_closest_ops, 35162306a36Sopenharmony_ci .flags = CLK_SET_RATE_PARENT, 35262306a36Sopenharmony_ci }, 35362306a36Sopenharmony_ci}; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic struct clk_regmap_mux perfcl_smux = { 35662306a36Sopenharmony_ci .reg = PERFCL_REG_OFFSET + MUX_OFFSET, 35762306a36Sopenharmony_ci .shift = 2, 35862306a36Sopenharmony_ci .width = 2, 35962306a36Sopenharmony_ci .parent_map = smux_parent_map, 36062306a36Sopenharmony_ci .clkr.hw.init = &(struct clk_init_data) { 36162306a36Sopenharmony_ci .name = "perfcl_smux", 36262306a36Sopenharmony_ci .parent_data = perfcl_smux_parents, 36362306a36Sopenharmony_ci .num_parents = ARRAY_SIZE(perfcl_smux_parents), 36462306a36Sopenharmony_ci .ops = &clk_regmap_mux_closest_ops, 36562306a36Sopenharmony_ci .flags = CLK_SET_RATE_PARENT, 36662306a36Sopenharmony_ci }, 36762306a36Sopenharmony_ci}; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic const struct clk_hw *pwrcl_pmux_parents[] = { 37062306a36Sopenharmony_ci [SMUX_INDEX] = &pwrcl_smux.clkr.hw, 37162306a36Sopenharmony_ci [PLL_INDEX] = &pwrcl_pll.clkr.hw, 37262306a36Sopenharmony_ci [ACD_INDEX] = &pwrcl_pll_acd.hw, 37362306a36Sopenharmony_ci [ALT_INDEX] = &pwrcl_alt_pll.clkr.hw, 37462306a36Sopenharmony_ci}; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic const struct clk_hw *perfcl_pmux_parents[] = { 37762306a36Sopenharmony_ci [SMUX_INDEX] = &perfcl_smux.clkr.hw, 37862306a36Sopenharmony_ci [PLL_INDEX] = &perfcl_pll.clkr.hw, 37962306a36Sopenharmony_ci [ACD_INDEX] = &perfcl_pll_acd.hw, 38062306a36Sopenharmony_ci [ALT_INDEX] = &perfcl_alt_pll.clkr.hw, 38162306a36Sopenharmony_ci}; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic struct clk_cpu_8996_pmux pwrcl_pmux = { 38462306a36Sopenharmony_ci .reg = PWRCL_REG_OFFSET + MUX_OFFSET, 38562306a36Sopenharmony_ci .nb.notifier_call = cpu_clk_notifier_cb, 38662306a36Sopenharmony_ci .clkr.hw.init = &(struct clk_init_data) { 38762306a36Sopenharmony_ci .name = "pwrcl_pmux", 38862306a36Sopenharmony_ci .parent_hws = pwrcl_pmux_parents, 38962306a36Sopenharmony_ci .num_parents = ARRAY_SIZE(pwrcl_pmux_parents), 39062306a36Sopenharmony_ci .ops = &clk_cpu_8996_pmux_ops, 39162306a36Sopenharmony_ci /* CPU clock is critical and should never be gated */ 39262306a36Sopenharmony_ci .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 39362306a36Sopenharmony_ci }, 39462306a36Sopenharmony_ci}; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic struct clk_cpu_8996_pmux perfcl_pmux = { 39762306a36Sopenharmony_ci .reg = PERFCL_REG_OFFSET + MUX_OFFSET, 39862306a36Sopenharmony_ci .nb.notifier_call = cpu_clk_notifier_cb, 39962306a36Sopenharmony_ci .clkr.hw.init = &(struct clk_init_data) { 40062306a36Sopenharmony_ci .name = "perfcl_pmux", 40162306a36Sopenharmony_ci .parent_hws = perfcl_pmux_parents, 40262306a36Sopenharmony_ci .num_parents = ARRAY_SIZE(perfcl_pmux_parents), 40362306a36Sopenharmony_ci .ops = &clk_cpu_8996_pmux_ops, 40462306a36Sopenharmony_ci /* CPU clock is critical and should never be gated */ 40562306a36Sopenharmony_ci .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 40662306a36Sopenharmony_ci }, 40762306a36Sopenharmony_ci}; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic const struct regmap_config cpu_msm8996_regmap_config = { 41062306a36Sopenharmony_ci .reg_bits = 32, 41162306a36Sopenharmony_ci .reg_stride = 4, 41262306a36Sopenharmony_ci .val_bits = 32, 41362306a36Sopenharmony_ci .max_register = 0x80210, 41462306a36Sopenharmony_ci .fast_io = true, 41562306a36Sopenharmony_ci .val_format_endian = REGMAP_ENDIAN_LITTLE, 41662306a36Sopenharmony_ci}; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic struct clk_hw *cpu_msm8996_hw_clks[] = { 41962306a36Sopenharmony_ci &pwrcl_pll_postdiv.hw, 42062306a36Sopenharmony_ci &perfcl_pll_postdiv.hw, 42162306a36Sopenharmony_ci &pwrcl_pll_acd.hw, 42262306a36Sopenharmony_ci &perfcl_pll_acd.hw, 42362306a36Sopenharmony_ci}; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic struct clk_regmap *cpu_msm8996_clks[] = { 42662306a36Sopenharmony_ci &pwrcl_pll.clkr, 42762306a36Sopenharmony_ci &perfcl_pll.clkr, 42862306a36Sopenharmony_ci &pwrcl_alt_pll.clkr, 42962306a36Sopenharmony_ci &perfcl_alt_pll.clkr, 43062306a36Sopenharmony_ci &pwrcl_smux.clkr, 43162306a36Sopenharmony_ci &perfcl_smux.clkr, 43262306a36Sopenharmony_ci &pwrcl_pmux.clkr, 43362306a36Sopenharmony_ci &perfcl_pmux.clkr, 43462306a36Sopenharmony_ci}; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic void qcom_cpu_clk_msm8996_acd_init(struct regmap *regmap); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic int qcom_cpu_clk_msm8996_register_clks(struct device *dev, 43962306a36Sopenharmony_ci struct regmap *regmap) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci int i, ret; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* Select GPLL0 for 300MHz for both clusters */ 44462306a36Sopenharmony_ci regmap_write(regmap, PERFCL_REG_OFFSET + MUX_OFFSET, 0xc); 44562306a36Sopenharmony_ci regmap_write(regmap, PWRCL_REG_OFFSET + MUX_OFFSET, 0xc); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* Ensure write goes through before PLLs are reconfigured */ 44862306a36Sopenharmony_ci udelay(5); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* Set the auto clock sel always-on source to GPLL0/2 (300MHz) */ 45162306a36Sopenharmony_ci regmap_update_bits(regmap, PWRCL_REG_OFFSET + MUX_OFFSET, 45262306a36Sopenharmony_ci MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK, 45362306a36Sopenharmony_ci MUX_AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL); 45462306a36Sopenharmony_ci regmap_update_bits(regmap, PERFCL_REG_OFFSET + MUX_OFFSET, 45562306a36Sopenharmony_ci MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK, 45662306a36Sopenharmony_ci MUX_AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci clk_alpha_pll_configure(&pwrcl_pll, regmap, &hfpll_config); 45962306a36Sopenharmony_ci clk_alpha_pll_configure(&perfcl_pll, regmap, &hfpll_config); 46062306a36Sopenharmony_ci clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config); 46162306a36Sopenharmony_ci clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* Wait for PLL(s) to lock */ 46462306a36Sopenharmony_ci udelay(50); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* Enable auto clock selection for both clusters */ 46762306a36Sopenharmony_ci regmap_update_bits(regmap, PWRCL_REG_OFFSET + CLK_CTL_OFFSET, 46862306a36Sopenharmony_ci CLK_CTL_AUTO_CLK_SEL, CLK_CTL_AUTO_CLK_SEL); 46962306a36Sopenharmony_ci regmap_update_bits(regmap, PERFCL_REG_OFFSET + CLK_CTL_OFFSET, 47062306a36Sopenharmony_ci CLK_CTL_AUTO_CLK_SEL, CLK_CTL_AUTO_CLK_SEL); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* Ensure write goes through before muxes are switched */ 47362306a36Sopenharmony_ci udelay(5); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci qcom_cpu_clk_msm8996_acd_init(regmap); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* Pulse swallower and soft-start settings */ 47862306a36Sopenharmony_ci regmap_write(regmap, PWRCL_REG_OFFSET + PSCTL_OFFSET, 0x00030005); 47962306a36Sopenharmony_ci regmap_write(regmap, PERFCL_REG_OFFSET + PSCTL_OFFSET, 0x00030005); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* Switch clusters to use the ACD leg */ 48262306a36Sopenharmony_ci regmap_write(regmap, PWRCL_REG_OFFSET + MUX_OFFSET, 0x32); 48362306a36Sopenharmony_ci regmap_write(regmap, PERFCL_REG_OFFSET + MUX_OFFSET, 0x32); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cpu_msm8996_hw_clks); i++) { 48662306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, cpu_msm8996_hw_clks[i]); 48762306a36Sopenharmony_ci if (ret) 48862306a36Sopenharmony_ci return ret; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cpu_msm8996_clks); i++) { 49262306a36Sopenharmony_ci ret = devm_clk_register_regmap(dev, cpu_msm8996_clks[i]); 49362306a36Sopenharmony_ci if (ret) 49462306a36Sopenharmony_ci return ret; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* Enable alt PLLs */ 49862306a36Sopenharmony_ci clk_prepare_enable(pwrcl_alt_pll.clkr.hw.clk); 49962306a36Sopenharmony_ci clk_prepare_enable(perfcl_alt_pll.clkr.hw.clk); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci devm_clk_notifier_register(dev, pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb); 50262306a36Sopenharmony_ci devm_clk_notifier_register(dev, perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci return ret; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci#define CPU_CLUSTER_AFFINITY_MASK 0xf00 50862306a36Sopenharmony_ci#define PWRCL_AFFINITY_MASK 0x000 50962306a36Sopenharmony_ci#define PERFCL_AFFINITY_MASK 0x100 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci#define L2ACDCR_REG 0x580ULL 51262306a36Sopenharmony_ci#define L2ACDTD_REG 0x581ULL 51362306a36Sopenharmony_ci#define L2ACDDVMRC_REG 0x584ULL 51462306a36Sopenharmony_ci#define L2ACDSSCR_REG 0x589ULL 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(qcom_clk_acd_lock); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic void qcom_cpu_clk_msm8996_acd_init(struct regmap *regmap) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci u64 hwid; 52162306a36Sopenharmony_ci u32 val; 52262306a36Sopenharmony_ci unsigned long flags; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci spin_lock_irqsave(&qcom_clk_acd_lock, flags); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci val = kryo_l2_get_indirect_reg(L2ACDTD_REG); 52762306a36Sopenharmony_ci if (val == 0x00006a11) 52862306a36Sopenharmony_ci goto out; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci kryo_l2_set_indirect_reg(L2ACDTD_REG, 0x00006a11); 53162306a36Sopenharmony_ci kryo_l2_set_indirect_reg(L2ACDDVMRC_REG, 0x000e0f0f); 53262306a36Sopenharmony_ci kryo_l2_set_indirect_reg(L2ACDSSCR_REG, 0x00000601); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci hwid = read_cpuid_mpidr(); 53762306a36Sopenharmony_ci if ((hwid & CPU_CLUSTER_AFFINITY_MASK) == PWRCL_AFFINITY_MASK) 53862306a36Sopenharmony_ci regmap_write(regmap, PWRCL_REG_OFFSET + SSSCTL_OFFSET, 0xf); 53962306a36Sopenharmony_ci else 54062306a36Sopenharmony_ci regmap_write(regmap, PERFCL_REG_OFFSET + SSSCTL_OFFSET, 0xf); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ciout: 54362306a36Sopenharmony_ci spin_unlock_irqrestore(&qcom_clk_acd_lock, flags); 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, 54762306a36Sopenharmony_ci void *data) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_nb(nb); 55062306a36Sopenharmony_ci struct clk_notifier_data *cnd = data; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci switch (event) { 55362306a36Sopenharmony_ci case PRE_RATE_CHANGE: 55462306a36Sopenharmony_ci qcom_cpu_clk_msm8996_acd_init(cpuclk->clkr.regmap); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* 55762306a36Sopenharmony_ci * Avoid overvolting. clk_core_set_rate_nolock() walks from top 55862306a36Sopenharmony_ci * to bottom, so it will change the rate of the PLL before 55962306a36Sopenharmony_ci * chaging the parent of PMUX. This can result in pmux getting 56062306a36Sopenharmony_ci * clocked twice the expected rate. 56162306a36Sopenharmony_ci * 56262306a36Sopenharmony_ci * Manually switch to PLL/2 here. 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci if (cnd->new_rate < DIV_2_THRESHOLD && 56562306a36Sopenharmony_ci cnd->old_rate > DIV_2_THRESHOLD) 56662306a36Sopenharmony_ci clk_cpu_8996_pmux_set_parent(&cpuclk->clkr.hw, SMUX_INDEX); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci break; 56962306a36Sopenharmony_ci case ABORT_RATE_CHANGE: 57062306a36Sopenharmony_ci /* Revert manual change */ 57162306a36Sopenharmony_ci if (cnd->new_rate < DIV_2_THRESHOLD && 57262306a36Sopenharmony_ci cnd->old_rate > DIV_2_THRESHOLD) 57362306a36Sopenharmony_ci clk_cpu_8996_pmux_set_parent(&cpuclk->clkr.hw, ACD_INDEX); 57462306a36Sopenharmony_ci break; 57562306a36Sopenharmony_ci default: 57662306a36Sopenharmony_ci break; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci return NOTIFY_OK; 58062306a36Sopenharmony_ci}; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci static void __iomem *base; 58562306a36Sopenharmony_ci struct regmap *regmap; 58662306a36Sopenharmony_ci struct clk_hw_onecell_data *data; 58762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 58862306a36Sopenharmony_ci int ret; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci data = devm_kzalloc(dev, struct_size(data, hws, 2), GFP_KERNEL); 59162306a36Sopenharmony_ci if (!data) 59262306a36Sopenharmony_ci return -ENOMEM; 59362306a36Sopenharmony_ci data->num = 2; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci base = devm_platform_ioremap_resource(pdev, 0); 59662306a36Sopenharmony_ci if (IS_ERR(base)) 59762306a36Sopenharmony_ci return PTR_ERR(base); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci regmap = devm_regmap_init_mmio(dev, base, &cpu_msm8996_regmap_config); 60062306a36Sopenharmony_ci if (IS_ERR(regmap)) 60162306a36Sopenharmony_ci return PTR_ERR(regmap); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci ret = qcom_cpu_clk_msm8996_register_clks(dev, regmap); 60462306a36Sopenharmony_ci if (ret) 60562306a36Sopenharmony_ci return ret; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci data->hws[0] = &pwrcl_pmux.clkr.hw; 60862306a36Sopenharmony_ci data->hws[1] = &perfcl_pmux.clkr.hw; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data); 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic const struct of_device_id qcom_cpu_clk_msm8996_match_table[] = { 61462306a36Sopenharmony_ci { .compatible = "qcom,msm8996-apcc" }, 61562306a36Sopenharmony_ci {} 61662306a36Sopenharmony_ci}; 61762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_cpu_clk_msm8996_match_table); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic struct platform_driver qcom_cpu_clk_msm8996_driver = { 62062306a36Sopenharmony_ci .probe = qcom_cpu_clk_msm8996_driver_probe, 62162306a36Sopenharmony_ci .driver = { 62262306a36Sopenharmony_ci .name = "qcom-msm8996-apcc", 62362306a36Sopenharmony_ci .of_match_table = qcom_cpu_clk_msm8996_match_table, 62462306a36Sopenharmony_ci }, 62562306a36Sopenharmony_ci}; 62662306a36Sopenharmony_cimodule_platform_driver(qcom_cpu_clk_msm8996_driver); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ciMODULE_DESCRIPTION("QCOM MSM8996 CPU Clock Driver"); 62962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 630