/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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");