18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2020, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/* 78c2ecf20Sopenharmony_ci * Each of the CPU clusters (Power and Perf) on msm8996 are 88c2ecf20Sopenharmony_ci * clocked via 2 PLLs, a primary and alternate. There are also 98c2ecf20Sopenharmony_ci * 2 Mux'es, a primary and secondary all connected together 108c2ecf20Sopenharmony_ci * as shown below 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * +-------+ 138c2ecf20Sopenharmony_ci * XO | | 148c2ecf20Sopenharmony_ci * +------------------>0 | 158c2ecf20Sopenharmony_ci * | | 168c2ecf20Sopenharmony_ci * PLL/2 | SMUX +----+ 178c2ecf20Sopenharmony_ci * +------->1 | | 188c2ecf20Sopenharmony_ci * | | | | 198c2ecf20Sopenharmony_ci * | +-------+ | +-------+ 208c2ecf20Sopenharmony_ci * | +---->0 | 218c2ecf20Sopenharmony_ci * | | | 228c2ecf20Sopenharmony_ci * +---------------+ | +----------->1 | CPU clk 238c2ecf20Sopenharmony_ci * |Primary PLL +----+ PLL_EARLY | | +------> 248c2ecf20Sopenharmony_ci * | +------+-----------+ +------>2 PMUX | 258c2ecf20Sopenharmony_ci * +---------------+ | | | | 268c2ecf20Sopenharmony_ci * | +------+ | +-->3 | 278c2ecf20Sopenharmony_ci * +--^+ ACD +-----+ | +-------+ 288c2ecf20Sopenharmony_ci * +---------------+ +------+ | 298c2ecf20Sopenharmony_ci * |Alt PLL | | 308c2ecf20Sopenharmony_ci * | +---------------------------+ 318c2ecf20Sopenharmony_ci * +---------------+ PLL_EARLY 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * The primary PLL is what drives the CPU clk, except for times 348c2ecf20Sopenharmony_ci * when we are reprogramming the PLL itself (for rate changes) when 358c2ecf20Sopenharmony_ci * we temporarily switch to an alternate PLL. 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * The primary PLL operates on a single VCO range, between 600MHz 388c2ecf20Sopenharmony_ci * and 3GHz. However the CPUs do support OPPs with frequencies 398c2ecf20Sopenharmony_ci * between 300MHz and 600MHz. In order to support running the CPUs 408c2ecf20Sopenharmony_ci * at those frequencies we end up having to lock the PLL at twice 418c2ecf20Sopenharmony_ci * the rate and drive the CPU clk via the PLL/2 output and SMUX. 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * So for frequencies above 600MHz we follow the following path 448c2ecf20Sopenharmony_ci * Primary PLL --> PLL_EARLY --> PMUX(1) --> CPU clk 458c2ecf20Sopenharmony_ci * and for frequencies between 300MHz and 600MHz we follow 468c2ecf20Sopenharmony_ci * Primary PLL --> PLL/2 --> SMUX(1) --> PMUX(0) --> CPU clk 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * ACD stands for Adaptive Clock Distribution and is used to 498c2ecf20Sopenharmony_ci * detect voltage droops. 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#include <linux/clk.h> 538c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 548c2ecf20Sopenharmony_ci#include <linux/io.h> 558c2ecf20Sopenharmony_ci#include <linux/module.h> 568c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 578c2ecf20Sopenharmony_ci#include <linux/regmap.h> 588c2ecf20Sopenharmony_ci#include <soc/qcom/kryo-l2-accessors.h> 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#include "clk-alpha-pll.h" 618c2ecf20Sopenharmony_ci#include "clk-regmap.h" 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cienum _pmux_input { 648c2ecf20Sopenharmony_ci DIV_2_INDEX = 0, 658c2ecf20Sopenharmony_ci PLL_INDEX, 668c2ecf20Sopenharmony_ci ACD_INDEX, 678c2ecf20Sopenharmony_ci ALT_INDEX, 688c2ecf20Sopenharmony_ci NUM_OF_PMUX_INPUTS 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define DIV_2_THRESHOLD 600000000 728c2ecf20Sopenharmony_ci#define PWRCL_REG_OFFSET 0x0 738c2ecf20Sopenharmony_ci#define PERFCL_REG_OFFSET 0x80000 748c2ecf20Sopenharmony_ci#define MUX_OFFSET 0x40 758c2ecf20Sopenharmony_ci#define ALT_PLL_OFFSET 0x100 768c2ecf20Sopenharmony_ci#define SSSCTL_OFFSET 0x160 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic const u8 prim_pll_regs[PLL_OFF_MAX_REGS] = { 798c2ecf20Sopenharmony_ci [PLL_OFF_L_VAL] = 0x04, 808c2ecf20Sopenharmony_ci [PLL_OFF_ALPHA_VAL] = 0x08, 818c2ecf20Sopenharmony_ci [PLL_OFF_USER_CTL] = 0x10, 828c2ecf20Sopenharmony_ci [PLL_OFF_CONFIG_CTL] = 0x18, 838c2ecf20Sopenharmony_ci [PLL_OFF_CONFIG_CTL_U] = 0x1c, 848c2ecf20Sopenharmony_ci [PLL_OFF_TEST_CTL] = 0x20, 858c2ecf20Sopenharmony_ci [PLL_OFF_TEST_CTL_U] = 0x24, 868c2ecf20Sopenharmony_ci [PLL_OFF_STATUS] = 0x28, 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic const u8 alt_pll_regs[PLL_OFF_MAX_REGS] = { 908c2ecf20Sopenharmony_ci [PLL_OFF_L_VAL] = 0x04, 918c2ecf20Sopenharmony_ci [PLL_OFF_ALPHA_VAL] = 0x08, 928c2ecf20Sopenharmony_ci [PLL_OFF_ALPHA_VAL_U] = 0x0c, 938c2ecf20Sopenharmony_ci [PLL_OFF_USER_CTL] = 0x10, 948c2ecf20Sopenharmony_ci [PLL_OFF_USER_CTL_U] = 0x14, 958c2ecf20Sopenharmony_ci [PLL_OFF_CONFIG_CTL] = 0x18, 968c2ecf20Sopenharmony_ci [PLL_OFF_TEST_CTL] = 0x20, 978c2ecf20Sopenharmony_ci [PLL_OFF_TEST_CTL_U] = 0x24, 988c2ecf20Sopenharmony_ci [PLL_OFF_STATUS] = 0x28, 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* PLLs */ 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic const struct alpha_pll_config hfpll_config = { 1048c2ecf20Sopenharmony_ci .l = 60, 1058c2ecf20Sopenharmony_ci .config_ctl_val = 0x200d4aa8, 1068c2ecf20Sopenharmony_ci .config_ctl_hi_val = 0x006, 1078c2ecf20Sopenharmony_ci .pre_div_mask = BIT(12), 1088c2ecf20Sopenharmony_ci .post_div_mask = 0x3 << 8, 1098c2ecf20Sopenharmony_ci .post_div_val = 0x1 << 8, 1108c2ecf20Sopenharmony_ci .main_output_mask = BIT(0), 1118c2ecf20Sopenharmony_ci .early_output_mask = BIT(3), 1128c2ecf20Sopenharmony_ci}; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic struct clk_alpha_pll perfcl_pll = { 1158c2ecf20Sopenharmony_ci .offset = PERFCL_REG_OFFSET, 1168c2ecf20Sopenharmony_ci .regs = prim_pll_regs, 1178c2ecf20Sopenharmony_ci .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, 1188c2ecf20Sopenharmony_ci .clkr.hw.init = &(struct clk_init_data){ 1198c2ecf20Sopenharmony_ci .name = "perfcl_pll", 1208c2ecf20Sopenharmony_ci .parent_names = (const char *[]){ "xo" }, 1218c2ecf20Sopenharmony_ci .num_parents = 1, 1228c2ecf20Sopenharmony_ci .ops = &clk_alpha_pll_huayra_ops, 1238c2ecf20Sopenharmony_ci }, 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic struct clk_alpha_pll pwrcl_pll = { 1278c2ecf20Sopenharmony_ci .offset = PWRCL_REG_OFFSET, 1288c2ecf20Sopenharmony_ci .regs = prim_pll_regs, 1298c2ecf20Sopenharmony_ci .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, 1308c2ecf20Sopenharmony_ci .clkr.hw.init = &(struct clk_init_data){ 1318c2ecf20Sopenharmony_ci .name = "pwrcl_pll", 1328c2ecf20Sopenharmony_ci .parent_names = (const char *[]){ "xo" }, 1338c2ecf20Sopenharmony_ci .num_parents = 1, 1348c2ecf20Sopenharmony_ci .ops = &clk_alpha_pll_huayra_ops, 1358c2ecf20Sopenharmony_ci }, 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic const struct pll_vco alt_pll_vco_modes[] = { 1398c2ecf20Sopenharmony_ci VCO(3, 250000000, 500000000), 1408c2ecf20Sopenharmony_ci VCO(2, 500000000, 750000000), 1418c2ecf20Sopenharmony_ci VCO(1, 750000000, 1000000000), 1428c2ecf20Sopenharmony_ci VCO(0, 1000000000, 2150400000), 1438c2ecf20Sopenharmony_ci}; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic const struct alpha_pll_config altpll_config = { 1468c2ecf20Sopenharmony_ci .l = 16, 1478c2ecf20Sopenharmony_ci .vco_val = 0x3 << 20, 1488c2ecf20Sopenharmony_ci .vco_mask = 0x3 << 20, 1498c2ecf20Sopenharmony_ci .config_ctl_val = 0x4001051b, 1508c2ecf20Sopenharmony_ci .post_div_mask = 0x3 << 8, 1518c2ecf20Sopenharmony_ci .post_div_val = 0x1 << 8, 1528c2ecf20Sopenharmony_ci .main_output_mask = BIT(0), 1538c2ecf20Sopenharmony_ci .early_output_mask = BIT(3), 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic struct clk_alpha_pll perfcl_alt_pll = { 1578c2ecf20Sopenharmony_ci .offset = PERFCL_REG_OFFSET + ALT_PLL_OFFSET, 1588c2ecf20Sopenharmony_ci .regs = alt_pll_regs, 1598c2ecf20Sopenharmony_ci .vco_table = alt_pll_vco_modes, 1608c2ecf20Sopenharmony_ci .num_vco = ARRAY_SIZE(alt_pll_vco_modes), 1618c2ecf20Sopenharmony_ci .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, 1628c2ecf20Sopenharmony_ci .clkr.hw.init = &(struct clk_init_data) { 1638c2ecf20Sopenharmony_ci .name = "perfcl_alt_pll", 1648c2ecf20Sopenharmony_ci .parent_names = (const char *[]){ "xo" }, 1658c2ecf20Sopenharmony_ci .num_parents = 1, 1668c2ecf20Sopenharmony_ci .ops = &clk_alpha_pll_hwfsm_ops, 1678c2ecf20Sopenharmony_ci }, 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic struct clk_alpha_pll pwrcl_alt_pll = { 1718c2ecf20Sopenharmony_ci .offset = PWRCL_REG_OFFSET + ALT_PLL_OFFSET, 1728c2ecf20Sopenharmony_ci .regs = alt_pll_regs, 1738c2ecf20Sopenharmony_ci .vco_table = alt_pll_vco_modes, 1748c2ecf20Sopenharmony_ci .num_vco = ARRAY_SIZE(alt_pll_vco_modes), 1758c2ecf20Sopenharmony_ci .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, 1768c2ecf20Sopenharmony_ci .clkr.hw.init = &(struct clk_init_data) { 1778c2ecf20Sopenharmony_ci .name = "pwrcl_alt_pll", 1788c2ecf20Sopenharmony_ci .parent_names = (const char *[]){ "xo" }, 1798c2ecf20Sopenharmony_ci .num_parents = 1, 1808c2ecf20Sopenharmony_ci .ops = &clk_alpha_pll_hwfsm_ops, 1818c2ecf20Sopenharmony_ci }, 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistruct clk_cpu_8996_mux { 1858c2ecf20Sopenharmony_ci u32 reg; 1868c2ecf20Sopenharmony_ci u8 shift; 1878c2ecf20Sopenharmony_ci u8 width; 1888c2ecf20Sopenharmony_ci struct notifier_block nb; 1898c2ecf20Sopenharmony_ci struct clk_hw *pll; 1908c2ecf20Sopenharmony_ci struct clk_hw *pll_div_2; 1918c2ecf20Sopenharmony_ci struct clk_regmap clkr; 1928c2ecf20Sopenharmony_ci}; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, 1958c2ecf20Sopenharmony_ci void *data); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci#define to_clk_cpu_8996_mux_nb(_nb) \ 1988c2ecf20Sopenharmony_ci container_of(_nb, struct clk_cpu_8996_mux, nb) 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic inline struct clk_cpu_8996_mux *to_clk_cpu_8996_mux_hw(struct clk_hw *hw) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci return container_of(to_clk_regmap(hw), struct clk_cpu_8996_mux, clkr); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic u8 clk_cpu_8996_mux_get_parent(struct clk_hw *hw) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct clk_regmap *clkr = to_clk_regmap(hw); 2088c2ecf20Sopenharmony_ci struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw); 2098c2ecf20Sopenharmony_ci u32 mask = GENMASK(cpuclk->width - 1, 0); 2108c2ecf20Sopenharmony_ci u32 val; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci regmap_read(clkr->regmap, cpuclk->reg, &val); 2138c2ecf20Sopenharmony_ci val >>= cpuclk->shift; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return val & mask; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct clk_regmap *clkr = to_clk_regmap(hw); 2218c2ecf20Sopenharmony_ci struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw); 2228c2ecf20Sopenharmony_ci u32 mask = GENMASK(cpuclk->width + cpuclk->shift - 1, cpuclk->shift); 2238c2ecf20Sopenharmony_ci u32 val; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci val = index; 2268c2ecf20Sopenharmony_ci val <<= cpuclk->shift; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return regmap_update_bits(clkr->regmap, cpuclk->reg, mask, val); 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic int clk_cpu_8996_mux_determine_rate(struct clk_hw *hw, 2328c2ecf20Sopenharmony_ci struct clk_rate_request *req) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw); 2358c2ecf20Sopenharmony_ci struct clk_hw *parent = cpuclk->pll; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (cpuclk->pll_div_2 && req->rate < DIV_2_THRESHOLD) { 2388c2ecf20Sopenharmony_ci if (req->rate < (DIV_2_THRESHOLD / 2)) 2398c2ecf20Sopenharmony_ci return -EINVAL; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci parent = cpuclk->pll_div_2; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci req->best_parent_rate = clk_hw_round_rate(parent, req->rate); 2458c2ecf20Sopenharmony_ci req->best_parent_hw = parent; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic const struct clk_ops clk_cpu_8996_mux_ops = { 2518c2ecf20Sopenharmony_ci .set_parent = clk_cpu_8996_mux_set_parent, 2528c2ecf20Sopenharmony_ci .get_parent = clk_cpu_8996_mux_get_parent, 2538c2ecf20Sopenharmony_ci .determine_rate = clk_cpu_8996_mux_determine_rate, 2548c2ecf20Sopenharmony_ci}; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic struct clk_cpu_8996_mux pwrcl_smux = { 2578c2ecf20Sopenharmony_ci .reg = PWRCL_REG_OFFSET + MUX_OFFSET, 2588c2ecf20Sopenharmony_ci .shift = 2, 2598c2ecf20Sopenharmony_ci .width = 2, 2608c2ecf20Sopenharmony_ci .clkr.hw.init = &(struct clk_init_data) { 2618c2ecf20Sopenharmony_ci .name = "pwrcl_smux", 2628c2ecf20Sopenharmony_ci .parent_names = (const char *[]){ 2638c2ecf20Sopenharmony_ci "xo", 2648c2ecf20Sopenharmony_ci "pwrcl_pll_main", 2658c2ecf20Sopenharmony_ci }, 2668c2ecf20Sopenharmony_ci .num_parents = 2, 2678c2ecf20Sopenharmony_ci .ops = &clk_cpu_8996_mux_ops, 2688c2ecf20Sopenharmony_ci .flags = CLK_SET_RATE_PARENT, 2698c2ecf20Sopenharmony_ci }, 2708c2ecf20Sopenharmony_ci}; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic struct clk_cpu_8996_mux perfcl_smux = { 2738c2ecf20Sopenharmony_ci .reg = PERFCL_REG_OFFSET + MUX_OFFSET, 2748c2ecf20Sopenharmony_ci .shift = 2, 2758c2ecf20Sopenharmony_ci .width = 2, 2768c2ecf20Sopenharmony_ci .clkr.hw.init = &(struct clk_init_data) { 2778c2ecf20Sopenharmony_ci .name = "perfcl_smux", 2788c2ecf20Sopenharmony_ci .parent_names = (const char *[]){ 2798c2ecf20Sopenharmony_ci "xo", 2808c2ecf20Sopenharmony_ci "perfcl_pll_main", 2818c2ecf20Sopenharmony_ci }, 2828c2ecf20Sopenharmony_ci .num_parents = 2, 2838c2ecf20Sopenharmony_ci .ops = &clk_cpu_8996_mux_ops, 2848c2ecf20Sopenharmony_ci .flags = CLK_SET_RATE_PARENT, 2858c2ecf20Sopenharmony_ci }, 2868c2ecf20Sopenharmony_ci}; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic struct clk_cpu_8996_mux pwrcl_pmux = { 2898c2ecf20Sopenharmony_ci .reg = PWRCL_REG_OFFSET + MUX_OFFSET, 2908c2ecf20Sopenharmony_ci .shift = 0, 2918c2ecf20Sopenharmony_ci .width = 2, 2928c2ecf20Sopenharmony_ci .pll = &pwrcl_pll.clkr.hw, 2938c2ecf20Sopenharmony_ci .pll_div_2 = &pwrcl_smux.clkr.hw, 2948c2ecf20Sopenharmony_ci .nb.notifier_call = cpu_clk_notifier_cb, 2958c2ecf20Sopenharmony_ci .clkr.hw.init = &(struct clk_init_data) { 2968c2ecf20Sopenharmony_ci .name = "pwrcl_pmux", 2978c2ecf20Sopenharmony_ci .parent_names = (const char *[]){ 2988c2ecf20Sopenharmony_ci "pwrcl_smux", 2998c2ecf20Sopenharmony_ci "pwrcl_pll", 3008c2ecf20Sopenharmony_ci "pwrcl_pll_acd", 3018c2ecf20Sopenharmony_ci "pwrcl_alt_pll", 3028c2ecf20Sopenharmony_ci }, 3038c2ecf20Sopenharmony_ci .num_parents = 4, 3048c2ecf20Sopenharmony_ci .ops = &clk_cpu_8996_mux_ops, 3058c2ecf20Sopenharmony_ci /* CPU clock is critical and should never be gated */ 3068c2ecf20Sopenharmony_ci .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 3078c2ecf20Sopenharmony_ci }, 3088c2ecf20Sopenharmony_ci}; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic struct clk_cpu_8996_mux perfcl_pmux = { 3118c2ecf20Sopenharmony_ci .reg = PERFCL_REG_OFFSET + MUX_OFFSET, 3128c2ecf20Sopenharmony_ci .shift = 0, 3138c2ecf20Sopenharmony_ci .width = 2, 3148c2ecf20Sopenharmony_ci .pll = &perfcl_pll.clkr.hw, 3158c2ecf20Sopenharmony_ci .pll_div_2 = &perfcl_smux.clkr.hw, 3168c2ecf20Sopenharmony_ci .nb.notifier_call = cpu_clk_notifier_cb, 3178c2ecf20Sopenharmony_ci .clkr.hw.init = &(struct clk_init_data) { 3188c2ecf20Sopenharmony_ci .name = "perfcl_pmux", 3198c2ecf20Sopenharmony_ci .parent_names = (const char *[]){ 3208c2ecf20Sopenharmony_ci "perfcl_smux", 3218c2ecf20Sopenharmony_ci "perfcl_pll", 3228c2ecf20Sopenharmony_ci "perfcl_pll_acd", 3238c2ecf20Sopenharmony_ci "perfcl_alt_pll", 3248c2ecf20Sopenharmony_ci }, 3258c2ecf20Sopenharmony_ci .num_parents = 4, 3268c2ecf20Sopenharmony_ci .ops = &clk_cpu_8996_mux_ops, 3278c2ecf20Sopenharmony_ci /* CPU clock is critical and should never be gated */ 3288c2ecf20Sopenharmony_ci .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 3298c2ecf20Sopenharmony_ci }, 3308c2ecf20Sopenharmony_ci}; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic const struct regmap_config cpu_msm8996_regmap_config = { 3338c2ecf20Sopenharmony_ci .reg_bits = 32, 3348c2ecf20Sopenharmony_ci .reg_stride = 4, 3358c2ecf20Sopenharmony_ci .val_bits = 32, 3368c2ecf20Sopenharmony_ci .max_register = 0x80210, 3378c2ecf20Sopenharmony_ci .fast_io = true, 3388c2ecf20Sopenharmony_ci .val_format_endian = REGMAP_ENDIAN_LITTLE, 3398c2ecf20Sopenharmony_ci}; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic struct clk_regmap *cpu_msm8996_clks[] = { 3428c2ecf20Sopenharmony_ci &perfcl_pll.clkr, 3438c2ecf20Sopenharmony_ci &pwrcl_pll.clkr, 3448c2ecf20Sopenharmony_ci &perfcl_alt_pll.clkr, 3458c2ecf20Sopenharmony_ci &pwrcl_alt_pll.clkr, 3468c2ecf20Sopenharmony_ci &perfcl_smux.clkr, 3478c2ecf20Sopenharmony_ci &pwrcl_smux.clkr, 3488c2ecf20Sopenharmony_ci &perfcl_pmux.clkr, 3498c2ecf20Sopenharmony_ci &pwrcl_pmux.clkr, 3508c2ecf20Sopenharmony_ci}; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic int qcom_cpu_clk_msm8996_register_clks(struct device *dev, 3538c2ecf20Sopenharmony_ci struct regmap *regmap) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci int i, ret; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci perfcl_smux.pll = clk_hw_register_fixed_factor(dev, "perfcl_pll_main", 3588c2ecf20Sopenharmony_ci "perfcl_pll", 3598c2ecf20Sopenharmony_ci CLK_SET_RATE_PARENT, 3608c2ecf20Sopenharmony_ci 1, 2); 3618c2ecf20Sopenharmony_ci if (IS_ERR(perfcl_smux.pll)) { 3628c2ecf20Sopenharmony_ci dev_err(dev, "Failed to initialize perfcl_pll_main\n"); 3638c2ecf20Sopenharmony_ci return PTR_ERR(perfcl_smux.pll); 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci pwrcl_smux.pll = clk_hw_register_fixed_factor(dev, "pwrcl_pll_main", 3678c2ecf20Sopenharmony_ci "pwrcl_pll", 3688c2ecf20Sopenharmony_ci CLK_SET_RATE_PARENT, 3698c2ecf20Sopenharmony_ci 1, 2); 3708c2ecf20Sopenharmony_ci if (IS_ERR(pwrcl_smux.pll)) { 3718c2ecf20Sopenharmony_ci dev_err(dev, "Failed to initialize pwrcl_pll_main\n"); 3728c2ecf20Sopenharmony_ci clk_hw_unregister(perfcl_smux.pll); 3738c2ecf20Sopenharmony_ci return PTR_ERR(pwrcl_smux.pll); 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cpu_msm8996_clks); i++) { 3778c2ecf20Sopenharmony_ci ret = devm_clk_register_regmap(dev, cpu_msm8996_clks[i]); 3788c2ecf20Sopenharmony_ci if (ret) { 3798c2ecf20Sopenharmony_ci clk_hw_unregister(perfcl_smux.pll); 3808c2ecf20Sopenharmony_ci clk_hw_unregister(pwrcl_smux.pll); 3818c2ecf20Sopenharmony_ci return ret; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci clk_alpha_pll_configure(&perfcl_pll, regmap, &hfpll_config); 3868c2ecf20Sopenharmony_ci clk_alpha_pll_configure(&pwrcl_pll, regmap, &hfpll_config); 3878c2ecf20Sopenharmony_ci clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config); 3888c2ecf20Sopenharmony_ci clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* Enable alt PLLs */ 3918c2ecf20Sopenharmony_ci clk_prepare_enable(pwrcl_alt_pll.clkr.hw.clk); 3928c2ecf20Sopenharmony_ci clk_prepare_enable(perfcl_alt_pll.clkr.hw.clk); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci clk_notifier_register(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb); 3958c2ecf20Sopenharmony_ci clk_notifier_register(perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci return ret; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic int qcom_cpu_clk_msm8996_unregister_clks(void) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci int ret = 0; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci ret = clk_notifier_unregister(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb); 4058c2ecf20Sopenharmony_ci if (ret) 4068c2ecf20Sopenharmony_ci return ret; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci ret = clk_notifier_unregister(perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb); 4098c2ecf20Sopenharmony_ci if (ret) 4108c2ecf20Sopenharmony_ci return ret; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci clk_hw_unregister(perfcl_smux.pll); 4138c2ecf20Sopenharmony_ci clk_hw_unregister(pwrcl_smux.pll); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci return 0; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci#define CPU_AFINITY_MASK 0xFFF 4198c2ecf20Sopenharmony_ci#define PWRCL_CPU_REG_MASK 0x3 4208c2ecf20Sopenharmony_ci#define PERFCL_CPU_REG_MASK 0x103 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci#define L2ACDCR_REG 0x580ULL 4238c2ecf20Sopenharmony_ci#define L2ACDTD_REG 0x581ULL 4248c2ecf20Sopenharmony_ci#define L2ACDDVMRC_REG 0x584ULL 4258c2ecf20Sopenharmony_ci#define L2ACDSSCR_REG 0x589ULL 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(qcom_clk_acd_lock); 4288c2ecf20Sopenharmony_cistatic void __iomem *base; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic void qcom_cpu_clk_msm8996_acd_init(void __iomem *base) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci u64 hwid; 4338c2ecf20Sopenharmony_ci unsigned long flags; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci spin_lock_irqsave(&qcom_clk_acd_lock, flags); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci hwid = read_cpuid_mpidr() & CPU_AFINITY_MASK; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci kryo_l2_set_indirect_reg(L2ACDTD_REG, 0x00006a11); 4408c2ecf20Sopenharmony_ci kryo_l2_set_indirect_reg(L2ACDDVMRC_REG, 0x000e0f0f); 4418c2ecf20Sopenharmony_ci kryo_l2_set_indirect_reg(L2ACDSSCR_REG, 0x00000601); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (PWRCL_CPU_REG_MASK == (hwid | PWRCL_CPU_REG_MASK)) { 4448c2ecf20Sopenharmony_ci writel(0xf, base + PWRCL_REG_OFFSET + SSSCTL_OFFSET); 4458c2ecf20Sopenharmony_ci kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd); 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (PERFCL_CPU_REG_MASK == (hwid | PERFCL_CPU_REG_MASK)) { 4498c2ecf20Sopenharmony_ci kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd); 4508c2ecf20Sopenharmony_ci writel(0xf, base + PERFCL_REG_OFFSET + SSSCTL_OFFSET); 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&qcom_clk_acd_lock, flags); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, 4578c2ecf20Sopenharmony_ci void *data) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_nb(nb); 4608c2ecf20Sopenharmony_ci struct clk_notifier_data *cnd = data; 4618c2ecf20Sopenharmony_ci int ret; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci switch (event) { 4648c2ecf20Sopenharmony_ci case PRE_RATE_CHANGE: 4658c2ecf20Sopenharmony_ci ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, ALT_INDEX); 4668c2ecf20Sopenharmony_ci qcom_cpu_clk_msm8996_acd_init(base); 4678c2ecf20Sopenharmony_ci break; 4688c2ecf20Sopenharmony_ci case POST_RATE_CHANGE: 4698c2ecf20Sopenharmony_ci if (cnd->new_rate < DIV_2_THRESHOLD) 4708c2ecf20Sopenharmony_ci ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, 4718c2ecf20Sopenharmony_ci DIV_2_INDEX); 4728c2ecf20Sopenharmony_ci else 4738c2ecf20Sopenharmony_ci ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, 4748c2ecf20Sopenharmony_ci ACD_INDEX); 4758c2ecf20Sopenharmony_ci break; 4768c2ecf20Sopenharmony_ci default: 4778c2ecf20Sopenharmony_ci ret = 0; 4788c2ecf20Sopenharmony_ci break; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci return notifier_from_errno(ret); 4828c2ecf20Sopenharmony_ci}; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci struct regmap *regmap; 4878c2ecf20Sopenharmony_ci struct clk_hw_onecell_data *data; 4888c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 4898c2ecf20Sopenharmony_ci int ret; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci data = devm_kzalloc(dev, struct_size(data, hws, 2), GFP_KERNEL); 4928c2ecf20Sopenharmony_ci if (!data) 4938c2ecf20Sopenharmony_ci return -ENOMEM; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci base = devm_platform_ioremap_resource(pdev, 0); 4968c2ecf20Sopenharmony_ci if (IS_ERR(base)) 4978c2ecf20Sopenharmony_ci return PTR_ERR(base); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci regmap = devm_regmap_init_mmio(dev, base, &cpu_msm8996_regmap_config); 5008c2ecf20Sopenharmony_ci if (IS_ERR(regmap)) 5018c2ecf20Sopenharmony_ci return PTR_ERR(regmap); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci ret = qcom_cpu_clk_msm8996_register_clks(dev, regmap); 5048c2ecf20Sopenharmony_ci if (ret) 5058c2ecf20Sopenharmony_ci return ret; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci qcom_cpu_clk_msm8996_acd_init(base); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci data->hws[0] = &pwrcl_pmux.clkr.hw; 5108c2ecf20Sopenharmony_ci data->hws[1] = &perfcl_pmux.clkr.hw; 5118c2ecf20Sopenharmony_ci data->num = 2; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data); 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic int qcom_cpu_clk_msm8996_driver_remove(struct platform_device *pdev) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci return qcom_cpu_clk_msm8996_unregister_clks(); 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic const struct of_device_id qcom_cpu_clk_msm8996_match_table[] = { 5228c2ecf20Sopenharmony_ci { .compatible = "qcom,msm8996-apcc" }, 5238c2ecf20Sopenharmony_ci {} 5248c2ecf20Sopenharmony_ci}; 5258c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_cpu_clk_msm8996_match_table); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic struct platform_driver qcom_cpu_clk_msm8996_driver = { 5288c2ecf20Sopenharmony_ci .probe = qcom_cpu_clk_msm8996_driver_probe, 5298c2ecf20Sopenharmony_ci .remove = qcom_cpu_clk_msm8996_driver_remove, 5308c2ecf20Sopenharmony_ci .driver = { 5318c2ecf20Sopenharmony_ci .name = "qcom-msm8996-apcc", 5328c2ecf20Sopenharmony_ci .of_match_table = qcom_cpu_clk_msm8996_match_table, 5338c2ecf20Sopenharmony_ci }, 5348c2ecf20Sopenharmony_ci}; 5358c2ecf20Sopenharmony_cimodule_platform_driver(qcom_cpu_clk_msm8996_driver); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("QCOM MSM8996 CPU Clock Driver"); 5388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 539