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 | 1503e342dcSLoic Poulain * | | 1603e342dcSLoic Poulain * PLL/2 | SMUX +----+ 1703e342dcSLoic Poulain * +------->1 | | 1803e342dcSLoic Poulain * | | | | 1903e342dcSLoic Poulain * | +-------+ | +-------+ 2003e342dcSLoic Poulain * | +---->0 | 2103e342dcSLoic Poulain * | | | 2203e342dcSLoic Poulain * +---------------+ | +----------->1 | CPU clk 2303e342dcSLoic Poulain * |Primary PLL +----+ PLL_EARLY | | +------> 2403e342dcSLoic Poulain * | +------+-----------+ +------>2 PMUX | 2503e342dcSLoic Poulain * +---------------+ | | | | 2603e342dcSLoic Poulain * | +------+ | +-->3 | 2703e342dcSLoic Poulain * +--^+ ACD +-----+ | +-------+ 2803e342dcSLoic Poulain * +---------------+ +------+ | 2903e342dcSLoic Poulain * |Alt PLL | | 3003e342dcSLoic Poulain * | +---------------------------+ 3103e342dcSLoic Poulain * +---------------+ PLL_EARLY 3203e342dcSLoic Poulain * 3303e342dcSLoic Poulain * The primary PLL is what drives the CPU clk, except for times 3403e342dcSLoic Poulain * when we are reprogramming the PLL itself (for rate changes) when 3503e342dcSLoic Poulain * we temporarily switch to an alternate PLL. 3603e342dcSLoic Poulain * 3703e342dcSLoic Poulain * The primary PLL operates on a single VCO range, between 600MHz 3803e342dcSLoic Poulain * and 3GHz. However the CPUs do support OPPs with frequencies 3903e342dcSLoic Poulain * between 300MHz and 600MHz. In order to support running the CPUs 4003e342dcSLoic Poulain * at those frequencies we end up having to lock the PLL at twice 4103e342dcSLoic Poulain * the rate and drive the CPU clk via the PLL/2 output and SMUX. 4203e342dcSLoic Poulain * 4303e342dcSLoic Poulain * So for frequencies above 600MHz we follow the following path 4403e342dcSLoic Poulain * Primary PLL --> PLL_EARLY --> PMUX(1) --> CPU clk 4503e342dcSLoic Poulain * and for frequencies between 300MHz and 600MHz we follow 4603e342dcSLoic Poulain * Primary PLL --> PLL/2 --> SMUX(1) --> PMUX(0) --> CPU clk 4703e342dcSLoic Poulain * 4803e342dcSLoic Poulain * ACD stands for Adaptive Clock Distribution and is used to 4903e342dcSLoic Poulain * detect voltage droops. 5003e342dcSLoic Poulain */ 5103e342dcSLoic Poulain 5203e342dcSLoic Poulain #include <linux/clk.h> 5303e342dcSLoic Poulain #include <linux/clk-provider.h> 5403e342dcSLoic Poulain #include <linux/io.h> 5503e342dcSLoic Poulain #include <linux/module.h> 5603e342dcSLoic Poulain #include <linux/platform_device.h> 5703e342dcSLoic Poulain #include <linux/regmap.h> 5803e342dcSLoic Poulain #include <soc/qcom/kryo-l2-accessors.h> 5903e342dcSLoic Poulain 6003e342dcSLoic Poulain #include "clk-alpha-pll.h" 6103e342dcSLoic Poulain #include "clk-regmap.h" 62*9a9f5f9aSYassine Oudjana #include "clk-regmap-mux.h" 6303e342dcSLoic Poulain 6403e342dcSLoic Poulain enum _pmux_input { 651ba0a3bbSYassine Oudjana SMUX_INDEX = 0, 6603e342dcSLoic Poulain PLL_INDEX, 6703e342dcSLoic Poulain ACD_INDEX, 6803e342dcSLoic Poulain ALT_INDEX, 6903e342dcSLoic Poulain NUM_OF_PMUX_INPUTS 7003e342dcSLoic Poulain }; 7103e342dcSLoic Poulain 7203e342dcSLoic Poulain #define DIV_2_THRESHOLD 600000000 7303e342dcSLoic Poulain #define PWRCL_REG_OFFSET 0x0 7403e342dcSLoic Poulain #define PERFCL_REG_OFFSET 0x80000 7503e342dcSLoic Poulain #define MUX_OFFSET 0x40 7603e342dcSLoic Poulain #define ALT_PLL_OFFSET 0x100 7703e342dcSLoic Poulain #define SSSCTL_OFFSET 0x160 7803e342dcSLoic Poulain 7903e342dcSLoic Poulain static const u8 prim_pll_regs[PLL_OFF_MAX_REGS] = { 8003e342dcSLoic Poulain [PLL_OFF_L_VAL] = 0x04, 8103e342dcSLoic Poulain [PLL_OFF_ALPHA_VAL] = 0x08, 8203e342dcSLoic Poulain [PLL_OFF_USER_CTL] = 0x10, 8303e342dcSLoic Poulain [PLL_OFF_CONFIG_CTL] = 0x18, 8403e342dcSLoic Poulain [PLL_OFF_CONFIG_CTL_U] = 0x1c, 8503e342dcSLoic Poulain [PLL_OFF_TEST_CTL] = 0x20, 8603e342dcSLoic Poulain [PLL_OFF_TEST_CTL_U] = 0x24, 8703e342dcSLoic Poulain [PLL_OFF_STATUS] = 0x28, 8803e342dcSLoic Poulain }; 8903e342dcSLoic Poulain 9003e342dcSLoic Poulain static const u8 alt_pll_regs[PLL_OFF_MAX_REGS] = { 9103e342dcSLoic Poulain [PLL_OFF_L_VAL] = 0x04, 9203e342dcSLoic Poulain [PLL_OFF_ALPHA_VAL] = 0x08, 9303e342dcSLoic Poulain [PLL_OFF_ALPHA_VAL_U] = 0x0c, 9403e342dcSLoic Poulain [PLL_OFF_USER_CTL] = 0x10, 9503e342dcSLoic Poulain [PLL_OFF_USER_CTL_U] = 0x14, 9603e342dcSLoic Poulain [PLL_OFF_CONFIG_CTL] = 0x18, 9703e342dcSLoic Poulain [PLL_OFF_TEST_CTL] = 0x20, 9803e342dcSLoic Poulain [PLL_OFF_TEST_CTL_U] = 0x24, 9903e342dcSLoic Poulain [PLL_OFF_STATUS] = 0x28, 10003e342dcSLoic Poulain }; 10103e342dcSLoic Poulain 10203e342dcSLoic Poulain /* PLLs */ 10303e342dcSLoic Poulain 10403e342dcSLoic Poulain static const struct alpha_pll_config hfpll_config = { 10503e342dcSLoic Poulain .l = 60, 10603e342dcSLoic Poulain .config_ctl_val = 0x200d4aa8, 10703e342dcSLoic Poulain .config_ctl_hi_val = 0x006, 10803e342dcSLoic Poulain .pre_div_mask = BIT(12), 10903e342dcSLoic Poulain .post_div_mask = 0x3 << 8, 11003e342dcSLoic Poulain .post_div_val = 0x1 << 8, 11103e342dcSLoic Poulain .main_output_mask = BIT(0), 11203e342dcSLoic Poulain .early_output_mask = BIT(3), 11303e342dcSLoic Poulain }; 11403e342dcSLoic Poulain 11503e342dcSLoic Poulain static struct clk_alpha_pll pwrcl_pll = { 11603e342dcSLoic Poulain .offset = PWRCL_REG_OFFSET, 11703e342dcSLoic Poulain .regs = prim_pll_regs, 11803e342dcSLoic Poulain .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, 11903e342dcSLoic Poulain .clkr.hw.init = &(struct clk_init_data){ 12003e342dcSLoic Poulain .name = "pwrcl_pll", 12103e342dcSLoic Poulain .parent_names = (const char *[]){ "xo" }, 12203e342dcSLoic Poulain .num_parents = 1, 12303e342dcSLoic Poulain .ops = &clk_alpha_pll_huayra_ops, 12403e342dcSLoic Poulain }, 12503e342dcSLoic Poulain }; 12603e342dcSLoic Poulain 127382139bfSYassine Oudjana static struct clk_alpha_pll perfcl_pll = { 128382139bfSYassine Oudjana .offset = PERFCL_REG_OFFSET, 129382139bfSYassine Oudjana .regs = prim_pll_regs, 130382139bfSYassine Oudjana .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, 131382139bfSYassine Oudjana .clkr.hw.init = &(struct clk_init_data){ 132382139bfSYassine Oudjana .name = "perfcl_pll", 133382139bfSYassine Oudjana .parent_names = (const char *[]){ "xo" }, 134382139bfSYassine Oudjana .num_parents = 1, 135382139bfSYassine Oudjana .ops = &clk_alpha_pll_huayra_ops, 136382139bfSYassine Oudjana }, 137382139bfSYassine Oudjana }; 138382139bfSYassine Oudjana 139de37e021SYassine Oudjana static struct clk_fixed_factor pwrcl_pll_postdiv = { 140de37e021SYassine Oudjana .mult = 1, 141de37e021SYassine Oudjana .div = 2, 142de37e021SYassine Oudjana .hw.init = &(struct clk_init_data){ 143de37e021SYassine Oudjana .name = "pwrcl_pll_postdiv", 144de37e021SYassine Oudjana .parent_data = &(const struct clk_parent_data){ 145de37e021SYassine Oudjana .hw = &pwrcl_pll.clkr.hw 146de37e021SYassine Oudjana }, 147de37e021SYassine Oudjana .num_parents = 1, 148de37e021SYassine Oudjana .ops = &clk_fixed_factor_ops, 149de37e021SYassine Oudjana .flags = CLK_SET_RATE_PARENT, 150de37e021SYassine Oudjana }, 151de37e021SYassine Oudjana }; 152de37e021SYassine Oudjana 153de37e021SYassine Oudjana static struct clk_fixed_factor perfcl_pll_postdiv = { 154de37e021SYassine Oudjana .mult = 1, 155de37e021SYassine Oudjana .div = 2, 156de37e021SYassine Oudjana .hw.init = &(struct clk_init_data){ 157de37e021SYassine Oudjana .name = "perfcl_pll_postdiv", 158de37e021SYassine Oudjana .parent_data = &(const struct clk_parent_data){ 159de37e021SYassine Oudjana .hw = &perfcl_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 16703e342dcSLoic Poulain static const struct pll_vco alt_pll_vco_modes[] = { 16803e342dcSLoic Poulain VCO(3, 250000000, 500000000), 16903e342dcSLoic Poulain VCO(2, 500000000, 750000000), 17003e342dcSLoic Poulain VCO(1, 750000000, 1000000000), 17103e342dcSLoic Poulain VCO(0, 1000000000, 2150400000), 17203e342dcSLoic Poulain }; 17303e342dcSLoic Poulain 17403e342dcSLoic Poulain static const struct alpha_pll_config altpll_config = { 17503e342dcSLoic Poulain .l = 16, 17603e342dcSLoic Poulain .vco_val = 0x3 << 20, 17703e342dcSLoic Poulain .vco_mask = 0x3 << 20, 17803e342dcSLoic Poulain .config_ctl_val = 0x4001051b, 17903e342dcSLoic Poulain .post_div_mask = 0x3 << 8, 18003e342dcSLoic Poulain .post_div_val = 0x1 << 8, 18103e342dcSLoic Poulain .main_output_mask = BIT(0), 18203e342dcSLoic Poulain .early_output_mask = BIT(3), 18303e342dcSLoic Poulain }; 18403e342dcSLoic Poulain 18503e342dcSLoic Poulain static struct clk_alpha_pll pwrcl_alt_pll = { 18603e342dcSLoic Poulain .offset = PWRCL_REG_OFFSET + ALT_PLL_OFFSET, 18703e342dcSLoic Poulain .regs = alt_pll_regs, 18803e342dcSLoic Poulain .vco_table = alt_pll_vco_modes, 18903e342dcSLoic Poulain .num_vco = ARRAY_SIZE(alt_pll_vco_modes), 19003e342dcSLoic Poulain .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, 19103e342dcSLoic Poulain .clkr.hw.init = &(struct clk_init_data) { 19203e342dcSLoic Poulain .name = "pwrcl_alt_pll", 19303e342dcSLoic Poulain .parent_names = (const char *[]){ "xo" }, 19403e342dcSLoic Poulain .num_parents = 1, 19503e342dcSLoic Poulain .ops = &clk_alpha_pll_hwfsm_ops, 19603e342dcSLoic Poulain }, 19703e342dcSLoic Poulain }; 19803e342dcSLoic Poulain 199382139bfSYassine Oudjana static struct clk_alpha_pll perfcl_alt_pll = { 200382139bfSYassine Oudjana .offset = PERFCL_REG_OFFSET + ALT_PLL_OFFSET, 201382139bfSYassine Oudjana .regs = alt_pll_regs, 202382139bfSYassine Oudjana .vco_table = alt_pll_vco_modes, 203382139bfSYassine Oudjana .num_vco = ARRAY_SIZE(alt_pll_vco_modes), 204382139bfSYassine Oudjana .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, 205382139bfSYassine Oudjana .clkr.hw.init = &(struct clk_init_data) { 206382139bfSYassine Oudjana .name = "perfcl_alt_pll", 207382139bfSYassine Oudjana .parent_names = (const char *[]){ "xo" }, 208382139bfSYassine Oudjana .num_parents = 1, 209382139bfSYassine Oudjana .ops = &clk_alpha_pll_hwfsm_ops, 210382139bfSYassine Oudjana }, 211382139bfSYassine Oudjana }; 212382139bfSYassine Oudjana 213*9a9f5f9aSYassine Oudjana struct clk_cpu_8996_pmux { 21403e342dcSLoic Poulain u32 reg; 21503e342dcSLoic Poulain u8 shift; 21603e342dcSLoic Poulain u8 width; 21703e342dcSLoic Poulain struct notifier_block nb; 21803e342dcSLoic Poulain struct clk_hw *pll; 21903e342dcSLoic Poulain struct clk_hw *pll_div_2; 22003e342dcSLoic Poulain struct clk_regmap clkr; 22103e342dcSLoic Poulain }; 22203e342dcSLoic Poulain 22303e342dcSLoic Poulain static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, 22403e342dcSLoic Poulain void *data); 22503e342dcSLoic Poulain 226*9a9f5f9aSYassine Oudjana #define to_clk_cpu_8996_pmux_nb(_nb) \ 227*9a9f5f9aSYassine Oudjana container_of(_nb, struct clk_cpu_8996_pmux, nb) 22803e342dcSLoic Poulain 229*9a9f5f9aSYassine Oudjana static inline struct clk_cpu_8996_pmux *to_clk_cpu_8996_pmux_hw(struct clk_hw *hw) 23003e342dcSLoic Poulain { 231*9a9f5f9aSYassine Oudjana return container_of(to_clk_regmap(hw), struct clk_cpu_8996_pmux, clkr); 23203e342dcSLoic Poulain } 23303e342dcSLoic Poulain 234*9a9f5f9aSYassine Oudjana static u8 clk_cpu_8996_pmux_get_parent(struct clk_hw *hw) 23503e342dcSLoic Poulain { 23603e342dcSLoic Poulain struct clk_regmap *clkr = to_clk_regmap(hw); 237*9a9f5f9aSYassine Oudjana struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_hw(hw); 23803e342dcSLoic Poulain u32 mask = GENMASK(cpuclk->width - 1, 0); 23903e342dcSLoic Poulain u32 val; 24003e342dcSLoic Poulain 24103e342dcSLoic Poulain regmap_read(clkr->regmap, cpuclk->reg, &val); 24203e342dcSLoic Poulain val >>= cpuclk->shift; 24303e342dcSLoic Poulain 24403e342dcSLoic Poulain return val & mask; 24503e342dcSLoic Poulain } 24603e342dcSLoic Poulain 247*9a9f5f9aSYassine Oudjana static int clk_cpu_8996_pmux_set_parent(struct clk_hw *hw, u8 index) 24803e342dcSLoic Poulain { 24903e342dcSLoic Poulain struct clk_regmap *clkr = to_clk_regmap(hw); 250*9a9f5f9aSYassine Oudjana struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_hw(hw); 25103e342dcSLoic Poulain u32 mask = GENMASK(cpuclk->width + cpuclk->shift - 1, cpuclk->shift); 25203e342dcSLoic Poulain u32 val; 25303e342dcSLoic Poulain 25403e342dcSLoic Poulain val = index; 25503e342dcSLoic Poulain val <<= cpuclk->shift; 25603e342dcSLoic Poulain 25703e342dcSLoic Poulain return regmap_update_bits(clkr->regmap, cpuclk->reg, mask, val); 25803e342dcSLoic Poulain } 25903e342dcSLoic Poulain 260*9a9f5f9aSYassine Oudjana static int clk_cpu_8996_pmux_determine_rate(struct clk_hw *hw, 26103e342dcSLoic Poulain struct clk_rate_request *req) 26203e342dcSLoic Poulain { 263*9a9f5f9aSYassine Oudjana struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_hw(hw); 26403e342dcSLoic Poulain struct clk_hw *parent = cpuclk->pll; 26503e342dcSLoic Poulain 26603e342dcSLoic Poulain if (cpuclk->pll_div_2 && req->rate < DIV_2_THRESHOLD) { 26703e342dcSLoic Poulain if (req->rate < (DIV_2_THRESHOLD / 2)) 26803e342dcSLoic Poulain return -EINVAL; 26903e342dcSLoic Poulain 27003e342dcSLoic Poulain parent = cpuclk->pll_div_2; 27103e342dcSLoic Poulain } 27203e342dcSLoic Poulain 27303e342dcSLoic Poulain req->best_parent_rate = clk_hw_round_rate(parent, req->rate); 27403e342dcSLoic Poulain req->best_parent_hw = parent; 27503e342dcSLoic Poulain 27603e342dcSLoic Poulain return 0; 27703e342dcSLoic Poulain } 27803e342dcSLoic Poulain 279*9a9f5f9aSYassine Oudjana static const struct clk_ops clk_cpu_8996_pmux_ops = { 280*9a9f5f9aSYassine Oudjana .set_parent = clk_cpu_8996_pmux_set_parent, 281*9a9f5f9aSYassine Oudjana .get_parent = clk_cpu_8996_pmux_get_parent, 282*9a9f5f9aSYassine Oudjana .determine_rate = clk_cpu_8996_pmux_determine_rate, 28303e342dcSLoic Poulain }; 28403e342dcSLoic Poulain 285*9a9f5f9aSYassine Oudjana static struct clk_regmap_mux pwrcl_smux = { 28603e342dcSLoic Poulain .reg = PWRCL_REG_OFFSET + MUX_OFFSET, 28703e342dcSLoic Poulain .shift = 2, 28803e342dcSLoic Poulain .width = 2, 28903e342dcSLoic Poulain .clkr.hw.init = &(struct clk_init_data) { 29003e342dcSLoic Poulain .name = "pwrcl_smux", 29103e342dcSLoic Poulain .parent_names = (const char *[]){ 29203e342dcSLoic Poulain "xo", 293de37e021SYassine Oudjana "pwrcl_pll_postdiv", 29403e342dcSLoic Poulain }, 29503e342dcSLoic Poulain .num_parents = 2, 296*9a9f5f9aSYassine Oudjana .ops = &clk_regmap_mux_closest_ops, 29703e342dcSLoic Poulain .flags = CLK_SET_RATE_PARENT, 29803e342dcSLoic Poulain }, 29903e342dcSLoic Poulain }; 30003e342dcSLoic Poulain 301*9a9f5f9aSYassine Oudjana static struct clk_regmap_mux perfcl_smux = { 30203e342dcSLoic Poulain .reg = PERFCL_REG_OFFSET + MUX_OFFSET, 30303e342dcSLoic Poulain .shift = 2, 30403e342dcSLoic Poulain .width = 2, 30503e342dcSLoic Poulain .clkr.hw.init = &(struct clk_init_data) { 30603e342dcSLoic Poulain .name = "perfcl_smux", 30703e342dcSLoic Poulain .parent_names = (const char *[]){ 30803e342dcSLoic Poulain "xo", 309de37e021SYassine Oudjana "perfcl_pll_postdiv", 31003e342dcSLoic Poulain }, 31103e342dcSLoic Poulain .num_parents = 2, 312*9a9f5f9aSYassine Oudjana .ops = &clk_regmap_mux_closest_ops, 31303e342dcSLoic Poulain .flags = CLK_SET_RATE_PARENT, 31403e342dcSLoic Poulain }, 31503e342dcSLoic Poulain }; 31603e342dcSLoic Poulain 317*9a9f5f9aSYassine Oudjana static struct clk_cpu_8996_pmux pwrcl_pmux = { 31803e342dcSLoic Poulain .reg = PWRCL_REG_OFFSET + MUX_OFFSET, 31903e342dcSLoic Poulain .shift = 0, 32003e342dcSLoic Poulain .width = 2, 32103e342dcSLoic Poulain .pll = &pwrcl_pll.clkr.hw, 32203e342dcSLoic Poulain .pll_div_2 = &pwrcl_smux.clkr.hw, 32303e342dcSLoic Poulain .nb.notifier_call = cpu_clk_notifier_cb, 32403e342dcSLoic Poulain .clkr.hw.init = &(struct clk_init_data) { 32503e342dcSLoic Poulain .name = "pwrcl_pmux", 32603e342dcSLoic Poulain .parent_names = (const char *[]){ 32703e342dcSLoic Poulain "pwrcl_smux", 32803e342dcSLoic Poulain "pwrcl_pll", 32903e342dcSLoic Poulain "pwrcl_pll_acd", 33003e342dcSLoic Poulain "pwrcl_alt_pll", 33103e342dcSLoic Poulain }, 33203e342dcSLoic Poulain .num_parents = 4, 333*9a9f5f9aSYassine Oudjana .ops = &clk_cpu_8996_pmux_ops, 33403e342dcSLoic Poulain /* CPU clock is critical and should never be gated */ 33503e342dcSLoic Poulain .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 33603e342dcSLoic Poulain }, 33703e342dcSLoic Poulain }; 33803e342dcSLoic Poulain 339*9a9f5f9aSYassine Oudjana static struct clk_cpu_8996_pmux perfcl_pmux = { 34003e342dcSLoic Poulain .reg = PERFCL_REG_OFFSET + MUX_OFFSET, 34103e342dcSLoic Poulain .shift = 0, 34203e342dcSLoic Poulain .width = 2, 34303e342dcSLoic Poulain .pll = &perfcl_pll.clkr.hw, 34403e342dcSLoic Poulain .pll_div_2 = &perfcl_smux.clkr.hw, 34503e342dcSLoic Poulain .nb.notifier_call = cpu_clk_notifier_cb, 34603e342dcSLoic Poulain .clkr.hw.init = &(struct clk_init_data) { 34703e342dcSLoic Poulain .name = "perfcl_pmux", 34803e342dcSLoic Poulain .parent_names = (const char *[]){ 34903e342dcSLoic Poulain "perfcl_smux", 35003e342dcSLoic Poulain "perfcl_pll", 35103e342dcSLoic Poulain "perfcl_pll_acd", 35203e342dcSLoic Poulain "perfcl_alt_pll", 35303e342dcSLoic Poulain }, 35403e342dcSLoic Poulain .num_parents = 4, 355*9a9f5f9aSYassine Oudjana .ops = &clk_cpu_8996_pmux_ops, 35603e342dcSLoic Poulain /* CPU clock is critical and should never be gated */ 35703e342dcSLoic Poulain .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 35803e342dcSLoic Poulain }, 35903e342dcSLoic Poulain }; 36003e342dcSLoic Poulain 36103e342dcSLoic Poulain static const struct regmap_config cpu_msm8996_regmap_config = { 36203e342dcSLoic Poulain .reg_bits = 32, 36303e342dcSLoic Poulain .reg_stride = 4, 36403e342dcSLoic Poulain .val_bits = 32, 36503e342dcSLoic Poulain .max_register = 0x80210, 36603e342dcSLoic Poulain .fast_io = true, 36703e342dcSLoic Poulain .val_format_endian = REGMAP_ENDIAN_LITTLE, 36803e342dcSLoic Poulain }; 36903e342dcSLoic Poulain 3708607fa16SWei Yongjun static struct clk_regmap *cpu_msm8996_clks[] = { 37103e342dcSLoic Poulain &pwrcl_pll.clkr, 372382139bfSYassine Oudjana &perfcl_pll.clkr, 37303e342dcSLoic Poulain &pwrcl_alt_pll.clkr, 374382139bfSYassine Oudjana &perfcl_alt_pll.clkr, 37503e342dcSLoic Poulain &pwrcl_smux.clkr, 376382139bfSYassine Oudjana &perfcl_smux.clkr, 37703e342dcSLoic Poulain &pwrcl_pmux.clkr, 378382139bfSYassine Oudjana &perfcl_pmux.clkr, 37903e342dcSLoic Poulain }; 38003e342dcSLoic Poulain 38103e342dcSLoic Poulain static int qcom_cpu_clk_msm8996_register_clks(struct device *dev, 38203e342dcSLoic Poulain struct regmap *regmap) 38303e342dcSLoic Poulain { 38403e342dcSLoic Poulain int i, ret; 38503e342dcSLoic Poulain 386de37e021SYassine Oudjana ret = devm_clk_hw_register(dev, &pwrcl_pll_postdiv.hw); 387de37e021SYassine Oudjana if (ret) { 388de37e021SYassine Oudjana dev_err(dev, "Failed to register pwrcl_pll_postdiv: %d", ret); 389de37e021SYassine Oudjana return ret; 39003e342dcSLoic Poulain } 39103e342dcSLoic Poulain 392de37e021SYassine Oudjana ret = devm_clk_hw_register(dev, &perfcl_pll_postdiv.hw); 393de37e021SYassine Oudjana if (ret) { 394de37e021SYassine Oudjana dev_err(dev, "Failed to register perfcl_pll_postdiv: %d", ret); 395de37e021SYassine Oudjana return ret; 39603e342dcSLoic Poulain } 39703e342dcSLoic Poulain 39803e342dcSLoic Poulain for (i = 0; i < ARRAY_SIZE(cpu_msm8996_clks); i++) { 39903e342dcSLoic Poulain ret = devm_clk_register_regmap(dev, cpu_msm8996_clks[i]); 400de37e021SYassine Oudjana if (ret) 40103e342dcSLoic Poulain return ret; 40203e342dcSLoic Poulain } 40303e342dcSLoic Poulain 40403e342dcSLoic Poulain clk_alpha_pll_configure(&pwrcl_pll, regmap, &hfpll_config); 405382139bfSYassine Oudjana clk_alpha_pll_configure(&perfcl_pll, regmap, &hfpll_config); 40603e342dcSLoic Poulain clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config); 407382139bfSYassine Oudjana clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config); 40803e342dcSLoic Poulain 40903e342dcSLoic Poulain /* Enable alt PLLs */ 41003e342dcSLoic Poulain clk_prepare_enable(pwrcl_alt_pll.clkr.hw.clk); 41103e342dcSLoic Poulain clk_prepare_enable(perfcl_alt_pll.clkr.hw.clk); 41203e342dcSLoic Poulain 41303e342dcSLoic Poulain clk_notifier_register(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb); 41403e342dcSLoic Poulain clk_notifier_register(perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb); 41503e342dcSLoic Poulain 41603e342dcSLoic Poulain return ret; 41703e342dcSLoic Poulain } 41803e342dcSLoic Poulain 41903e342dcSLoic Poulain static int qcom_cpu_clk_msm8996_unregister_clks(void) 42003e342dcSLoic Poulain { 42103e342dcSLoic Poulain int ret = 0; 42203e342dcSLoic Poulain 42303e342dcSLoic Poulain ret = clk_notifier_unregister(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb); 42403e342dcSLoic Poulain if (ret) 42503e342dcSLoic Poulain return ret; 42603e342dcSLoic Poulain 42703e342dcSLoic Poulain ret = clk_notifier_unregister(perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb); 42803e342dcSLoic Poulain if (ret) 42903e342dcSLoic Poulain return ret; 43003e342dcSLoic Poulain 43103e342dcSLoic Poulain return 0; 43203e342dcSLoic Poulain } 43303e342dcSLoic Poulain 43403e342dcSLoic Poulain #define CPU_AFINITY_MASK 0xFFF 43503e342dcSLoic Poulain #define PWRCL_CPU_REG_MASK 0x3 43603e342dcSLoic Poulain #define PERFCL_CPU_REG_MASK 0x103 43703e342dcSLoic Poulain 43803e342dcSLoic Poulain #define L2ACDCR_REG 0x580ULL 43903e342dcSLoic Poulain #define L2ACDTD_REG 0x581ULL 44003e342dcSLoic Poulain #define L2ACDDVMRC_REG 0x584ULL 44103e342dcSLoic Poulain #define L2ACDSSCR_REG 0x589ULL 44203e342dcSLoic Poulain 44303e342dcSLoic Poulain static DEFINE_SPINLOCK(qcom_clk_acd_lock); 44403e342dcSLoic Poulain static void __iomem *base; 44503e342dcSLoic Poulain 44603e342dcSLoic Poulain static void qcom_cpu_clk_msm8996_acd_init(void __iomem *base) 44703e342dcSLoic Poulain { 44803e342dcSLoic Poulain u64 hwid; 44903e342dcSLoic Poulain unsigned long flags; 45003e342dcSLoic Poulain 45103e342dcSLoic Poulain spin_lock_irqsave(&qcom_clk_acd_lock, flags); 45203e342dcSLoic Poulain 45303e342dcSLoic Poulain hwid = read_cpuid_mpidr() & CPU_AFINITY_MASK; 45403e342dcSLoic Poulain 45503e342dcSLoic Poulain kryo_l2_set_indirect_reg(L2ACDTD_REG, 0x00006a11); 45603e342dcSLoic Poulain kryo_l2_set_indirect_reg(L2ACDDVMRC_REG, 0x000e0f0f); 45703e342dcSLoic Poulain kryo_l2_set_indirect_reg(L2ACDSSCR_REG, 0x00000601); 45803e342dcSLoic Poulain 45903e342dcSLoic Poulain if (PWRCL_CPU_REG_MASK == (hwid | PWRCL_CPU_REG_MASK)) { 46003e342dcSLoic Poulain writel(0xf, base + PWRCL_REG_OFFSET + SSSCTL_OFFSET); 46103e342dcSLoic Poulain kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd); 46203e342dcSLoic Poulain } 46303e342dcSLoic Poulain 46403e342dcSLoic Poulain if (PERFCL_CPU_REG_MASK == (hwid | PERFCL_CPU_REG_MASK)) { 46503e342dcSLoic Poulain kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd); 46603e342dcSLoic Poulain writel(0xf, base + PERFCL_REG_OFFSET + SSSCTL_OFFSET); 46703e342dcSLoic Poulain } 46803e342dcSLoic Poulain 46903e342dcSLoic Poulain spin_unlock_irqrestore(&qcom_clk_acd_lock, flags); 47003e342dcSLoic Poulain } 47103e342dcSLoic Poulain 47203e342dcSLoic Poulain static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, 47303e342dcSLoic Poulain void *data) 47403e342dcSLoic Poulain { 475*9a9f5f9aSYassine Oudjana struct clk_cpu_8996_pmux *cpuclk = to_clk_cpu_8996_pmux_nb(nb); 47603e342dcSLoic Poulain struct clk_notifier_data *cnd = data; 47703e342dcSLoic Poulain int ret; 47803e342dcSLoic Poulain 47903e342dcSLoic Poulain switch (event) { 48003e342dcSLoic Poulain case PRE_RATE_CHANGE: 481*9a9f5f9aSYassine Oudjana ret = clk_cpu_8996_pmux_set_parent(&cpuclk->clkr.hw, ALT_INDEX); 48203e342dcSLoic Poulain qcom_cpu_clk_msm8996_acd_init(base); 48303e342dcSLoic Poulain break; 48403e342dcSLoic Poulain case POST_RATE_CHANGE: 48503e342dcSLoic Poulain if (cnd->new_rate < DIV_2_THRESHOLD) 486*9a9f5f9aSYassine Oudjana ret = clk_cpu_8996_pmux_set_parent(&cpuclk->clkr.hw, 4871ba0a3bbSYassine Oudjana SMUX_INDEX); 48803e342dcSLoic Poulain else 489*9a9f5f9aSYassine Oudjana ret = clk_cpu_8996_pmux_set_parent(&cpuclk->clkr.hw, 49003e342dcSLoic Poulain ACD_INDEX); 49103e342dcSLoic Poulain break; 49203e342dcSLoic Poulain default: 49303e342dcSLoic Poulain ret = 0; 49403e342dcSLoic Poulain break; 49503e342dcSLoic Poulain } 49603e342dcSLoic Poulain 49703e342dcSLoic Poulain return notifier_from_errno(ret); 49803e342dcSLoic Poulain }; 49903e342dcSLoic Poulain 50003e342dcSLoic Poulain static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev) 50103e342dcSLoic Poulain { 50203e342dcSLoic Poulain struct regmap *regmap; 50303e342dcSLoic Poulain struct clk_hw_onecell_data *data; 50403e342dcSLoic Poulain struct device *dev = &pdev->dev; 50503e342dcSLoic Poulain int ret; 50603e342dcSLoic Poulain 50703e342dcSLoic Poulain data = devm_kzalloc(dev, struct_size(data, hws, 2), GFP_KERNEL); 50803e342dcSLoic Poulain if (!data) 50903e342dcSLoic Poulain return -ENOMEM; 51003e342dcSLoic Poulain 51103e342dcSLoic Poulain base = devm_platform_ioremap_resource(pdev, 0); 51203e342dcSLoic Poulain if (IS_ERR(base)) 51303e342dcSLoic Poulain return PTR_ERR(base); 51403e342dcSLoic Poulain 51503e342dcSLoic Poulain regmap = devm_regmap_init_mmio(dev, base, &cpu_msm8996_regmap_config); 51603e342dcSLoic Poulain if (IS_ERR(regmap)) 51703e342dcSLoic Poulain return PTR_ERR(regmap); 51803e342dcSLoic Poulain 51903e342dcSLoic Poulain ret = qcom_cpu_clk_msm8996_register_clks(dev, regmap); 52003e342dcSLoic Poulain if (ret) 52103e342dcSLoic Poulain return ret; 52203e342dcSLoic Poulain 52303e342dcSLoic Poulain qcom_cpu_clk_msm8996_acd_init(base); 52403e342dcSLoic Poulain 52503e342dcSLoic Poulain data->hws[0] = &pwrcl_pmux.clkr.hw; 52603e342dcSLoic Poulain data->hws[1] = &perfcl_pmux.clkr.hw; 52703e342dcSLoic Poulain data->num = 2; 52803e342dcSLoic Poulain 52903e342dcSLoic Poulain return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data); 53003e342dcSLoic Poulain } 53103e342dcSLoic Poulain 53203e342dcSLoic Poulain static int qcom_cpu_clk_msm8996_driver_remove(struct platform_device *pdev) 53303e342dcSLoic Poulain { 53403e342dcSLoic Poulain return qcom_cpu_clk_msm8996_unregister_clks(); 53503e342dcSLoic Poulain } 53603e342dcSLoic Poulain 53703e342dcSLoic Poulain static const struct of_device_id qcom_cpu_clk_msm8996_match_table[] = { 53803e342dcSLoic Poulain { .compatible = "qcom,msm8996-apcc" }, 53903e342dcSLoic Poulain {} 54003e342dcSLoic Poulain }; 54103e342dcSLoic Poulain MODULE_DEVICE_TABLE(of, qcom_cpu_clk_msm8996_match_table); 54203e342dcSLoic Poulain 54303e342dcSLoic Poulain static struct platform_driver qcom_cpu_clk_msm8996_driver = { 54403e342dcSLoic Poulain .probe = qcom_cpu_clk_msm8996_driver_probe, 54503e342dcSLoic Poulain .remove = qcom_cpu_clk_msm8996_driver_remove, 54603e342dcSLoic Poulain .driver = { 54703e342dcSLoic Poulain .name = "qcom-msm8996-apcc", 54803e342dcSLoic Poulain .of_match_table = qcom_cpu_clk_msm8996_match_table, 54903e342dcSLoic Poulain }, 55003e342dcSLoic Poulain }; 55103e342dcSLoic Poulain module_platform_driver(qcom_cpu_clk_msm8996_driver); 55203e342dcSLoic Poulain 55303e342dcSLoic Poulain MODULE_DESCRIPTION("QCOM MSM8996 CPU Clock Driver"); 55403e342dcSLoic Poulain MODULE_LICENSE("GPL v2"); 555