summaryrefslogtreecommitdiff
path: root/src/soc/qualcomm/common
diff options
context:
space:
mode:
authorTaniya Das <tdas@codeaurora.org>2021-06-23 09:08:57 +0530
committerShelley Chen <shchen@google.com>2021-09-03 06:04:18 +0000
commitd37aeb1bcbd8950806bec3c0b83337c8beecdfa1 (patch)
treeb0dd7ba5214b63c3ed394f00e74767460c0c3175 /src/soc/qualcomm/common
parent310edec6177445a69968a7b8c7a9a89252f58008 (diff)
soc/qualcomm/common: clock: Add support for common clock driver
The clock driver supports configuring the general purpose PLLs, configuring the root clock generator (RCG), enable clock branch, enable gdsc and also the block resets. The common clock driver exposes PLL configuration functions and also different Agera PLL enable functions for the CPU PLLs. While at it, the common driver also supports reset of subsystems like AOP and SHRM. SC7180 clock driver is also refactored to use the common clock driver APIs. BUG=b:182963902 TEST=Validated on qualcomm sc7180 and sc7280 development board. Change-Id: I03d1b4a2fb90303c7259ec08f312d78b4e33ec39 Signed-off-by: Taniya Das <tdas@codeaurora.org> Reviewed-on: https://review.coreboot.org/c/coreboot/+/56588 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Shelley Chen <shchen@google.com>
Diffstat (limited to 'src/soc/qualcomm/common')
-rw-r--r--src/soc/qualcomm/common/clock.c243
-rw-r--r--src/soc/qualcomm/common/include/soc/clock_common.h173
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