1*036935a8SXiaoDong Huang // SPDX-License-Identifier: BSD-3-Clause
2*036935a8SXiaoDong Huang /*
3*036935a8SXiaoDong Huang * Copyright (c) 2025, Rockchip Electronics Co., Ltd.
4*036935a8SXiaoDong Huang */
5*036935a8SXiaoDong Huang
6*036935a8SXiaoDong Huang #include <assert.h>
7*036935a8SXiaoDong Huang #include <errno.h>
8*036935a8SXiaoDong Huang
9*036935a8SXiaoDong Huang #include <arch_helpers.h>
10*036935a8SXiaoDong Huang #include <bl31/bl31.h>
11*036935a8SXiaoDong Huang #include <common/debug.h>
12*036935a8SXiaoDong Huang #include <drivers/console.h>
13*036935a8SXiaoDong Huang #include <drivers/delay_timer.h>
14*036935a8SXiaoDong Huang #include <lib/mmio.h>
15*036935a8SXiaoDong Huang #include <platform.h>
16*036935a8SXiaoDong Huang #include <platform_def.h>
17*036935a8SXiaoDong Huang
18*036935a8SXiaoDong Huang #include <dmc_rk3576.h>
19*036935a8SXiaoDong Huang #include <rk3576_def.h>
20*036935a8SXiaoDong Huang #include <soc.h>
21*036935a8SXiaoDong Huang
22*036935a8SXiaoDong Huang struct rk3576_dmc_config dmc_config;
23*036935a8SXiaoDong Huang
24*036935a8SXiaoDong Huang /* DDR_PHY */
25*036935a8SXiaoDong Huang #define LP_CON0 0x0018
26*036935a8SXiaoDong Huang #define DFI_LP_CON0 0x0e04
27*036935a8SXiaoDong Huang /* DDR_CTL */
28*036935a8SXiaoDong Huang #define DDRCTL_STAT 0x10014
29*036935a8SXiaoDong Huang #define DDRCTL_PWRCTL 0x10180
30*036935a8SXiaoDong Huang #define DDRCTL_CLKGATECTL 0x1018c
31*036935a8SXiaoDong Huang
32*036935a8SXiaoDong Huang /* LP_CON0 */
33*036935a8SXiaoDong Huang #define DS_IO_PD BIT(14)
34*036935a8SXiaoDong Huang #define SCHD_HW_CLOCK_GATING_DISABLE BIT(13)
35*036935a8SXiaoDong Huang #define PCL_PD BIT(12)
36*036935a8SXiaoDong Huang #define DQS_ENABLE BIT(10)
37*036935a8SXiaoDong Huang #define WCK_ENABLE BIT(9)
38*036935a8SXiaoDong Huang #define CTRL_DQS_DRV_OFF BIT(8)
39*036935a8SXiaoDong Huang #define CTRL_SCHEDULER_EN BIT(6)
40*036935a8SXiaoDong Huang
41*036935a8SXiaoDong Huang /* DFI_LP_CON0 0x0e04 */
42*036935a8SXiaoDong Huang #define DFI_LP_MODE_APB BIT(31)
43*036935a8SXiaoDong Huang
44*036935a8SXiaoDong Huang /* DDRCTL_STAT 0x10014 */
45*036935a8SXiaoDong Huang #define CTL_SELFREF_STATE_SHIFT (12)
46*036935a8SXiaoDong Huang #define CTL_SELFREF_STATE_MASK (0x7 << CTL_SELFREF_STATE_SHIFT)
47*036935a8SXiaoDong Huang #define CTL_NOT_IN_SELF_REFRESH (0x0 << CTL_SELFREF_STATE_SHIFT)
48*036935a8SXiaoDong Huang #define CTL_SELF_REFRESH_1 (0x1 << CTL_SELFREF_STATE_SHIFT)
49*036935a8SXiaoDong Huang #define CTL_SELF_REFRESH_POWER_DOWN (0x2 << CTL_SELFREF_STATE_SHIFT)
50*036935a8SXiaoDong Huang #define CTL_SELF_REFRESH_2 (0x3 << CTL_SELFREF_STATE_SHIFT)
51*036935a8SXiaoDong Huang #define CTL_SELF_REFRESH_DEEP_SLEEP (0x4 << CTL_SELFREF_STATE_SHIFT)
52*036935a8SXiaoDong Huang #define CTL_SELFREF_TYPE_SHIFT (4)
53*036935a8SXiaoDong Huang #define CTL_SELFREF_TYPE_MASK (0x3 << CTL_SELFREF_TYPE_SHIFT)
54*036935a8SXiaoDong Huang #define CTL_SELFREF_NOT_BY_PHY (0x1 << CTL_SELFREF_TYPE_SHIFT)
55*036935a8SXiaoDong Huang #define CTL_SELFREF_NOT_BY_AUTO (0x2 << CTL_SELFREF_TYPE_SHIFT)
56*036935a8SXiaoDong Huang #define CTL_SELFREF_BY_AUTO (0x3 << CTL_SELFREF_TYPE_SHIFT)
57*036935a8SXiaoDong Huang #define CTL_OPERATING_MODE_MASK (0x7)
58*036935a8SXiaoDong Huang #define CTL_OPERATING_MODE_INIT (0x0)
59*036935a8SXiaoDong Huang #define CTL_OPERATING_MODE_NORMAL (0x1)
60*036935a8SXiaoDong Huang #define CTL_OPERATING_MODE_PD (0x2)
61*036935a8SXiaoDong Huang #define CTL_OPERATING_MODE_SR_SRPD (0x3)
62*036935a8SXiaoDong Huang
63*036935a8SXiaoDong Huang /* DDRCTL_PWRCTL 0x10180 */
64*036935a8SXiaoDong Huang #define CTL_DSM_EN BIT(18)
65*036935a8SXiaoDong Huang #define CTL_STAY_IN_SELFREF BIT(15)
66*036935a8SXiaoDong Huang #define CTL_SELFREF_SW BIT(11)
67*036935a8SXiaoDong Huang #define CTL_EN_DFI_DRAM_CLK_DISABLE BIT(9)
68*036935a8SXiaoDong Huang #define CTL_POWERDOWN_EN_MASK (0xf)
69*036935a8SXiaoDong Huang #define CTL_POWERDOWN_EN_SHIFT (4)
70*036935a8SXiaoDong Huang #define CTL_SELFREF_EN_MASK (0xf)
71*036935a8SXiaoDong Huang #define CTL_SELFREF_EN_SHIFT (0)
72*036935a8SXiaoDong Huang
73*036935a8SXiaoDong Huang #define SYS_REG_DEC_CHINFO(n, ch) (((n) >> (28 + (ch))) & 0x1)
74*036935a8SXiaoDong Huang #define SYS_REG_DEC_CHINFO_V3(reg2, ch) SYS_REG_DEC_CHINFO(reg2, ch)
75*036935a8SXiaoDong Huang
76*036935a8SXiaoDong Huang #define SYS_REG_DEC_NUM_CH(n) (1 + (((n) >> 12) & 0x1))
77*036935a8SXiaoDong Huang #define SYS_REG_DEC_NUM_CH_V3(reg2) SYS_REG_DEC_NUM_CH(reg2)
78*036935a8SXiaoDong Huang
exit_low_power(uint32_t ch,struct rk3576_dmc_config * configs)79*036935a8SXiaoDong Huang static void exit_low_power(uint32_t ch, struct rk3576_dmc_config *configs)
80*036935a8SXiaoDong Huang {
81*036935a8SXiaoDong Huang /* LP_CON0: [12]pcl_pd */
82*036935a8SXiaoDong Huang configs->low_power[ch].pcl_pd = mmio_read_32(DDRPHY_BASE_CH(0) + LP_CON0) & PCL_PD;
83*036935a8SXiaoDong Huang mmio_clrbits_32(DDRPHY_BASE_CH(ch) + LP_CON0, PCL_PD);
84*036935a8SXiaoDong Huang
85*036935a8SXiaoDong Huang /* Disable low power activities */
86*036935a8SXiaoDong Huang configs->low_power[ch].pwrctl = mmio_read_32(UMCTL_BASE_CH(ch) + DDRCTL_PWRCTL);
87*036935a8SXiaoDong Huang mmio_clrbits_32(UMCTL_BASE_CH(ch) + DDRCTL_PWRCTL,
88*036935a8SXiaoDong Huang CTL_DSM_EN | (CTL_POWERDOWN_EN_MASK << CTL_POWERDOWN_EN_SHIFT) |
89*036935a8SXiaoDong Huang (CTL_SELFREF_EN_MASK << CTL_SELFREF_EN_SHIFT));
90*036935a8SXiaoDong Huang while ((mmio_read_32(UMCTL_BASE_CH(ch) + DDRCTL_STAT) & CTL_OPERATING_MODE_MASK) !=
91*036935a8SXiaoDong Huang CTL_OPERATING_MODE_NORMAL)
92*036935a8SXiaoDong Huang continue;
93*036935a8SXiaoDong Huang
94*036935a8SXiaoDong Huang /* DDR_GRF_CHA_CON6: [6:0]rd_lat_delay, [14:8]wr_lat_delay, [15]cmd_dly_eq0_en */
95*036935a8SXiaoDong Huang configs->low_power[ch].grf_ddr_con6 =
96*036935a8SXiaoDong Huang mmio_read_32(DDR_GRF_BASE + GRF_CH_CON(ch, 6)) & 0xff7f;
97*036935a8SXiaoDong Huang mmio_write_32(DDR_GRF_BASE + GRF_CH_CON(ch, 6), (0x1ul << (15 + 16)));
98*036935a8SXiaoDong Huang
99*036935a8SXiaoDong Huang /* DDR_GRF_CHA_CON0: [12:8]ddrctl_axi_cg_en */
100*036935a8SXiaoDong Huang configs->low_power[ch].grf_ddr_con0 =
101*036935a8SXiaoDong Huang mmio_read_32(DDR_GRF_BASE + GRF_CH_CON(ch, 0)) & 0x1f00;
102*036935a8SXiaoDong Huang mmio_write_32(DDR_GRF_BASE + GRF_CH_CON(ch, 0), 0x1f000000);
103*036935a8SXiaoDong Huang
104*036935a8SXiaoDong Huang /*
105*036935a8SXiaoDong Huang * DDR_GRF_CHA_CON1:
106*036935a8SXiaoDong Huang * [15]ddrctl_apb_pclk_cg_en, [12]ddrmon_pclk_cg_en, [7]dfi_scramble_cg_en,
107*036935a8SXiaoDong Huang * [6]ddrctl_mem_cg_en, [5]bsm_clk_cg_en, [2]ddrctl_core_cg_en, [1]ddrctl_apb_cg_en
108*036935a8SXiaoDong Huang */
109*036935a8SXiaoDong Huang configs->low_power[ch].grf_ddr_con1 =
110*036935a8SXiaoDong Huang mmio_read_32(DDR_GRF_BASE + GRF_CH_CON(ch, 1)) & 0x90e6;
111*036935a8SXiaoDong Huang mmio_write_32(DDR_GRF_BASE + GRF_CH_CON(ch, 1), 0x90e60000);
112*036935a8SXiaoDong Huang
113*036935a8SXiaoDong Huang configs->low_power[ch].hwlp_0 = mmio_read_32(HWLP_BASE_CH(ch) + 0x0);
114*036935a8SXiaoDong Huang mmio_write_32(HWLP_BASE_CH(ch) + 0x0, 0x0);
115*036935a8SXiaoDong Huang configs->low_power[ch].hwlp_c = mmio_read_32(HWLP_BASE_CH(ch) + 0xc);
116*036935a8SXiaoDong Huang mmio_write_32(HWLP_BASE_CH(ch) + 0xc, 0x0);
117*036935a8SXiaoDong Huang
118*036935a8SXiaoDong Huang /* DDR_GRF_CHA_PHY_CON0: [14]ddrphy_pclk_cg_en */
119*036935a8SXiaoDong Huang configs->low_power[ch].grf_ddrphy_con0 =
120*036935a8SXiaoDong Huang mmio_read_32(DDR_GRF_BASE + GRF_DDRPHY_CON0(ch)) & BIT(14);
121*036935a8SXiaoDong Huang mmio_write_32(DDR_GRF_BASE + GRF_DDRPHY_CON0(ch), BIT(14 + 16));
122*036935a8SXiaoDong Huang
123*036935a8SXiaoDong Huang /* CLKGATECTL: [5:0]bsm_clk_on */
124*036935a8SXiaoDong Huang configs->low_power[ch].clkgatectl =
125*036935a8SXiaoDong Huang mmio_read_32(UMCTL_BASE_CH(ch) + DDRCTL_CLKGATECTL) & 0x3f;
126*036935a8SXiaoDong Huang /* DFI_LP_CON0: [31]dfi_lp_mode_apb */
127*036935a8SXiaoDong Huang configs->low_power[ch].dfi_lp_mode_apb =
128*036935a8SXiaoDong Huang (mmio_read_32(DDRPHY_BASE_CH(ch) + DFI_LP_CON0) >> 31) & 0x1;
129*036935a8SXiaoDong Huang }
130*036935a8SXiaoDong Huang
resume_low_power(uint32_t ch,struct rk3576_dmc_config * configs)131*036935a8SXiaoDong Huang static void resume_low_power(uint32_t ch, struct rk3576_dmc_config *configs)
132*036935a8SXiaoDong Huang {
133*036935a8SXiaoDong Huang /* DFI_LP_CON0: [31]dfi_lp_mode_apb */
134*036935a8SXiaoDong Huang if (configs->low_power[ch].dfi_lp_mode_apb != 0)
135*036935a8SXiaoDong Huang mmio_setbits_32(DDRPHY_BASE_CH(ch) + DFI_LP_CON0, DFI_LP_MODE_APB);
136*036935a8SXiaoDong Huang
137*036935a8SXiaoDong Huang /* CLKGATECTL: [5:0]bsm_clk_on */
138*036935a8SXiaoDong Huang mmio_clrsetbits_32(UMCTL_BASE_CH(ch) + DDRCTL_CLKGATECTL,
139*036935a8SXiaoDong Huang 0x3f, configs->low_power[ch].clkgatectl & 0x3f);
140*036935a8SXiaoDong Huang
141*036935a8SXiaoDong Huang /* DDR_GRF_CHA_CON6: [6:0]rd_lat_delay, [14:8]wr_lat_delay, [15]cmd_dly_eq0_en */
142*036935a8SXiaoDong Huang mmio_write_32(DDR_GRF_BASE + GRF_CH_CON(ch, 6),
143*036935a8SXiaoDong Huang (0xff7ful << 16) | configs->low_power[ch].grf_ddr_con6);
144*036935a8SXiaoDong Huang
145*036935a8SXiaoDong Huang mmio_write_32(HWLP_BASE_CH(ch) + 0xc, configs->low_power[ch].hwlp_c);
146*036935a8SXiaoDong Huang mmio_write_32(HWLP_BASE_CH(ch) + 0x0, configs->low_power[ch].hwlp_0);
147*036935a8SXiaoDong Huang
148*036935a8SXiaoDong Huang /* DDR_GRF_CHA_CON0: [12:8]ddrctl_axi_cg_en */
149*036935a8SXiaoDong Huang mmio_write_32(DDR_GRF_BASE + GRF_CH_CON(ch, 0),
150*036935a8SXiaoDong Huang (0x1f00ul << 16) | configs->low_power[ch].grf_ddr_con0);
151*036935a8SXiaoDong Huang
152*036935a8SXiaoDong Huang /*
153*036935a8SXiaoDong Huang * DDR_GRF_CHA_CON1:
154*036935a8SXiaoDong Huang * [15]ddrctl_apb_pclk_cg_en, [12]ddrmon_pclk_cg_en, [7]dfi_scramble_cg_en,
155*036935a8SXiaoDong Huang * [6]ddrctl_mem_cg_en, [5]bsm_clk_cg_en, [2]ddrctl_core_cg_en, [1]ddrctl_apb_cg_en
156*036935a8SXiaoDong Huang */
157*036935a8SXiaoDong Huang mmio_write_32(DDR_GRF_BASE + GRF_CH_CON(ch, 1),
158*036935a8SXiaoDong Huang (0x90e6ul << 16) | configs->low_power[ch].grf_ddr_con1);
159*036935a8SXiaoDong Huang
160*036935a8SXiaoDong Huang /* DDR_GRF_CHA_PHY_CON0: [14]ddrphy_pclk_cg_en */
161*036935a8SXiaoDong Huang mmio_write_32(DDR_GRF_BASE + GRF_DDRPHY_CON0(ch),
162*036935a8SXiaoDong Huang BIT(14 + 16) | configs->low_power[ch].grf_ddrphy_con0);
163*036935a8SXiaoDong Huang
164*036935a8SXiaoDong Huang /* reset low power activities */
165*036935a8SXiaoDong Huang mmio_write_32(UMCTL_BASE_CH(ch) + DDRCTL_PWRCTL, configs->low_power[ch].pwrctl);
166*036935a8SXiaoDong Huang
167*036935a8SXiaoDong Huang /* LP_CON0: [12]pcl_pd */
168*036935a8SXiaoDong Huang if (configs->low_power[ch].pcl_pd != 0)
169*036935a8SXiaoDong Huang mmio_setbits_32(DDRPHY_BASE_CH(ch) + LP_CON0, PCL_PD);
170*036935a8SXiaoDong Huang }
171*036935a8SXiaoDong Huang
dmc_save(void)172*036935a8SXiaoDong Huang void dmc_save(void)
173*036935a8SXiaoDong Huang {
174*036935a8SXiaoDong Huang uint32_t i, channel_num;
175*036935a8SXiaoDong Huang
176*036935a8SXiaoDong Huang channel_num =
177*036935a8SXiaoDong Huang SYS_REG_DEC_NUM_CH_V3(mmio_read_32(PMU1_GRF_BASE + PMUGRF_OS_REG(2)));
178*036935a8SXiaoDong Huang
179*036935a8SXiaoDong Huang for (i = 0; i < channel_num; i++)
180*036935a8SXiaoDong Huang exit_low_power(i, &dmc_config);
181*036935a8SXiaoDong Huang }
182*036935a8SXiaoDong Huang
dmc_restore(void)183*036935a8SXiaoDong Huang void dmc_restore(void)
184*036935a8SXiaoDong Huang {
185*036935a8SXiaoDong Huang uint32_t i, channel_num;
186*036935a8SXiaoDong Huang
187*036935a8SXiaoDong Huang channel_num = SYS_REG_DEC_NUM_CH_V3(mmio_read_32(PMU1_GRF_BASE + PMUGRF_OS_REG(2)));
188*036935a8SXiaoDong Huang
189*036935a8SXiaoDong Huang for (i = 0; i < channel_num; i++)
190*036935a8SXiaoDong Huang resume_low_power(i, &dmc_config);
191*036935a8SXiaoDong Huang }
192