diff options
Diffstat (limited to 'src/soc/qualcomm/common')
-rw-r--r-- | src/soc/qualcomm/common/clock.c | 243 | ||||
-rw-r--r-- | src/soc/qualcomm/common/include/soc/clock_common.h | 173 |
2 files changed, 416 insertions, 0 deletions
diff --git a/src/soc/qualcomm/common/clock.c b/src/soc/qualcomm/common/clock.c new file mode 100644 index 0000000000..e83f979e81 --- /dev/null +++ b/src/soc/qualcomm/common/clock.c @@ -0,0 +1,243 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <assert.h> +#include <commonlib/helpers.h> +#include <delay.h> +#include <device/mmio.h> +#include <soc/clock.h> +#include <timer.h> +#include <types.h> + +/* Clock Branch Operations */ +static bool clock_is_off(u32 *cbcr_addr) +{ + return (read32(cbcr_addr) & CLK_CTL_OFF_BMSK); +} + +enum cb_err clock_enable_vote(void *cbcr_addr, void *vote_addr, + uint32_t vote_bit) +{ + int count = 100; + + setbits32(vote_addr, BIT(vote_bit)); + + /* Ensure clock is enabled */ + while (count-- > 0) { + if (!clock_is_off(cbcr_addr)) + return CB_SUCCESS; + udelay(1); + } + printk(BIOS_ERR, "ERROR: Failed to enable clock, register val: 0x%x\n", + read32(cbcr_addr)); + return CB_ERR; +} + +enum cb_err clock_enable(void *cbcr_addr) +{ + int count = 100; + + /* Set clock enable bit */ + setbits32(cbcr_addr, BIT(CLK_CTL_EN_SHFT)); + + /* Ensure clock is enabled */ + while (count-- > 0) { + if (!clock_is_off(cbcr_addr)) + return CB_SUCCESS; + udelay(1); + } + printk(BIOS_ERR, "ERROR: Failed to enable clock, register val: 0x%x\n", + read32(cbcr_addr)); + return CB_ERR; +} + +/* Clock Block Reset Operations */ +void clock_reset_bcr(void *bcr_addr, bool assert) +{ + if (assert) + setbits32(bcr_addr, BIT(CLK_CTL_BCR_BLK_SHFT)); + else + clrbits32(bcr_addr, BIT(CLK_CTL_BCR_BLK_SHFT)); +} + +/* Clock GDSC Operations */ +enum cb_err enable_and_poll_gdsc_status(void *gdscr_addr) +{ + if (read32(gdscr_addr) & CLK_CTL_OFF_BMSK) + return CB_SUCCESS; + + clrbits32(gdscr_addr, BIT(GDSC_ENABLE_BIT)); + + /* Ensure gdsc is enabled */ + if (!wait_us(100, (read32(gdscr_addr) & CLK_CTL_OFF_BMSK))) + return CB_ERR; + + return CB_SUCCESS; +} + +/* Clock Root clock Generator with MND Operations */ +static void clock_configure_mnd(struct clock_rcg *clk, uint32_t m, uint32_t n, + uint32_t d_2) +{ + struct clock_rcg_mnd *mnd = (struct clock_rcg_mnd *)clk; + + setbits32(&clk->rcg_cfg, + RCG_MODE_DUAL_EDGE << CLK_CTL_CFG_MODE_SHFT); + + write32(&mnd->m, m & CLK_CTL_RCG_MND_BMSK); + write32(&mnd->n, ~(n-m) & CLK_CTL_RCG_MND_BMSK); + write32(&mnd->d_2, ~(d_2) & CLK_CTL_RCG_MND_BMSK); +} + +/* Clock Root clock Generator Operations */ +enum cb_err clock_configure(struct clock_rcg *clk, + struct clock_freq_config *clk_cfg, uint32_t hz, + uint32_t num_perfs) +{ + uint32_t reg_val, idx; + + for (idx = 0; idx < num_perfs; idx++) + if (hz <= clk_cfg[idx].hz) + break; + + reg_val = (clk_cfg[idx].src << CLK_CTL_CFG_SRC_SEL_SHFT) | + (clk_cfg[idx].div << CLK_CTL_CFG_SRC_DIV_SHFT); + + /* Set clock config */ + write32(&clk->rcg_cfg, reg_val); + + if (clk_cfg[idx].m != 0) + clock_configure_mnd(clk, clk_cfg[idx].m, clk_cfg[idx].n, + clk_cfg[idx].d_2); + + /* Commit config to RCG */ + setbits32(&clk->rcg_cmd, BIT(CLK_CTL_CMD_UPDATE_SHFT)); + + return CB_SUCCESS; +} + +/* Clock Root clock Generator with DFS Operations */ +void clock_configure_dfsr_table(int qup, struct clock_freq_config *clk_cfg, + uint32_t num_perfs) +{ + struct qupv3_clock *qup_clk; + unsigned int idx, s = qup % QUP_WRAP1_S0; + uint32_t reg_val; + + qup_clk = qup < QUP_WRAP1_S0 ? + &gcc->qup_wrap0_s[s] : &gcc->qup_wrap1_s[s]; + + clrsetbits32(&qup_clk->dfsr_clk.cmd_dfsr, + BIT(CLK_CTL_CMD_RCG_SW_CTL_SHFT), + BIT(CLK_CTL_CMD_DFSR_SHFT)); + + for (idx = 0; idx < num_perfs; idx++) { + reg_val = (clk_cfg[idx].src << CLK_CTL_CFG_SRC_SEL_SHFT) | + (clk_cfg[idx].div << CLK_CTL_CFG_SRC_DIV_SHFT); + + write32(&qup_clk->dfsr_clk.perf_dfsr[idx], reg_val); + + if (clk_cfg[idx].m == 0) + continue; + + setbits32(&qup_clk->dfsr_clk.perf_dfsr[idx], + RCG_MODE_DUAL_EDGE << CLK_CTL_CFG_MODE_SHFT); + + reg_val = clk_cfg[idx].m & CLK_CTL_RCG_MND_BMSK; + write32(&qup_clk->dfsr_clk.perf_m_dfsr[idx], reg_val); + + reg_val = ~(clk_cfg[idx].n - clk_cfg[idx].m) + & CLK_CTL_RCG_MND_BMSK; + write32(&qup_clk->dfsr_clk.perf_n_dfsr[idx], reg_val); + + reg_val = ~(clk_cfg[idx].d_2) & CLK_CTL_RCG_MND_BMSK; + write32(&qup_clk->dfsr_clk.perf_d_dfsr[idx], reg_val); + } +} + +/* General Purpose PLL configuration and enable Operations */ +enum cb_err clock_configure_enable_gpll(struct alpha_pll_reg_val_config *cfg, + bool enable, int br_enable) +{ + if (cfg->l_val) + write32(cfg->reg_l, cfg->l_val); + + if (cfg->cal_l_val) + write32(cfg->reg_cal_l, cfg->cal_l_val); + + if (cfg->alpha_val) + write32(cfg->reg_alpha, cfg->alpha_val); + + if (cfg->user_ctl_val) + write32(cfg->reg_user_ctl, cfg->user_ctl_val); + + if (cfg->user_ctl_hi_val) + write32(cfg->reg_user_ctl_hi, cfg->user_ctl_hi_val); + + if (cfg->user_ctl_hi1_val) + write32(cfg->reg_user_ctl_hi1, cfg->user_ctl_hi1_val); + + if (cfg->config_ctl_val) + write32(cfg->reg_config_ctl, cfg->config_ctl_val); + + if (cfg->config_ctl_hi_val) + write32(cfg->reg_config_ctl_hi, cfg->config_ctl_hi_val); + + if (cfg->config_ctl_hi1_val) + write32(cfg->reg_config_ctl_hi1, cfg->config_ctl_hi1_val); + + if (cfg->fsm_enable) + setbits32(cfg->reg_mode, BIT(PLL_FSM_EN_SHFT)); + + if (enable) { + setbits32(cfg->reg_opmode, BIT(PLL_STANDBY_MODE)); + + /* + * H/W requires a 1us delay between placing PLL in STANDBY and + * de-asserting the reset. + */ + udelay(1); + setbits32(cfg->reg_mode, BIT(PLL_RESET_N_SHFT)); + + /* + * H/W requires a 10us delay between de-asserting the reset and + * enabling the PLL branch bit. + */ + udelay(10); + setbits32(cfg->reg_apcs_pll_br_en, BIT(br_enable)); + + /* Wait for Lock Detection */ + if (!wait_us(100, read32(cfg->reg_mode) & PLL_LOCK_DET_BMSK)) { + printk(BIOS_ERR, "ERROR: PLL did not lock!\n"); + return CB_ERR; + } + } + + return CB_SUCCESS; +} + +enum cb_err agera_pll_enable(struct alpha_pll_reg_val_config *cfg) +{ + setbits32(cfg->reg_mode, BIT(PLL_BYPASSNL_SHFT)); + + /* + * H/W requires a 5us delay between disabling the bypass and + * de-asserting the reset. + */ + udelay(5); + setbits32(cfg->reg_mode, BIT(PLL_RESET_SHFT)); + + if (!wait_us(100, read32(cfg->reg_mode) & PLL_LOCK_DET_BMSK)) { + printk(BIOS_ERR, "ERROR: CPU PLL did not lock!\n"); + return CB_ERR; + } + + setbits32(cfg->reg_mode, BIT(PLL_OUTCTRL_SHFT)); + + return CB_SUCCESS; +} + +/* Bring subsystem out of RESET */ +void clock_reset_subsystem(u32 *misc, u32 shft) +{ + clrbits32(misc, BIT(shft)); +} diff --git a/src/soc/qualcomm/common/include/soc/clock_common.h b/src/soc/qualcomm/common/include/soc/clock_common.h new file mode 100644 index 0000000000..b9241944c2 --- /dev/null +++ b/src/soc/qualcomm/common/include/soc/clock_common.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_QUALCOMM_COMMON_CLOCK_H__ +#define __SOC_QUALCOMM_COMMON_CLOCK_H__ + +#define QCOM_CLOCK_DIV(div) (2 * div - 1) + +/* Root Clock Generator */ +struct clock_rcg { + u32 rcg_cmd; + u32 rcg_cfg; +}; + +/* Root Clock Generator with MND */ +struct clock_rcg_mnd { + struct clock_rcg clock; + u32 m; + u32 n; + u32 d_2; +}; + +/* DFS controlled Root Clock Generator */ +struct clock_rcg_dfsr { + u32 cmd_dfsr; + u8 _res0[0x20 - 0x1c]; + u32 perf_dfsr[8]; + u8 _res1[0x60 - 0x40]; + u32 perf_m_dfsr[8]; + u8 _res2[0xa0 - 0x80]; + u32 perf_n_dfsr[8]; + u8 _res3[0xe0 - 0xc0]; + u32 perf_d_dfsr[8]; + u8 _res4[0x130 - 0x100]; +}; + +/* Clock Frequency Table */ +struct clock_freq_config { + uint32_t hz; + uint8_t src; + uint8_t div; + uint16_t m; + uint16_t n; + uint16_t d_2; +}; + +struct qupv3_clock { + u32 cbcr; + struct clock_rcg_mnd clk; + struct clock_rcg_dfsr dfsr_clk; +}; + +/* PLL Configuration */ +struct alpha_pll_reg_val_config { + void *reg_mode; + u32 mode_val; + void *reg_l; + u32 l_val; + void *reg_cal_l; + u32 cal_l_val; + void *reg_user_ctl; + u32 user_ctl_val; + void *reg_user_ctl_hi; + u32 user_ctl_hi_val; + void *reg_user_ctl_hi1; + u32 user_ctl_hi1_val; + void *reg_config_ctl; + u32 config_ctl_val; + void *reg_config_ctl_hi; + u32 config_ctl_hi_val; + void *reg_config_ctl_hi1; + u32 config_ctl_hi1_val; + void *reg_alpha; + u32 alpha_val; + void *reg_opmode; + void *reg_apcs_pll_br_en; + bool fsm_enable; +}; + +enum clk_ctl_gpll_user_ctl { + PLL_PLLOUT_MAIN_SHFT = 0, + PLL_PLLOUT_EVEN_SHFT = 1, + PLL_PLLOUT_ODD_SHFT = 2, + PLL_POST_DIV_EVEN_SHFT = 8, + PLL_POST_DIV_ODD_SHFT = 12, + PLL_PLLOUT_EVEN_BMSK = 0x2, +}; + +enum gpll_mode { + PLL_LOCK_DET_BMSK = 0x80000000, + PLL_BYPASSNL_BMSK = 0x2, + PLL_OUTCTRL_BMSK = 0x1, + PLL_USERCTL_BMSK = 0xF, + PLL_STANDBY_MODE = 0, + PLL_RUN_MODE = 1, + PLL_OPMODE_SHFT = 0, + PLL_OUTCTRL_SHFT = 0, + PLL_BYPASSNL_SHFT = 1, + PLL_RESET_SHFT = 2, + PLL_RESET_N_SHFT = 2, + PLL_FSM_EN_SHFT = 20, +}; + +enum clk_ctl_cfg_rcgr { + CLK_CTL_CFG_SRC_DIV_SHFT = 0, + CLK_CTL_CFG_SRC_SEL_SHFT = 8, + CLK_CTL_CFG_MODE_SHFT = 12, +}; + +enum clk_ctl_cmd_rcgr { + CLK_CTL_CMD_UPDATE_SHFT = 0, +}; + +enum clk_ctl_cbcr { + CLK_CTL_EN_SHFT = 0, + CLK_CTL_OFF_SHFT = 31, + CLK_CTL_EN_BMSK = 0x1, + CLK_CTL_OFF_BMSK = 0x80000000, +}; + +enum clk_ctl_rcg_mnd { + RCG_MODE_DUAL_EDGE = 2, + CLK_CTL_RCG_MND_SHFT = 0, + CLK_CTL_RCG_MND_BMSK = 0xFFFF, +}; + +enum clk_ctl_bcr { + CLK_CTL_BCR_BLK_SHFT = 0, + CLK_CTL_BCR_BLK_BMSK = 0x1, +}; + +enum clk_ctl_dfsr { + CLK_CTL_CMD_DFSR_SHFT = 0, + CLK_CTL_CMD_RCG_SW_CTL_SHFT = 15, + CLK_CTL_CMD_DFSR_BMSK = 0x1, +}; + +#define GDSC_ENABLE_BIT 0 + +enum cb_err clock_enable_vote(void *cbcr_addr, void *vote_addr, + uint32_t vote_bit); + +enum cb_err clock_enable(void *cbcr_addr); + +enum cb_err enable_and_poll_gdsc_status(void *gdscr_addr); + +void clock_reset_bcr(void *bcr_addr, bool assert); + +enum cb_err clock_configure(struct clock_rcg *clk, struct clock_freq_config *clk_cfg, + uint32_t hz, uint32_t num_perfs); + +void clock_configure_dfsr_table(int qup, struct clock_freq_config *clk_cfg, + uint32_t num_perfs); + +enum cb_err clock_configure_enable_gpll(struct alpha_pll_reg_val_config *cfg, + bool enable, int br_enable); +enum cb_err agera_pll_enable(struct alpha_pll_reg_val_config *cfg); + +struct aoss { + u8 _res0[0x50020]; + u32 aoss_cc_reset_status; + u8 _res1[0x5002c - 0x50024]; + u32 aoss_cc_apcs_misc; +}; +check_member(aoss, aoss_cc_reset_status, 0x50020); +check_member(aoss, aoss_cc_apcs_misc, 0x5002c); + +struct shrm { + u32 shrm_sproc_ctrl; +}; + +void clock_reset_subsystem(u32 *misc, u32 shft); + +#endif |