1478af8d3SJacky Bai /*
2478af8d3SJacky Bai * Copyright 2021-2024 NXP
3478af8d3SJacky Bai *
4478af8d3SJacky Bai * SPDX-License-Identifier: BSD-3-Clause
5478af8d3SJacky Bai */
6478af8d3SJacky Bai
7478af8d3SJacky Bai #include <assert.h>
8478af8d3SJacky Bai #include <stdbool.h>
9478af8d3SJacky Bai
10caee2733SJacky Bai #include <arch_helpers.h>
11caee2733SJacky Bai #include <bl31/interrupt_mgmt.h>
12caee2733SJacky Bai #include <common/runtime_svc.h>
13478af8d3SJacky Bai #include <lib/mmio.h>
14caee2733SJacky Bai #include <lib/spinlock.h>
15caee2733SJacky Bai #include <plat/common/platform.h>
16caee2733SJacky Bai
17478af8d3SJacky Bai #include <platform_def.h>
18478af8d3SJacky Bai
19caee2733SJacky Bai #include <dram.h>
20478af8d3SJacky Bai #include <upower_api.h>
21478af8d3SJacky Bai
22478af8d3SJacky Bai #define PHY_FREQ_SEL_INDEX(x) ((x) << 16)
23478af8d3SJacky Bai #define PHY_FREQ_MULTICAST_EN(x) ((x) << 8)
24478af8d3SJacky Bai #define DENALI_PHY_1537 U(0x5804)
25478af8d3SJacky Bai
26478af8d3SJacky Bai #define IMX_DDRC_BASE U(0x2E060000)
27478af8d3SJacky Bai #define SAVED_DRAM_DATA_BASE U(0x20055000)
28ee25e6a5SAdrian Alonso #define DENALI_CTL_143 U(0x23C)
29ee25e6a5SAdrian Alonso #define DENALI_CTL_144 U(0x240)
30ee25e6a5SAdrian Alonso #define DENALI_CTL_146 U(0x248)
31ee25e6a5SAdrian Alonso #define LP_STATE_CS_IDLE U(0x404000)
32ee25e6a5SAdrian Alonso #define LP_STATE_CS_PD_CG U(0x4F4F00)
33478af8d3SJacky Bai #define LPI_WAKEUP_EN_SHIFT U(8)
34478af8d3SJacky Bai #define IMX_LPAV_SIM_BASE 0x2DA50000
35478af8d3SJacky Bai #define LPDDR_CTRL 0x14
36478af8d3SJacky Bai #define LPDDR_AUTO_LP_MODE_DISABLE BIT(24)
37478af8d3SJacky Bai #define SOC_LP_CMD_SHIFT U(15)
38478af8d3SJacky Bai #define LPDDR_CTRL2 0x18
39ee25e6a5SAdrian Alonso #define LPDDR_EN_CLKGATE (0x1<<17)
40ee25e6a5SAdrian Alonso #define LPDDR_MAX_CLKDIV_EN (0x1 << 16)
41ee25e6a5SAdrian Alonso #define LP_AUTO_ENTRY_EN 0x4
42ee25e6a5SAdrian Alonso #define LP_AUTO_EXIT_EN 0xF
43478af8d3SJacky Bai
44478af8d3SJacky Bai #define DENALI_CTL_00 U(0x0)
45478af8d3SJacky Bai #define DENALI_CTL_23 U(0x5c)
46478af8d3SJacky Bai #define DFIBUS_FREQ_INIT_SHIFT U(24)
47478af8d3SJacky Bai #define TSREF2PHYMSTR_SHIFT U(8)
48478af8d3SJacky Bai #define TSREF2PHYMSTR_MASK GENMASK(13, 8)
49478af8d3SJacky Bai
50478af8d3SJacky Bai #define DENALI_CTL_24 U(0x60)
51478af8d3SJacky Bai #define DENALI_CTL_25 U(0x64)
52478af8d3SJacky Bai
53478af8d3SJacky Bai #define DENALI_CTL_93 U(0x174)
54478af8d3SJacky Bai #define PWRUP_SREFRESH_EXIT BIT(0)
55478af8d3SJacky Bai
56478af8d3SJacky Bai #define DENALI_CTL_127 U(0x1fc)
57478af8d3SJacky Bai #define PHYMSTR_TRAIN_AFTER_INIT_COMPLETE BIT(16)
58478af8d3SJacky Bai
59478af8d3SJacky Bai #define DENALI_CTL_147 U(0x24c)
60478af8d3SJacky Bai #define DENALI_CTL_153 U(0x264)
61478af8d3SJacky Bai #define PCPCS_PD_EN BIT(8)
62478af8d3SJacky Bai
63478af8d3SJacky Bai #define DENALI_CTL_249 U(0x3E4)
64478af8d3SJacky Bai #define DENALI_CTL_266 U(0x428)
65478af8d3SJacky Bai
66478af8d3SJacky Bai #define DENALI_PHY_1547 U(0x582c)
67478af8d3SJacky Bai #define PHY_LP4_BOOT_DISABLE BIT(8)
68478af8d3SJacky Bai
69478af8d3SJacky Bai #define DENALI_PHY_1559 U(0x585c)
70478af8d3SJacky Bai #define DENALI_PHY_1590 U(0x58D8)
71478af8d3SJacky Bai
72478af8d3SJacky Bai #define DENALI_PI_00 U(0x2000)
73478af8d3SJacky Bai #define DENALI_PI_04 U(0x2010)
74478af8d3SJacky Bai #define DENALI_PI_52 U(0x20D0)
75478af8d3SJacky Bai #define DENALI_PI_26 U(0x2068)
76478af8d3SJacky Bai #define DENALI_PI_33 U(0x2084)
77478af8d3SJacky Bai #define DENALI_PI_65 U(0x2104)
78478af8d3SJacky Bai #define DENALI_PI_77 U(0x2134)
79478af8d3SJacky Bai #define DENALI_PI_134 U(0x2218)
80478af8d3SJacky Bai #define DENALI_PI_131 U(0x220C)
81478af8d3SJacky Bai #define DENALI_PI_132 U(0x2210)
82478af8d3SJacky Bai #define DENALI_PI_134 U(0x2218)
83478af8d3SJacky Bai #define DENALI_PI_137 U(0x2224)
84478af8d3SJacky Bai #define DENALI_PI_174 U(0x22B8)
85478af8d3SJacky Bai #define DENALI_PI_175 U(0x22BC)
86478af8d3SJacky Bai #define DENALI_PI_181 U(0x22D4)
87478af8d3SJacky Bai #define DENALI_PI_182 U(0x22D8)
88478af8d3SJacky Bai #define DENALI_PI_191 U(0x22FC)
89478af8d3SJacky Bai #define DENALI_PI_192 U(0x2300)
90478af8d3SJacky Bai #define DENALI_PI_212 U(0x2350)
91478af8d3SJacky Bai #define DENALI_PI_214 U(0x2358)
92478af8d3SJacky Bai #define DENALI_PI_217 U(0x2364)
93478af8d3SJacky Bai
94478af8d3SJacky Bai #define LPDDR3_TYPE U(0x7)
95478af8d3SJacky Bai #define LPDDR4_TYPE U(0xB)
96478af8d3SJacky Bai
97478af8d3SJacky Bai extern void upower_wait_resp(void);
98478af8d3SJacky Bai
99478af8d3SJacky Bai struct dram_cfg_param {
100478af8d3SJacky Bai uint32_t reg;
101478af8d3SJacky Bai uint32_t val;
102478af8d3SJacky Bai };
103478af8d3SJacky Bai
104478af8d3SJacky Bai struct dram_timing_info {
105478af8d3SJacky Bai /* ddr controller config */
106478af8d3SJacky Bai struct dram_cfg_param *ctl_cfg;
107478af8d3SJacky Bai unsigned int ctl_cfg_num;
108478af8d3SJacky Bai /* pi config */
109478af8d3SJacky Bai struct dram_cfg_param *pi_cfg;
110478af8d3SJacky Bai unsigned int pi_cfg_num;
111478af8d3SJacky Bai /* phy freq1 config */
112478af8d3SJacky Bai struct dram_cfg_param *phy_f1_cfg;
113478af8d3SJacky Bai unsigned int phy_f1_cfg_num;
114478af8d3SJacky Bai /* phy freq2 config */
115478af8d3SJacky Bai struct dram_cfg_param *phy_f2_cfg;
116478af8d3SJacky Bai unsigned int phy_f2_cfg_num;
117ee25e6a5SAdrian Alonso /* automatic low power config */
118ee25e6a5SAdrian Alonso struct dram_cfg_param *auto_lp_cfg;
119ee25e6a5SAdrian Alonso unsigned int auto_lp_cfg_num;
120478af8d3SJacky Bai /* initialized drate table */
121478af8d3SJacky Bai unsigned int fsp_table[3];
122478af8d3SJacky Bai };
123478af8d3SJacky Bai
124478af8d3SJacky Bai #define CTL_NUM U(680)
125478af8d3SJacky Bai #define PI_NUM U(298)
126478af8d3SJacky Bai #define PHY_NUM U(1654)
127478af8d3SJacky Bai #define PHY_DIFF_NUM U(49)
128ee25e6a5SAdrian Alonso #define AUTO_LP_NUM U(3)
129478af8d3SJacky Bai struct dram_cfg {
130478af8d3SJacky Bai uint32_t ctl_cfg[CTL_NUM];
131478af8d3SJacky Bai uint32_t pi_cfg[PI_NUM];
132478af8d3SJacky Bai uint32_t phy_full[PHY_NUM];
133478af8d3SJacky Bai uint32_t phy_diff[PHY_DIFF_NUM];
134ee25e6a5SAdrian Alonso uint32_t auto_lp_cfg[AUTO_LP_NUM];
135478af8d3SJacky Bai };
136478af8d3SJacky Bai
137478af8d3SJacky Bai struct dram_timing_info *info;
138478af8d3SJacky Bai struct dram_cfg *dram_timing_cfg;
139478af8d3SJacky Bai
140478af8d3SJacky Bai /* mark if dram cfg is already saved */
141478af8d3SJacky Bai static bool dram_cfg_saved;
142ee25e6a5SAdrian Alonso static bool dram_auto_lp_true;
143ee25e6a5SAdrian Alonso static uint32_t dram_class, dram_ctl_143;
144478af8d3SJacky Bai
145478af8d3SJacky Bai /* PHY register index for frequency diff */
146478af8d3SJacky Bai uint32_t freq_specific_reg_array[PHY_DIFF_NUM] = {
147478af8d3SJacky Bai 90, 92, 93, 96, 97, 100, 101, 102, 103, 104, 114,
148478af8d3SJacky Bai 346, 348, 349, 352, 353, 356, 357, 358, 359, 360,
149478af8d3SJacky Bai 370, 602, 604, 605, 608, 609, 612, 613, 614, 615,
150478af8d3SJacky Bai 616, 626, 858, 860, 861, 864, 865, 868, 869, 870,
151478af8d3SJacky Bai 871, 872, 882, 1063, 1319, 1566, 1624, 1625
152478af8d3SJacky Bai };
153478af8d3SJacky Bai
154caee2733SJacky Bai /* lock used for DDR DVFS */
155caee2733SJacky Bai spinlock_t dfs_lock;
156caee2733SJacky Bai static volatile uint32_t core_count;
157caee2733SJacky Bai static volatile bool in_progress;
158416c4433SJacky Bai static volatile bool sys_dvfs;
159caee2733SJacky Bai static int num_fsp;
160caee2733SJacky Bai
ddr_init(void)161478af8d3SJacky Bai static void ddr_init(void)
162478af8d3SJacky Bai {
163478af8d3SJacky Bai unsigned int i;
164478af8d3SJacky Bai
165478af8d3SJacky Bai /* restore the ddr ctl config */
166478af8d3SJacky Bai for (i = 0U; i < CTL_NUM; i++) {
167478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + i * 4, dram_timing_cfg->ctl_cfg[i]);
168478af8d3SJacky Bai }
169478af8d3SJacky Bai
170478af8d3SJacky Bai /* load the PI registers */
171478af8d3SJacky Bai for (i = 0U; i < PI_NUM; i++) {
172478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + 0x2000 + i * 4, dram_timing_cfg->pi_cfg[i]);
173478af8d3SJacky Bai }
174478af8d3SJacky Bai
175478af8d3SJacky Bai
176478af8d3SJacky Bai /* restore all PHY registers for all the fsp. */
177478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1537, 0x100);
178478af8d3SJacky Bai /* restore all the phy configs */
179478af8d3SJacky Bai for (i = 0U; i < PHY_NUM; i++) {
180478af8d3SJacky Bai /* skip the reserved registers space */
181478af8d3SJacky Bai if (i >= 121U && i <= 255U) {
182478af8d3SJacky Bai continue;
183478af8d3SJacky Bai }
184478af8d3SJacky Bai if (i >= 377U && i <= 511U) {
185478af8d3SJacky Bai continue;
186478af8d3SJacky Bai }
187478af8d3SJacky Bai if (i >= 633U && i <= 767U) {
188478af8d3SJacky Bai continue;
189478af8d3SJacky Bai }
190478af8d3SJacky Bai if (i >= 889U && i <= 1023U) {
191478af8d3SJacky Bai continue;
192478af8d3SJacky Bai }
193478af8d3SJacky Bai if (i >= 1065U && i <= 1279U) {
194478af8d3SJacky Bai continue;
195478af8d3SJacky Bai }
196478af8d3SJacky Bai if (i >= 1321U && i <= 1535U) {
197478af8d3SJacky Bai continue;
198478af8d3SJacky Bai }
199478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + 0x4000 + i * 4, dram_timing_cfg->phy_full[i]);
200478af8d3SJacky Bai }
201478af8d3SJacky Bai
202478af8d3SJacky Bai if (dram_class == LPDDR4_TYPE) {
203478af8d3SJacky Bai /* restore only the diff. */
204478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1537, 0x0);
205478af8d3SJacky Bai for (i = 0U; i < PHY_DIFF_NUM; i++) {
206478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + 0x4000 + freq_specific_reg_array[i] * 4,
207478af8d3SJacky Bai dram_timing_cfg->phy_diff[i]);
208478af8d3SJacky Bai }
209478af8d3SJacky Bai }
210478af8d3SJacky Bai
211478af8d3SJacky Bai /* Re-enable MULTICAST mode */
212478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1537, PHY_FREQ_MULTICAST_EN(1));
213478af8d3SJacky Bai }
214478af8d3SJacky Bai
dram_lp_auto_disable(void)215ee25e6a5SAdrian Alonso void dram_lp_auto_disable(void)
216ee25e6a5SAdrian Alonso {
217ee25e6a5SAdrian Alonso uint32_t lp_auto_en;
218ee25e6a5SAdrian Alonso
219ee25e6a5SAdrian Alonso dram_timing_cfg = (struct dram_cfg *)(SAVED_DRAM_DATA_BASE +
220ee25e6a5SAdrian Alonso sizeof(struct dram_timing_info));
221ee25e6a5SAdrian Alonso lp_auto_en = (mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_146) & (LP_AUTO_ENTRY_EN << 24));
222ee25e6a5SAdrian Alonso /* Save initial config */
223ee25e6a5SAdrian Alonso dram_ctl_143 = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_143);
224ee25e6a5SAdrian Alonso
225ee25e6a5SAdrian Alonso if (lp_auto_en && !dram_auto_lp_true) {
226ee25e6a5SAdrian Alonso /* 0.a Save DDRC auto low-power mode parameter */
227ee25e6a5SAdrian Alonso dram_timing_cfg->auto_lp_cfg[0] = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_144);
228ee25e6a5SAdrian Alonso dram_timing_cfg->auto_lp_cfg[1] = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_147);
229ee25e6a5SAdrian Alonso dram_timing_cfg->auto_lp_cfg[2] = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_146);
230ee25e6a5SAdrian Alonso /* Set LPI_SRPD_LONG_MCCLK_GATE_WAKEUP_F2 to Maximum */
231ee25e6a5SAdrian Alonso mmio_setbits_32(IMX_DDRC_BASE + DENALI_CTL_143, 0xF << 24);
232ee25e6a5SAdrian Alonso /* 0.b Disable DDRC auto low-power mode interface */
233ee25e6a5SAdrian Alonso mmio_clrbits_32(IMX_DDRC_BASE + DENALI_CTL_146, LP_AUTO_ENTRY_EN << 24);
234ee25e6a5SAdrian Alonso /* 0.c Read any location to get DRAM out of Self-refresh */
235*8d50c91bSJi Luo mmio_read_32(DEVICE2_BASE);
236ee25e6a5SAdrian Alonso /* 0.d Confirm DRAM is out of Self-refresh */
237ee25e6a5SAdrian Alonso while ((mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_146) &
238ee25e6a5SAdrian Alonso LP_STATE_CS_PD_CG) != LP_STATE_CS_IDLE) {
239ee25e6a5SAdrian Alonso ;
240ee25e6a5SAdrian Alonso }
241ee25e6a5SAdrian Alonso /* 0.e Disable DDRC auto low-power exit */
242ee25e6a5SAdrian Alonso mmio_clrbits_32(IMX_DDRC_BASE + DENALI_CTL_147, LP_AUTO_EXIT_EN);
243ee25e6a5SAdrian Alonso /* dram low power mode flag */
244ee25e6a5SAdrian Alonso dram_auto_lp_true = true;
245ee25e6a5SAdrian Alonso }
246ee25e6a5SAdrian Alonso }
247ee25e6a5SAdrian Alonso
dram_lp_auto_enable(void)248ee25e6a5SAdrian Alonso void dram_lp_auto_enable(void)
249ee25e6a5SAdrian Alonso {
250ee25e6a5SAdrian Alonso /* Switch back to Auto Low-power mode */
251ee25e6a5SAdrian Alonso if (dram_auto_lp_true) {
252ee25e6a5SAdrian Alonso /* 12.a Confirm DRAM is out of Self-refresh */
253ee25e6a5SAdrian Alonso while ((mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_146) &
254ee25e6a5SAdrian Alonso LP_STATE_CS_PD_CG) != LP_STATE_CS_IDLE) {
255ee25e6a5SAdrian Alonso ;
256ee25e6a5SAdrian Alonso }
257ee25e6a5SAdrian Alonso /* 12.b Enable DDRC auto low-power exit */
258ee25e6a5SAdrian Alonso /*
259ee25e6a5SAdrian Alonso * 12.c TBC! : Set DENALI_CTL_144 [LPI_CTRL_REQ_EN[24]] and
260ee25e6a5SAdrian Alonso * [DFI_LP_VERSION[16]] back to default settings = 1b'1.
261ee25e6a5SAdrian Alonso */
262ee25e6a5SAdrian Alonso /*
263ee25e6a5SAdrian Alonso * 12.d Reconfigure DENALI_CTL_144 [LPI_WAKEUP_EN[5:0]] bit
264ee25e6a5SAdrian Alonso * LPI_WAKEUP_EN[3] = 1b'1.
265ee25e6a5SAdrian Alonso */
266ee25e6a5SAdrian Alonso mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_144, dram_timing_cfg->auto_lp_cfg[0]);
267ee25e6a5SAdrian Alonso mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_147, dram_timing_cfg->auto_lp_cfg[1]);
268ee25e6a5SAdrian Alonso /* 12.e Re-enable DDRC auto low-power mode interface */
269ee25e6a5SAdrian Alonso mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_146, dram_timing_cfg->auto_lp_cfg[2]);
270ee25e6a5SAdrian Alonso /* restore ctl config */
271ee25e6a5SAdrian Alonso mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_143, dram_ctl_143);
272ee25e6a5SAdrian Alonso /* dram low power mode flag */
273ee25e6a5SAdrian Alonso dram_auto_lp_true = false;
274ee25e6a5SAdrian Alonso }
275ee25e6a5SAdrian Alonso }
276ee25e6a5SAdrian Alonso
dram_enter_self_refresh(void)277ee25e6a5SAdrian Alonso void dram_enter_self_refresh(void)
278ee25e6a5SAdrian Alonso {
279ee25e6a5SAdrian Alonso /* disable auto low power interface */
280ee25e6a5SAdrian Alonso dram_lp_auto_disable();
281ee25e6a5SAdrian Alonso /* 1. config the PCC_LPDDR4[SSADO] to 2b'11 for ACK domain 0/1's STOP */
282ee25e6a5SAdrian Alonso mmio_setbits_32(IMX_PCC5_BASE + 0x108, 0x2 << 22);
283ee25e6a5SAdrian Alonso /* 1.a Clock gate PCC_LPDDR4[CGC] and no software reset PCC_LPDDR4[SWRST] */
284ee25e6a5SAdrian Alonso mmio_setbits_32(IMX_PCC5_BASE + 0x108, (BIT(30) | BIT(28)));
285ee25e6a5SAdrian Alonso
286ee25e6a5SAdrian Alonso /*
287ee25e6a5SAdrian Alonso * 2. Make sure the DENALI_CTL_144[LPI_WAKEUP_EN[5:0]] has the bit
288ee25e6a5SAdrian Alonso * LPI_WAKEUP_EN[3] = 1b'1. This enables the option 'self-refresh
289ee25e6a5SAdrian Alonso * long with mem and ctlr clk gating or self-refresh power-down long
290ee25e6a5SAdrian Alonso * with mem and ctlr clk gating'
291ee25e6a5SAdrian Alonso */
292ee25e6a5SAdrian Alonso mmio_setbits_32(IMX_DDRC_BASE + DENALI_CTL_144, BIT(3) << LPI_WAKEUP_EN_SHIFT);
293ee25e6a5SAdrian Alonso /* TODO: Needed ? 2.a DENALI_CTL_144[LPI_TIMER_WAKEUP_F2] */
294ee25e6a5SAdrian Alonso //mmio_setbits_32(IMX_DDRC_BASE + DENALI_CTL_144, BIT(0));
295ee25e6a5SAdrian Alonso
296ee25e6a5SAdrian Alonso /*
297ee25e6a5SAdrian Alonso * 3a. Config SIM_LPAV LPDDR_CTRL[LPDDR_AUTO_LP_MODE_DISABLE] to 1b'0(enable
298ee25e6a5SAdrian Alonso * the logic to automatic handles low power entry/exit. This is the recommended
299ee25e6a5SAdrian Alonso * option over handling through software.
300ee25e6a5SAdrian Alonso * 3b. Config the SIM_LPAV LPDDR_CTRL[SOC_LP_CMD] to 6b'101001(encoding for
301ee25e6a5SAdrian Alonso * self_refresh with both DDR controller and DRAM clock gate. THis is mandatory
302ee25e6a5SAdrian Alonso * since LPPDR logic will be power gated).
303ee25e6a5SAdrian Alonso */
304ee25e6a5SAdrian Alonso mmio_clrbits_32(IMX_LPAV_SIM_BASE + LPDDR_CTRL, LPDDR_AUTO_LP_MODE_DISABLE);
305ee25e6a5SAdrian Alonso mmio_clrsetbits_32(IMX_LPAV_SIM_BASE + LPDDR_CTRL,
306ee25e6a5SAdrian Alonso 0x3f << SOC_LP_CMD_SHIFT, 0x29 << SOC_LP_CMD_SHIFT);
307ee25e6a5SAdrian Alonso /* 3.c clock gate ddr controller */
308ee25e6a5SAdrian Alonso mmio_setbits_32(IMX_LPAV_SIM_BASE + LPDDR_CTRL2, LPDDR_EN_CLKGATE);
309ee25e6a5SAdrian Alonso /* 3.d lpddr max clk div en */
310ee25e6a5SAdrian Alonso mmio_clrbits_32(IMX_LPAV_SIM_BASE + LPDDR_CTRL2, LPDDR_MAX_CLKDIV_EN);
311ee25e6a5SAdrian Alonso }
312ee25e6a5SAdrian Alonso
dram_exit_self_refresh(void)313ee25e6a5SAdrian Alonso void dram_exit_self_refresh(void)
314ee25e6a5SAdrian Alonso {
315ee25e6a5SAdrian Alonso dram_lp_auto_enable();
316ee25e6a5SAdrian Alonso }
317ee25e6a5SAdrian Alonso
dram_enter_retention(void)318478af8d3SJacky Bai void dram_enter_retention(void)
319478af8d3SJacky Bai {
320478af8d3SJacky Bai unsigned int i;
321478af8d3SJacky Bai
322ee25e6a5SAdrian Alonso dram_lp_auto_disable();
323ee25e6a5SAdrian Alonso
324478af8d3SJacky Bai /* 1. config the PCC_LPDDR4[SSADO] to 2b'11 for ACK domain 0/1's STOP */
325478af8d3SJacky Bai mmio_setbits_32(IMX_PCC5_BASE + 0x108, 0x2 << 22);
326478af8d3SJacky Bai
327478af8d3SJacky Bai /*
328478af8d3SJacky Bai * 2. Make sure the DENALI_CTL_144[LPI_WAKEUP_EN[5:0]] has the bit
329478af8d3SJacky Bai * LPI_WAKEUP_EN[3] = 1b'1. This enables the option 'self-refresh
330478af8d3SJacky Bai * long with mem and ctlr clk gating or self-refresh power-down
331478af8d3SJacky Bai * long with mem and ctlr clk gating'
332478af8d3SJacky Bai */
333478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_CTL_144, BIT(3) << LPI_WAKEUP_EN_SHIFT);
334478af8d3SJacky Bai
335478af8d3SJacky Bai /*
336478af8d3SJacky Bai * 3a. Config SIM_LPAV LPDDR_CTRL[LPDDR_AUTO_LP_MODE_DISABLE] to 1b'0(enable
337478af8d3SJacky Bai * the logic to automatic handles low power entry/exit. This is the recommended
338478af8d3SJacky Bai * option over handling through software.
339478af8d3SJacky Bai * 3b. Config the SIM_LPAV LPDDR_CTRL[SOC_LP_CMD] to 6b'101001(encoding for
340478af8d3SJacky Bai * self_refresh with both DDR controller and DRAM clock gate. THis is mandatory
341478af8d3SJacky Bai * since LPPDR logic will be power gated).
342478af8d3SJacky Bai */
343478af8d3SJacky Bai mmio_clrbits_32(IMX_LPAV_SIM_BASE + LPDDR_CTRL, LPDDR_AUTO_LP_MODE_DISABLE);
344478af8d3SJacky Bai mmio_clrsetbits_32(IMX_LPAV_SIM_BASE + LPDDR_CTRL,
345478af8d3SJacky Bai 0x3f << SOC_LP_CMD_SHIFT, 0x29 << SOC_LP_CMD_SHIFT);
346478af8d3SJacky Bai
347478af8d3SJacky Bai /* Save DDR Controller & PHY config.
348478af8d3SJacky Bai * Set PHY_FREQ_SEL_MULTICAST_EN=0 & PHY_FREQ_SEL_INDEX=1. Read and store all
349478af8d3SJacky Bai * the PHY registers for F2 into phy_f1_cfg, then read/store the diff between
350478af8d3SJacky Bai * F1 & F2 into phy_f2_cfg.
351478af8d3SJacky Bai */
352478af8d3SJacky Bai if (!dram_cfg_saved) {
353478af8d3SJacky Bai info = (struct dram_timing_info *)SAVED_DRAM_DATA_BASE;
354478af8d3SJacky Bai dram_timing_cfg = (struct dram_cfg *)(SAVED_DRAM_DATA_BASE +
355478af8d3SJacky Bai sizeof(struct dram_timing_info));
356478af8d3SJacky Bai
357478af8d3SJacky Bai /* get the dram type */
358478af8d3SJacky Bai dram_class = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_00);
359478af8d3SJacky Bai dram_class = (dram_class >> 8) & 0xf;
360478af8d3SJacky Bai
361478af8d3SJacky Bai /* save the ctl registers */
362478af8d3SJacky Bai for (i = 0U; i < CTL_NUM; i++) {
363478af8d3SJacky Bai dram_timing_cfg->ctl_cfg[i] = mmio_read_32(IMX_DDRC_BASE + i * 4);
364478af8d3SJacky Bai }
365478af8d3SJacky Bai dram_timing_cfg->ctl_cfg[0] = dram_timing_cfg->ctl_cfg[0] & 0xFFFFFFFE;
366478af8d3SJacky Bai
367478af8d3SJacky Bai /* save the PI registers */
368478af8d3SJacky Bai for (i = 0U; i < PI_NUM; i++) {
369478af8d3SJacky Bai dram_timing_cfg->pi_cfg[i] = mmio_read_32(IMX_DDRC_BASE + 0x2000 + i * 4);
370478af8d3SJacky Bai }
371478af8d3SJacky Bai dram_timing_cfg->pi_cfg[0] = dram_timing_cfg->pi_cfg[0] & 0xFFFFFFFE;
372478af8d3SJacky Bai
373478af8d3SJacky Bai /*
374478af8d3SJacky Bai * Read and store all PHY registers. full array is a full
375478af8d3SJacky Bai * copy for all the setpoint
376478af8d3SJacky Bai */
377478af8d3SJacky Bai if (dram_class == LPDDR4_TYPE) {
378478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1537, 0x10000);
379478af8d3SJacky Bai for (i = 0U; i < PHY_NUM; i++) {
380478af8d3SJacky Bai /* Make sure MULTICASE is enabled */
381478af8d3SJacky Bai if (i == 1537U) {
382478af8d3SJacky Bai dram_timing_cfg->phy_full[i] = 0x100;
383478af8d3SJacky Bai } else {
384478af8d3SJacky Bai dram_timing_cfg->phy_full[i] = mmio_read_32(IMX_DDRC_BASE + 0x4000 + i * 4);
385478af8d3SJacky Bai }
386478af8d3SJacky Bai }
387478af8d3SJacky Bai
388478af8d3SJacky Bai /*
389478af8d3SJacky Bai * set PHY_FREQ_SEL_MULTICAST_EN=0 & PHY_FREQ_SEL_INDEX=0.
390478af8d3SJacky Bai * Read and store only the diff.
391478af8d3SJacky Bai */
392478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1537, 0x0);
393478af8d3SJacky Bai /* save only the frequency based diff config to save memory */
394478af8d3SJacky Bai for (i = 0U; i < PHY_DIFF_NUM; i++) {
395478af8d3SJacky Bai dram_timing_cfg->phy_diff[i] = mmio_read_32(IMX_DDRC_BASE + 0x4000 +
396478af8d3SJacky Bai freq_specific_reg_array[i] * 4);
397478af8d3SJacky Bai }
398478af8d3SJacky Bai } else {
399478af8d3SJacky Bai /* LPDDR3, only f1 need to save */
400478af8d3SJacky Bai for (i = 0U; i < info->phy_f1_cfg_num; i++) {
401478af8d3SJacky Bai info->phy_f1_cfg[i].val = mmio_read_32(info->phy_f1_cfg[i].reg);
402478af8d3SJacky Bai }
403478af8d3SJacky Bai }
404478af8d3SJacky Bai
405478af8d3SJacky Bai dram_cfg_saved = true;
406478af8d3SJacky Bai }
407478af8d3SJacky Bai }
408478af8d3SJacky Bai
dram_exit_retention(void)409478af8d3SJacky Bai void dram_exit_retention(void)
410478af8d3SJacky Bai {
411478af8d3SJacky Bai uint32_t val;
412478af8d3SJacky Bai
413478af8d3SJacky Bai /* 1. Config the LPAV PLL4 and DDR clock for the desired LPDDR operating frequency. */
414478af8d3SJacky Bai mmio_setbits_32(IMX_PCC5_BASE + 0x108, BIT(30));
415478af8d3SJacky Bai
416478af8d3SJacky Bai /* 2. Write PCC5.PCC_LPDDR4[SWRST] to 1b'1 to release LPDDR from reset. */
417478af8d3SJacky Bai mmio_setbits_32(IMX_PCC5_BASE + 0x108, BIT(28));
418478af8d3SJacky Bai
419478af8d3SJacky Bai /* 3. Reload the LPDDR CTL/PI/PHY register */
420478af8d3SJacky Bai ddr_init();
421478af8d3SJacky Bai
422478af8d3SJacky Bai if (dram_class == LPDDR4_TYPE) {
423478af8d3SJacky Bai /* 4a. FIXME Set PHY_SET_DFI_INPUT_N parameters to 4'h1. LPDDR4 only */
424478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1559, 0x01010101);
425478af8d3SJacky Bai
426478af8d3SJacky Bai /*
427478af8d3SJacky Bai * 4b. CTL PWRUP_SREFRESH_EXIT=1'b0 for disabling self refresh exit
428478af8d3SJacky Bai * from controller.
429478af8d3SJacky Bai */
430478af8d3SJacky Bai /*
431478af8d3SJacky Bai * 4c. PI_PWRUP_SELF_REF_EXIT=1, PI_MC_PWRUP_SELF_REF_EXIT=0 for enabling
432478af8d3SJacky Bai * self refresh exit from PI
433478af8d3SJacky Bai */
434478af8d3SJacky Bai /* 4c. PI_INT_LVL_EN=0 to skip Initialization trainings. */
435478af8d3SJacky Bai /*
436478af8d3SJacky Bai * 4d. PI_WRLVL_EN_F0/1/2= PI_CALVL_EN_F0/1/2= PI_RDLVL_EN_F0/1/2=
437478af8d3SJacky Bai * PI_RDLVL_GATE_EN_F0/1/2= PI_WDQLVL_EN_F0/1/2=0x2.
438478af8d3SJacky Bai * Enable non initialization trainings.
439478af8d3SJacky Bai */
440478af8d3SJacky Bai /* 4e. PI_PWRUP_SREFRESH_EXIT_CS=0xF */
441478af8d3SJacky Bai /* 4f. PI_DLL_RESET=0x1 */
442478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_137, 0x1);
443478af8d3SJacky Bai /* PI_PWRUP_SELF_REF_EXIT = 1 */
444478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_132, 0x01000000);
445478af8d3SJacky Bai /* PI_MC_PWRUP_SELF_REF_EXIT = 0 */
446478af8d3SJacky Bai mmio_clrbits_32(IMX_DDRC_BASE + DENALI_PI_132, BIT(16));
447478af8d3SJacky Bai /* PI_INT_LVL_EN = 0 */
448478af8d3SJacky Bai mmio_clrbits_32(IMX_DDRC_BASE + DENALI_PI_04, BIT(0));
449478af8d3SJacky Bai /* PI_WRLVL_EN_F0 = 3, PI_WRLVL_EN_F1 = 3 */
450478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_174, 0x03030000);
451478af8d3SJacky Bai /* PI_WRLVL_EN_F2 = 3 */
452478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_175, 0x03);
453478af8d3SJacky Bai /* PI_CALVL_EN_F0 = 3, PI_CALVL_EN_F1 = 3 */
454478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_191, 0x03030000);
455478af8d3SJacky Bai /* PI_CALVL_EN_F2 = 3 */
456478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_192, 0x03);
457478af8d3SJacky Bai /* PI_WDQLVL_EN_F0 = 3 */
458478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_212, 0x300);
459478af8d3SJacky Bai /* PI_WDQLVL_EN_F1 = 3 */
460478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_214, 0x03000000);
461478af8d3SJacky Bai /* PI_WDQLVL_EN_F2 = 3 */
462478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_217, 0x300);
463478af8d3SJacky Bai /* PI_EDLVL_EN_F0 = 3, PI_EDLVL_GATE_EN_F0 = 3 */
464478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_181, 0x03030000);
465478af8d3SJacky Bai /*
466478af8d3SJacky Bai * PI_RDLVL_EN_F1 = 3, PI_RDLVL_GATE_EN_F1 = 3,
467478af8d3SJacky Bai * PI_RDLVL_EN_F2 = 3, PI_RDLVL_GATE_EN_F2 = 3
468478af8d3SJacky Bai */
469478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_182, 0x03030303);
470478af8d3SJacky Bai /* PI_PWRUP_SREFRESH_EXIT_CS = 0xF */
471478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_134, 0x000F0000);
472478af8d3SJacky Bai } else {
473478af8d3SJacky Bai /* PI_DLL_RESET=1 */
474478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_137, 0x1);
475478af8d3SJacky Bai /* PI_PWRUP_SELF_REF_EXIT=1 */
476478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_132, 0x01000000);
477478af8d3SJacky Bai /* PI_MC_PWRUP_SELF_REF_EXIT=0 */
478478af8d3SJacky Bai mmio_clrbits_32(IMX_DDRC_BASE + DENALI_PI_132, BIT(16));
479478af8d3SJacky Bai /* PI_INT_LVL_EN=0 */
480478af8d3SJacky Bai mmio_clrbits_32(IMX_DDRC_BASE + DENALI_PI_04, BIT(0));
481478af8d3SJacky Bai /* PI_WRLVL_EN_F0=3 */
482478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_174, 0x00030000);
483478af8d3SJacky Bai /* PI_CALVL_EN_F0=3 */
484478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_191, 0x00030000);
485478af8d3SJacky Bai /* PI_RDLVL_EN_F0=3,PI_RDLVL_GATE_EN_F0=3 */
486478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_181, 0x03030000);
487478af8d3SJacky Bai /* PI_PWRUP_SREFRESH_EXIT_CS=0xF */
488478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_134, 0x000F0000);
489478af8d3SJacky Bai }
490478af8d3SJacky Bai
491478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_144, 0x00002D00);
492478af8d3SJacky Bai
493478af8d3SJacky Bai /* Force in-order AXI read data */
494478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_144, 0x1);
495478af8d3SJacky Bai
496478af8d3SJacky Bai /*
497478af8d3SJacky Bai * Disable special R/W group switches so that R/W group placement
498478af8d3SJacky Bai * is always at END of R/W group.
499478af8d3SJacky Bai */
500478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_249, 0x0);
501478af8d3SJacky Bai
502478af8d3SJacky Bai /* Reduce time for IO pad calibration */
503478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1590, 0x01000000);
504478af8d3SJacky Bai
505478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_25, 0x00020100);
506478af8d3SJacky Bai
507478af8d3SJacky Bai /* PD disable */
508478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_153, 0x04040000);
509478af8d3SJacky Bai /*
510478af8d3SJacky Bai * 5. Disable automatic LP entry and PCPCS modes LP_AUTO_ENTRY_EN
511478af8d3SJacky Bai * to 1b'0, PCPCS_PD_EN to 1b'0
512478af8d3SJacky Bai */
513478af8d3SJacky Bai
514478af8d3SJacky Bai upwr_xcp_set_ddr_retention(APD_DOMAIN, 0, NULL);
515478af8d3SJacky Bai upower_wait_resp();
516478af8d3SJacky Bai
517478af8d3SJacky Bai if (dram_class == LPDDR4_TYPE) {
518478af8d3SJacky Bai /* 7. Write PI START parameter to 1'b1 */
519478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_PI_00, 0x00000b01);
520478af8d3SJacky Bai
521478af8d3SJacky Bai /* 8. Write CTL START parameter to 1'b1 */
522478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_00, 0x00000b01);
523478af8d3SJacky Bai } else {
524478af8d3SJacky Bai /* 7. Write PI START parameter to 1'b1 */
525478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_PI_00, 0x00000701);
526478af8d3SJacky Bai
527478af8d3SJacky Bai /* 8. Write CTL START parameter to 1'b1 */
528478af8d3SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_00, 0x00000701);
529478af8d3SJacky Bai }
530478af8d3SJacky Bai
531478af8d3SJacky Bai /* 9. DENALI_CTL_266: Wait for INT_STATUS_INIT=0x2 */
532478af8d3SJacky Bai do {
533478af8d3SJacky Bai val = (mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_266) >> 8) & 0xFF;
534478af8d3SJacky Bai } while (val != 0x2);
535478af8d3SJacky Bai
536478af8d3SJacky Bai /*
537478af8d3SJacky Bai * 10. Run SW trainings by setting PI_CALVL_REQ,PI_WRLVL_REQ,PI_RDLVL_GATE_REQ,
538478af8d3SJacky Bai * PI_RDLVL_REQ,PI_WDQLVL_REQ(NA for LPDDR3) in same order.
539478af8d3SJacky Bai */
540478af8d3SJacky Bai if (dram_class == LPDDR4_TYPE) {
541478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_52, 0x10000); /* CALVL */
542478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_26, 0x100); /* WRLVL */
543478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_33, 0x10000); /* RDGATE */
544478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_33, 0x100); /* RDQLVL */
545478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_65, 0x10000); /* WDQLVL */
546478af8d3SJacky Bai
547478af8d3SJacky Bai /* 11. Wait for trainings to get complete by polling PI_INT_STATUS */
548478af8d3SJacky Bai while ((mmio_read_32(IMX_DDRC_BASE + DENALI_PI_77) & 0x07E00000) != 0x07E00000) {
549478af8d3SJacky Bai ;
550478af8d3SJacky Bai }
551478af8d3SJacky Bai } else {
552478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_52, 0x10000); /* CALVL */
553478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_26, 0x100); /* WRLVL */
554478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_33, 0x10000); /* RDGATE */
555478af8d3SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_PI_33, 0x100); /* RDQLVL */
556478af8d3SJacky Bai while ((mmio_read_32(IMX_DDRC_BASE + DENALI_PI_77) & 0x05E00000) != 0x05E00000) {
557478af8d3SJacky Bai ;
558478af8d3SJacky Bai }
559478af8d3SJacky Bai }
560ee25e6a5SAdrian Alonso
561ee25e6a5SAdrian Alonso dram_lp_auto_enable();
562478af8d3SJacky Bai }
563caee2733SJacky Bai
564caee2733SJacky Bai #define LPDDR_DONE (0x1<<4)
565caee2733SJacky Bai #define SOC_FREQ_CHG_ACK (0x1<<6)
566caee2733SJacky Bai #define SOC_FREQ_CHG_REQ (0x1<<7)
567caee2733SJacky Bai #define LPI_WAKEUP_EN (0x4<<8)
568caee2733SJacky Bai #define SOC_FREQ_REQ (0x1<<11)
569caee2733SJacky Bai
set_cgc2_ddrclk(uint8_t src,uint8_t div)570caee2733SJacky Bai static void set_cgc2_ddrclk(uint8_t src, uint8_t div)
571caee2733SJacky Bai {
572caee2733SJacky Bai
573caee2733SJacky Bai /* Wait until the reg is unlocked for writing */
574caee2733SJacky Bai while (mmio_read_32(IMX_CGC2_BASE + 0x40) & BIT(31))
575caee2733SJacky Bai ;
576caee2733SJacky Bai
577caee2733SJacky Bai mmio_write_32(IMX_CGC2_BASE + 0x40, (src << 28) | (div << 21));
578caee2733SJacky Bai /* Wait for the clock switching done */
579caee2733SJacky Bai while (!(mmio_read_32(IMX_CGC2_BASE + 0x40) & BIT(27)))
580caee2733SJacky Bai ;
581caee2733SJacky Bai }
set_ddr_clk(uint32_t ddr_freq)582caee2733SJacky Bai static void set_ddr_clk(uint32_t ddr_freq)
583caee2733SJacky Bai {
584caee2733SJacky Bai /* Disable DDR clock */
585caee2733SJacky Bai mmio_clrbits_32(IMX_PCC5_BASE + 0x108, BIT(30));
586caee2733SJacky Bai switch (ddr_freq) {
587caee2733SJacky Bai /* boot frequency ? */
588caee2733SJacky Bai case 48:
589caee2733SJacky Bai set_cgc2_ddrclk(2, 0);
590caee2733SJacky Bai break;
591caee2733SJacky Bai /* default bypass frequency for fsp 1 */
592caee2733SJacky Bai case 192:
593caee2733SJacky Bai set_cgc2_ddrclk(0, 1);
594caee2733SJacky Bai break;
595caee2733SJacky Bai case 384:
596caee2733SJacky Bai set_cgc2_ddrclk(0, 0);
597caee2733SJacky Bai break;
598caee2733SJacky Bai case 264:
599caee2733SJacky Bai set_cgc2_ddrclk(4, 3);
600caee2733SJacky Bai break;
601caee2733SJacky Bai case 528:
602caee2733SJacky Bai set_cgc2_ddrclk(4, 1);
603caee2733SJacky Bai break;
604caee2733SJacky Bai default:
605caee2733SJacky Bai break;
606caee2733SJacky Bai }
607caee2733SJacky Bai /* Enable DDR clock */
608caee2733SJacky Bai mmio_setbits_32(IMX_PCC5_BASE + 0x108, BIT(30));
609caee2733SJacky Bai
610caee2733SJacky Bai /* Wait until the reg is unlocked for writing */
611caee2733SJacky Bai while (mmio_read_32(IMX_CGC2_BASE + 0x40) & BIT(31)) {
612caee2733SJacky Bai ;
613caee2733SJacky Bai }
614caee2733SJacky Bai }
615caee2733SJacky Bai
616caee2733SJacky Bai #define AVD_SIM_LPDDR_CTRL (IMX_LPAV_SIM_BASE + 0x14)
617caee2733SJacky Bai #define AVD_SIM_LPDDR_CTRL2 (IMX_LPAV_SIM_BASE + 0x18)
618caee2733SJacky Bai #define MAX_FSP_NUM U(3)
619caee2733SJacky Bai #define DDR_DFS_GET_FSP_COUNT 0x10
620caee2733SJacky Bai #define DDR_BYPASS_DRATE U(400)
621caee2733SJacky Bai
622416c4433SJacky Bai extern int upower_pmic_i2c_write(uint32_t reg_addr, uint32_t reg_val);
623416c4433SJacky Bai
624caee2733SJacky Bai /* Normally, we only switch frequency between 1(bypass) and 2(highest) */
lpddr4_dfs(uint32_t freq_index)625caee2733SJacky Bai int lpddr4_dfs(uint32_t freq_index)
626caee2733SJacky Bai {
627caee2733SJacky Bai uint32_t lpddr_ctrl, lpddr_ctrl2;
628caee2733SJacky Bai uint32_t ddr_ctl_144;
629caee2733SJacky Bai
630caee2733SJacky Bai /*
631caee2733SJacky Bai * Valid index: 0 to 2
632caee2733SJacky Bai * index 0: boot frequency
633caee2733SJacky Bai * index 1: bypass frequency
634caee2733SJacky Bai * index 2: highest frequency
635caee2733SJacky Bai */
636caee2733SJacky Bai if (freq_index > 2U) {
637caee2733SJacky Bai return -1;
638caee2733SJacky Bai }
639caee2733SJacky Bai
640416c4433SJacky Bai /*
641416c4433SJacky Bai * increase the voltage to 1.1V firstly before increase frequency
642416c4433SJacky Bai * and APD enter OD mode
643416c4433SJacky Bai */
644416c4433SJacky Bai if (freq_index == 2U && sys_dvfs) {
645416c4433SJacky Bai upower_pmic_i2c_write(0x22, 0x28);
646416c4433SJacky Bai }
647416c4433SJacky Bai
648caee2733SJacky Bai /* Enable LPI_WAKEUP_EN */
649caee2733SJacky Bai ddr_ctl_144 = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_144);
650caee2733SJacky Bai mmio_setbits_32(IMX_DDRC_BASE + DENALI_CTL_144, LPI_WAKEUP_EN);
651caee2733SJacky Bai
652caee2733SJacky Bai /* put DRAM into long self-refresh & clock gating */
653caee2733SJacky Bai lpddr_ctrl = mmio_read_32(AVD_SIM_LPDDR_CTRL);
654caee2733SJacky Bai lpddr_ctrl = (lpddr_ctrl & ~((0x3f << 15) | (0x3 << 9))) | (0x28 << 15) | (freq_index << 9);
655caee2733SJacky Bai mmio_write_32(AVD_SIM_LPDDR_CTRL, lpddr_ctrl);
656caee2733SJacky Bai
657caee2733SJacky Bai /* Gating the clock */
658caee2733SJacky Bai lpddr_ctrl2 = mmio_read_32(AVD_SIM_LPDDR_CTRL2);
659caee2733SJacky Bai mmio_setbits_32(AVD_SIM_LPDDR_CTRL2, LPDDR_EN_CLKGATE);
660caee2733SJacky Bai
661caee2733SJacky Bai /* Request frequency change */
662caee2733SJacky Bai mmio_setbits_32(AVD_SIM_LPDDR_CTRL, SOC_FREQ_REQ);
663caee2733SJacky Bai
664caee2733SJacky Bai do {
665caee2733SJacky Bai lpddr_ctrl = mmio_read_32(AVD_SIM_LPDDR_CTRL);
666caee2733SJacky Bai if (lpddr_ctrl & SOC_FREQ_CHG_REQ) {
667caee2733SJacky Bai /* Bypass mode */
668caee2733SJacky Bai if (info->fsp_table[freq_index] < DDR_BYPASS_DRATE) {
669caee2733SJacky Bai /* Change to PLL bypass mode */
670caee2733SJacky Bai mmio_write_32(IMX_LPAV_SIM_BASE, 0x1);
671caee2733SJacky Bai /* change the ddr clock source & frequency */
672caee2733SJacky Bai set_ddr_clk(info->fsp_table[freq_index]);
673caee2733SJacky Bai } else {
674caee2733SJacky Bai /* Change to PLL unbypass mode */
675caee2733SJacky Bai mmio_write_32(IMX_LPAV_SIM_BASE, 0x0);
676caee2733SJacky Bai /* change the ddr clock source & frequency */
677caee2733SJacky Bai set_ddr_clk(info->fsp_table[freq_index] >> 1);
678caee2733SJacky Bai }
679caee2733SJacky Bai
680caee2733SJacky Bai mmio_clrsetbits_32(AVD_SIM_LPDDR_CTRL, SOC_FREQ_CHG_REQ, SOC_FREQ_CHG_ACK);
681caee2733SJacky Bai continue;
682caee2733SJacky Bai }
683caee2733SJacky Bai } while ((lpddr_ctrl & LPDDR_DONE) != 0); /* several try? */
684caee2733SJacky Bai
685caee2733SJacky Bai /* restore the original setting */
686caee2733SJacky Bai mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_144, ddr_ctl_144);
687caee2733SJacky Bai mmio_write_32(AVD_SIM_LPDDR_CTRL2, lpddr_ctrl2);
688caee2733SJacky Bai
689caee2733SJacky Bai /* Check the DFS result */
690caee2733SJacky Bai lpddr_ctrl = mmio_read_32(AVD_SIM_LPDDR_CTRL) & 0xF;
691caee2733SJacky Bai if (lpddr_ctrl != 0U) {
692caee2733SJacky Bai /* Must be something wrong, return failure */
693caee2733SJacky Bai return -1;
694caee2733SJacky Bai }
695caee2733SJacky Bai
696416c4433SJacky Bai /* decrease the BUCK3 voltage after frequency changed to lower
697416c4433SJacky Bai * and APD in ND_MODE
698416c4433SJacky Bai */
699416c4433SJacky Bai if (freq_index == 1U && sys_dvfs) {
700416c4433SJacky Bai upower_pmic_i2c_write(0x22, 0x20);
701416c4433SJacky Bai }
702416c4433SJacky Bai
703caee2733SJacky Bai /* DFS done successfully */
704caee2733SJacky Bai return 0;
705caee2733SJacky Bai }
706caee2733SJacky Bai
707caee2733SJacky Bai /* for the non-primary core, waiting for DFS done */
waiting_dvfs(uint32_t id,uint32_t flags,void * handle,void * cookie)708caee2733SJacky Bai static uint64_t waiting_dvfs(uint32_t id, uint32_t flags,
709caee2733SJacky Bai void *handle, void *cookie)
710caee2733SJacky Bai {
711caee2733SJacky Bai uint32_t irq;
712caee2733SJacky Bai
713caee2733SJacky Bai irq = plat_ic_acknowledge_interrupt();
714caee2733SJacky Bai if (irq < 1022U) {
715caee2733SJacky Bai plat_ic_end_of_interrupt(irq);
716caee2733SJacky Bai }
717caee2733SJacky Bai
718caee2733SJacky Bai /* set the WFE done status */
719caee2733SJacky Bai spin_lock(&dfs_lock);
720caee2733SJacky Bai core_count++;
721caee2733SJacky Bai dsb();
722caee2733SJacky Bai spin_unlock(&dfs_lock);
723caee2733SJacky Bai
724caee2733SJacky Bai while (in_progress) {
725caee2733SJacky Bai wfe();
726caee2733SJacky Bai }
727caee2733SJacky Bai
728caee2733SJacky Bai return 0;
729caee2733SJacky Bai }
730caee2733SJacky Bai
dram_dvfs_handler(uint32_t smc_fid,void * handle,u_register_t x1,u_register_t x2,u_register_t x3)731caee2733SJacky Bai int dram_dvfs_handler(uint32_t smc_fid, void *handle,
732caee2733SJacky Bai u_register_t x1, u_register_t x2, u_register_t x3)
733caee2733SJacky Bai {
734caee2733SJacky Bai unsigned int fsp_index = x1;
735caee2733SJacky Bai uint32_t online_cpus = x2 - 1;
736caee2733SJacky Bai uint64_t mpidr = read_mpidr_el1();
737caee2733SJacky Bai unsigned int cpu_id = MPIDR_AFFLVL0_VAL(mpidr);
738caee2733SJacky Bai
739caee2733SJacky Bai /* Get the number of FSPs */
740caee2733SJacky Bai if (x1 == DDR_DFS_GET_FSP_COUNT) {
741caee2733SJacky Bai SMC_RET2(handle, num_fsp, info->fsp_table[1]);
742caee2733SJacky Bai }
743caee2733SJacky Bai
744caee2733SJacky Bai /* start lpddr frequency scaling */
745caee2733SJacky Bai in_progress = true;
746416c4433SJacky Bai sys_dvfs = x3 ? true : false;
747caee2733SJacky Bai dsb();
748caee2733SJacky Bai
749caee2733SJacky Bai /* notify other core wait for scaling done */
750caee2733SJacky Bai for (unsigned int i = 0; i < PLATFORM_CORE_COUNT; i++)
751caee2733SJacky Bai /* Skip raise SGI for current CPU */
752caee2733SJacky Bai if (i != cpu_id) {
753caee2733SJacky Bai plat_ic_raise_el3_sgi(0x8, i);
754caee2733SJacky Bai }
755caee2733SJacky Bai
756caee2733SJacky Bai /* Make sure all the cpu in WFE */
757caee2733SJacky Bai while (online_cpus != core_count) {
758caee2733SJacky Bai ;
759caee2733SJacky Bai }
760caee2733SJacky Bai
761caee2733SJacky Bai /* Flush the L1/L2 cache */
762caee2733SJacky Bai dcsw_op_all(DCCSW);
763caee2733SJacky Bai
764caee2733SJacky Bai lpddr4_dfs(fsp_index);
765caee2733SJacky Bai
766caee2733SJacky Bai in_progress = false;
767caee2733SJacky Bai core_count = 0;
768caee2733SJacky Bai dsb();
769caee2733SJacky Bai sev();
770caee2733SJacky Bai isb();
771caee2733SJacky Bai
772caee2733SJacky Bai SMC_RET1(handle, 0);
773caee2733SJacky Bai }
774caee2733SJacky Bai
dram_init(void)775caee2733SJacky Bai void dram_init(void)
776caee2733SJacky Bai {
777caee2733SJacky Bai uint32_t flags = 0;
778caee2733SJacky Bai uint32_t rc;
779caee2733SJacky Bai unsigned int i;
780caee2733SJacky Bai
781caee2733SJacky Bai /* Register the EL3 handler for DDR DVFS */
782caee2733SJacky Bai set_interrupt_rm_flag(flags, NON_SECURE);
783caee2733SJacky Bai rc = register_interrupt_type_handler(INTR_TYPE_EL3, waiting_dvfs, flags);
784caee2733SJacky Bai if (rc) {
785caee2733SJacky Bai panic();
786caee2733SJacky Bai }
787caee2733SJacky Bai
788caee2733SJacky Bai info = (struct dram_timing_info *)SAVED_DRAM_DATA_BASE;
789caee2733SJacky Bai
790caee2733SJacky Bai /* Get the num of the supported Fsp */
791caee2733SJacky Bai for (i = 0; i < MAX_FSP_NUM; i++) {
792caee2733SJacky Bai if (!info->fsp_table[i]) {
793caee2733SJacky Bai break;
794caee2733SJacky Bai }
795caee2733SJacky Bai }
796caee2733SJacky Bai
797caee2733SJacky Bai num_fsp = (i > MAX_FSP_NUM) ? MAX_FSP_NUM : i;
798caee2733SJacky Bai }
799