diff options
-rw-r--r-- | src/soc/qualcomm/qcs405/Makefile.inc | 4 | ||||
-rw-r--r-- | src/soc/qualcomm/qcs405/bootblock.c | 6 | ||||
-rw-r--r-- | src/soc/qualcomm/qcs405/clock.c | 269 | ||||
-rw-r--r-- | src/soc/qualcomm/qcs405/include/soc/addressmap.h | 1 | ||||
-rw-r--r-- | src/soc/qualcomm/qcs405/include/soc/clock.h | 169 |
5 files changed, 449 insertions, 0 deletions
diff --git a/src/soc/qualcomm/qcs405/Makefile.inc b/src/soc/qualcomm/qcs405/Makefile.inc index c702264739..89a7354557 100644 --- a/src/soc/qualcomm/qcs405/Makefile.inc +++ b/src/soc/qualcomm/qcs405/Makefile.inc @@ -7,17 +7,20 @@ bootblock-y += timer.c bootblock-y += spi.c bootblock-y += mmu.c bootblock-y += gpio.c +bootblock-y += clock.c ################################################################################ verstage-y += timer.c verstage-y += spi.c verstage-y += gpio.c +verstage-y += clock.c ################################################################################ romstage-y += timer.c romstage-y += spi.c romstage-y += cbmem.c romstage-y += gpio.c +romstage-y += clock.c ################################################################################ ramstage-y += soc.c @@ -25,6 +28,7 @@ ramstage-y += timer.c ramstage-y += spi.c ramstage-y += cbmem.c ramstage-y += gpio.c +ramstage-y += clock.c ################################################################################ diff --git a/src/soc/qualcomm/qcs405/bootblock.c b/src/soc/qualcomm/qcs405/bootblock.c index 5e63f132fc..15d1c18fe1 100644 --- a/src/soc/qualcomm/qcs405/bootblock.c +++ b/src/soc/qualcomm/qcs405/bootblock.c @@ -15,6 +15,12 @@ #include <bootblock_common.h> #include <soc/mmu.h> +#include <soc/clock.h> + +void bootblock_soc_early_init(void) +{ + clock_init(); +} void bootblock_soc_init(void) { diff --git a/src/soc/qualcomm/qcs405/clock.c b/src/soc/qualcomm/qcs405/clock.c new file mode 100644 index 0000000000..31fdc3ddc8 --- /dev/null +++ b/src/soc/qualcomm/qcs405/clock.c @@ -0,0 +1,269 @@ + /* 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 <device/mmio.h> +#include <types.h> +#include <console/console.h> +#include <delay.h> +#include <timer.h> +#include <timestamp.h> +#include <commonlib/helpers.h> +#include <string.h> + +#include <soc/clock.h> + +#define DIV(div) (div ? (2*div - 1) : 0) +#define HALF_DIVIDER(div2x) (div2x ? (div2x - 1) : 0) + +struct clock_config uart_cfg[] = { + { + .hz = 1843200, + .hw_ctl = 0x0, + .src = SRC_GPLL0_MAIN_800MHZ, + .div = DIV(0), + .m = 36, + .n = 15625, + .d_2 = 15625, + }, + { + .hz = 3686400, + .hw_ctl = 0x0, + .src = SRC_GPLL0_MAIN_800MHZ, + .div = DIV(0), + .m = 72, + .n = 15625, + .d_2 = 15625, + } +}; + +struct clock_config i2c_cfg[] = { + { + .hz = 19200000, + .hw_ctl = 0x0, + .src = SRC_XO_19_2MHZ, + .div = DIV(0), + }, + { + .hz = 50000000, + .hw_ctl = 0x0, + .src = SRC_GPLL0_MAIN_800MHZ, + .div = DIV(32), + } +}; + +struct clock_config spi_cfg[] = { + { + .hz = 1000000, + .hw_ctl = 0x0, + .src = SRC_XO_19_2MHZ, + .div = DIV(48), + }, + { + .hz = 7372800, + .src = SRC_GPLL0_MAIN_800MHZ, + .div = DIV(1), + .m = 144, + .n = 15625, + .d_2 = 15625, + }, + { + .hz = 19200000, + .hw_ctl = 0x0, + .src = SRC_XO_19_2MHZ, + .div = DIV(0), + }, + { + .hz = 30000000, + .hw_ctl = 0x0, + .src = SRC_XO_19_2MHZ, + .div = DIV(0), + }, + { + .hz = 50000000, + .hw_ctl = 0x0, + .src = SRC_GPLL0_MAIN_800MHZ, + .div = DIV(32), + } +}; + +static int clock_configure_gpll0(void) +{ + /* Keep existing GPLL0 configuration, in RUN mode @800Mhz. */ + setbits_le32(&gcc->gpll0.user_ctl, + 1 << CLK_CTL_GPLL_PLLOUT_LV_EARLY_SHFT | + 1 << CLK_CTL_GPLL_PLLOUT_AUX2_SHFT | + 1 << CLK_CTL_GPLL_PLLOUT_AUX_SHFT | + 1 << CLK_CTL_GPLL_PLLOUT_MAIN_SHFT); + return 0; +} + +static int clock_configure_mnd(struct qcs405_clock *clk, uint32_t m, uint32_t n, + uint32_t d_2) +{ + uint32_t reg_val; + + /* Configure Root Clock Generator(RCG) for Dual Edge Mode */ + reg_val = read32(&clk->rcg.cfg); + reg_val |= (2 << CLK_CTL_CFG_MODE_SHFT); + write32(&clk->rcg.cfg, reg_val); + + /* Set M/N/D config */ + 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 qcs405_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; + + 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(void *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; +} + +static int clock_disable(void *cbcr_addr) +{ + + /* Set clock enable bit */ + clrbits_le32(cbcr_addr, BIT(CLK_CTL_CBC_CLK_EN_SHFT)); + return 0; +} + +int clock_reset_bcr(void *bcr_addr, bool reset) +{ + struct qcs405_bcr *bcr = bcr_addr; + + if (reset) + setbits_le32(&bcr->bcr, BIT(CLK_CTL_BCR_BLK_ARES_SHFT)); + else + clrbits_le32(&bcr->bcr, BIT(CLK_CTL_BCR_BLK_ARES_SHFT)); + + return 0; +} + +void clock_configure_uart(uint32_t hz) +{ + struct qcs405_clock *uart_clk = (struct qcs405_clock *) + &gcc->blsp1_uart2_apps_clk; + + clock_configure(uart_clk, uart_cfg, hz, ARRAY_SIZE(uart_cfg)); +} + +void clock_configure_spi(int blsp, uint32_t hz) +{ + struct qcs405_clock *spi_clk = (blsp == 1) ? + (struct qcs405_clock *)&gcc->blsp1_qup4_spi_clk + : (struct qcs405_clock *)&gcc->blsp2_qup0_spi_clk; + + clock_configure(spi_clk, spi_cfg, hz, ARRAY_SIZE(spi_cfg)); +} + +void clock_enable_uart(void) +{ + clock_enable(&gcc->blsp1_uart2_apps_cbcr); +} + +void clock_disable_uart(void) +{ + clock_disable(&gcc->blsp1_uart2_apps_cbcr); +} + +void clock_enable_spi(int blsp) +{ + (blsp == 1) ? clock_enable(&gcc->blsp1_qup4_spi_apps_cbcr) + : clock_enable(&gcc->blsp2_qup0_spi_apps_cbcr); +} + +void clock_disable_spi(int blsp) +{ + (blsp == 1) ? clock_disable(&gcc->blsp1_qup4_spi_apps_cbcr) + : clock_disable(&gcc->blsp2_qup0_spi_apps_cbcr); +} + +void clock_init(void) +{ + + clock_configure_gpll0(); + + clock_configure(&gcc->blsp1_uart2_apps_clk, uart_cfg, 1843200, + ARRAY_SIZE(uart_cfg)); + + clock_enable(&gcc->blsp1_uart2_apps_cbcr); + clock_enable_vote(&gcc->blsp1_ahb_cbcr, + &gcc->gcc_apcs_clock_branch_en_vote, + BLSP1_AHB_CLK_ENA); + + clock_configure(&gcc->blsp1_qup4_spi_clk, spi_cfg, 1000000, + ARRAY_SIZE(spi_cfg)); + clock_enable(&gcc->blsp1_qup4_spi_apps_cbcr); + + clock_configure(&gcc->blsp2_qup0_spi_clk, spi_cfg, 50000000, + ARRAY_SIZE(spi_cfg)); + clock_enable(&gcc->blsp2_qup0_spi_apps_cbcr); + clock_enable_vote(&gcc->blsp2_ahb_cbcr, + &gcc->gcc_apcs_clock_branch_en_vote, + BLSP2_AHB_CLK_ENA); +} diff --git a/src/soc/qualcomm/qcs405/include/soc/addressmap.h b/src/soc/qualcomm/qcs405/include/soc/addressmap.h index c85341b46c..30a30b6cb2 100644 --- a/src/soc/qualcomm/qcs405/include/soc/addressmap.h +++ b/src/soc/qualcomm/qcs405/include/soc/addressmap.h @@ -22,5 +22,6 @@ #define TLMM_EAST_TILE_BASE 0x7B00000 #define TLMM_NORTH_TILE_BASE 0x1300000 #define TLMM_SOUTH_TILE_BASE 0x1000000 +#define GCC_BASE 0x01800000 #endif /* __SOC_QUALCOMM_QCS405_ADDRESS_MAP_H__ */ diff --git a/src/soc/qualcomm/qcs405/include/soc/clock.h b/src/soc/qualcomm/qcs405/include/soc/clock.h new file mode 100644 index 0000000000..2766f0975b --- /dev/null +++ b/src/soc/qualcomm/qcs405/include/soc/clock.h @@ -0,0 +1,169 @@ + /* 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_QCS405_CLOCK_H__ +#define __SOC_QUALCOMM_QCS405_CLOCK_H__ + +#define BLSP1_AHB_CLK_ENA 10 +#define BLSP2_AHB_CLK_ENA 20 +#define SRC_XO_19_2MHZ 0 +#define SRC_GPLL0_MAIN_800MHZ 1 +#define REG(addr) ((void *)addr) + +/** + * USB BCR registers + */ +#define GCC_USB_HS_PHY_CFG_AHB_BCR 0x1841038 +#define GCC_USB_HS_BCR 0x1841000 +#define GCC_USB_30_BCR 0x1839000 +#define GCC_USB2A_PHY_BCR 0x1841028 +#define GCC_USB2_HS_PHY_ONLY_BCR 0x1841034 +#define GCC_QUSB2_PHY_BCR 0x184103C + +struct qcs405_rcg { + u32 cmd; + u32 cfg; +}; + +struct qcs405_clock { + struct qcs405_rcg rcg; + uint32_t m; + uint32_t n; + uint32_t d_2; +}; + +struct qcs405_bcr { + uint32_t bcr; +}; + +struct qcs405_gpll { + u32 mode; + u32 l_val; + u32 gpll_alpha_val; + u32 gpll_alpha_val_u; + u32 user_ctl; + u32 user_ctl_u; + u32 config_ctl; + u32 test_ctl; + u32 test_ctl_u; +}; + +struct qcs405_gcc { + u8 _res0[0x1000 - 0x0]; + u32 blsp1_bcr; + u32 blsp1_sleep_cbcr; + u32 blsp1_ahb_cbcr; + u8 _res1[0x3028 - 0x100c]; + u32 blsp1_uart2_bcr; + u32 blsp1_uart2_apps_cbcr; + u32 blsp1_uart2_sim_cbcr; + struct qcs405_clock blsp1_uart2_apps_clk; + u8 _res2[0x5018 - 0x3048]; + u32 blsp1_qup4_bcr; + u32 blsp1_qup4_spi_apps_cbcr; + u8 _res3[0x5024 - 0x5020]; + struct qcs405_clock blsp1_qup4_spi_clk; + u8 _res4[0xB000 - 0x5038]; + u32 blsp2_bcr; + u32 blsp2_sleep_cbcr; + u32 blsp2_ahb_cbcr; + u8 _res5[0xC000 - 0xB00C]; + u32 blsp2_qup0_bcr; + u32 blsp2_qup0_spi_apps_cbcr; + u8 _res6[0xC024 - 0xC008]; + struct qcs405_clock blsp2_qup0_spi_clk; + u8 _res7[0x21000 - 0xC038]; + struct qcs405_gpll gpll0; + u8 _res8[0x45004 - 0x21024]; + u32 gcc_apcs_clock_branch_en_vote; +}; + +struct mdss_clock_config { + const char *clk_name; + struct qcs405_clock *rcgr; + uint32_t *cbcr; +}; + +enum clk_ctl_gpll_user_ctl { + CLK_CTL_GPLL_PLLOUT_LV_EARLY_BMSK = 0x8, + CLK_CTL_GPLL_PLLOUT_AUX2_BMSK = 0x4, + CLK_CTL_GPLL_PLLOUT_AUX_BMSK = 0x2, + CLK_CTL_GPLL_PLLOUT_MAIN_BMSK = 0x1, + CLK_CTL_GPLL_PLLOUT_LV_EARLY_SHFT = 3, + CLK_CTL_GPLL_PLLOUT_AUX2_SHFT = 2, + CLK_CTL_GPLL_PLLOUT_AUX_SHFT = 1, + CLK_CTL_GPLL_PLLOUT_MAIN_SHFT = 0, +}; + +enum clk_ctl_cfg_rcgr { + 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, +}; + +struct clock_config { + uint32_t hz; + uint32_t hw_ctl; + uint32_t src; + uint32_t div; + uint32_t m; + uint32_t n; + uint32_t d_2; +}; + +static struct qcs405_gcc *const gcc = (void *)GCC_BASE; + +void clock_init(void); +void clock_reset_aop(void); +int clock_configure_qspi(uint32_t hz); +int clock_reset_bcr(void *bcr_addr, bool reset); +void clock_configure_uart(uint32_t hz); +void clock_configure_spi(int blsp, uint32_t hz); +void clock_enable_uart(void); +void clock_disable_uart(void); +void clock_enable_spi(int blsp); +void clock_disable_spi(int blsp); + +#endif // __SOC_QUALCOMM_QCS405_CLOCK_H__ |