diff --git a/plat/rockchip/rk3399/drivers/pmu/plat_pmu_macros.S b/plat/rockchip/rk3399/drivers/pmu/plat_pmu_macros.S index 72419648b..405e1d5e0 100644 --- a/plat/rockchip/rk3399/drivers/pmu/plat_pmu_macros.S +++ b/plat/rockchip/rk3399/drivers/pmu/plat_pmu_macros.S @@ -27,9 +27,25 @@ #include #include #include +#include .globl clst_warmboot_data + .macro sram_func _name + .section .sram.text, "ax" + .type \_name, %function + .func \_name + \_name: + .endm + +#define CRU_CLKSEL_CON6 0x118 + +#define DDRCTL0_C_SYSREQ_CFG 0x0100 +#define DDRCTL1_C_SYSREQ_CFG 0x1000 + +#define DDRC0_SREF_DONE_EXT 0x01 +#define DDRC1_SREF_DONE_EXT 0x04 + #define PLL_MODE_SHIFT (0x8) #define PLL_NORMAL_MODE ((0x3 << (PLL_MODE_SHIFT + 16)) | \ (0x1 << PLL_MODE_SHIFT)) @@ -65,3 +81,75 @@ clst_warmboot_data: .word 0 .endr .endm + + /* ----------------------------------------------- + * void sram_func_set_ddrctl_pll(uint32_t pll_src) + * Function to switch the PLL source for ddrctrl + * In: x0 - The PLL of the clk_ddrc clock source + * out: None + * Clobber list : x0 - x3, x5, x8 - x10 + * ----------------------------------------------- + */ + + .globl sram_func_set_ddrctl_pll + +sram_func sram_func_set_ddrctl_pll + /* backup parameter */ + mov x8, x0 + + /* disable the MMU at EL3 */ + mrs x9, sctlr_el3 + bic x10, x9, #(SCTLR_M_BIT) + msr sctlr_el3, x10 + isb + dsb sy + + /* enable ddrctl0_1 idle request */ + mov x5, PMU_BASE + ldr w0, [x5, #PMU_SFT_CON] + orr w0, w0, #DDRCTL0_C_SYSREQ_CFG + orr w0, w0, #DDRCTL1_C_SYSREQ_CFG + str w0, [x5, #PMU_SFT_CON] + +check_ddrc0_1_sref_enter: + ldr w1, [x5, #PMU_DDR_SREF_ST] + and w2, w1, #DDRC0_SREF_DONE_EXT + and w3, w1, #DDRC1_SREF_DONE_EXT + orr w2, w2, w3 + cmp w2, #(DDRC0_SREF_DONE_EXT | DDRC1_SREF_DONE_EXT) + b.eq check_ddrc0_1_sref_enter + + /* + * select a PLL for ddrctrl: + * x0 = 0: ALPLL + * x0 = 1: ABPLL + * x0 = 2: DPLL + * x0 = 3: GPLLL + */ + mov x5, CRU_BASE + lsl w0, w8, #4 + orr w0, w0, #0x00300000 + str w0, [x5, #CRU_CLKSEL_CON6] + + /* disable ddrctl0_1 idle request */ + mov x5, PMU_BASE + ldr w0, [x5, #PMU_SFT_CON] + bic w0, w0, #DDRCTL0_C_SYSREQ_CFG + bic w0, w0, #DDRCTL1_C_SYSREQ_CFG + str w0, [x5, #PMU_SFT_CON] + +check_ddrc0_1_sref_exit: + ldr w1, [x5, #PMU_DDR_SREF_ST] + and w2, w1, #DDRC0_SREF_DONE_EXT + and w3, w1, #DDRC1_SREF_DONE_EXT + orr w2, w2, w3 + cmp w2, #0x0 + b.eq check_ddrc0_1_sref_exit + + /* reenable the MMU at EL3 */ + msr sctlr_el3, x9 + isb + dsb sy + + ret +endfunc sram_func_set_ddrctl_pll diff --git a/plat/rockchip/rk3399/drivers/pmu/pmu.c b/plat/rockchip/rk3399/drivers/pmu/pmu.c index 8d3f482f4..5a385cb34 100644 --- a/plat/rockchip/rk3399/drivers/pmu/pmu.c +++ b/plat/rockchip/rk3399/drivers/pmu/pmu.c @@ -46,9 +46,9 @@ #include #include #include -#include #include #include +#include DEFINE_BAKERY_LOCK(rockchip_pd_lock); @@ -102,7 +102,6 @@ static void pmu_bus_idle_req(uint32_t bus, uint32_t state) mmio_read_32(PMU_BASE + PMU_BUS_IDLE_ACK), bus_ack); } - } struct pmu_slpdata_s pmu_slpdata; @@ -818,10 +817,19 @@ static void init_pmu_counts(void) mmio_write_32(PMU_BASE + PMU_GPU_PWRUP_CNT, CYCL_24M_CNT_US(1)); } +static uint32_t clk_ddrc_save; + static void sys_slp_config(void) { uint32_t slp_mode_cfg = 0; + /* keep enabling clk_ddrc_bpll_src_en gate for DDRC */ + clk_ddrc_save = mmio_read_32(CRU_BASE + CRU_CLKGATE_CON(3)); + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(3), WMSK_BIT(1)); + + prepare_abpll_for_ddrctrl(); + sram_func_set_ddrctl_pll(ABPLL_ID); + mmio_write_32(GRF_BASE + GRF_SOC_CON4, CCI_FORCE_WAKEUP); mmio_write_32(PMU_BASE + PMU_CCI500_CON, BIT_WITH_WMSK(PMU_CLR_PREQ_CCI500_HW) | @@ -849,6 +857,7 @@ static void sys_slp_config(void) BIT(PMU_DDRIO0_RET_EN) | BIT(PMU_DDRIO1_RET_EN) | BIT(PMU_DDRIO_RET_HW_DE_REQ) | + BIT(PMU_CENTER_PD_EN) | BIT(PMU_PLL_PD_EN) | BIT(PMU_CLK_CENTER_SRC_GATE_EN) | BIT(PMU_OSC_DIS) | @@ -857,7 +866,6 @@ static void sys_slp_config(void) mmio_setbits_32(PMU_BASE + PMU_WKUP_CFG4, BIT(PMU_GPIO_WKUP_EN)); mmio_write_32(PMU_BASE + PMU_PWRMODE_CON, slp_mode_cfg); - mmio_write_32(PMU_BASE + PMU_PLL_CON, PLL_PD_HW); mmio_write_32(PMUGRF_BASE + PMUGRF_SOC_CON0, EXTERNAL_32K); mmio_write_32(PMUGRF_BASE, IOMUX_CLK_32K); /* 32k iomux */ @@ -1094,6 +1102,9 @@ static int sys_pwr_domain_suspend(void) uint32_t wait_cnt = 0; uint32_t status = 0; + dmc_save(); + pmu_scu_b_pwrdn(); + pmu_power_domains_suspend(); set_hw_idle(BIT(PMU_CLR_CENTER1) | BIT(PMU_CLR_ALIVE) | @@ -1114,8 +1125,6 @@ static int sys_pwr_domain_suspend(void) (PMUSRAM_BASE >> CPU_BOOT_ADDR_ALIGN) | CPU_BOOT_ADDR_WMASK); - pmu_scu_b_pwrdn(); - mmio_write_32(PMU_BASE + PMU_ADB400_CON, BIT_WITH_WMSK(PMU_PWRDWN_REQ_CORE_B_2GIC_SW) | BIT_WITH_WMSK(PMU_PWRDWN_REQ_CORE_B_SW) | @@ -1134,6 +1143,7 @@ static int sys_pwr_domain_suspend(void) } } mmio_setbits_32(PMU_BASE + PMU_PWRDN_CON, BIT(PMU_SCU_B_PWRDWN_EN)); + /* * Disabling PLLs/PWM/DVFS is approaching WFI which is * the last steps in suspend. @@ -1163,6 +1173,10 @@ static int sys_pwr_domain_resume(void) enable_dvfs_plls(); plls_resume_finish(); + /* restore clk_ddrc_bpll_src_en gate */ + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(3), + BITS_WITH_WMASK(clk_ddrc_save, 0xff, 0)); + /* * The wakeup status is not cleared by itself, we need to clear it * manually. Otherwise we will alway query some interrupt next time. @@ -1209,8 +1223,12 @@ static int sys_pwr_domain_resume(void) pmu_sgrf_rst_hld_release(); pmu_scu_b_pwrup(); - pmu_power_domains_resume(); + + restore_dpll(); + sram_func_set_ddrctl_pll(DPLL_ID); + restore_abpll(); + clr_hw_idle(BIT(PMU_CLR_CENTER1) | BIT(PMU_CLR_ALIVE) | BIT(PMU_CLR_MSCH0) | @@ -1301,9 +1319,10 @@ void plat_rockchip_pmu_init(void) for (cpu = 0; cpu < PLATFORM_CLUSTER_COUNT; cpu++) clst_warmboot_data[cpu] = 0; - psram_sleep_cfg->ddr_func = 0x00; - psram_sleep_cfg->ddr_data = 0x00; - psram_sleep_cfg->ddr_flag = 0x00; + psram_sleep_cfg->ddr_func = (uint64_t)dmc_restore; + psram_sleep_cfg->ddr_data = (uint64_t)&sdram_config; + psram_sleep_cfg->ddr_flag = 0x01; + psram_sleep_cfg->boot_mpidr = read_mpidr_el1() & 0xffff; /* config cpu's warm boot address */ diff --git a/plat/rockchip/rk3399/drivers/pmu/pmu.h b/plat/rockchip/rk3399/drivers/pmu/pmu.h index 3e1eb4a12..22c8c6374 100644 --- a/plat/rockchip/rk3399/drivers/pmu/pmu.h +++ b/plat/rockchip/rk3399/drivers/pmu/pmu.h @@ -32,6 +32,7 @@ #define __PMU_H__ #include +#include /* Allocate sp reginon in pmusram */ #define PSRAM_SP_SIZE 0x80 @@ -843,4 +844,7 @@ struct pmu_slpdata_s { }; extern uint32_t clst_warmboot_data[PLATFORM_CLUSTER_COUNT]; + +extern void sram_func_set_ddrctl_pll(uint32_t pll_src); + #endif /* __PMU_H__ */ diff --git a/plat/rockchip/rk3399/drivers/soc/soc.c b/plat/rockchip/rk3399/drivers/soc/soc.c index 2f6e67af6..9529cb25f 100644 --- a/plat/rockchip/rk3399/drivers/soc/soc.c +++ b/plat/rockchip/rk3399/drivers/soc/soc.c @@ -43,6 +43,8 @@ const mmap_region_t plat_rk_mmap[] = { MAP_REGION_FLAT(RK3399_DEV_RNG0_BASE, RK3399_DEV_RNG0_SIZE, MT_DEVICE | MT_RW | MT_SECURE), + MAP_REGION_FLAT(PMUSRAM_BASE, PMUSRAM_SIZE, + MT_MEMORY | MT_RW | MT_SECURE), { 0 } }; @@ -238,21 +240,105 @@ static void _pll_suspend(uint32_t pll_id) set_pll_bypass(pll_id); } +/** + * disable_dvfs_plls - To suspend the specific PLLs + * + * When we close the center logic, the DPLL will be closed, + * so we need to keep the ABPLL and switch to it to supply + * clock for DDR during suspend, then we should not close + * the ABPLL and exclude ABPLL_ID. + */ void disable_dvfs_plls(void) { _pll_suspend(CPLL_ID); _pll_suspend(NPLL_ID); _pll_suspend(VPLL_ID); _pll_suspend(GPLL_ID); - _pll_suspend(ABPLL_ID); _pll_suspend(ALPLL_ID); } +/** + * disable_nodvfs_plls - To suspend the PPLL + */ void disable_nodvfs_plls(void) { _pll_suspend(PPLL_ID); } +/** + * restore_pll - Copy PLL settings from memory to a PLL. + * + * This will copy PLL settings from an array in memory to the memory mapped + * registers for a PLL. + * + * Note that: above the PLL exclude PPLL. + * + * pll_id: One of the values from enum plls_id + * src: Pointer to the array of values to restore from + */ +static void restore_pll(int pll_id, uint32_t *src) +{ + /* Nice to have PLL off while configuring */ + mmio_write_32((CRU_BASE + CRU_PLL_CON(pll_id, 3)), PLL_SLOW_MODE); + + mmio_write_32(CRU_BASE + CRU_PLL_CON(pll_id, 0), src[0] | REG_SOC_WMSK); + mmio_write_32(CRU_BASE + CRU_PLL_CON(pll_id, 1), src[1] | REG_SOC_WMSK); + mmio_write_32(CRU_BASE + CRU_PLL_CON(pll_id, 2), src[2]); + mmio_write_32(CRU_BASE + CRU_PLL_CON(pll_id, 4), src[4] | REG_SOC_WMSK); + mmio_write_32(CRU_BASE + CRU_PLL_CON(pll_id, 5), src[5] | REG_SOC_WMSK); + + /* Do PLL_CON3 since that will enable things */ + mmio_write_32(CRU_BASE + CRU_PLL_CON(pll_id, 3), src[3] | REG_SOC_WMSK); + + /* Wait for PLL lock done */ + while ((mmio_read_32(CRU_BASE + CRU_PLL_CON(pll_id, 2)) & + 0x80000000) == 0x0) + ; +} + +/** + * save_pll - Copy PLL settings a PLL to memory + * + * This will copy PLL settings from the memory mapped registers for a PLL to + * an array in memory. + * + * Note that: above the PLL exclude PPLL. + * + * pll_id: One of the values from enum plls_id + * src: Pointer to the array of values to save to. + */ +static void save_pll(uint32_t *dst, int pll_id) +{ + int i; + + for (i = 0; i < PLL_CON_COUNT; i++) + dst[i] = mmio_read_32(CRU_BASE + CRU_PLL_CON(pll_id, i)); +} + +/** + * prepare_abpll_for_ddrctrl - Copy DPLL settings to ABPLL + * + * This will copy DPLL settings from the memory mapped registers for a PLL to + * an array in memory. + */ +void prepare_abpll_for_ddrctrl(void) +{ + save_pll(slp_data.plls_con[ABPLL_ID], ABPLL_ID); + save_pll(slp_data.plls_con[DPLL_ID], DPLL_ID); + + restore_pll(ABPLL_ID, slp_data.plls_con[DPLL_ID]); +} + +void restore_abpll(void) +{ + restore_pll(ABPLL_ID, slp_data.plls_con[ABPLL_ID]); +} + +void restore_dpll(void) +{ + restore_pll(DPLL_ID, slp_data.plls_con[DPLL_ID]); +} + void plls_suspend_prepare(void) { uint32_t i, pll_id; @@ -343,16 +429,25 @@ void plls_resume_finish(void) REG_SOC_WMSK | slp_data.pmucru_clksel_con[i]); } +/** + * enable_dvfs_plls - To resume the specific PLLs + * + * Please see the comment at the disable_dvfs_plls() + * we don't suspend the ABPLL, so don't need resume + * it too. + */ void enable_dvfs_plls(void) { _pll_resume(ALPLL_ID); - _pll_resume(ABPLL_ID); _pll_resume(GPLL_ID); _pll_resume(VPLL_ID); _pll_resume(NPLL_ID); _pll_resume(CPLL_ID); } +/** + * enable_nodvfs_plls - To resume the PPLL + */ void enable_nodvfs_plls(void) { _pll_resume(PPLL_ID); diff --git a/plat/rockchip/rk3399/drivers/soc/soc.h b/plat/rockchip/rk3399/drivers/soc/soc.h index 4804be620..16897cc5b 100644 --- a/plat/rockchip/rk3399/drivers/soc/soc.h +++ b/plat/rockchip/rk3399/drivers/soc/soc.h @@ -336,7 +336,11 @@ void disable_nodvfs_plls(void); void plls_resume_finish(void); void enable_dvfs_plls(void); void enable_nodvfs_plls(void); +void prepare_abpll_for_ddrctrl(void); +void restore_abpll(void); +void restore_dpll(void); void clk_gate_con_save(void); void clk_gate_con_disable(void); void clk_gate_con_restore(void); +void sgrf_init(void); #endif /* __SOC_H__ */