2020-07-03 10:49:42 +02:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( c ) 2020 , The Linux Foundation . All rights reserved .
*/
/*
* Each of the CPU clusters ( Power and Perf ) on msm8996 are
* clocked via 2 PLLs , a primary and alternate . There are also
* 2 Mux ' es , a primary and secondary all connected together
* as shown below
*
* + - - - - - - - +
* XO | |
* + - - - - - - - - - - - - - - - - - - > 0 |
2023-01-13 14:05:35 +02:00
* SYS_APCS_AUX | |
* + - - - - - - - - - - - - - - - - - - > 3 |
2020-07-03 10:49:42 +02:00
* | |
* PLL / 2 | SMUX + - - - - +
* + - - - - - - - > 1 | |
* | | | |
* | + - - - - - - - + | + - - - - - - - +
* | + - - - - > 0 |
* | | |
* + - - - - - - - - - - - - - - - + | + - - - - - - - - - - - > 1 | CPU clk
* | Primary PLL + - - - - + PLL_EARLY | | + - - - - - - >
* | + - - - - - - + - - - - - - - - - - - + + - - - - - - > 2 PMUX |
* + - - - - - - - - - - - - - - - + | | | |
* | + - - - - - - + | + - - > 3 |
* + - - ^ + ACD + - - - - - + | + - - - - - - - +
* + - - - - - - - - - - - - - - - + + - - - - - - + |
* | Alt PLL | |
* | + - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* + - - - - - - - - - - - - - - - + PLL_EARLY
*
* The primary PLL is what drives the CPU clk , except for times
* when we are reprogramming the PLL itself ( for rate changes ) when
* we temporarily switch to an alternate PLL .
*
* The primary PLL operates on a single VCO range , between 600 MHz
* and 3 GHz . However the CPUs do support OPPs with frequencies
* between 300 MHz and 600 MHz . In order to support running the CPUs
* at those frequencies we end up having to lock the PLL at twice
* the rate and drive the CPU clk via the PLL / 2 output and SMUX .
*
* So for frequencies above 600 MHz we follow the following path
* Primary PLL - - > PLL_EARLY - - > PMUX ( 1 ) - - > CPU clk
* and for frequencies between 300 MHz and 600 MHz we follow
* Primary PLL - - > PLL / 2 - - > SMUX ( 1 ) - - > PMUX ( 0 ) - - > CPU clk
*
* ACD stands for Adaptive Clock Distribution and is used to
* detect voltage droops .
*/
2022-07-14 13:03:51 +03:00
# include <linux/bitfield.h>
2020-07-03 10:49:42 +02:00
# include <linux/clk.h>
# include <linux/clk-provider.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <soc/qcom/kryo-l2-accessors.h>
2023-01-23 21:18:10 +01:00
# include <asm/cputype.h>
2020-07-03 10:49:42 +02:00
# include "clk-alpha-pll.h"
# include "clk-regmap.h"
2022-06-21 20:06:19 +04:00
# include "clk-regmap-mux.h"
2020-07-03 10:49:42 +02:00
enum _pmux_input {
2022-06-21 20:06:16 +04:00
SMUX_INDEX = 0 ,
2020-07-03 10:49:42 +02:00
PLL_INDEX ,
ACD_INDEX ,
ALT_INDEX ,
NUM_OF_PMUX_INPUTS
} ;
# define DIV_2_THRESHOLD 600000000
# define PWRCL_REG_OFFSET 0x0
# define PERFCL_REG_OFFSET 0x80000
# define MUX_OFFSET 0x40
2023-01-13 14:05:43 +02:00
# define CLK_CTL_OFFSET 0x44
# define CLK_CTL_AUTO_CLK_SEL BIT(8)
2020-07-03 10:49:42 +02:00
# define ALT_PLL_OFFSET 0x100
# define SSSCTL_OFFSET 0x160
2023-01-13 14:05:43 +02:00
# define PSCTL_OFFSET 0x164
2020-07-03 10:49:42 +02:00
2022-07-14 13:03:51 +03:00
# define PMUX_MASK 0x3
2023-01-13 14:05:43 +02:00
# define MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK GENMASK(5, 4)
# define MUX_AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL \
FIELD_PREP ( MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK , 0x03 )
2022-07-14 13:03:51 +03:00
2020-07-03 10:49:42 +02:00
static const u8 prim_pll_regs [ PLL_OFF_MAX_REGS ] = {
[ PLL_OFF_L_VAL ] = 0x04 ,
[ PLL_OFF_ALPHA_VAL ] = 0x08 ,
[ PLL_OFF_USER_CTL ] = 0x10 ,
[ PLL_OFF_CONFIG_CTL ] = 0x18 ,
[ PLL_OFF_CONFIG_CTL_U ] = 0x1c ,
[ PLL_OFF_TEST_CTL ] = 0x20 ,
[ PLL_OFF_TEST_CTL_U ] = 0x24 ,
[ PLL_OFF_STATUS ] = 0x28 ,
} ;
static const u8 alt_pll_regs [ PLL_OFF_MAX_REGS ] = {
[ PLL_OFF_L_VAL ] = 0x04 ,
[ PLL_OFF_ALPHA_VAL ] = 0x08 ,
[ PLL_OFF_USER_CTL ] = 0x10 ,
[ PLL_OFF_CONFIG_CTL ] = 0x18 ,
[ PLL_OFF_TEST_CTL ] = 0x20 ,
[ PLL_OFF_STATUS ] = 0x28 ,
} ;
/* PLLs */
static const struct alpha_pll_config hfpll_config = {
2023-01-13 14:05:34 +02:00
. l = 54 ,
2023-01-13 14:05:33 +02:00
. config_ctl_val = 0x200d4828 ,
2020-07-03 10:49:42 +02:00
. config_ctl_hi_val = 0x006 ,
2023-01-13 14:05:33 +02:00
. test_ctl_val = 0x1c000000 ,
. test_ctl_hi_val = 0x00004000 ,
2020-07-03 10:49:42 +02:00
. pre_div_mask = BIT ( 12 ) ,
. post_div_mask = 0x3 < < 8 ,
. post_div_val = 0x1 < < 8 ,
. main_output_mask = BIT ( 0 ) ,
. early_output_mask = BIT ( 3 ) ,
} ;
2022-07-14 13:03:46 +03:00
static const struct clk_parent_data pll_parent [ ] = {
{ . fw_name = " xo " } ,
} ;
2022-06-21 20:06:18 +04:00
static struct clk_alpha_pll pwrcl_pll = {
. offset = PWRCL_REG_OFFSET ,
2020-07-03 10:49:42 +02:00
. regs = prim_pll_regs ,
. flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE ,
. clkr . hw . init = & ( struct clk_init_data ) {
2022-06-21 20:06:18 +04:00
. name = " pwrcl_pll " ,
2022-07-14 13:03:46 +03:00
. parent_data = pll_parent ,
. num_parents = ARRAY_SIZE ( pll_parent ) ,
2023-01-13 14:05:42 +02:00
. ops = & clk_alpha_pll_hwfsm_ops ,
2020-07-03 10:49:42 +02:00
} ,
} ;
2022-06-21 20:06:18 +04:00
static struct clk_alpha_pll perfcl_pll = {
. offset = PERFCL_REG_OFFSET ,
2020-07-03 10:49:42 +02:00
. regs = prim_pll_regs ,
. flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE ,
. clkr . hw . init = & ( struct clk_init_data ) {
2022-06-21 20:06:18 +04:00
. name = " perfcl_pll " ,
2022-07-14 13:03:46 +03:00
. parent_data = pll_parent ,
. num_parents = ARRAY_SIZE ( pll_parent ) ,
2023-01-13 14:05:42 +02:00
. ops = & clk_alpha_pll_hwfsm_ops ,
2020-07-03 10:49:42 +02:00
} ,
} ;
2022-06-21 20:06:17 +04:00
static struct clk_fixed_factor pwrcl_pll_postdiv = {
. mult = 1 ,
. div = 2 ,
. hw . init = & ( struct clk_init_data ) {
. name = " pwrcl_pll_postdiv " ,
. parent_data = & ( const struct clk_parent_data ) {
. hw = & pwrcl_pll . clkr . hw
} ,
. num_parents = 1 ,
. ops = & clk_fixed_factor_ops ,
. flags = CLK_SET_RATE_PARENT ,
} ,
} ;
static struct clk_fixed_factor perfcl_pll_postdiv = {
. mult = 1 ,
. div = 2 ,
. hw . init = & ( struct clk_init_data ) {
. name = " perfcl_pll_postdiv " ,
. parent_data = & ( const struct clk_parent_data ) {
. hw = & perfcl_pll . clkr . hw
} ,
. num_parents = 1 ,
. ops = & clk_fixed_factor_ops ,
. flags = CLK_SET_RATE_PARENT ,
} ,
} ;
2022-07-14 13:03:48 +03:00
static struct clk_fixed_factor perfcl_pll_acd = {
. mult = 1 ,
. div = 1 ,
. hw . init = & ( struct clk_init_data ) {
. name = " perfcl_pll_acd " ,
. parent_data = & ( const struct clk_parent_data ) {
. hw = & perfcl_pll . clkr . hw
} ,
. num_parents = 1 ,
. ops = & clk_fixed_factor_ops ,
. flags = CLK_SET_RATE_PARENT ,
} ,
} ;
static struct clk_fixed_factor pwrcl_pll_acd = {
. mult = 1 ,
. div = 1 ,
. hw . init = & ( struct clk_init_data ) {
. name = " pwrcl_pll_acd " ,
. parent_data = & ( const struct clk_parent_data ) {
. hw = & pwrcl_pll . clkr . hw
} ,
. num_parents = 1 ,
. ops = & clk_fixed_factor_ops ,
. flags = CLK_SET_RATE_PARENT ,
} ,
} ;
2020-07-03 10:49:42 +02:00
static const struct pll_vco alt_pll_vco_modes [ ] = {
VCO ( 3 , 250000000 , 500000000 ) ,
VCO ( 2 , 500000000 , 750000000 ) ,
VCO ( 1 , 750000000 , 1000000000 ) ,
VCO ( 0 , 1000000000 , 2150400000 ) ,
} ;
static const struct alpha_pll_config altpll_config = {
. l = 16 ,
. vco_val = 0x3 < < 20 ,
. vco_mask = 0x3 < < 20 ,
. config_ctl_val = 0x4001051b ,
. post_div_mask = 0x3 < < 8 ,
. post_div_val = 0x1 < < 8 ,
. main_output_mask = BIT ( 0 ) ,
. early_output_mask = BIT ( 3 ) ,
} ;
2022-06-21 20:06:18 +04:00
static struct clk_alpha_pll pwrcl_alt_pll = {
. offset = PWRCL_REG_OFFSET + ALT_PLL_OFFSET ,
2020-07-03 10:49:42 +02:00
. regs = alt_pll_regs ,
. vco_table = alt_pll_vco_modes ,
. num_vco = ARRAY_SIZE ( alt_pll_vco_modes ) ,
. flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE ,
. clkr . hw . init = & ( struct clk_init_data ) {
2022-06-21 20:06:18 +04:00
. name = " pwrcl_alt_pll " ,
2022-07-14 13:03:46 +03:00
. parent_data = pll_parent ,
. num_parents = ARRAY_SIZE ( pll_parent ) ,
2020-07-03 10:49:42 +02:00
. ops = & clk_alpha_pll_hwfsm_ops ,
} ,
} ;
2022-06-21 20:06:18 +04:00
static struct clk_alpha_pll perfcl_alt_pll = {
. offset = PERFCL_REG_OFFSET + ALT_PLL_OFFSET ,
2020-07-03 10:49:42 +02:00
. regs = alt_pll_regs ,
. vco_table = alt_pll_vco_modes ,
. num_vco = ARRAY_SIZE ( alt_pll_vco_modes ) ,
. flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE ,
. clkr . hw . init = & ( struct clk_init_data ) {
2022-06-21 20:06:18 +04:00
. name = " perfcl_alt_pll " ,
2022-07-14 13:03:46 +03:00
. parent_data = pll_parent ,
. num_parents = ARRAY_SIZE ( pll_parent ) ,
2020-07-03 10:49:42 +02:00
. ops = & clk_alpha_pll_hwfsm_ops ,
} ,
} ;
2022-06-21 20:06:19 +04:00
struct clk_cpu_8996_pmux {
2020-07-03 10:49:42 +02:00
u32 reg ;
struct notifier_block nb ;
struct clk_regmap clkr ;
} ;
static int cpu_clk_notifier_cb ( struct notifier_block * nb , unsigned long event ,
void * data ) ;
2022-06-21 20:06:19 +04:00
# define to_clk_cpu_8996_pmux_nb(_nb) \
container_of ( _nb , struct clk_cpu_8996_pmux , nb )
2020-07-03 10:49:42 +02:00
2022-06-21 20:06:19 +04:00
static inline struct clk_cpu_8996_pmux * to_clk_cpu_8996_pmux_hw ( struct clk_hw * hw )
2020-07-03 10:49:42 +02:00
{
2022-06-21 20:06:19 +04:00
return container_of ( to_clk_regmap ( hw ) , struct clk_cpu_8996_pmux , clkr ) ;
2020-07-03 10:49:42 +02:00
}
2022-06-21 20:06:19 +04:00
static u8 clk_cpu_8996_pmux_get_parent ( struct clk_hw * hw )
2020-07-03 10:49:42 +02:00
{
struct clk_regmap * clkr = to_clk_regmap ( hw ) ;
2022-06-21 20:06:19 +04:00
struct clk_cpu_8996_pmux * cpuclk = to_clk_cpu_8996_pmux_hw ( hw ) ;
2020-07-03 10:49:42 +02:00
u32 val ;
regmap_read ( clkr - > regmap , cpuclk - > reg , & val ) ;
2022-07-14 13:03:51 +03:00
return FIELD_GET ( PMUX_MASK , val ) ;
2020-07-03 10:49:42 +02:00
}
2022-06-21 20:06:19 +04:00
static int clk_cpu_8996_pmux_set_parent ( struct clk_hw * hw , u8 index )
2020-07-03 10:49:42 +02:00
{
struct clk_regmap * clkr = to_clk_regmap ( hw ) ;
2022-06-21 20:06:19 +04:00
struct clk_cpu_8996_pmux * cpuclk = to_clk_cpu_8996_pmux_hw ( hw ) ;
2020-07-03 10:49:42 +02:00
u32 val ;
2022-07-14 13:03:51 +03:00
val = FIELD_PREP ( PMUX_MASK , index ) ;
2020-07-03 10:49:42 +02:00
2022-07-14 13:03:51 +03:00
return regmap_update_bits ( clkr - > regmap , cpuclk - > reg , PMUX_MASK , val ) ;
2020-07-03 10:49:42 +02:00
}
2022-06-21 20:06:19 +04:00
static int clk_cpu_8996_pmux_determine_rate ( struct clk_hw * hw ,
2020-07-03 10:49:42 +02:00
struct clk_rate_request * req )
{
2022-07-14 13:03:50 +03:00
struct clk_hw * parent ;
2020-07-03 10:49:42 +02:00
2022-07-14 13:03:50 +03:00
if ( req - > rate < ( DIV_2_THRESHOLD / 2 ) )
return - EINVAL ;
2020-07-03 10:49:42 +02:00
2022-07-14 13:03:50 +03:00
if ( req - > rate < DIV_2_THRESHOLD )
parent = clk_hw_get_parent_by_index ( hw , SMUX_INDEX ) ;
else
parent = clk_hw_get_parent_by_index ( hw , ACD_INDEX ) ;
if ( ! parent )
return - EINVAL ;
2020-07-03 10:49:42 +02:00
req - > best_parent_rate = clk_hw_round_rate ( parent , req - > rate ) ;
req - > best_parent_hw = parent ;
return 0 ;
}
2022-06-21 20:06:19 +04:00
static const struct clk_ops clk_cpu_8996_pmux_ops = {
. set_parent = clk_cpu_8996_pmux_set_parent ,
. get_parent = clk_cpu_8996_pmux_get_parent ,
. determine_rate = clk_cpu_8996_pmux_determine_rate ,
2020-07-03 10:49:42 +02:00
} ;
2023-01-13 14:05:35 +02:00
static const struct parent_map smux_parent_map [ ] = {
{ . cfg = 0 , } , /* xo */
{ . cfg = 1 , } , /* pll */
{ . cfg = 3 , } , /* sys_apcs_aux */
} ;
2022-07-14 13:03:46 +03:00
static const struct clk_parent_data pwrcl_smux_parents [ ] = {
{ . fw_name = " xo " } ,
{ . hw = & pwrcl_pll_postdiv . hw } ,
2023-01-13 14:05:35 +02:00
{ . fw_name = " sys_apcs_aux " } ,
2022-07-14 13:03:46 +03:00
} ;
static const struct clk_parent_data perfcl_smux_parents [ ] = {
{ . fw_name = " xo " } ,
{ . hw = & perfcl_pll_postdiv . hw } ,
2023-01-13 14:05:35 +02:00
{ . fw_name = " sys_apcs_aux " } ,
2022-07-14 13:03:46 +03:00
} ;
2022-06-21 20:06:19 +04:00
static struct clk_regmap_mux pwrcl_smux = {
2020-07-03 10:49:42 +02:00
. reg = PWRCL_REG_OFFSET + MUX_OFFSET ,
. shift = 2 ,
. width = 2 ,
2023-01-13 14:05:35 +02:00
. parent_map = smux_parent_map ,
2020-07-03 10:49:42 +02:00
. clkr . hw . init = & ( struct clk_init_data ) {
. name = " pwrcl_smux " ,
2022-07-14 13:03:46 +03:00
. parent_data = pwrcl_smux_parents ,
. num_parents = ARRAY_SIZE ( pwrcl_smux_parents ) ,
2022-06-21 20:06:19 +04:00
. ops = & clk_regmap_mux_closest_ops ,
2020-07-03 10:49:42 +02:00
. flags = CLK_SET_RATE_PARENT ,
} ,
} ;
2022-06-21 20:06:19 +04:00
static struct clk_regmap_mux perfcl_smux = {
2020-07-03 10:49:42 +02:00
. reg = PERFCL_REG_OFFSET + MUX_OFFSET ,
. shift = 2 ,
. width = 2 ,
2023-01-13 14:05:35 +02:00
. parent_map = smux_parent_map ,
2020-07-03 10:49:42 +02:00
. clkr . hw . init = & ( struct clk_init_data ) {
. name = " perfcl_smux " ,
2022-07-14 13:03:46 +03:00
. parent_data = perfcl_smux_parents ,
. num_parents = ARRAY_SIZE ( perfcl_smux_parents ) ,
2022-06-21 20:06:19 +04:00
. ops = & clk_regmap_mux_closest_ops ,
2020-07-03 10:49:42 +02:00
. flags = CLK_SET_RATE_PARENT ,
} ,
} ;
2022-07-14 13:03:46 +03:00
static const struct clk_hw * pwrcl_pmux_parents [ ] = {
[ SMUX_INDEX ] = & pwrcl_smux . clkr . hw ,
[ PLL_INDEX ] = & pwrcl_pll . clkr . hw ,
2022-07-14 13:03:48 +03:00
[ ACD_INDEX ] = & pwrcl_pll_acd . hw ,
2022-07-14 13:03:46 +03:00
[ ALT_INDEX ] = & pwrcl_alt_pll . clkr . hw ,
} ;
static const struct clk_hw * perfcl_pmux_parents [ ] = {
[ SMUX_INDEX ] = & perfcl_smux . clkr . hw ,
[ PLL_INDEX ] = & perfcl_pll . clkr . hw ,
2022-07-14 13:03:48 +03:00
[ ACD_INDEX ] = & perfcl_pll_acd . hw ,
2022-07-14 13:03:46 +03:00
[ ALT_INDEX ] = & perfcl_alt_pll . clkr . hw ,
} ;
2022-06-21 20:06:19 +04:00
static struct clk_cpu_8996_pmux pwrcl_pmux = {
2020-07-03 10:49:42 +02:00
. reg = PWRCL_REG_OFFSET + MUX_OFFSET ,
. nb . notifier_call = cpu_clk_notifier_cb ,
. clkr . hw . init = & ( struct clk_init_data ) {
. name = " pwrcl_pmux " ,
2022-07-14 13:03:46 +03:00
. parent_hws = pwrcl_pmux_parents ,
. num_parents = ARRAY_SIZE ( pwrcl_pmux_parents ) ,
2022-06-21 20:06:19 +04:00
. ops = & clk_cpu_8996_pmux_ops ,
2020-07-03 10:49:42 +02:00
/* CPU clock is critical and should never be gated */
. flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL ,
} ,
} ;
2022-06-21 20:06:19 +04:00
static struct clk_cpu_8996_pmux perfcl_pmux = {
2020-07-03 10:49:42 +02:00
. reg = PERFCL_REG_OFFSET + MUX_OFFSET ,
. nb . notifier_call = cpu_clk_notifier_cb ,
. clkr . hw . init = & ( struct clk_init_data ) {
. name = " perfcl_pmux " ,
2022-07-14 13:03:46 +03:00
. parent_hws = perfcl_pmux_parents ,
. num_parents = ARRAY_SIZE ( perfcl_pmux_parents ) ,
2022-06-21 20:06:19 +04:00
. ops = & clk_cpu_8996_pmux_ops ,
2020-07-03 10:49:42 +02:00
/* CPU clock is critical and should never be gated */
. flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL ,
} ,
} ;
static const struct regmap_config cpu_msm8996_regmap_config = {
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
. max_register = 0x80210 ,
. fast_io = true ,
. val_format_endian = REGMAP_ENDIAN_LITTLE ,
} ;
2022-07-14 13:03:48 +03:00
static struct clk_hw * cpu_msm8996_hw_clks [ ] = {
& pwrcl_pll_postdiv . hw ,
& perfcl_pll_postdiv . hw ,
& pwrcl_pll_acd . hw ,
& perfcl_pll_acd . hw ,
} ;
2020-07-14 22:21:55 +08:00
static struct clk_regmap * cpu_msm8996_clks [ ] = {
2020-07-03 10:49:42 +02:00
& pwrcl_pll . clkr ,
2022-06-21 20:06:18 +04:00
& perfcl_pll . clkr ,
2020-07-03 10:49:42 +02:00
& pwrcl_alt_pll . clkr ,
2022-06-21 20:06:18 +04:00
& perfcl_alt_pll . clkr ,
2020-07-03 10:49:42 +02:00
& pwrcl_smux . clkr ,
2022-06-21 20:06:18 +04:00
& perfcl_smux . clkr ,
2020-07-03 10:49:42 +02:00
& pwrcl_pmux . clkr ,
2022-06-21 20:06:18 +04:00
& perfcl_pmux . clkr ,
2020-07-03 10:49:42 +02:00
} ;
2023-01-13 14:05:39 +02:00
static void qcom_cpu_clk_msm8996_acd_init ( struct regmap * regmap ) ;
2020-07-03 10:49:42 +02:00
static int qcom_cpu_clk_msm8996_register_clks ( struct device * dev ,
struct regmap * regmap )
{
int i , ret ;
2023-01-13 14:05:40 +02:00
/* Select GPLL0 for 300MHz for both clusters */
regmap_write ( regmap , PERFCL_REG_OFFSET + MUX_OFFSET , 0xc ) ;
regmap_write ( regmap , PWRCL_REG_OFFSET + MUX_OFFSET , 0xc ) ;
/* Ensure write goes through before PLLs are reconfigured */
udelay ( 5 ) ;
2023-01-13 14:05:43 +02:00
/* Set the auto clock sel always-on source to GPLL0/2 (300MHz) */
regmap_update_bits ( regmap , PWRCL_REG_OFFSET + MUX_OFFSET ,
MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK ,
MUX_AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL ) ;
regmap_update_bits ( regmap , PERFCL_REG_OFFSET + MUX_OFFSET ,
MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK ,
MUX_AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL ) ;
2023-01-13 14:05:38 +02:00
clk_alpha_pll_configure ( & pwrcl_pll , regmap , & hfpll_config ) ;
clk_alpha_pll_configure ( & perfcl_pll , regmap , & hfpll_config ) ;
clk_alpha_pll_configure ( & pwrcl_alt_pll , regmap , & altpll_config ) ;
clk_alpha_pll_configure ( & perfcl_alt_pll , regmap , & altpll_config ) ;
2023-01-13 14:05:40 +02:00
/* Wait for PLL(s) to lock */
udelay ( 50 ) ;
2023-01-13 14:05:43 +02:00
/* Enable auto clock selection for both clusters */
regmap_update_bits ( regmap , PWRCL_REG_OFFSET + CLK_CTL_OFFSET ,
CLK_CTL_AUTO_CLK_SEL , CLK_CTL_AUTO_CLK_SEL ) ;
regmap_update_bits ( regmap , PERFCL_REG_OFFSET + CLK_CTL_OFFSET ,
CLK_CTL_AUTO_CLK_SEL , CLK_CTL_AUTO_CLK_SEL ) ;
/* Ensure write goes through before muxes are switched */
udelay ( 5 ) ;
2023-01-13 14:05:39 +02:00
qcom_cpu_clk_msm8996_acd_init ( regmap ) ;
2023-01-13 14:05:43 +02:00
/* Pulse swallower and soft-start settings */
regmap_write ( regmap , PWRCL_REG_OFFSET + PSCTL_OFFSET , 0x00030005 ) ;
regmap_write ( regmap , PERFCL_REG_OFFSET + PSCTL_OFFSET , 0x00030005 ) ;
2023-01-13 14:05:40 +02:00
/* Switch clusters to use the ACD leg */
2023-01-13 14:05:43 +02:00
regmap_write ( regmap , PWRCL_REG_OFFSET + MUX_OFFSET , 0x32 ) ;
regmap_write ( regmap , PERFCL_REG_OFFSET + MUX_OFFSET , 0x32 ) ;
2023-01-13 14:05:40 +02:00
2022-07-14 13:03:48 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( cpu_msm8996_hw_clks ) ; i + + ) {
ret = devm_clk_hw_register ( dev , cpu_msm8996_hw_clks [ i ] ) ;
if ( ret )
return ret ;
2020-07-03 10:49:42 +02:00
}
for ( i = 0 ; i < ARRAY_SIZE ( cpu_msm8996_clks ) ; i + + ) {
ret = devm_clk_register_regmap ( dev , cpu_msm8996_clks [ i ] ) ;
2022-06-21 20:06:17 +04:00
if ( ret )
2020-07-03 10:49:42 +02:00
return ret ;
}
/* Enable alt PLLs */
clk_prepare_enable ( pwrcl_alt_pll . clkr . hw . clk ) ;
clk_prepare_enable ( perfcl_alt_pll . clkr . hw . clk ) ;
2022-07-14 13:03:47 +03:00
devm_clk_notifier_register ( dev , pwrcl_pmux . clkr . hw . clk , & pwrcl_pmux . nb ) ;
devm_clk_notifier_register ( dev , perfcl_pmux . clkr . hw . clk , & perfcl_pmux . nb ) ;
2020-07-03 10:49:42 +02:00
return ret ;
}
2023-01-13 14:05:41 +02:00
# define CPU_CLUSTER_AFFINITY_MASK 0xf00
# define PWRCL_AFFINITY_MASK 0x000
# define PERFCL_AFFINITY_MASK 0x100
2020-07-03 10:49:42 +02:00
# define L2ACDCR_REG 0x580ULL
# define L2ACDTD_REG 0x581ULL
# define L2ACDDVMRC_REG 0x584ULL
# define L2ACDSSCR_REG 0x589ULL
static DEFINE_SPINLOCK ( qcom_clk_acd_lock ) ;
2023-01-13 14:05:39 +02:00
static void qcom_cpu_clk_msm8996_acd_init ( struct regmap * regmap )
2020-07-03 10:49:42 +02:00
{
u64 hwid ;
2023-01-13 14:05:36 +02:00
u32 val ;
2020-07-03 10:49:42 +02:00
unsigned long flags ;
spin_lock_irqsave ( & qcom_clk_acd_lock , flags ) ;
2023-01-13 14:05:36 +02:00
val = kryo_l2_get_indirect_reg ( L2ACDTD_REG ) ;
if ( val = = 0x00006a11 )
goto out ;
2020-07-03 10:49:42 +02:00
kryo_l2_set_indirect_reg ( L2ACDTD_REG , 0x00006a11 ) ;
kryo_l2_set_indirect_reg ( L2ACDDVMRC_REG , 0x000e0f0f ) ;
kryo_l2_set_indirect_reg ( L2ACDSSCR_REG , 0x00000601 ) ;
2023-01-13 14:05:41 +02:00
kryo_l2_set_indirect_reg ( L2ACDCR_REG , 0x002c5ffd ) ;
2020-07-03 10:49:42 +02:00
2023-01-13 14:05:41 +02:00
hwid = read_cpuid_mpidr ( ) ;
if ( ( hwid & CPU_CLUSTER_AFFINITY_MASK ) = = PWRCL_AFFINITY_MASK )
regmap_write ( regmap , PWRCL_REG_OFFSET + SSSCTL_OFFSET , 0xf ) ;
else
2023-01-13 14:05:39 +02:00
regmap_write ( regmap , PERFCL_REG_OFFSET + SSSCTL_OFFSET , 0xf ) ;
2020-07-03 10:49:42 +02:00
2023-01-13 14:05:36 +02:00
out :
2020-07-03 10:49:42 +02:00
spin_unlock_irqrestore ( & qcom_clk_acd_lock , flags ) ;
}
static int cpu_clk_notifier_cb ( struct notifier_block * nb , unsigned long event ,
void * data )
{
2022-06-21 20:06:19 +04:00
struct clk_cpu_8996_pmux * cpuclk = to_clk_cpu_8996_pmux_nb ( nb ) ;
2020-07-03 10:49:42 +02:00
struct clk_notifier_data * cnd = data ;
switch ( event ) {
case PRE_RATE_CHANGE :
2023-01-13 14:05:39 +02:00
qcom_cpu_clk_msm8996_acd_init ( cpuclk - > clkr . regmap ) ;
2023-01-13 14:05:37 +02:00
/*
* Avoid overvolting . clk_core_set_rate_nolock ( ) walks from top
* to bottom , so it will change the rate of the PLL before
* chaging the parent of PMUX . This can result in pmux getting
* clocked twice the expected rate .
*
* Manually switch to PLL / 2 here .
*/
if ( cnd - > new_rate < DIV_2_THRESHOLD & &
cnd - > old_rate > DIV_2_THRESHOLD )
clk_cpu_8996_pmux_set_parent ( & cpuclk - > clkr . hw , SMUX_INDEX ) ;
2020-07-03 10:49:42 +02:00
break ;
2023-01-13 14:05:37 +02:00
case ABORT_RATE_CHANGE :
/* Revert manual change */
if ( cnd - > new_rate < DIV_2_THRESHOLD & &
cnd - > old_rate > DIV_2_THRESHOLD )
clk_cpu_8996_pmux_set_parent ( & cpuclk - > clkr . hw , ACD_INDEX ) ;
2020-07-03 10:49:42 +02:00
break ;
default :
break ;
}
2023-01-13 14:05:37 +02:00
return NOTIFY_OK ;
2020-07-03 10:49:42 +02:00
} ;
static int qcom_cpu_clk_msm8996_driver_probe ( struct platform_device * pdev )
{
2023-01-13 14:05:39 +02:00
static void __iomem * base ;
2020-07-03 10:49:42 +02:00
struct regmap * regmap ;
struct clk_hw_onecell_data * data ;
struct device * dev = & pdev - > dev ;
int ret ;
data = devm_kzalloc ( dev , struct_size ( data , hws , 2 ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
regmap = devm_regmap_init_mmio ( dev , base , & cpu_msm8996_regmap_config ) ;
if ( IS_ERR ( regmap ) )
return PTR_ERR ( regmap ) ;
ret = qcom_cpu_clk_msm8996_register_clks ( dev , regmap ) ;
if ( ret )
return ret ;
data - > hws [ 0 ] = & pwrcl_pmux . clkr . hw ;
data - > hws [ 1 ] = & perfcl_pmux . clkr . hw ;
data - > num = 2 ;
return devm_of_clk_add_hw_provider ( dev , of_clk_hw_onecell_get , data ) ;
}
static const struct of_device_id qcom_cpu_clk_msm8996_match_table [ ] = {
{ . compatible = " qcom,msm8996-apcc " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , qcom_cpu_clk_msm8996_match_table ) ;
static struct platform_driver qcom_cpu_clk_msm8996_driver = {
. probe = qcom_cpu_clk_msm8996_driver_probe ,
. driver = {
. name = " qcom-msm8996-apcc " ,
. of_match_table = qcom_cpu_clk_msm8996_match_table ,
} ,
} ;
module_platform_driver ( qcom_cpu_clk_msm8996_driver ) ;
MODULE_DESCRIPTION ( " QCOM MSM8996 CPU Clock Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;