| /* |
| * Copyright 2018-2023 NXP |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <stdbool.h> |
| #include <lib/mmio.h> |
| |
| #include <dram.h> |
| #include <platform_def.h> |
| |
| #define SRC_DDR1_RCR (IMX_SRC_BASE + 0x1000) |
| #define SRC_DDR2_RCR (IMX_SRC_BASE + 0x1004) |
| |
| #define PU_PGC_UP_TRG 0xf8 |
| #define PU_PGC_DN_TRG 0x104 |
| #define GPC_PU_PWRHSK (IMX_GPC_BASE + 0x01FC) |
| #define CCM_SRC_CTRL_OFFSET (IMX_CCM_BASE + 0x800) |
| #define CCM_CCGR_OFFSET (IMX_CCM_BASE + 0x4000) |
| #define CCM_TARGET_ROOT_OFFSET (IMX_CCM_BASE + 0x8000) |
| #define CCM_SRC_CTRL(n) (CCM_SRC_CTRL_OFFSET + 0x10 * (n)) |
| #define CCM_CCGR(n) (CCM_CCGR_OFFSET + 0x10 * (n)) |
| #define CCM_TARGET_ROOT(n) (CCM_TARGET_ROOT_OFFSET + 0x80 * (n)) |
| |
| #define DBGCAM_EMPTY 0x36000000 |
| |
| static void rank_setting_update(void) |
| { |
| uint32_t i, offset; |
| uint32_t pstate_num = dram_info.num_fsp; |
| |
| /* only support maximum 3 setpoints */ |
| pstate_num = (pstate_num > MAX_FSP_NUM) ? MAX_FSP_NUM : pstate_num; |
| |
| for (i = 0U; i < pstate_num; i++) { |
| offset = i ? (i + 1) * 0x1000 : 0U; |
| mmio_write_32(DDRC_DRAMTMG2(0) + offset, dram_info.rank_setting[i][0]); |
| if (dram_info.dram_type != DDRC_LPDDR4) { |
| mmio_write_32(DDRC_DRAMTMG9(0) + offset, dram_info.rank_setting[i][1]); |
| } |
| |
| #if !defined(PLAT_imx8mq) |
| mmio_write_32(DDRC_RANKCTL(0) + offset, |
| dram_info.rank_setting[i][2]); |
| #endif |
| } |
| #if defined(PLAT_imx8mq) |
| mmio_write_32(DDRC_RANKCTL(0), dram_info.rank_setting[0][2]); |
| #endif |
| } |
| |
| void dram_enter_retention(void) |
| { |
| /* Wait DBGCAM to be empty */ |
| while (mmio_read_32(DDRC_DBGCAM(0)) != DBGCAM_EMPTY) { |
| ; |
| } |
| |
| /* Block AXI ports from taking anymore transactions */ |
| mmio_write_32(DDRC_PCTRL_0(0), 0x0); |
| /* Wait until all AXI ports are idle */ |
| while (mmio_read_32(DDRC_PSTAT(0)) & 0x10001) { |
| ; |
| } |
| |
| /* Enter self refresh */ |
| mmio_write_32(DDRC_PWRCTL(0), 0xaa); |
| |
| /* LPDDR4 & DDR4/DDR3L need to check different status */ |
| if (dram_info.dram_type == DDRC_LPDDR4) { |
| while (0x223 != (mmio_read_32(DDRC_STAT(0)) & 0x33f)) { |
| ; |
| } |
| } else { |
| while (0x23 != (mmio_read_32(DDRC_STAT(0)) & 0x3f)) { |
| ; |
| } |
| } |
| |
| mmio_write_32(DDRC_DFIMISC(0), 0x0); |
| mmio_write_32(DDRC_SWCTL(0), 0x0); |
| mmio_write_32(DDRC_DFIMISC(0), 0x1f00); |
| mmio_write_32(DDRC_DFIMISC(0), 0x1f20); |
| |
| while (mmio_read_32(DDRC_DFISTAT(0)) & 0x1) { |
| ; |
| } |
| |
| mmio_write_32(DDRC_DFIMISC(0), 0x1f00); |
| /* wait DFISTAT.dfi_init_complete to 1 */ |
| while (!(mmio_read_32(DDRC_DFISTAT(0)) & 0x1)) { |
| ; |
| } |
| |
| mmio_write_32(DDRC_SWCTL(0), 0x1); |
| |
| /* should check PhyInLP3 pub reg */ |
| dwc_ddrphy_apb_wr(0xd0000, 0x0); |
| if (!(dwc_ddrphy_apb_rd(0x90028) & 0x1)) { |
| INFO("PhyInLP3 = 1\n"); |
| } |
| dwc_ddrphy_apb_wr(0xd0000, 0x1); |
| |
| #if defined(PLAT_imx8mq) |
| /* pwrdnreqn_async adbm/adbs of ddr */ |
| mmio_clrbits_32(GPC_PU_PWRHSK, BIT(1)); |
| while (mmio_read_32(GPC_PU_PWRHSK) & BIT(18)) { |
| ; |
| } |
| mmio_setbits_32(GPC_PU_PWRHSK, BIT(1)); |
| #else |
| /* pwrdnreqn_async adbm/adbs of ddr */ |
| mmio_clrbits_32(GPC_PU_PWRHSK, BIT(2)); |
| while (mmio_read_32(GPC_PU_PWRHSK) & BIT(20)) { |
| ; |
| } |
| mmio_setbits_32(GPC_PU_PWRHSK, BIT(2)); |
| #endif |
| /* remove PowerOk */ |
| mmio_write_32(SRC_DDR1_RCR, 0x8F000008); |
| |
| mmio_write_32(CCM_CCGR(5), 0); |
| mmio_write_32(CCM_SRC_CTRL(15), 2); |
| |
| /* enable the phy iso */ |
| mmio_setbits_32(IMX_GPC_BASE + 0xd40, 1); |
| mmio_setbits_32(IMX_GPC_BASE + PU_PGC_DN_TRG, BIT(5)); |
| |
| VERBOSE("dram enter retention\n"); |
| } |
| |
| void dram_exit_retention(void) |
| { |
| VERBOSE("dram exit retention\n"); |
| /* assert all reset */ |
| #if defined(PLAT_imx8mq) |
| mmio_write_32(SRC_DDR2_RCR, 0x8F000003); |
| mmio_write_32(SRC_DDR1_RCR, 0x8F00000F); |
| mmio_write_32(SRC_DDR2_RCR, 0x8F000000); |
| #else |
| mmio_write_32(SRC_DDR1_RCR, 0x8F00001F); |
| mmio_write_32(SRC_DDR1_RCR, 0x8F00000F); |
| #endif |
| mmio_write_32(CCM_CCGR(5), 2); |
| mmio_write_32(CCM_SRC_CTRL(15), 2); |
| |
| /* change the clock source of dram_apb_clk_root */ |
| mmio_write_32(CCM_TARGET_ROOT(65) + 0x8, (0x7 << 24) | (0x7 << 16)); |
| mmio_write_32(CCM_TARGET_ROOT(65) + 0x4, (0x4 << 24) | (0x3 << 16)); |
| |
| /* disable iso */ |
| mmio_setbits_32(IMX_GPC_BASE + PU_PGC_UP_TRG, BIT(5)); |
| mmio_write_32(SRC_DDR1_RCR, 0x8F000006); |
| |
| /* wait dram pll locked */ |
| while (!(mmio_read_32(DRAM_PLL_CTRL) & BIT(31))) { |
| ; |
| } |
| |
| /* ddrc re-init */ |
| dram_umctl2_init(dram_info.timing_info); |
| |
| /* |
| * Skips the DRAM init routine and starts up in selfrefresh mode |
| * Program INIT0.skip_dram_init = 2'b11 |
| */ |
| mmio_setbits_32(DDRC_INIT0(0), 0xc0000000); |
| /* Keeps the controller in self-refresh mode */ |
| mmio_write_32(DDRC_PWRCTL(0), 0xaa); |
| mmio_write_32(DDRC_DBG1(0), 0x0); |
| mmio_write_32(SRC_DDR1_RCR, 0x8F000004); |
| mmio_write_32(SRC_DDR1_RCR, 0x8F000000); |
| |
| /* before write Dynamic reg, sw_done should be 0 */ |
| mmio_write_32(DDRC_SWCTL(0), 0x0); |
| |
| #if !PLAT_imx8mn |
| if (dram_info.dram_type == DDRC_LPDDR4) { |
| mmio_write_32(DDRC_DDR_SS_GPR0, 0x01); /*LPDDR4 mode */ |
| } |
| #endif /* !PLAT_imx8mn */ |
| |
| mmio_write_32(DDRC_DFIMISC(0), 0x0); |
| |
| /* dram phy re-init */ |
| dram_phy_init(dram_info.timing_info); |
| |
| /* workaround for rank-to-rank issue */ |
| rank_setting_update(); |
| |
| /* DWC_DDRPHYA_APBONLY0_MicroContMuxSel */ |
| dwc_ddrphy_apb_wr(0xd0000, 0x0); |
| while (dwc_ddrphy_apb_rd(0x20097)) { |
| ; |
| } |
| dwc_ddrphy_apb_wr(0xd0000, 0x1); |
| |
| /* before write Dynamic reg, sw_done should be 0 */ |
| mmio_write_32(DDRC_SWCTL(0), 0x0); |
| mmio_write_32(DDRC_DFIMISC(0), 0x20); |
| /* wait DFISTAT.dfi_init_complete to 1 */ |
| while (!(mmio_read_32(DDRC_DFISTAT(0)) & 0x1)) { |
| ; |
| } |
| |
| /* clear DFIMISC.dfi_init_start */ |
| mmio_write_32(DDRC_DFIMISC(0), 0x0); |
| /* set DFIMISC.dfi_init_complete_en */ |
| mmio_write_32(DDRC_DFIMISC(0), 0x1); |
| |
| /* set SWCTL.sw_done to enable quasi-dynamic register programming */ |
| mmio_write_32(DDRC_SWCTL(0), 0x1); |
| /* wait SWSTAT.sw_done_ack to 1 */ |
| while (!(mmio_read_32(DDRC_SWSTAT(0)) & 0x1)) { |
| ; |
| } |
| |
| mmio_write_32(DDRC_PWRCTL(0), 0x88); |
| /* wait STAT to normal state */ |
| while (0x1 != (mmio_read_32(DDRC_STAT(0)) & 0x7)) { |
| ; |
| } |
| |
| mmio_write_32(DDRC_PCTRL_0(0), 0x1); |
| /* dis_auto-refresh is set to 0 */ |
| mmio_write_32(DDRC_RFSHCTL3(0), 0x0); |
| |
| /* should check PhyInLP3 pub reg */ |
| dwc_ddrphy_apb_wr(0xd0000, 0x0); |
| if (!(dwc_ddrphy_apb_rd(0x90028) & 0x1)) { |
| VERBOSE("PHYInLP3 = 0\n"); |
| } |
| dwc_ddrphy_apb_wr(0xd0000, 0x1); |
| } |