diff options
author | David Dai <daidavid1@codeaurora.org> | 2018-05-09 14:38:23 -0700 |
---|---|---|
committer | Julius Werner <jwerner@chromium.org> | 2018-11-30 21:12:30 +0000 |
commit | 46551573b48f225e1be230786f48e1c95d1c4287 (patch) | |
tree | ace316804ac33b6fc166477f8810d916a2f7831b /src/soc/qualcomm | |
parent | c22ad581c80d276bc5509b7a8be79784b14a60af (diff) | |
download | coreboot-46551573b48f225e1be230786f48e1c95d1c4287.tar.xz |
sdm845: Add clock support
This sets up initial clock configuration for QUP and QSPI,
and includes configuration of Root Clock Generators(RCG) and
clock branches enablement.
TEST=build & run
Change-Id: I0b1d7f6daa179c0b24a97d42b66c1a9ee596b0a3
Signed-off-by: David Dai <daidavid1@codeaurora.org>
Reviewed-on: https://review.coreboot.org/c/25454
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
Diffstat (limited to 'src/soc/qualcomm')
-rw-r--r-- | src/soc/qualcomm/sdm845/Makefile.inc | 6 | ||||
-rw-r--r-- | src/soc/qualcomm/sdm845/bootblock.c | 2 | ||||
-rw-r--r-- | src/soc/qualcomm/sdm845/clock.c | 232 | ||||
-rw-r--r-- | src/soc/qualcomm/sdm845/include/soc/addressmap.h | 2 | ||||
-rw-r--r-- | src/soc/qualcomm/sdm845/include/soc/clock.h | 192 |
5 files changed, 433 insertions, 1 deletions
diff --git a/src/soc/qualcomm/sdm845/Makefile.inc b/src/soc/qualcomm/sdm845/Makefile.inc index 507d91397f..fc8edd56e3 100644 --- a/src/soc/qualcomm/sdm845/Makefile.inc +++ b/src/soc/qualcomm/sdm845/Makefile.inc @@ -7,18 +7,21 @@ bootblock-y += spi.c bootblock-y += mmu.c bootblock-y += timer.c bootblock-y += gpio.c -bootblock-y += uart_bitbang.c +bootblock-$(CONFIG_DRIVERS_UART) += uart_bitbang.c +bootblock-y += clock.c ################################################################################ verstage-y += spi.c verstage-y += timer.c verstage-y += gpio.c +verstage-y += clock.c ################################################################################ romstage-y += spi.c romstage-y += cbmem.c romstage-y += timer.c romstage-y += gpio.c +romstage-y += clock.c ################################################################################ ramstage-y += soc.c @@ -26,6 +29,7 @@ ramstage-y += spi.c ramstage-y += cbmem.c ramstage-y += timer.c ramstage-y += gpio.c +ramstage-y += clock.c ################################################################################ diff --git a/src/soc/qualcomm/sdm845/bootblock.c b/src/soc/qualcomm/sdm845/bootblock.c index 019639d763..eb4289b202 100644 --- a/src/soc/qualcomm/sdm845/bootblock.c +++ b/src/soc/qualcomm/sdm845/bootblock.c @@ -15,8 +15,10 @@ #include <bootblock_common.h> #include <soc/mmu.h> +#include <soc/clock.h> void bootblock_soc_init(void) { + clock_init(); sdm845_mmu_init(); } diff --git a/src/soc/qualcomm/sdm845/clock.c b/src/soc/qualcomm/sdm845/clock.c new file mode 100644 index 0000000000..32b0d5e5f8 --- /dev/null +++ b/src/soc/qualcomm/sdm845/clock.c @@ -0,0 +1,232 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2018 Qualcomm Inc. + * + * 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 <arch/io.h> +#include <types.h> +#include <console/console.h> +#include <delay.h> +#include <timer.h> +#include <timestamp.h> +#include <commonlib/helpers.h> +#include <assert.h> + +#include <soc/clock.h> + +#define DIV(div) (2*div - 1) + +struct clock_config qup_cfg[] = { + { + .hz = 7372800, + .src = SRC_GPLL0_EVEN_300MHZ, + .div = DIV(1), + .m = 384, + .n = 15625, + .d_2 = 15625, + }, + { + .hz = 19200*KHz, + .src = SRC_XO_19_2MHZ, + .div = DIV(1), + } +}; + +struct clock_config qspi_core_cfg[] = { + { + .hz = 19200*KHz, + .src = SRC_XO_19_2MHZ, + .div = DIV(0), + }, + { + .hz = 100*MHz, + .src = SRC_GPLL0_MAIN_600MHZ, + .div = DIV(6), + }, + { + .hz = 150*MHz, + .src = SRC_GPLL0_MAIN_600MHZ, + .div = DIV(4), + }, + { + .hz = 300*MHz, + .src = SRC_GPLL0_MAIN_600MHZ, + .div = DIV(2), + } +}; + +static int clock_configure_gpll0(void) +{ + /* Keep existing GPLL0 configuration, in RUN mode @600Mhz. */ + setbits_le32(&gcc->gpll0.user_ctl, + 1 << CLK_CTL_GPLL_PLLOUT_EVEN_SHFT | + 1 << CLK_CTL_GPLL_PLLOUT_MAIN_SHFT | + 1 << CLK_CTL_GPLL_PLLOUT_ODD_SHFT); + return 0; +} + +static int clock_configure_mnd(struct sdm845_clock *clk, uint32_t m, uint32_t n, + uint32_t d_2) +{ + setbits_le32(&clk->rcg.cfg, + RCG_MODE_DUAL_EDGE << CLK_CTL_CFG_MODE_SHFT); + + write32(&clk->m, m & CLK_CTL_RCG_MND_BMSK); + write32(&clk->n, ~(n-m) & CLK_CTL_RCG_MND_BMSK); + write32(&clk->d_2, ~(d_2) & CLK_CTL_RCG_MND_BMSK); + + return 0; +} + +static int clock_configure(struct sdm845_clock *clk, + struct clock_config *clk_cfg, + uint32_t hz, uint32_t num_perfs) +{ + uint32_t reg_val; + uint32_t idx; + + for (idx = 0; idx < num_perfs; idx++) + if (hz <= clk_cfg[idx].hz) + break; + + assert(hz == clk_cfg[idx].hz); + + 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*/ + setbits_le32(&clk->rcg.cmd, BIT(CLK_CTL_CMD_UPDATE_SHFT)); + + return 0; +} + +static bool clock_is_off(u32 *cbcr_addr) +{ + return (read32(cbcr_addr) & CLK_CTL_CBC_CLK_OFF_BMSK); +} + +static int clock_enable_vote(void *cbcr_addr, void *vote_addr, + uint32_t vote_bit) +{ + + /* Set clock vote bit */ + setbits_le32(vote_addr, BIT(vote_bit)); + + /* Ensure clock is enabled */ + while (clock_is_off(cbcr_addr)) + ; + + return 0; +} + +static int clock_enable(void *cbcr_addr) +{ + + /* Set clock enable bit */ + setbits_le32(cbcr_addr, BIT(CLK_CTL_CBC_CLK_EN_SHFT)); + + /* Ensure clock is enabled */ + while (clock_is_off(cbcr_addr)) + ; + + return 0; +} + +void clock_reset_aop(void) +{ + + /* Bring AOP out of RESET */ + clrbits_le32(&aoss->aoss_cc_apcs_misc, BIT(AOP_RESET_SHFT)); +} + +void clock_configure_qspi(uint32_t hz) +{ + clock_configure((struct sdm845_clock *)&gcc->qspi_core, + qspi_core_cfg, hz, + ARRAY_SIZE(qspi_core_cfg)); + clock_enable(&gcc->qspi_cnoc_ahb_cbcr); + clock_enable(&gcc->qspi_core_cbcr); +} + +int clock_reset_bcr(void *bcr_addr, bool reset) +{ + struct sdm845_bcr *bcr = bcr_addr; + + if (reset) + setbits_le32(bcr, BIT(CLK_CTL_BCR_BLK_ARES_SHFT)); + else + clrbits_le32(bcr, BIT(CLK_CTL_BCR_BLK_ARES_SHFT)); + + return 0; +} + +void clock_configure_qup(int qup, uint32_t hz) +{ + int s = qup % QUP_WRAP0_S7; + struct sdm845_qupv3_clock *qup_clk = qup < QUP_WRAP1_S0 ? + (struct sdm845_qupv3_clock *)&gcc->qup_wrap0_s[s] : + (struct sdm845_qupv3_clock *)&gcc->qup_wrap1_s[s]; + clock_configure(&qup_clk->clk, qup_cfg, hz, ARRAY_SIZE(qup_cfg)); +} + +void clock_enable_qup(int qup) +{ + int s = qup % QUP_WRAP0_S7; + int clk_en_off = qup < QUP_WRAP1_S0 ? + QUPV3_WRAP0_CLK_ENA_S(s) : QUPV3_WRAP1_CLK_ENA_S(s); + struct sdm845_qupv3_clock *qup_clk = qup < QUP_WRAP1_S0 ? + &gcc->qup_wrap0_s[s] : &gcc->qup_wrap1_s[s]; + + clock_enable_vote(&qup_clk->clk, &gcc->apcs_clk_br_en1, + clk_en_off); + +} + +void clock_init(void) +{ + + clock_configure_gpll0(); + + clock_enable_vote(&gcc->qup_wrap0_core_2x_cbcr, + &gcc->apcs_clk_br_en1, + QUPV3_WRAP0_CORE_2X_CLK_ENA); + clock_enable_vote(&gcc->qup_wrap0_core_cbcr, + &gcc->apcs_clk_br_en1, + QUPV3_WRAP0_CORE_CLK_ENA); + clock_enable_vote(&gcc->qup_wrap0_m_ahb_cbcr, + &gcc->apcs_clk_br_en1, + QUPV3_WRAP_0_M_AHB_CLK_ENA); + clock_enable_vote(&gcc->qup_wrap0_s_ahb_cbcr, + &gcc->apcs_clk_br_en1, + QUPV3_WRAP_0_S_AHB_CLK_ENA); + + clock_enable_vote(&gcc->qup_wrap1_core_2x_cbcr, + &gcc->apcs_clk_br_en1, + QUPV3_WRAP1_CORE_2X_CLK_ENA); + clock_enable_vote(&gcc->qup_wrap1_core_cbcr, + &gcc->apcs_clk_br_en1, + QUPV3_WRAP1_CORE_CLK_ENA); + clock_enable_vote(&gcc->qup_wrap1_m_ahb_cbcr, + &gcc->apcs_clk_br_en1, + QUPV3_WRAP_1_M_AHB_CLK_ENA); + clock_enable_vote(&gcc->qup_wrap1_s_ahb_cbcr, + &gcc->apcs_clk_br_en1, + QUPV3_WRAP_1_S_AHB_CLK_ENA); +} diff --git a/src/soc/qualcomm/sdm845/include/soc/addressmap.h b/src/soc/qualcomm/sdm845/include/soc/addressmap.h index 2ae77e5cb6..5a94e23e96 100644 --- a/src/soc/qualcomm/sdm845/include/soc/addressmap.h +++ b/src/soc/qualcomm/sdm845/include/soc/addressmap.h @@ -22,5 +22,7 @@ #define TLMM_EAST_TILE_BASE 0x03500000 #define TLMM_NORTH_TILE_BASE 0x03900000 #define TLMM_SOUTH_TILE_BASE 0x03D00000 +#define GCC_BASE 0x00100000 +#define AOSS_CC_BASE 0x0C2F0000 #endif /* __SOC_QUALCOMM_SDM845_ADDRESS_MAP_H__ */ diff --git a/src/soc/qualcomm/sdm845/include/soc/clock.h b/src/soc/qualcomm/sdm845/include/soc/clock.h new file mode 100644 index 0000000000..9f0533f33f --- /dev/null +++ b/src/soc/qualcomm/sdm845/include/soc/clock.h @@ -0,0 +1,192 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2018 Qualcomm Inc. + * + * 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 <soc/addressmap.h> +#include <types.h> + +#ifndef __SOC_QUALCOMM_SDM845_CLOCK_H__ +#define __SOC_QUALCOMM_SDM845_CLOCK_H__ + +#define QUPV3_WRAP_0_M_AHB_CLK_ENA 6 +#define QUPV3_WRAP_0_S_AHB_CLK_ENA 7 +#define QUPV3_WRAP0_CORE_2X_CLK_ENA 9 +#define QUPV3_WRAP0_CORE_CLK_ENA 8 +#define QUPV3_WRAP1_CORE_2X_CLK_ENA 18 +#define QUPV3_WRAP1_CORE_CLK_ENA 19 +#define QUPV3_WRAP_1_M_AHB_CLK_ENA 20 +#define QUPV3_WRAP_1_S_AHB_CLK_ENA 21 +#define QUPV3_WRAP0_CLK_ENA_S(idx) (10 + idx) +#define QUPV3_WRAP1_CLK_ENA_S(idx) (22 + idx) + +#define GPLL0_EVEN_HZ (300*Mhz) +#define GPLL0_MAIN_HZ (600*Mhz) +#define QUP_WRAP_CORE_2X_19_2MHZ (19200*Khz) + +#define SRC_XO_19_2MHZ 0 +#define SRC_GPLL0_MAIN_600MHZ 1 +#define SRC_GPLL0_EVEN_300MHZ 6 + +#define AOP_RESET_SHFT 0 +#define RCG_MODE_DUAL_EDGE 2 + +struct sdm845_rcg { + u32 cmd; + u32 cfg; +}; + +struct sdm845_clock { + u32 cbcr; + struct sdm845_rcg rcg; + u32 m; + u32 n; + u32 d_2; +}; + +struct sdm845_qupv3_clock { + struct sdm845_clock clk; + u8 _res[0x130 - 0x18]; +}; + +struct sdm845_gpll { + u32 mode; + u32 l_val; + u32 cal_l_val; + u32 user_ctl; + u32 user_ctl_u; + u32 config_ctl; + u32 config_ctl_u; + u32 test_ctl; + u32 test_ctl_u; + u8 _res[0x1000 - 0x24]; +}; + +struct sdm845_gcc { + struct sdm845_gpll gpll0; + u8 _res0[0x17000 - 0x1000]; + u32 qup_wrap0_bcr; + u32 qup_wrap0_m_ahb_cbcr; + u32 qup_wrap0_s_ahb_cbcr; + u32 qup_wrap0_core_cbcr; + u32 qup_wrap0_core_cdivr; + u32 qup_wrap0_core_2x_cbcr; + struct sdm845_rcg qup_wrap0_core_2x; + u8 _res1[0x17030 - 0x17020]; + struct sdm845_qupv3_clock qup_wrap0_s[8]; + u8 _res2[0x18000 - 0x179b0]; + u32 qup_wrap1_bcr; + u32 qup_wrap1_core_2x_cbcr; + u32 qup_wrap1_core_cbcr; + u32 qup_wrap1_m_ahb_cbcr; + u32 qup_wrap1_s_ahb_cbcr; + struct sdm845_qupv3_clock qup_wrap1_s[8]; + u32 qup_wrap1_core_cdivr; + u8 _res4[0x4B000 - 0x18998]; + u32 qspi_cnoc_ahb_cbcr; + u32 qspi_core_cbcr; + struct sdm845_rcg qspi_core; + u8 _res5[0x5200c-0x4b010]; + u32 apcs_clk_br_en1; + u8 _res6[0x1000000-0x52010]; +}; +check_member(sdm845_gcc, apcs_clk_br_en1, 0x5200c); + +struct sdm845_aoss { + u8 _res[0x2c]; + u32 aoss_cc_apcs_misc; +}; + +enum clk_ctl_gpll_user_ctl { + CLK_CTL_GPLL_PLLOUT_EVEN_BMSK = 0x2, + CLK_CTL_GPLL_PLLOUT_MAIN_SHFT = 0, + CLK_CTL_GPLL_PLLOUT_EVEN_SHFT = 1, + CLK_CTL_GPLL_PLLOUT_ODD_SHFT = 2 +}; + +enum clk_ctl_cfg_rcgr { + CLK_CTL_CFG_HW_CTL_BMSK = 0x100000, + CLK_CTL_CFG_HW_CTL_SHFT = 20, + CLK_CTL_CFG_MODE_BMSK = 0x3000, + CLK_CTL_CFG_MODE_SHFT = 12, + CLK_CTL_CFG_SRC_SEL_BMSK = 0x700, + CLK_CTL_CFG_SRC_SEL_SHFT = 8, + CLK_CTL_CFG_SRC_DIV_BMSK = 0x1F, + CLK_CTL_CFG_SRC_DIV_SHFT = 0 +}; + +enum clk_ctl_cmd_rcgr { + CLK_CTL_CMD_ROOT_OFF_BMSK = 0x80000000, + CLK_CTL_CMD_ROOT_OFF_SHFT = 31, + CLK_CTL_CMD_ROOT_EN_BMSK = 0x2, + CLK_CTL_CMD_ROOT_EN_SHFT = 1, + CLK_CTL_CMD_UPDATE_BMSK = 0x1, + CLK_CTL_CMD_UPDATE_SHFT = 0 +}; + +enum clk_ctl_cbcr { + CLK_CTL_CBC_CLK_OFF_BMSK = 0x80000000, + CLK_CTL_CBC_CLK_OFF_SHFT = 31, + CLK_CTL_CBC_CLK_EN_BMSK = 0x1, + CLK_CTL_CBC_CLK_EN_SHFT = 0 +}; + +enum clk_ctl_rcg_mnd { + CLK_CTL_RCG_MND_BMSK = 0xFFFF, + CLK_CTL_RCG_MND_SHFT = 0, +}; + +enum clk_ctl_bcr { + CLK_CTL_BCR_BLK_ARES_BMSK = 0x1, + CLK_CTL_BCR_BLK_ARES_SHFT = 0, +}; + +enum clk_qup { + QUP_WRAP0_S0, + QUP_WRAP0_S1, + QUP_WRAP0_S2, + QUP_WRAP0_S3, + QUP_WRAP0_S4, + QUP_WRAP0_S5, + QUP_WRAP0_S6, + QUP_WRAP0_S7, + QUP_WRAP1_S0, + QUP_WRAP1_S1, + QUP_WRAP1_S2, + QUP_WRAP1_S3, + QUP_WRAP1_S4, + QUP_WRAP1_S5, + QUP_WRAP1_S6, + QUP_WRAP1_S7 +}; + +struct clock_config { + uint32_t hz; + uint8_t src; + uint8_t div; + uint16_t m; + uint16_t n; + uint16_t d_2; +}; + +static struct sdm845_gcc *const gcc = (void *)GCC_BASE; +static struct sdm845_aoss *const aoss = (void *)AOSS_CC_BASE; + +void clock_init(void); +void clock_reset_aop(void); +void clock_configure_qspi(uint32_t hz); +int clock_reset_bcr(void *bcr_addr, bool reset); +void clock_configure_qup(int qup, uint32_t hz); +void clock_enable_qup(int qup); + +#endif // __SOC_QUALCOMM_SDM845_CLOCK_H__ |