M7350/kernel/drivers/clk/msm/clock-cpu-8996.c

1816 lines
48 KiB
C
Raw Normal View History

2024-09-09 08:57:42 +00:00
/*
* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/clk.h>
#include <linux/clk/msm-clk-provider.h>
#include <linux/clk/msm-clk.h>
#include <linux/clk/msm-clock-generic.h>
#include <linux/cpu.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/pm_opp.h>
#include <linux/pm_qos.h>
#include <asm/cputype.h>
#include <soc/qcom/scm.h>
#include <soc/qcom/clock-pll.h>
#include <soc/qcom/clock-local2.h>
#include <soc/qcom/clock-alpha-pll.h>
#include <soc/qcom/kryo-l2-accessors.h>
#include <dt-bindings/clock/msm-clocks-8996.h>
#include "clock.h"
#include "vdd-level-8994.h"
enum {
APC0_PLL_BASE,
APC1_PLL_BASE,
CBF_PLL_BASE,
APC0_BASE,
APC1_BASE,
CBF_BASE,
EFUSE_BASE,
DEBUG_BASE,
NUM_BASES
};
static char *base_names[] = {
"pwrcl_pll",
"perfcl_pll",
"cbf_pll",
"pwrcl_mux",
"perfcl_mux",
"cbf_mux",
"efuse",
"debug",
};
static void *vbases[NUM_BASES];
static bool cpu_clocks_v3;
static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner, NULL);
/* Power cluster primary PLL */
#define C0_PLL_MODE 0x0
#define C0_PLL_L_VAL 0x4
#define C0_PLL_ALPHA 0x8
#define C0_PLL_USER_CTL 0x10
#define C0_PLL_CONFIG_CTL 0x18
#define C0_PLL_CONFIG_CTL_HI 0x1C
#define C0_PLL_STATUS 0x28
#define C0_PLL_TEST_CTL_LO 0x20
#define C0_PLL_TEST_CTL_HI 0x24
/* Power cluster alt PLL */
#define C0_PLLA_MODE 0x100
#define C0_PLLA_L_VAL 0x104
#define C0_PLLA_ALPHA 0x108
#define C0_PLLA_USER_CTL 0x110
#define C0_PLLA_CONFIG_CTL 0x118
#define C0_PLLA_STATUS 0x128
#define C0_PLLA_TEST_CTL_LO 0x120
/* Perf cluster primary PLL */
#define C1_PLL_MODE 0x0
#define C1_PLL_L_VAL 0x4
#define C1_PLL_ALPHA 0x8
#define C1_PLL_USER_CTL 0x10
#define C1_PLL_CONFIG_CTL 0x18
#define C1_PLL_CONFIG_CTL_HI 0x1C
#define C1_PLL_STATUS 0x28
#define C1_PLL_TEST_CTL_LO 0x20
#define C1_PLL_TEST_CTL_HI 0x24
/* Perf cluster alt PLL */
#define C1_PLLA_MODE 0x100
#define C1_PLLA_L_VAL 0x104
#define C1_PLLA_ALPHA 0x108
#define C1_PLLA_USER_CTL 0x110
#define C1_PLLA_CONFIG_CTL 0x118
#define C1_PLLA_STATUS 0x128
#define C1_PLLA_TEST_CTL_LO 0x120
#define CBF_PLL_MODE 0x0
#define CBF_PLL_L_VAL 0x8
#define CBF_PLL_ALPHA 0x10
#define CBF_PLL_USER_CTL 0x18
#define CBF_PLL_CONFIG_CTL 0x20
#define CBF_PLL_CONFIG_CTL_HI 0x24
#define CBF_PLL_STATUS 0x28
#define CBF_PLL_TEST_CTL_LO 0x30
#define CBF_PLL_TEST_CTL_HI 0x34
#define APC_DIAG_OFFSET 0x48
#define MUX_OFFSET 0x40
DEFINE_EXT_CLK(xo_ao, NULL);
DEFINE_CLK_DUMMY(alpha_xo_ao, 19200000);
DEFINE_EXT_CLK(sys_apcsaux_clk_gcc, NULL);
DEFINE_FIXED_SLAVE_DIV_CLK(sys_apcsaux_clk, 2, &sys_apcsaux_clk_gcc.c);
#define L2ACDCR_REG 0x580ULL
#define L2ACDTD_REG 0x581ULL
#define L2ACDDVMRC_REG 0x584ULL
#define L2ACDSSCR_REG 0x589ULL
#define EFUSE_SHIFT 29
#define EFUSE_MASK 0x7
/* ACD static settings */
static int acdtd_val_pwrcl = 0x00006A11;
static int acdtd_val_perfcl = 0x00006A11;
static int dvmrc_val = 0x000E0F0F;
static int acdsscr_val = 0x00000601;
static int acdcr_val_pwrcl = 0x002D5FFD;
module_param(acdcr_val_pwrcl, int, 0444);
static int acdcr_val_perfcl = 0x002D5FFD;
module_param(acdcr_val_perfcl, int, 0444);
int enable_acd = 1;
module_param(enable_acd, int, 0444);
#define WRITE_L2ACDCR(val) \
set_l2_indirect_reg(L2ACDCR_REG, (val))
#define WRITE_L2ACDTD(val) \
set_l2_indirect_reg(L2ACDTD_REG, (val))
#define WRITE_L2ACDDVMRC(val) \
set_l2_indirect_reg(L2ACDDVMRC_REG, (val))
#define WRITE_L2ACDSSCR(val) \
set_l2_indirect_reg(L2ACDSSCR_REG, (val))
#define READ_L2ACDCR(reg) \
(reg = get_l2_indirect_reg(L2ACDCR_REG))
#define READ_L2ACDTD(reg) \
(reg = get_l2_indirect_reg(L2ACDTD_REG))
#define READ_L2ACDDVMRC(reg) \
(reg = get_l2_indirect_reg(L2ACDDVMRC_REG))
#define READ_L2ACDSSCR(reg) \
(reg = get_l2_indirect_reg(L2ACDSSCR_REG))
static struct pll_clk perfcl_pll = {
.mode_reg = (void __iomem *)C1_PLL_MODE,
.l_reg = (void __iomem *)C1_PLL_L_VAL,
.alpha_reg = (void __iomem *)C1_PLL_ALPHA,
.config_reg = (void __iomem *)C1_PLL_USER_CTL,
.config_ctl_reg = (void __iomem *)C1_PLL_CONFIG_CTL,
.config_ctl_hi_reg = (void __iomem *)C1_PLL_CONFIG_CTL_HI,
.status_reg = (void __iomem *)C1_PLL_MODE,
.test_ctl_lo_reg = (void __iomem *)C1_PLL_TEST_CTL_LO,
.test_ctl_hi_reg = (void __iomem *)C1_PLL_TEST_CTL_HI,
.pgm_test_ctl_enable = true,
.init_test_ctl = true,
.masks = {
.pre_div_mask = BIT(12),
.post_div_mask = BM(9, 8),
.mn_en_mask = BIT(24),
.main_output_mask = BIT(0),
.early_output_mask = BIT(3),
.apc_pdn_mask = BIT(24),
.lock_mask = BIT(31),
},
.vals = {
.post_div_masked = 0x100,
.pre_div_masked = 0x0,
.test_ctl_hi_val = 0x00004000,
.test_ctl_lo_val = 0x04000000,
.config_ctl_val = 0x200D4AA8,
.config_ctl_hi_val = 0x002,
},
.min_rate = 600000000,
.max_rate = 3000000000,
.src_rate = 19200000,
.base = &vbases[APC1_PLL_BASE],
.c = {
.always_on = true,
.parent = &xo_ao.c,
.dbg_name = "perfcl_pll",
.ops = &clk_ops_variable_rate_pll_hwfsm,
VDD_DIG_FMAX_MAP1(LOW, 3000000000),
CLK_INIT(perfcl_pll.c),
},
};
static struct alpha_pll_masks alt_pll_masks = {
.lock_mask = BIT(31),
.active_mask = BIT(30),
.vco_mask = BM(21, 20) >> 20,
.vco_shift = 20,
.alpha_en_mask = BIT(24),
.output_mask = 0xf,
.post_div_mask = 0xf00,
};
static struct alpha_pll_vco_tbl alt_pll_vco_modes[] = {
VCO(3, 250000000, 500000000),
VCO(2, 500000000, 750000000),
VCO(1, 750000000, 1000000000),
VCO(0, 1000000000, 2150400000),
};
static struct alpha_pll_clk perfcl_alt_pll = {
.masks = &alt_pll_masks,
.base = &vbases[APC1_PLL_BASE],
.offset = C1_PLLA_MODE,
.vco_tbl = alt_pll_vco_modes,
.num_vco = ARRAY_SIZE(alt_pll_vco_modes),
.enable_config = 0x9, /* Main and early outputs */
.post_div_config = 0x100, /* Div-2 */
.config_ctl_val = 0x4001051B,
.offline_bit_workaround = true,
.c = {
.always_on = true,
.parent = &alpha_xo_ao.c,
.dbg_name = "perfcl_alt_pll",
.ops = &clk_ops_alpha_pll_hwfsm,
CLK_INIT(perfcl_alt_pll.c),
},
};
static struct pll_clk pwrcl_pll = {
.mode_reg = (void __iomem *)C0_PLL_MODE,
.l_reg = (void __iomem *)C0_PLL_L_VAL,
.alpha_reg = (void __iomem *)C0_PLL_ALPHA,
.config_reg = (void __iomem *)C0_PLL_USER_CTL,
.config_ctl_reg = (void __iomem *)C0_PLL_CONFIG_CTL,
.config_ctl_hi_reg = (void __iomem *)C0_PLL_CONFIG_CTL_HI,
.status_reg = (void __iomem *)C0_PLL_MODE,
.test_ctl_lo_reg = (void __iomem *)C0_PLL_TEST_CTL_LO,
.test_ctl_hi_reg = (void __iomem *)C0_PLL_TEST_CTL_HI,
.pgm_test_ctl_enable = true,
.init_test_ctl = true,
.masks = {
.pre_div_mask = BIT(12),
.post_div_mask = BM(9, 8),
.mn_en_mask = BIT(24),
.main_output_mask = BIT(0),
.early_output_mask = BIT(3),
.apc_pdn_mask = BIT(24),
.lock_mask = BIT(31),
},
.vals = {
.post_div_masked = 0x100,
.pre_div_masked = 0x0,
.test_ctl_hi_val = 0x00004000,
.test_ctl_lo_val = 0x04000000,
.config_ctl_val = 0x200D4AA8,
.config_ctl_hi_val = 0x002,
},
.min_rate = 600000000,
.max_rate = 3000000000,
.src_rate = 19200000,
.base = &vbases[APC0_PLL_BASE],
.c = {
.always_on = true,
.parent = &xo_ao.c,
.dbg_name = "pwrcl_pll",
.ops = &clk_ops_variable_rate_pll_hwfsm,
VDD_DIG_FMAX_MAP1(LOW, 3000000000),
CLK_INIT(pwrcl_pll.c),
},
};
static struct alpha_pll_clk pwrcl_alt_pll = {
.masks = &alt_pll_masks,
.base = &vbases[APC0_PLL_BASE],
.offset = C0_PLLA_MODE,
.vco_tbl = alt_pll_vco_modes,
.num_vco = ARRAY_SIZE(alt_pll_vco_modes),
.enable_config = 0x9, /* Main and early outputs */
.post_div_config = 0x100, /* Div-2 */
.config_ctl_val = 0x4001051B,
.offline_bit_workaround = true,
.c = {
.always_on = true,
.dbg_name = "pwrcl_alt_pll",
.parent = &alpha_xo_ao.c,
.ops = &clk_ops_alpha_pll_hwfsm,
CLK_INIT(pwrcl_alt_pll.c),
},
};
static DEFINE_SPINLOCK(mux_reg_lock);
DEFINE_FIXED_DIV_CLK(pwrcl_pll_main, 2, &pwrcl_pll.c);
DEFINE_FIXED_DIV_CLK(perfcl_pll_main, 2, &perfcl_pll.c);
static void __cpu_mux_set_sel(struct mux_clk *mux, int sel)
{
u32 regval;
unsigned long flags;
spin_lock_irqsave(&mux_reg_lock, flags);
if (mux->priv)
regval = scm_io_read(*(u32 *)mux->priv + mux->offset);
else
regval = readl_relaxed(*mux->base + mux->offset);
regval &= ~(mux->mask << mux->shift);
regval |= (sel & mux->mask) << mux->shift;
if (mux->priv)
scm_io_write(*(u32 *)mux->priv + mux->offset, regval);
else
writel_relaxed(regval, *mux->base + mux->offset);
spin_unlock_irqrestore(&mux_reg_lock, flags);
/* Ensure switch request goes through before returning */
mb();
/* Hardware mandated delay */
udelay(5);
}
/* It is assumed that the mux enable state is locked in this function */
static int cpu_mux_set_sel(struct mux_clk *mux, int sel)
{
__cpu_mux_set_sel(mux, sel);
return 0;
}
static int cpu_mux_get_sel(struct mux_clk *mux)
{
u32 regval;
if (mux->priv)
regval = scm_io_read(*(u32 *)mux->priv + mux->offset);
else
regval = readl_relaxed(*mux->base + mux->offset);
return (regval >> mux->shift) & mux->mask;
}
static int cpu_mux_enable(struct mux_clk *mux)
{
return 0;
}
static void cpu_mux_disable(struct mux_clk *mux)
{
}
/* It is assumed that the mux enable state is locked in this function */
static int cpu_debug_mux_set_sel(struct mux_clk *mux, int sel)
{
__cpu_mux_set_sel(mux, sel);
return 0;
}
static int cpu_debug_mux_get_sel(struct mux_clk *mux)
{
u32 regval = readl_relaxed(*mux->base + mux->offset);
return (regval >> mux->shift) & mux->mask;
}
static int cpu_debug_mux_enable(struct mux_clk *mux)
{
u32 val;
/* Enable debug clocks */
val = readl_relaxed(vbases[APC0_BASE] + APC_DIAG_OFFSET);
val |= BM(11, 8);
writel_relaxed(val, vbases[APC0_BASE] + APC_DIAG_OFFSET);
val = readl_relaxed(vbases[APC1_BASE] + APC_DIAG_OFFSET);
val |= BM(11, 8);
writel_relaxed(val, vbases[APC1_BASE] + APC_DIAG_OFFSET);
/* Ensure enable request goes through for correct measurement*/
mb();
udelay(5);
return 0;
}
static void cpu_debug_mux_disable(struct mux_clk *mux)
{
u32 val;
/* Disable debug clocks */
val = readl_relaxed(vbases[APC0_BASE] + APC_DIAG_OFFSET);
val &= ~BM(11, 8);
writel_relaxed(val, vbases[APC0_BASE] + APC_DIAG_OFFSET);
val = readl_relaxed(vbases[APC1_BASE] + APC_DIAG_OFFSET);
val &= ~BM(11, 8);
writel_relaxed(val, vbases[APC1_BASE] + APC_DIAG_OFFSET);
}
static struct clk_mux_ops cpu_mux_ops = {
.enable = cpu_mux_enable,
.disable = cpu_mux_disable,
.set_mux_sel = cpu_mux_set_sel,
.get_mux_sel = cpu_mux_get_sel,
};
static struct clk_mux_ops cpu_debug_mux_ops = {
.enable = cpu_debug_mux_enable,
.disable = cpu_debug_mux_disable,
.set_mux_sel = cpu_debug_mux_set_sel,
.get_mux_sel = cpu_debug_mux_get_sel,
};
static struct mux_clk pwrcl_lf_mux = {
.offset = MUX_OFFSET,
MUX_SRC_LIST(
{ &pwrcl_pll_main.c, 1 },
{ &sys_apcsaux_clk.c, 3 },
),
.ops = &cpu_mux_ops,
.mask = 0x3,
.shift = 2,
.base = &vbases[APC0_BASE],
.c = {
.dbg_name = "pwrcl_lf_mux",
.flags = CLKFLAG_NO_RATE_CACHE,
.ops = &clk_ops_gen_mux,
CLK_INIT(pwrcl_lf_mux.c),
},
};
static struct mux_clk pwrcl_hf_mux = {
.offset = MUX_OFFSET,
MUX_SRC_LIST(
/* This may be changed by acd_init to select the ACD leg */
{ &pwrcl_pll.c, 1 },
{ &pwrcl_lf_mux.c, 0 },
),
.ops = &cpu_mux_ops,
.mask = 0x3,
.shift = 0,
.base = &vbases[APC0_BASE],
.c = {
.dbg_name = "pwrcl_hf_mux",
.flags = CLKFLAG_NO_RATE_CACHE,
.ops = &clk_ops_gen_mux,
CLK_INIT(pwrcl_hf_mux.c),
},
};
static struct mux_clk perfcl_lf_mux = {
.offset = MUX_OFFSET,
MUX_SRC_LIST(
{ &perfcl_pll_main.c, 1 },
{ &sys_apcsaux_clk.c, 3 },
),
.ops = &cpu_mux_ops,
.mask = 0x3,
.shift = 2,
.base = &vbases[APC1_BASE],
.c = {
.dbg_name = "perfcl_lf_mux",
.flags = CLKFLAG_NO_RATE_CACHE,
.ops = &clk_ops_gen_mux,
CLK_INIT(perfcl_lf_mux.c),
},
};
static struct mux_clk perfcl_hf_mux = {
.offset = MUX_OFFSET,
MUX_SRC_LIST(
/* This may be changed by acd_init to select the ACD leg */
{ &perfcl_pll.c, 1 },
{ &perfcl_lf_mux.c, 0 },
),
.ops = &cpu_mux_ops,
.mask = 0x3,
.shift = 0,
.base = &vbases[APC1_BASE],
.c = {
.dbg_name = "perfcl_hf_mux",
.ops = &clk_ops_gen_mux,
CLK_INIT(perfcl_hf_mux.c),
},
};
static struct clk_src clk_src_perfcl_hf_mux_alt[] = {
{ &perfcl_alt_pll.c, 3 },
{ &perfcl_lf_mux.c, 0 },
};
static struct clk_src clk_src_pwrcl_hf_mux_alt[] = {
{ &pwrcl_alt_pll.c, 3 },
{ &pwrcl_lf_mux.c, 0 },
};
static struct clk_src clk_src_perfcl_lf_mux_alt[] = {
{ &sys_apcsaux_clk.c, 3 },
};
static struct clk_src clk_src_pwrcl_lf_mux_alt[] = {
{ &sys_apcsaux_clk.c, 3 },
};
struct cpu_clk_8996 {
u32 cpu_reg_mask;
struct clk *alt_pll;
unsigned long *alt_pll_freqs;
unsigned long alt_pll_thresh;
int n_alt_pll_freqs;
struct clk c;
bool hw_low_power_ctrl;
int pm_qos_latency;
cpumask_t cpumask;
struct pm_qos_request req;
bool do_half_rate;
bool has_acd;
};
static inline struct cpu_clk_8996 *to_cpu_clk_8996(struct clk *c)
{
return container_of(c, struct cpu_clk_8996, c);
}
static enum handoff cpu_clk_8996_handoff(struct clk *c)
{
c->rate = clk_get_rate(c->parent);
return HANDOFF_DISABLED_CLK;
}
static long cpu_clk_8996_round_rate(struct clk *c, unsigned long rate)
{
return clk_round_rate(c->parent, rate);
}
static unsigned long alt_pll_perfcl_freqs[] = {
307200000,
556800000,
};
static void do_nothing(void *unused) { }
/* ACD programming */
static struct cpu_clk_8996 perfcl_clk;
static struct cpu_clk_8996 pwrcl_clk;
static void cpu_clock_8996_acd_init(void);
static void cpu_clk_8996_disable(struct clk *c)
{
struct cpu_clk_8996 *cpuclk = to_cpu_clk_8996(c);
if (!enable_acd)
return;
/* Ensure that we switch to GPLL0 across D5 */
if (cpuclk == &pwrcl_clk)
writel_relaxed(0x3C, vbases[APC0_BASE] + MUX_OFFSET);
else if (cpuclk == &perfcl_clk)
writel_relaxed(0x3C, vbases[APC1_BASE] + MUX_OFFSET);
}
#define MAX_PLL_MAIN_FREQ 595200000
/*
* Returns the max safe frequency that will guarantee we switch to main output
*/
unsigned long acd_safe_freq(unsigned long freq)
{
/*
* If we're running at less than double the max PLL main rate,
* just return half the rate. This will ensure we switch to
* the main output, without violating voltage constraints
* that might happen if we choose to go with MAX_PLL_MAIN_FREQ.
*/
if (freq > MAX_PLL_MAIN_FREQ && freq <= MAX_PLL_MAIN_FREQ*2)
return freq/2;
/*
* We're higher than the max main output, and higher than twice
* the max main output. Safe to go to the max main output.
*/
if (freq > MAX_PLL_MAIN_FREQ)
return MAX_PLL_MAIN_FREQ;
/* Shouldn't get here, just return the safest rate possible */
return sys_apcsaux_clk.c.rate;
}
static int cpu_clk_8996_pre_set_rate(struct clk *c, unsigned long rate)
{
struct cpu_clk_8996 *cpuclk = to_cpu_clk_8996(c);
int ret;
bool hw_low_power_ctrl = cpuclk->hw_low_power_ctrl;
bool on_acd_leg = c->rate > MAX_PLL_MAIN_FREQ;
bool increase_freq = rate > c->rate;
/*
* If hardware control of the clock tree is enabled during power
* collapse, setup a PM QOS request to prevent power collapse and
* wake up one of the CPUs in this clock domain, to ensure software
* control while the clock rate is being switched.
*/
if (hw_low_power_ctrl) {
memset(&cpuclk->req, 0, sizeof(cpuclk->req));
cpuclk->req.cpus_affine = cpuclk->cpumask;
cpuclk->req.type = PM_QOS_REQ_AFFINE_CORES;
pm_qos_add_request(&cpuclk->req, PM_QOS_CPU_DMA_LATENCY,
cpuclk->pm_qos_latency);
ret = smp_call_function_any(&cpuclk->cpumask, do_nothing,
NULL, 1);
}
/* Select a non-ACD'ed safe source across voltage and freq switches */
if (enable_acd && cpuclk->has_acd && on_acd_leg && increase_freq) {
/*
* We're on the ACD leg, and the voltage will be
* scaled before clk_set_rate. Switch to the main output.
*/
return clk_set_rate(c->parent, acd_safe_freq(c->rate));
}
return 0;
}
static void cpu_clk_8996_post_set_rate(struct clk *c, unsigned long start_rate)
{
struct cpu_clk_8996 *cpuclk = to_cpu_clk_8996(c);
int ret;
bool hw_low_power_ctrl = cpuclk->hw_low_power_ctrl;
bool on_acd_leg = c->rate > MAX_PLL_MAIN_FREQ;
bool decrease_freq = start_rate > c->rate;
if (cpuclk->has_acd && enable_acd && on_acd_leg && decrease_freq) {
ret = clk_set_rate(c->parent, c->rate);
if (ret)
pr_err("Unable to reset parent rate!\n");
}
if (hw_low_power_ctrl)
pm_qos_remove_request(&cpuclk->req);
}
static int cpu_clk_8996_set_rate(struct clk *c, unsigned long rate)
{
struct cpu_clk_8996 *cpuclk = to_cpu_clk_8996(c);
int ret, err_ret;
unsigned long alt_pll_prev_rate;
unsigned long alt_pll_rate;
unsigned long n_alt_freqs = cpuclk->n_alt_pll_freqs;
bool on_acd_leg = rate > MAX_PLL_MAIN_FREQ;
bool decrease_freq = rate < c->rate;
if (cpuclk->alt_pll && (n_alt_freqs > 0)) {
alt_pll_prev_rate = cpuclk->alt_pll->rate;
alt_pll_rate = cpuclk->alt_pll_freqs[0];
if (rate > cpuclk->alt_pll_thresh)
alt_pll_rate = cpuclk->alt_pll_freqs[1];
if (!cpu_clocks_v3)
mutex_lock(&scm_lmh_lock);
ret = clk_set_rate(cpuclk->alt_pll, alt_pll_rate);
if (!cpu_clocks_v3)
mutex_unlock(&scm_lmh_lock);
if (ret) {
pr_err("failed to set rate %lu on alt_pll when setting %lu on %s (%d)\n",
alt_pll_rate, rate, c->dbg_name, ret);
goto out;
}
}
/*
* Special handling needed for the case when using the div-2 output.
* Since the PLL needs to be running at twice the requested rate,
* we need to switch the mux first and then change the PLL rate.
* Otherwise the current voltage may not suffice with the PLL running at
* 2 * rate. Example: switching from 768MHz down to 550Mhz - if we raise
* the PLL to 1.1GHz, that's undervolting the system. So, we switch to
* the div-2 mux select first (by requesting rate/2), allowing the CPU
* to run at 384MHz. Then, we ramp up the PLL to 1.1GHz, allowing the
* CPU frequency to ramp up from 384MHz to 550MHz.
*/
if (cpuclk->do_half_rate
&& c->rate > 600000000 && rate < 600000000) {
if (!cpu_clocks_v3)
mutex_lock(&scm_lmh_lock);
ret = clk_set_rate(c->parent, c->rate/2);
if (!cpu_clocks_v3)
mutex_unlock(&scm_lmh_lock);
if (ret) {
pr_err("failed to set rate %lu on %s (%d)\n",
c->rate/2, c->dbg_name, ret);
goto fail;
}
}
if (!cpu_clocks_v3)
mutex_lock(&scm_lmh_lock);
ret = clk_set_rate(c->parent, rate);
if (!cpu_clocks_v3)
mutex_unlock(&scm_lmh_lock);
if (ret) {
pr_err("failed to set rate %lu on %s (%d)\n",
rate, c->dbg_name, ret);
goto set_rate_fail;
}
/*
* If we're on the ACD leg and decreasing freq, voltage will be changed
* after this function. Switch to main output.
*/
if (enable_acd && cpuclk->has_acd && decrease_freq && on_acd_leg)
return clk_set_rate(c->parent, acd_safe_freq(c->rate));
return 0;
set_rate_fail:
/* Restore parent rate if we halved it */
if (cpuclk->do_half_rate && c->rate > 600000000 && rate < 600000000) {
if (!cpu_clocks_v3)
mutex_lock(&scm_lmh_lock);
err_ret = clk_set_rate(c->parent, c->rate);
if (!cpu_clocks_v3)
mutex_unlock(&scm_lmh_lock);
if (err_ret)
pr_err("failed to restore %s rate to %lu\n",
c->dbg_name, c->rate);
}
fail:
if (cpuclk->alt_pll && (n_alt_freqs > 0)) {
if (!cpu_clocks_v3)
mutex_lock(&scm_lmh_lock);
err_ret = clk_set_rate(cpuclk->alt_pll, alt_pll_prev_rate);
if (!cpu_clocks_v3)
mutex_unlock(&scm_lmh_lock);
if (err_ret)
pr_err("failed to reset rate to %lu on alt pll after failing to set %lu on %s (%d)\n",
alt_pll_prev_rate, rate, c->dbg_name, err_ret);
}
out:
return ret;
}
static struct clk_ops clk_ops_cpu_8996 = {
.disable = cpu_clk_8996_disable,
.set_rate = cpu_clk_8996_set_rate,
.pre_set_rate = cpu_clk_8996_pre_set_rate,
.post_set_rate = cpu_clk_8996_post_set_rate,
.round_rate = cpu_clk_8996_round_rate,
.handoff = cpu_clk_8996_handoff,
};
DEFINE_VDD_REGS_INIT(vdd_pwrcl, 1);
#define PERFCL_LATENCY_NO_L2_PC_US (1)
#define PWRCL_LATENCY_NO_L2_PC_US (1)
static struct cpu_clk_8996 pwrcl_clk = {
.cpu_reg_mask = 0x3,
.pm_qos_latency = PWRCL_LATENCY_NO_L2_PC_US,
.do_half_rate = true,
.c = {
.parent = &pwrcl_hf_mux.c,
.dbg_name = "pwrcl_clk",
.ops = &clk_ops_cpu_8996,
.vdd_class = &vdd_pwrcl,
CLK_INIT(pwrcl_clk.c),
},
};
DEFINE_VDD_REGS_INIT(vdd_perfcl, 1);
static struct cpu_clk_8996 perfcl_clk = {
.cpu_reg_mask = 0x103,
.alt_pll = &perfcl_alt_pll.c,
.alt_pll_freqs = alt_pll_perfcl_freqs,
.alt_pll_thresh = 1190400000,
.n_alt_pll_freqs = ARRAY_SIZE(alt_pll_perfcl_freqs),
.pm_qos_latency = PERFCL_LATENCY_NO_L2_PC_US,
.do_half_rate = true,
.c = {
.parent = &perfcl_hf_mux.c,
.dbg_name = "perfcl_clk",
.ops = &clk_ops_cpu_8996,
.vdd_class = &vdd_perfcl,
CLK_INIT(perfcl_clk.c),
},
};
static DEFINE_SPINLOCK(acd_lock);
static struct clk *mpidr_to_clk(void)
{
u64 hwid = read_cpuid_mpidr() & 0xFFF;
if ((hwid | pwrcl_clk.cpu_reg_mask) == pwrcl_clk.cpu_reg_mask)
return &pwrcl_clk.c;
if ((hwid | perfcl_clk.cpu_reg_mask) == perfcl_clk.cpu_reg_mask)
return &perfcl_clk.c;
return NULL;
}
#define SSSCTL_OFFSET 0x160
/*
* This *has* to be called on the intended CPU
*/
static void cpu_clock_8996_acd_init(void)
{
u64 l2acdtd;
unsigned long flags;
spin_lock_irqsave(&acd_lock, flags);
if (!enable_acd) {
spin_unlock_irqrestore(&acd_lock, flags);
return;
}
READ_L2ACDTD(l2acdtd);
/* If we have init'ed and the config is still present, return */
if (mpidr_to_clk() == &pwrcl_clk.c && l2acdtd == acdtd_val_pwrcl) {
spin_unlock_irqrestore(&acd_lock, flags);
return;
} else if (mpidr_to_clk() == &pwrcl_clk.c) {
WRITE_L2ACDTD(acdtd_val_pwrcl);
}
if (mpidr_to_clk() == &perfcl_clk.c && l2acdtd == acdtd_val_perfcl) {
spin_unlock_irqrestore(&acd_lock, flags);
return;
} else if (mpidr_to_clk() == &perfcl_clk.c) {
WRITE_L2ACDTD(acdtd_val_perfcl);
}
/* Initial ACD for *this* cluster */
WRITE_L2ACDDVMRC(dvmrc_val);
WRITE_L2ACDSSCR(acdsscr_val);
if (mpidr_to_clk() == &pwrcl_clk.c) {
if (vbases[APC0_BASE])
writel_relaxed(0x0000000F, vbases[APC0_BASE] +
SSSCTL_OFFSET);
/* Ensure SSSCTL config goes through before enabling ACD. */
mb();
WRITE_L2ACDCR(acdcr_val_pwrcl);
} else {
WRITE_L2ACDCR(acdcr_val_perfcl);
if (vbases[APC1_BASE])
writel_relaxed(0x0000000F, vbases[APC1_BASE] +
SSSCTL_OFFSET);
/* Ensure SSSCTL config goes through before enabling ACD. */
mb();
}
spin_unlock_irqrestore(&acd_lock, flags);
}
static struct clk *logical_cpu_to_clk(int cpu)
{
struct device_node *cpu_node;
const u32 *cell;
u64 hwid;
static struct clk *cpu_clk_map[NR_CPUS];
if (cpu_clk_map[cpu])
return cpu_clk_map[cpu];
cpu_node = of_get_cpu_node(cpu, NULL);
if (!cpu_node)
goto fail;
cell = of_get_property(cpu_node, "reg", NULL);
if (!cell) {
pr_err("%s: missing reg property\n", cpu_node->full_name);
goto fail;
}
hwid = of_read_number(cell, of_n_addr_cells(cpu_node));
if ((hwid | pwrcl_clk.cpu_reg_mask) == pwrcl_clk.cpu_reg_mask) {
cpu_clk_map[cpu] = &pwrcl_clk.c;
return &pwrcl_clk.c;
}
if ((hwid | perfcl_clk.cpu_reg_mask) == perfcl_clk.cpu_reg_mask) {
cpu_clk_map[cpu] = &perfcl_clk.c;
return &perfcl_clk.c;
}
fail:
return NULL;
}
static struct pll_clk cbf_pll = {
.mode_reg = (void __iomem *)CBF_PLL_MODE,
.l_reg = (void __iomem *)CBF_PLL_L_VAL,
.alpha_reg = (void __iomem *)CBF_PLL_ALPHA,
.config_reg = (void __iomem *)CBF_PLL_USER_CTL,
.config_ctl_reg = (void __iomem *)CBF_PLL_CONFIG_CTL,
.config_ctl_hi_reg = (void __iomem *)CBF_PLL_CONFIG_CTL_HI,
.status_reg = (void __iomem *)CBF_PLL_MODE,
.test_ctl_lo_reg = (void __iomem *)CBF_PLL_TEST_CTL_LO,
.test_ctl_hi_reg = (void __iomem *)CBF_PLL_TEST_CTL_HI,
.pgm_test_ctl_enable = true,
.init_test_ctl = true,
.masks = {
.pre_div_mask = BIT(12),
.post_div_mask = BM(9, 8),
.mn_en_mask = BIT(24),
.main_output_mask = BIT(0),
.early_output_mask = BIT(3),
.apc_pdn_mask = BIT(24),
.lock_mask = BIT(31),
},
.vals = {
.post_div_masked = 0x100,
.pre_div_masked = 0x0,
.test_ctl_hi_val = 0x00004000,
.test_ctl_lo_val = 0x04000000,
.config_ctl_val = 0x200D4AA8,
.config_ctl_hi_val = 0x002,
},
.min_rate = 600000000,
.max_rate = 3000000000,
.src_rate = 19200000,
.base = &vbases[CBF_PLL_BASE],
.c = {
.parent = &xo_ao.c,
.dbg_name = "cbf_pll",
.ops = &clk_ops_variable_rate_pll_hwfsm,
VDD_DIG_FMAX_MAP1(LOW, 3000000000),
CLK_INIT(cbf_pll.c),
},
};
DEFINE_FIXED_DIV_CLK(cbf_pll_main, 2, &cbf_pll.c);
#define CBF_MUX_OFFSET 0x018
DEFINE_VDD_REGS_INIT(vdd_cbf, 1);
static struct mux_clk cbf_hf_mux = {
.offset = CBF_MUX_OFFSET,
MUX_SRC_LIST(
{ &cbf_pll.c, 1 },
{ &cbf_pll_main.c, 2 },
{ &sys_apcsaux_clk.c, 3 },
),
.en_mask = 0,
.ops = &cpu_mux_ops,
.mask = 0x3,
.shift = 0,
.base = &vbases[CBF_BASE],
.c = {
.dbg_name = "cbf_hf_mux",
.ops = &clk_ops_gen_mux,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(cbf_hf_mux.c),
},
};
static struct cpu_clk_8996 cbf_clk = {
.do_half_rate = true,
.c = {
.parent = &cbf_hf_mux.c,
.dbg_name = "cbf_clk",
.ops = &clk_ops_cpu_8996,
.vdd_class = &vdd_cbf,
CLK_INIT(cbf_clk.c),
},
};
#define APCS_CLK_DIAG 0x78
DEFINE_FIXED_SLAVE_DIV_CLK(pwrcl_div_clk, 16, &pwrcl_clk.c);
DEFINE_FIXED_SLAVE_DIV_CLK(perfcl_div_clk, 16, &perfcl_clk.c);
static struct mux_clk cpu_debug_mux = {
.offset = APCS_CLK_DIAG,
MUX_SRC_LIST(
{ &cbf_clk.c, 0x01 },
{ &pwrcl_div_clk.c, 0x11 },
{ &perfcl_div_clk.c, 0x21 },
),
MUX_REC_SRC_LIST(
&cbf_clk.c,
&pwrcl_div_clk.c,
&perfcl_div_clk.c,
),
.ops = &cpu_debug_mux_ops,
.mask = 0xFF,
.shift = 8,
.base = &vbases[DEBUG_BASE],
.c = {
.dbg_name = "cpu_debug_mux",
.ops = &clk_ops_gen_mux,
.flags = CLKFLAG_NO_RATE_CACHE,
CLK_INIT(cpu_debug_mux.c),
},
};
static struct clk_lookup cpu_clocks_8996[] = {
CLK_LIST(pwrcl_clk),
CLK_LIST(pwrcl_pll),
CLK_LIST(pwrcl_alt_pll),
CLK_LIST(pwrcl_pll_main),
CLK_LIST(pwrcl_hf_mux),
CLK_LIST(pwrcl_lf_mux),
CLK_LIST(perfcl_clk),
CLK_LIST(perfcl_pll),
CLK_LIST(perfcl_alt_pll),
CLK_LIST(perfcl_pll_main),
CLK_LIST(perfcl_hf_mux),
CLK_LIST(perfcl_lf_mux),
CLK_LIST(cbf_pll),
CLK_LIST(cbf_hf_mux),
CLK_LIST(cbf_clk),
CLK_LIST(xo_ao),
CLK_LIST(sys_apcsaux_clk),
CLK_LIST(cpu_debug_mux),
};
static int of_get_fmax_vdd_class(struct platform_device *pdev, struct clk *c,
char *prop_name)
{
struct device_node *of = pdev->dev.of_node;
int prop_len, i;
struct clk_vdd_class *vdd = c->vdd_class;
u32 *array;
if (!of_find_property(of, prop_name, &prop_len)) {
dev_err(&pdev->dev, "missing %s\n", prop_name);
return -EINVAL;
}
prop_len /= sizeof(u32);
if (prop_len % 2) {
dev_err(&pdev->dev, "bad length %d\n", prop_len);
return -EINVAL;
}
prop_len /= 2;
vdd->level_votes = devm_kzalloc(&pdev->dev, prop_len * sizeof(int),
GFP_KERNEL);
if (!vdd->level_votes)
return -ENOMEM;
vdd->vdd_uv = devm_kzalloc(&pdev->dev, prop_len * sizeof(int),
GFP_KERNEL);
if (!vdd->vdd_uv)
return -ENOMEM;
c->fmax = devm_kzalloc(&pdev->dev, prop_len * sizeof(unsigned long),
GFP_KERNEL);
if (!c->fmax)
return -ENOMEM;
array = devm_kzalloc(&pdev->dev,
prop_len * sizeof(u32) * 2, GFP_KERNEL);
if (!array)
return -ENOMEM;
of_property_read_u32_array(of, prop_name, array, prop_len * 2);
for (i = 0; i < prop_len; i++) {
c->fmax[i] = array[2 * i];
vdd->vdd_uv[i] = array[2 * i + 1];
}
devm_kfree(&pdev->dev, array);
vdd->num_levels = prop_len;
vdd->cur_level = prop_len;
c->num_fmax = prop_len;
return 0;
}
static int cpu_clock_8996_resources_init(struct platform_device *pdev)
{
struct resource *res;
struct clk *c;
int i;
for (i = 0; i < ARRAY_SIZE(base_names); i++) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
base_names[i]);
if (!res) {
dev_err(&pdev->dev,
"Unable to get platform resource for %s",
base_names[i]);
return -ENOMEM;
}
vbases[i] = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (!vbases[i]) {
dev_err(&pdev->dev, "Unable to map in base %s\n",
base_names[i]);
return -ENOMEM;
}
}
vdd_dig.regulator[0] = devm_regulator_get(&pdev->dev, "vdd-dig");
if (IS_ERR(vdd_dig.regulator[0])) {
if (PTR_ERR(vdd_dig.regulator[0]) != -EPROBE_DEFER)
dev_err(&pdev->dev, "Unable to get the CX regulator");
return PTR_ERR(vdd_dig.regulator[0]);
}
vdd_pwrcl.regulator[0] = devm_regulator_get(&pdev->dev, "vdd-pwrcl");
if (IS_ERR(vdd_pwrcl.regulator[0])) {
if (PTR_ERR(vdd_pwrcl.regulator[0]) != -EPROBE_DEFER)
dev_err(&pdev->dev, "Unable to get the pwrcl vreg\n");
return PTR_ERR(vdd_pwrcl.regulator[0]);
}
vdd_perfcl.regulator[0] = devm_regulator_get(&pdev->dev, "vdd-perfcl");
if (IS_ERR(vdd_perfcl.regulator[0])) {
if (PTR_ERR(vdd_perfcl.regulator[0]) != -EPROBE_DEFER)
dev_err(&pdev->dev, "Unable to get the perfcl vreg\n");
return PTR_ERR(vdd_perfcl.regulator[0]);
}
/* Leakage constraints disallow a turbo vote during bootup */
vdd_perfcl.skip_handoff = true;
vdd_cbf.regulator[0] = devm_regulator_get(&pdev->dev, "vdd-cbf");
if (IS_ERR(vdd_cbf.regulator[0])) {
if (PTR_ERR(vdd_cbf.regulator[0]) != -EPROBE_DEFER)
dev_err(&pdev->dev, "Unable to get the cbf vreg\n");
return PTR_ERR(vdd_cbf.regulator[0]);
}
c = devm_clk_get(&pdev->dev, "xo_ao");
if (IS_ERR(c)) {
if (PTR_ERR(c) != -EPROBE_DEFER)
dev_err(&pdev->dev, "Unable to get xo (rc = %ld)!\n",
PTR_ERR(c));
return PTR_ERR(c);
}
xo_ao.c.parent = c;
c = devm_clk_get(&pdev->dev, "aux_clk");
if (IS_ERR(c)) {
if (PTR_ERR(c) != -EPROBE_DEFER)
dev_err(&pdev->dev, "Unable to get gpll0 (rc = %ld)!\n",
PTR_ERR(c));
return PTR_ERR(c);
}
sys_apcsaux_clk_gcc.c.parent = c;
vdd_perfcl.use_max_uV = true;
vdd_pwrcl.use_max_uV = true;
vdd_cbf.use_max_uV = true;
return 0;
}
static int add_opp(struct clk *c, struct device *dev, unsigned long max_rate)
{
unsigned long rate = 0;
int level;
int uv;
int *vdd_uv = c->vdd_class->vdd_uv;
struct regulator *reg = c->vdd_class->regulator[0];
long ret;
bool first = true;
int j = 1;
while (1) {
rate = c->fmax[j++];
level = find_vdd_level(c, rate);
if (level <= 0) {
pr_warn("clock-cpu: no corner for %lu.\n", rate);
return -EINVAL;
}
uv = regulator_list_corner_voltage(reg, vdd_uv[level]);
if (uv < 0) {
pr_warn("clock-cpu: no uv for %lu.\n", rate);
return -EINVAL;
}
ret = dev_pm_opp_add(dev, rate, uv);
if (ret) {
pr_warn("clock-cpu: failed to add OPP for %lu\n", rate);
return ret;
}
/*
* Print the OPP pair for the lowest and highest frequency for
* each device that we're populating. This is important since
* this information will be used by thermal mitigation and the
* scheduler.
*/
if ((rate >= max_rate) || first) {
/* one time print at bootup */
pr_info("clock-cpu-8996: set OPP pair (%lu Hz, %d uv) on %s\n",
rate, uv, dev_name(dev));
if (first)
first = false;
else
break;
}
}
return 0;
}
static void populate_opp_table(struct platform_device *pdev)
{
unsigned long pwrcl_fmax, perfcl_fmax, cbf_fmax;
struct device_node *cbf_node;
struct platform_device *cbf_dev;
int cpu;
pwrcl_fmax = pwrcl_clk.c.fmax[pwrcl_clk.c.num_fmax - 1];
perfcl_fmax = perfcl_clk.c.fmax[perfcl_clk.c.num_fmax - 1];
cbf_fmax = cbf_clk.c.fmax[cbf_clk.c.num_fmax - 1];
for_each_possible_cpu(cpu) {
if (logical_cpu_to_clk(cpu) == &pwrcl_clk.c) {
WARN(add_opp(&pwrcl_clk.c, get_cpu_device(cpu),
pwrcl_fmax),
"Failed to add OPP levels for power cluster\n");
}
if (logical_cpu_to_clk(cpu) == &perfcl_clk.c) {
WARN(add_opp(&perfcl_clk.c, get_cpu_device(cpu),
perfcl_fmax),
"Failed to add OPP levels for perf cluster\n");
}
}
cbf_node = of_parse_phandle(pdev->dev.of_node, "cbf-dev", 0);
if (!cbf_node) {
pr_err("can't find the CBF dt node\n");
return;
}
cbf_dev = of_find_device_by_node(cbf_node);
if (!cbf_dev) {
pr_err("can't find the CBF dt device\n");
return;
}
WARN(add_opp(&cbf_clk.c, &cbf_dev->dev, cbf_fmax),
"Failed to add OPP levels for CBF\n");
}
static int perfclspeedbin;
unsigned long pwrcl_early_boot_rate = 883200000;
unsigned long perfcl_early_boot_rate = 883200000;
unsigned long cbf_early_boot_rate = 614400000;
unsigned long alt_pll_early_boot_rate = 307200000;
static int cpu_clock_8996_driver_probe(struct platform_device *pdev)
{
int ret, cpu;
unsigned long pwrclrate, perfclrate, cbfrate;
int pvs_ver = 0;
u32 pte_efuse;
char perfclspeedbinstr[] = "qcom,perfcl-speedbinXX-vXX";
char pwrclspeedbinstr[] = "qcom,pwrcl-speedbinXX-vXX";
char cbfspeedbinstr[] = "qcom,cbf-speedbinXX-vXX";
pwrcl_pll_main.c.flags = CLKFLAG_NO_RATE_CACHE;
perfcl_pll_main.c.flags = CLKFLAG_NO_RATE_CACHE;
cbf_pll_main.c.flags = CLKFLAG_NO_RATE_CACHE;
pwrcl_clk.hw_low_power_ctrl = true;
perfcl_clk.hw_low_power_ctrl = true;
ret = cpu_clock_8996_resources_init(pdev);
if (ret) {
dev_err(&pdev->dev, "resources init failed\n");
return ret;
}
pte_efuse = readl_relaxed(vbases[EFUSE_BASE]);
perfclspeedbin = ((pte_efuse >> EFUSE_SHIFT) & EFUSE_MASK);
dev_info(&pdev->dev, "using perf/pwr/cbf speed bin %u and pvs_ver %d\n",
perfclspeedbin, pvs_ver);
snprintf(perfclspeedbinstr, ARRAY_SIZE(perfclspeedbinstr),
"qcom,perfcl-speedbin%d-v%d", perfclspeedbin, pvs_ver);
ret = of_get_fmax_vdd_class(pdev, &perfcl_clk.c, perfclspeedbinstr);
if (ret) {
dev_err(&pdev->dev, "Can't get speed bin for perfcl. Falling back to zero.\n");
ret = of_get_fmax_vdd_class(pdev, &perfcl_clk.c,
"qcom,perfcl-speedbin0-v0");
if (ret) {
dev_err(&pdev->dev, "Unable to retrieve plan for perf. Bailing...\n");
return ret;
}
}
snprintf(pwrclspeedbinstr, ARRAY_SIZE(pwrclspeedbinstr),
"qcom,pwrcl-speedbin%d-v%d", perfclspeedbin, pvs_ver);
ret = of_get_fmax_vdd_class(pdev, &pwrcl_clk.c, pwrclspeedbinstr);
if (ret) {
dev_err(&pdev->dev, "Can't get speed bin for pwrcl. Falling back to zero.\n");
ret = of_get_fmax_vdd_class(pdev, &pwrcl_clk.c,
"qcom,pwrcl-speedbin0-v0");
if (ret) {
dev_err(&pdev->dev, "Unable to retrieve plan for pwrcl\n");
return ret;
}
}
snprintf(cbfspeedbinstr, ARRAY_SIZE(cbfspeedbinstr),
"qcom,cbf-speedbin%d-v%d", perfclspeedbin, pvs_ver);
ret = of_get_fmax_vdd_class(pdev, &cbf_clk.c, cbfspeedbinstr);
if (ret) {
dev_err(&pdev->dev, "Can't get speed bin for cbf. Falling back to zero.\n");
ret = of_get_fmax_vdd_class(pdev, &cbf_clk.c,
"qcom,cbf-speedbin0-v0");
if (ret) {
dev_err(&pdev->dev, "Unable to retrieve plan for cbf\n");
return ret;
}
}
get_online_cpus();
for_each_possible_cpu(cpu) {
if (logical_cpu_to_clk(cpu) == &pwrcl_clk.c)
cpumask_set_cpu(cpu, &pwrcl_clk.cpumask);
if (logical_cpu_to_clk(cpu) == &perfcl_clk.c)
cpumask_set_cpu(cpu, &perfcl_clk.cpumask);
}
ret = of_msm_clock_register(pdev->dev.of_node, cpu_clocks_8996,
ARRAY_SIZE(cpu_clocks_8996));
if (ret) {
dev_err(&pdev->dev, "Unable to register CPU clocks.\n");
return ret;
}
for_each_online_cpu(cpu) {
WARN(clk_prepare_enable(&cbf_clk.c),
"Failed to enable cbf clock.\n");
WARN(clk_prepare_enable(logical_cpu_to_clk(cpu)),
"Failed to enable clock for cpu %d\n", cpu);
}
pwrclrate = clk_get_rate(&pwrcl_clk.c);
perfclrate = clk_get_rate(&perfcl_clk.c);
cbfrate = clk_get_rate(&cbf_clk.c);
if (!pwrclrate) {
dev_err(&pdev->dev, "Unknown pwrcl rate. Setting safe rate\n");
ret = clk_set_rate(&pwrcl_clk.c, sys_apcsaux_clk.c.rate);
if (ret) {
dev_err(&pdev->dev, "Can't set a safe rate on A53.\n");
return -EINVAL;
}
pwrclrate = sys_apcsaux_clk.c.rate;
}
if (!perfclrate) {
dev_err(&pdev->dev, "Unknown perfcl rate. Setting safe rate\n");
ret = clk_set_rate(&perfcl_clk.c, sys_apcsaux_clk.c.rate);
if (ret) {
dev_err(&pdev->dev, "Can't set a safe rate on perf.\n");
return -EINVAL;
}
perfclrate = sys_apcsaux_clk.c.rate;
}
if (!cbfrate) {
dev_err(&pdev->dev, "Unknown CBF rate. Setting safe rate\n");
cbfrate = sys_apcsaux_clk.c.rate;
ret = clk_set_rate(&cbf_clk.c, cbfrate);
if (ret) {
dev_err(&pdev->dev, "Can't set a safe rate on CBF.\n");
return -EINVAL;
}
}
/* Permanently enable the cluster PLLs */
clk_prepare_enable(&perfcl_pll.c);
clk_prepare_enable(&pwrcl_pll.c);
clk_prepare_enable(&perfcl_alt_pll.c);
clk_prepare_enable(&pwrcl_alt_pll.c);
clk_prepare_enable(&cbf_pll.c);
/* Set the early boot rate. This may also switch us to the ACD leg */
clk_set_rate(&pwrcl_clk.c, pwrcl_early_boot_rate);
clk_set_rate(&perfcl_clk.c, perfcl_early_boot_rate);
populate_opp_table(pdev);
put_online_cpus();
return 0;
}
static struct of_device_id match_table[] = {
{ .compatible = "qcom,cpu-clock-8996" },
{ .compatible = "qcom,cpu-clock-8996-v3" },
{}
};
static struct platform_driver cpu_clock_8996_driver = {
.probe = cpu_clock_8996_driver_probe,
.driver = {
.name = "cpu-clock-8996",
.of_match_table = match_table,
.owner = THIS_MODULE,
},
};
static int __init cpu_clock_8996_init(void)
{
return platform_driver_register(&cpu_clock_8996_driver);
}
arch_initcall(cpu_clock_8996_init);
static void __exit cpu_clock_8996_exit(void)
{
platform_driver_unregister(&cpu_clock_8996_driver);
}
module_exit(cpu_clock_8996_exit);
#define APC0_BASE_PHY 0x06400000
#define APC1_BASE_PHY 0x06480000
#define CBF_BASE_PHY 0x09A11000
#define CBF_PLL_BASE_PHY 0x09A20000
#define AUX_BASE_PHY 0x09820050
#define CLK_CTL_OFFSET 0x44
#define PSCTL_OFFSET 0x164
#define AUTO_CLK_SEL_BIT BIT(8)
#define CBF_AUTO_CLK_SEL_BIT BIT(6)
#define AUTO_CLK_SEL_ALWAYS_ON_MASK BM(5, 4)
#define AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL (0x3 << 4)
#define HF_MUX_MASK 0x3
#define LF_MUX_MASK 0x3
#define LF_MUX_SHIFT 0x2
#define HF_MUX_SEL_EARLY_PLL 0x1
#define HF_MUX_SEL_LF_MUX 0x1
#define LF_MUX_SEL_ALT_PLL 0x1
static int use_alt_pll;
module_param(use_alt_pll, int, 0444);
static int clock_cpu_8996_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
if (!enable_acd)
return NOTIFY_OK;
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_STARTING:
/* This is needed for the first time that CPUs come up */
cpu_clock_8996_acd_init();
break;
default:
break;
}
return NOTIFY_OK;
}
static struct notifier_block __refdata clock_cpu_8996_cpu_notifier = {
.notifier_call = clock_cpu_8996_cpu_callback,
};
int __init cpu_clock_8996_early_init(void)
{
int ret = 0;
void __iomem *auxbase;
u32 regval;
struct device_node *ofnode;
ofnode = of_find_compatible_node(NULL, NULL,
"qcom,cpu-clock-8996-v3");
if (ofnode)
cpu_clocks_v3 = true;
else {
ofnode = of_find_compatible_node(NULL, NULL,
"qcom,cpu-clock-8996");
if (!ofnode)
return 0;
}
pr_info("clock-cpu-8996: configuring clocks for the perf cluster\n");
if (cpu_clocks_v3) {
pwrcl_alt_pll.offline_bit_workaround = false;
perfcl_alt_pll.offline_bit_workaround = false;
pwrcl_pll.pgm_test_ctl_enable = false;
perfcl_pll.pgm_test_ctl_enable = false;
pwrcl_pll.vals.config_ctl_val = 0x200d4828;
pwrcl_pll.vals.config_ctl_hi_val = 0x006;
perfcl_pll.vals.config_ctl_val = 0x200d4828;
perfcl_pll.vals.config_ctl_hi_val = 0x006;
cbf_pll.vals.config_ctl_val = 0x200d4828;
cbf_pll.vals.config_ctl_hi_val = 0x006;
pwrcl_pll.vals.test_ctl_lo_val = 0x1C000000;
perfcl_pll.vals.test_ctl_lo_val = 0x1C000000;
cbf_pll.vals.test_ctl_lo_val = 0x1C000000;
}
/*
* We definitely don't want to parse DT here - this is too early and in
* the critical path for boot timing. Just ioremap the bases.
*/
vbases[APC0_BASE] = ioremap(APC0_BASE_PHY, SZ_4K);
if (!vbases[APC0_BASE]) {
WARN(1, "Unable to ioremap power mux base. Can't configure CPU clocks\n");
ret = -ENOMEM;
goto fail;
}
vbases[APC1_BASE] = ioremap(APC1_BASE_PHY, SZ_4K);
if (!vbases[APC1_BASE]) {
WARN(1, "Unable to ioremap perf mux base. Can't configure CPU clocks\n");
ret = -ENOMEM;
goto apc1_fail;
}
vbases[CBF_BASE] = ioremap(CBF_BASE_PHY, SZ_4K);
if (!vbases[CBF_BASE]) {
WARN(1, "Unable to ioremap cbf mux base. Can't configure CPU clocks\n");
ret = -ENOMEM;
goto cbf_map_fail;
}
vbases[CBF_PLL_BASE] = ioremap(CBF_PLL_BASE_PHY, SZ_4K);
if (!vbases[CBF_BASE]) {
WARN(1, "Unable to ioremap cbf pll base. Can't configure CPU clocks\n");
ret = -ENOMEM;
goto cbf_pll_map_fail;
}
vbases[APC0_PLL_BASE] = vbases[APC0_BASE];
vbases[APC1_PLL_BASE] = vbases[APC1_BASE];
auxbase = ioremap(AUX_BASE_PHY, SZ_4K);
if (!auxbase) {
WARN(1, "Unable to ioremap aux base. Can't configure CPU clocks\n");
ret = -ENOMEM;
goto auxbase_fail;
}
/*
* Set GPLL0 divider for div-2 to get 300Mhz. This divider
* can be programmed dynamically.
*/
regval = readl_relaxed(auxbase);
regval &= ~BM(17, 16);
regval |= 0x1 << 16;
writel_relaxed(regval, auxbase);
/* Ensure write goes through before selecting the aux clock */
mb();
udelay(5);
/* Select GPLL0 for 300MHz for the perf cluster */
writel_relaxed(0xC, vbases[APC1_BASE] + MUX_OFFSET);
/* Select GPLL0 for 300MHz for the power cluster */
writel_relaxed(0xC, vbases[APC0_BASE] + MUX_OFFSET);
/* Select GPLL0 for 300MHz on the CBF */
writel_relaxed(0x3, vbases[CBF_BASE] + CBF_MUX_OFFSET);
/* Ensure write goes through before PLLs are reconfigured */
mb();
udelay(5);
/* Set the auto clock sel always-on source to GPLL0/2 (300MHz) */
regval = readl_relaxed(vbases[APC0_BASE] + MUX_OFFSET);
regval &= ~AUTO_CLK_SEL_ALWAYS_ON_MASK;
regval |= AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL;
writel_relaxed(regval, vbases[APC0_BASE] + MUX_OFFSET);
regval = readl_relaxed(vbases[APC1_BASE] + MUX_OFFSET);
regval &= ~AUTO_CLK_SEL_ALWAYS_ON_MASK;
regval |= AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL;
writel_relaxed(regval, vbases[APC1_BASE] + MUX_OFFSET);
regval = readl_relaxed(vbases[CBF_BASE] + MUX_OFFSET);
regval &= ~AUTO_CLK_SEL_ALWAYS_ON_MASK;
regval |= AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL;
writel_relaxed(regval, vbases[CBF_BASE] + MUX_OFFSET);
/* == Setup PLLs in FSM mode == */
/* Disable all PLLs (we're already on GPLL0 for both clusters) */
perfcl_alt_pll.c.ops->disable(&perfcl_alt_pll.c);
pwrcl_alt_pll.c.ops->disable(&pwrcl_alt_pll.c);
writel_relaxed(0x0, vbases[APC0_BASE] +
(unsigned long)pwrcl_pll.mode_reg);
writel_relaxed(0x0, vbases[APC1_BASE] +
(unsigned long)perfcl_pll.mode_reg);
writel_relaxed(0x0, vbases[CBF_PLL_BASE] +
(unsigned long)cbf_pll.mode_reg);
/* Let PLLs disable before re-init'ing them */
mb();
/* Initialize all the PLLs */
__variable_rate_pll_init(&perfcl_pll.c);
__variable_rate_pll_init(&pwrcl_pll.c);
__variable_rate_pll_init(&cbf_pll.c);
__init_alpha_pll(&perfcl_alt_pll.c);
__init_alpha_pll(&pwrcl_alt_pll.c);
/* Set an appropriate rate on the perf clusters PLLs */
perfcl_pll.c.ops->set_rate(&perfcl_pll.c, perfcl_early_boot_rate);
perfcl_alt_pll.c.ops->set_rate(&perfcl_alt_pll.c,
alt_pll_early_boot_rate);
pwrcl_pll.c.ops->set_rate(&pwrcl_pll.c, pwrcl_early_boot_rate);
pwrcl_alt_pll.c.ops->set_rate(&pwrcl_alt_pll.c,
alt_pll_early_boot_rate);
/* Set an appropriate rate on the CBF PLL */
cbf_pll.c.ops->set_rate(&cbf_pll.c, cbf_early_boot_rate);
/*
* Enable FSM mode on the primary PLLs.
* This should turn on the PLLs as well.
*/
writel_relaxed(0x00118000, vbases[APC0_BASE] +
(unsigned long)pwrcl_pll.mode_reg);
writel_relaxed(0x00118000, vbases[APC1_BASE] +
(unsigned long)perfcl_pll.mode_reg);
/*
* Enable FSM mode on the alternate PLLs.
* This should turn on the PLLs as well.
*/
writel_relaxed(0x00118000, vbases[APC0_BASE] +
(unsigned long)pwrcl_alt_pll.offset);
writel_relaxed(0x00118000, vbases[APC1_BASE] +
(unsigned long)perfcl_alt_pll.offset);
/*
* Enable FSM mode on the CBF PLL.
* This should turn on the PLL as well.
*/
writel_relaxed(0x00118000, vbases[CBF_PLL_BASE] +
(unsigned long)cbf_pll.mode_reg);
/*
* If we're on MSM8996 V1, the CBF FSM bits are not present, and
* the mode register will read zero at this point. In that case,
* just enable the CBF PLL to "simulate" FSM mode.
*/
if (!readl_relaxed(vbases[CBF_PLL_BASE] +
(unsigned long)cbf_pll.mode_reg))
clk_ops_variable_rate_pll.enable(&cbf_pll.c);
/* Ensure write goes through before auto clock selection is enabled */
mb();
/* Wait for PLL(s) to lock */
udelay(50);
/* Enable auto clock selection for both clusters and the CBF */
regval = readl_relaxed(vbases[APC0_BASE] + CLK_CTL_OFFSET);
regval |= AUTO_CLK_SEL_BIT;
writel_relaxed(regval, vbases[APC0_BASE] + CLK_CTL_OFFSET);
regval = readl_relaxed(vbases[APC1_BASE] + CLK_CTL_OFFSET);
regval |= AUTO_CLK_SEL_BIT;
writel_relaxed(regval, vbases[APC1_BASE] + CLK_CTL_OFFSET);
regval = readl_relaxed(vbases[CBF_BASE] + CBF_MUX_OFFSET);
regval |= CBF_AUTO_CLK_SEL_BIT;
writel_relaxed(regval, vbases[CBF_BASE] + CBF_MUX_OFFSET);
/* Ensure write goes through before muxes are switched */
mb();
udelay(5);
if (use_alt_pll) {
perfcl_hf_mux.safe_parent = &perfcl_lf_mux.c;
pwrcl_hf_mux.safe_parent = &pwrcl_lf_mux.c;
pwrcl_clk.alt_pll = NULL;
perfcl_clk.alt_pll = NULL;
pwrcl_clk.do_half_rate = false;
perfcl_clk.do_half_rate = false;
perfcl_hf_mux.parents = (struct clk_src *)
&clk_src_perfcl_hf_mux_alt;
perfcl_hf_mux.num_parents =
ARRAY_SIZE(clk_src_perfcl_hf_mux_alt);
pwrcl_hf_mux.parents = (struct clk_src *)
&clk_src_pwrcl_hf_mux_alt;
pwrcl_hf_mux.num_parents =
ARRAY_SIZE(clk_src_pwrcl_hf_mux_alt);
perfcl_lf_mux.parents = (struct clk_src *)
&clk_src_perfcl_lf_mux_alt;
perfcl_lf_mux.num_parents =
ARRAY_SIZE(clk_src_perfcl_lf_mux_alt);
pwrcl_lf_mux.parents = (struct clk_src *)
&clk_src_pwrcl_lf_mux_alt;
pwrcl_lf_mux.num_parents =
ARRAY_SIZE(clk_src_pwrcl_lf_mux_alt);
/* Switch the clusters to use the alternate PLLs */
writel_relaxed(0x33, vbases[APC0_BASE] + MUX_OFFSET);
writel_relaxed(0x33, vbases[APC1_BASE] + MUX_OFFSET);
}
/* Switch the CBF to use the primary PLL */
regval = readl_relaxed(vbases[CBF_BASE] + CBF_MUX_OFFSET);
regval &= ~BM(1, 0);
regval |= 0x1;
writel_relaxed(regval, vbases[CBF_BASE] + CBF_MUX_OFFSET);
if (!cpu_clocks_v3)
enable_acd = 0;
if (enable_acd) {
int i;
if (use_alt_pll)
panic("Can't enable ACD on the the alternate PLL\n");
perfcl_clk.has_acd = true;
pwrcl_clk.has_acd = true;
/* Enable ACD on this cluster if necessary */
cpu_clock_8996_acd_init();
/* Ensure we never use the non-ACD leg of the GFMUX */
for (i = 0; i < pwrcl_hf_mux.num_parents; i++)
if (pwrcl_hf_mux.parents[i].src == &pwrcl_pll.c)
pwrcl_hf_mux.parents[i].sel = 2;
for (i = 0; i < perfcl_hf_mux.num_parents; i++)
if (perfcl_hf_mux.parents[i].src == &perfcl_pll.c)
perfcl_hf_mux.parents[i].sel = 2;
BUG_ON(register_hotcpu_notifier(&clock_cpu_8996_cpu_notifier));
/* Pulse swallower and soft-start settings */
writel_relaxed(0x00030005, vbases[APC0_BASE] + PSCTL_OFFSET);
writel_relaxed(0x00030005, vbases[APC1_BASE] + PSCTL_OFFSET);
/* Ensure all config above goes through before the ACD switch */
mb();
/* Switch the clusters to use the ACD leg */
writel_relaxed(0x32, vbases[APC0_BASE] + MUX_OFFSET);
writel_relaxed(0x32, vbases[APC1_BASE] + MUX_OFFSET);
} else {
/* Switch the clusters to use the primary PLLs */
writel_relaxed(0x31, vbases[APC0_BASE] + MUX_OFFSET);
writel_relaxed(0x31, vbases[APC1_BASE] + MUX_OFFSET);
}
/*
* One time print during boot - this is the earliest time
* that Linux configures the CPU clocks. It's critical for
* debugging that we know that this configuration completed,
* especially when debugging CPU hangs.
*/
pr_info("%s: finished CPU clock configuration\n", __func__);
iounmap(auxbase);
auxbase_fail:
iounmap(vbases[CBF_PLL_BASE]);
cbf_pll_map_fail:
iounmap(vbases[CBF_BASE]);
cbf_map_fail:
if (ret) {
iounmap(vbases[APC1_BASE]);
vbases[APC1_BASE] = NULL;
}
apc1_fail:
if (ret) {
iounmap(vbases[APC0_BASE]);
vbases[APC0_BASE] = NULL;
}
fail:
return ret;
}
early_initcall(cpu_clock_8996_early_init);
MODULE_DESCRIPTION("CPU clock driver for msm8996");
MODULE_LICENSE("GPL v2");