xref: /rk3399_ARM-atf/drivers/nxp/ddr/s32cc/ddr_utils.c (revision bded46a86f2415b0536fb1aee75e8ab30eb00872)
130c8a20dSKhristine Andreea Barbulescu /*
2*a60aeae7SKhristine Andreea Barbulescu  * Copyright 2020-2026 NXP
330c8a20dSKhristine Andreea Barbulescu  *
430c8a20dSKhristine Andreea Barbulescu  * SPDX-License-Identifier: BSD-3-Clause
530c8a20dSKhristine Andreea Barbulescu  */
630c8a20dSKhristine Andreea Barbulescu #include <errno.h>
730c8a20dSKhristine Andreea Barbulescu 
8a4efd428SKhristine Andreea Barbulescu #include <assert.h>
930c8a20dSKhristine Andreea Barbulescu #include <common/debug.h>
1030c8a20dSKhristine Andreea Barbulescu #include <ddr_utils.h>
1130c8a20dSKhristine Andreea Barbulescu #include <mmio_poll.h>
12717ab11bSKhristine Andreea Barbulescu #include <s32cc-clk-drv.h>
1330c8a20dSKhristine Andreea Barbulescu 
14*a60aeae7SKhristine Andreea Barbulescu static uint32_t enable_axi_ports(void);
1530c8a20dSKhristine Andreea Barbulescu static uint32_t get_mail(uint32_t *mail);
1630c8a20dSKhristine Andreea Barbulescu static uint32_t ack_mail(void);
17a4efd428SKhristine Andreea Barbulescu static uint8_t get_max_cdd(const uint32_t cdd_addr[], size_t size);
18a4efd428SKhristine Andreea Barbulescu static uint16_t get_max_delay(const uint32_t delay_addr[], size_t size);
19a4efd428SKhristine Andreea Barbulescu static uint8_t get_avg_vref(const uint32_t vref_addr[], size_t size);
20*a60aeae7SKhristine Andreea Barbulescu static uint32_t adjust_ddrc_config(void);
21a4efd428SKhristine Andreea Barbulescu static bool is_lpddr4(void);
22a4efd428SKhristine Andreea Barbulescu 
23a4efd428SKhristine Andreea Barbulescu static struct space_timing_params tr_res = {
24a4efd428SKhristine Andreea Barbulescu 		.cdd = {.rr = 0, .rw = 0, .wr = 0, .ww = 0},
25a4efd428SKhristine Andreea Barbulescu 		.vref_ca = 0,
26a4efd428SKhristine Andreea Barbulescu 		.vref_dq = 0,
27a4efd428SKhristine Andreea Barbulescu 		.tphy_wrdata_delay = 0
28a4efd428SKhristine Andreea Barbulescu };
29a4efd428SKhristine Andreea Barbulescu 
30a4efd428SKhristine Andreea Barbulescu /* Modify bitfield value with delta, given bitfield position and mask */
update_bf(uint32_t * v,uint8_t pos,uint32_t mask,int32_t delta)31a4efd428SKhristine Andreea Barbulescu bool update_bf(uint32_t *v, uint8_t pos, uint32_t mask, int32_t delta)
32a4efd428SKhristine Andreea Barbulescu {
33a4efd428SKhristine Andreea Barbulescu 	uint32_t bf_val;
34a4efd428SKhristine Andreea Barbulescu 	int64_t new_val;
35a4efd428SKhristine Andreea Barbulescu 
36a4efd428SKhristine Andreea Barbulescu 	bf_val = (*v >> pos) & mask;
37a4efd428SKhristine Andreea Barbulescu 	new_val = (int64_t)bf_val + delta;
38a4efd428SKhristine Andreea Barbulescu 
39a4efd428SKhristine Andreea Barbulescu     /* Check if new value is within valid range [0, mask] */
40a4efd428SKhristine Andreea Barbulescu 	if ((new_val < 0) || (new_val > (int64_t)mask)) {
41a4efd428SKhristine Andreea Barbulescu 		return false;
42a4efd428SKhristine Andreea Barbulescu 	}
43a4efd428SKhristine Andreea Barbulescu 
44a4efd428SKhristine Andreea Barbulescu 	*v = (*v & ~(mask << pos)) | ((uint32_t)new_val << pos);
45a4efd428SKhristine Andreea Barbulescu 	return true;
46a4efd428SKhristine Andreea Barbulescu }
47a4efd428SKhristine Andreea Barbulescu 
48a4efd428SKhristine Andreea Barbulescu /* Sets default AXI parity. */
set_axi_parity(void)49a4efd428SKhristine Andreea Barbulescu uint32_t set_axi_parity(void)
50a4efd428SKhristine Andreea Barbulescu {
51a4efd428SKhristine Andreea Barbulescu 	uint32_t swstat_reg, timeout = DEFAULT_TIMEOUT_US;
52a4efd428SKhristine Andreea Barbulescu 	int err;
53a4efd428SKhristine Andreea Barbulescu 
54a4efd428SKhristine Andreea Barbulescu 	/* Enable Parity For All AXI Interfaces */
55a4efd428SKhristine Andreea Barbulescu 	mmio_setbits_32(DDR_SS_REG, DDR_SS_AXI_PARITY_ENABLE_MASK);
56a4efd428SKhristine Andreea Barbulescu 
57a4efd428SKhristine Andreea Barbulescu 	/* Set AXI_PARITY_TYPE to 0x1ff;   0-even, 1-odd */
58a4efd428SKhristine Andreea Barbulescu 	mmio_setbits_32(DDR_SS_REG, DDR_SS_AXI_PARITY_TYPE_MASK);
59a4efd428SKhristine Andreea Barbulescu 
60a4efd428SKhristine Andreea Barbulescu 	/* For LPDDR4 Set DFI1_ENABLED to 0x1 */
61a4efd428SKhristine Andreea Barbulescu 	if (is_lpddr4()) {
62a4efd428SKhristine Andreea Barbulescu 		mmio_setbits_32(DDR_SS_REG, DDR_SS_DFI_1_ENABLED);
63a4efd428SKhristine Andreea Barbulescu 	}
64a4efd428SKhristine Andreea Barbulescu 
65717ab11bSKhristine Andreea Barbulescu 	if (plat_deassert_ddr_reset() != 0) {
66717ab11bSKhristine Andreea Barbulescu 		return DEASSERT_FAILED;
67717ab11bSKhristine Andreea Barbulescu 	}
68717ab11bSKhristine Andreea Barbulescu 
69a4efd428SKhristine Andreea Barbulescu 	/* Enable HIF, CAM Queueing */
70a4efd428SKhristine Andreea Barbulescu 	mmio_write_32(DDRC_BASE + OFFSET_DDRC_DBG1, DBG1_DISABLE_DE_QUEUEING);
71a4efd428SKhristine Andreea Barbulescu 
72a4efd428SKhristine Andreea Barbulescu 	/* Disable auto-refresh: RFSHCTL3.dis_auto_refresh = 1 */
73a4efd428SKhristine Andreea Barbulescu 	mmio_setbits_32(DDRC_BASE + OFFSET_DDRC_RFSHCTL3, RFSHCTL3_DISABLE_AUTO_REFRESH);
74a4efd428SKhristine Andreea Barbulescu 
75a4efd428SKhristine Andreea Barbulescu 	/* Disable power down: PWRCTL.powerdown_en = 0 */
76a4efd428SKhristine Andreea Barbulescu 	mmio_clrbits_32(DDRC_BASE + OFFSET_DDRC_PWRCTL, PWRCTL_POWER_DOWN_ENABLE_MASK);
77a4efd428SKhristine Andreea Barbulescu 
78a4efd428SKhristine Andreea Barbulescu 	/* Disable self-refresh: PWRCTL.selfref_en = 0 */
79a4efd428SKhristine Andreea Barbulescu 	mmio_clrbits_32(DDRC_BASE + OFFSET_DDRC_PWRCTL, PWRCTL_SELF_REFRESH_ENABLE_MASK);
80a4efd428SKhristine Andreea Barbulescu 
81a4efd428SKhristine Andreea Barbulescu 	/*
82a4efd428SKhristine Andreea Barbulescu 	 * Disable assertion of dfi_dram_clk_disable:
83a4efd428SKhristine Andreea Barbulescu 	 * PWRTL.en_dfi_dram_clk_disable = 0
84a4efd428SKhristine Andreea Barbulescu 	 */
85a4efd428SKhristine Andreea Barbulescu 	mmio_clrbits_32(DDRC_BASE + OFFSET_DDRC_PWRCTL, PWRCTL_EN_DFI_DRAM_CLOCK_DIS_MASK);
86a4efd428SKhristine Andreea Barbulescu 
87a4efd428SKhristine Andreea Barbulescu 	/* Enable Quasi-Dynamic Programming */
88a4efd428SKhristine Andreea Barbulescu 	mmio_write_32(DDRC_BASE + OFFSET_DDRC_SWCTL, SWCTL_SWDONE_ENABLE);
89a4efd428SKhristine Andreea Barbulescu 
90a4efd428SKhristine Andreea Barbulescu 	/* Confirm Register Programming Done Ack is Cleared */
91a4efd428SKhristine Andreea Barbulescu 	err = mmio_read_32_poll_timeout(DDRC_BASE + OFFSET_DDRC_SWSTAT, swstat_reg,
92a4efd428SKhristine Andreea Barbulescu 					(swstat_reg & SWSTAT_SWDONE_ACK_MASK) != SWSTAT_SW_DONE,
93a4efd428SKhristine Andreea Barbulescu 					timeout);
94a4efd428SKhristine Andreea Barbulescu 	if (err != 0) {
95a4efd428SKhristine Andreea Barbulescu 		ERROR("Failed to clear register programming done ACK\n");
96a4efd428SKhristine Andreea Barbulescu 		return TIMEOUT_ERR;
97a4efd428SKhristine Andreea Barbulescu 	}
98a4efd428SKhristine Andreea Barbulescu 
99a4efd428SKhristine Andreea Barbulescu 	/* DFI_INIT_COMPLETE_EN set to 0 */
100a4efd428SKhristine Andreea Barbulescu 	mmio_clrbits_32(DDRC_BASE + OFFSET_DDRC_DFIMISC, DFIMISC_DFI_INIT_COMPLETE_EN_MASK);
101a4efd428SKhristine Andreea Barbulescu 
102a4efd428SKhristine Andreea Barbulescu 	/* Set SWCTL.sw_done to 1 */
103a4efd428SKhristine Andreea Barbulescu 	mmio_write_32(DDRC_BASE + OFFSET_DDRC_SWCTL, SWCTL_SWDONE_DONE);
104a4efd428SKhristine Andreea Barbulescu 
105a4efd428SKhristine Andreea Barbulescu 	err = mmio_read_32_poll_timeout(DDRC_BASE + OFFSET_DDRC_SWSTAT, swstat_reg,
106a4efd428SKhristine Andreea Barbulescu 					(swstat_reg & SWSTAT_SWDONE_ACK_MASK) != SWSTAT_SW_NOT_DONE,
107a4efd428SKhristine Andreea Barbulescu 					timeout);
108a4efd428SKhristine Andreea Barbulescu 	if (err != 0) {
109a4efd428SKhristine Andreea Barbulescu 		ERROR("Failed to confirm DDRC SWSTAT switch done ACK\n");
110a4efd428SKhristine Andreea Barbulescu 		return TIMEOUT_ERR;
111a4efd428SKhristine Andreea Barbulescu 	}
112a4efd428SKhristine Andreea Barbulescu 
113a4efd428SKhristine Andreea Barbulescu 	return NO_ERR;
114a4efd428SKhristine Andreea Barbulescu }
11530c8a20dSKhristine Andreea Barbulescu 
116*a60aeae7SKhristine Andreea Barbulescu /* Enables AXI port n. Programming Mode: Dynamic */
enable_axi_ports(void)117*a60aeae7SKhristine Andreea Barbulescu static uint32_t enable_axi_ports(void)
118*a60aeae7SKhristine Andreea Barbulescu {
119*a60aeae7SKhristine Andreea Barbulescu 	/* Port 0 Control Register */
120*a60aeae7SKhristine Andreea Barbulescu 	mmio_write_32(DDRC_UMCTL2_MP_BASE + OFFSET_DDRC_PCTRL_0, ENABLE_AXI_PORT);
121*a60aeae7SKhristine Andreea Barbulescu 	/* Port 1 Control Register */
122*a60aeae7SKhristine Andreea Barbulescu 	mmio_write_32(DDRC_UMCTL2_MP_BASE + OFFSET_DDRC_PCTRL_1, ENABLE_AXI_PORT);
123*a60aeae7SKhristine Andreea Barbulescu 	/* Port 2 Control Register */
124*a60aeae7SKhristine Andreea Barbulescu 	mmio_write_32(DDRC_UMCTL2_MP_BASE + OFFSET_DDRC_PCTRL_2, ENABLE_AXI_PORT);
125*a60aeae7SKhristine Andreea Barbulescu 
126*a60aeae7SKhristine Andreea Barbulescu 	return NO_ERR;
127*a60aeae7SKhristine Andreea Barbulescu }
128*a60aeae7SKhristine Andreea Barbulescu 
129*a60aeae7SKhristine Andreea Barbulescu /*
130*a60aeae7SKhristine Andreea Barbulescu  * Post PHY training setup - complementary settings that need to be
131*a60aeae7SKhristine Andreea Barbulescu  * performed after running the firmware.
132*a60aeae7SKhristine Andreea Barbulescu  * @param options - various flags controlling post training actions
133*a60aeae7SKhristine Andreea Barbulescu  */
post_train_setup(uint8_t options)134*a60aeae7SKhristine Andreea Barbulescu uint32_t post_train_setup(uint8_t options)
135*a60aeae7SKhristine Andreea Barbulescu {
136*a60aeae7SKhristine Andreea Barbulescu 	uint32_t calbusy_reg, swstat_reg, swctl_reg, phymstr_reg;
137*a60aeae7SKhristine Andreea Barbulescu 	uint32_t umctl2_reg, dfistat_reg;
138*a60aeae7SKhristine Andreea Barbulescu 	uint32_t ret = NO_ERR, timeout = DEFAULT_TIMEOUT_US;
139*a60aeae7SKhristine Andreea Barbulescu 	int err;
140*a60aeae7SKhristine Andreea Barbulescu 
141*a60aeae7SKhristine Andreea Barbulescu 	/*
142*a60aeae7SKhristine Andreea Barbulescu 	 * CalBusy.0 = 1, indicates the calibrator is actively calibrating.
143*a60aeae7SKhristine Andreea Barbulescu 	 * Wait Calibrating done.
144*a60aeae7SKhristine Andreea Barbulescu 	 */
145*a60aeae7SKhristine Andreea Barbulescu 	err = mmio_read_32_poll_timeout(DDR_PHYA_MASTER0_CALBUSY, calbusy_reg,
146*a60aeae7SKhristine Andreea Barbulescu 				  (calbusy_reg & MASTER0_CAL_ACTIVE) == MASTER0_CAL_DONE,
147*a60aeae7SKhristine Andreea Barbulescu 				  timeout);
148*a60aeae7SKhristine Andreea Barbulescu 	if (err != 0) {
149*a60aeae7SKhristine Andreea Barbulescu 		ERROR("PHY Master0 calibrator did not complete\n");
150*a60aeae7SKhristine Andreea Barbulescu 		return TIMEOUT_ERR;
151*a60aeae7SKhristine Andreea Barbulescu 	}
152*a60aeae7SKhristine Andreea Barbulescu 
153*a60aeae7SKhristine Andreea Barbulescu 	/* Set SWCTL.sw_done to 0 */
154*a60aeae7SKhristine Andreea Barbulescu 	mmio_write_32(DDRC_BASE + OFFSET_DDRC_SWCTL, SWCTL_SWDONE_ENABLE);
155*a60aeae7SKhristine Andreea Barbulescu 	err = mmio_read_32_poll_timeout(DDRC_BASE + OFFSET_DDRC_SWSTAT, swctl_reg,
156*a60aeae7SKhristine Andreea Barbulescu 				  (swctl_reg & SWSTAT_SWDONE_ACK_MASK) == SWSTAT_SW_NOT_DONE,
157*a60aeae7SKhristine Andreea Barbulescu 				  timeout);
158*a60aeae7SKhristine Andreea Barbulescu 	if (err != 0) {
159*a60aeae7SKhristine Andreea Barbulescu 		ERROR("Failed to clear register DDRC SWCTL.sw_done\n");
160*a60aeae7SKhristine Andreea Barbulescu 		return TIMEOUT_ERR;
161*a60aeae7SKhristine Andreea Barbulescu 	}
162*a60aeae7SKhristine Andreea Barbulescu 
163*a60aeae7SKhristine Andreea Barbulescu 	/* Disable PHY Master. */
164*a60aeae7SKhristine Andreea Barbulescu 	mmio_clrbits_32(DDRC_BASE + OFFSET_DFIPHYMSTR, DFIPHYMSTR_ENABLE);
165*a60aeae7SKhristine Andreea Barbulescu 
166*a60aeae7SKhristine Andreea Barbulescu 	/* Wait for PHY Master to be disabled. */
167*a60aeae7SKhristine Andreea Barbulescu 	err = mmio_read_32_poll_timeout(DDRC_BASE + OFFSET_DFIPHYMSTR, phymstr_reg,
168*a60aeae7SKhristine Andreea Barbulescu 				  (phymstr_reg & DFIPHYMSTR_ENABLE) == DFIPHYMSTR_DISABLED,
169*a60aeae7SKhristine Andreea Barbulescu 				  timeout);
170*a60aeae7SKhristine Andreea Barbulescu 	if (err != 0) {
171*a60aeae7SKhristine Andreea Barbulescu 		ERROR("Failed tO disable PHY Master\n");
172*a60aeae7SKhristine Andreea Barbulescu 		return TIMEOUT_ERR;
173*a60aeae7SKhristine Andreea Barbulescu 	}
174*a60aeae7SKhristine Andreea Barbulescu 
175*a60aeae7SKhristine Andreea Barbulescu 	/* Wait for PHY Master request to be finished. */
176*a60aeae7SKhristine Andreea Barbulescu 	err = mmio_read_32_poll_timeout(DDRC_BASE + OFFSET_DDRC_STAT, phymstr_reg,
177*a60aeae7SKhristine Andreea Barbulescu 				  (((phymstr_reg & SELFREF_TYPE_MASK) >> SELFREF_TYPE_POS)
178*a60aeae7SKhristine Andreea Barbulescu 				  != PHY_MASTER_REQUEST),
179*a60aeae7SKhristine Andreea Barbulescu 				  timeout);
180*a60aeae7SKhristine Andreea Barbulescu 	if (err != 0) {
181*a60aeae7SKhristine Andreea Barbulescu 		ERROR("Failed to finish PHY Master request\n");
182*a60aeae7SKhristine Andreea Barbulescu 		return TIMEOUT_ERR;
183*a60aeae7SKhristine Andreea Barbulescu 	}
184*a60aeae7SKhristine Andreea Barbulescu 
185*a60aeae7SKhristine Andreea Barbulescu 	/* Set DFIMISC.dfi_init_start to 1*/
186*a60aeae7SKhristine Andreea Barbulescu 	mmio_setbits_32(DDRC_BASE + OFFSET_DDRC_DFIMISC, DFIMISC_DFI_INIT_START_MASK);
187*a60aeae7SKhristine Andreea Barbulescu 
188*a60aeae7SKhristine Andreea Barbulescu 	/* Set SWCTL.sw_done to 1 */
189*a60aeae7SKhristine Andreea Barbulescu 	mmio_write_32(DDRC_BASE + OFFSET_DDRC_SWCTL, SWCTL_SWDONE_DONE);
190*a60aeae7SKhristine Andreea Barbulescu 
191*a60aeae7SKhristine Andreea Barbulescu 	/* Wait SWSTAT.sw_done_ack to 1*/
192*a60aeae7SKhristine Andreea Barbulescu 	err = mmio_read_32_poll_timeout(DDRC_BASE + OFFSET_DDRC_SWSTAT, swstat_reg,
193*a60aeae7SKhristine Andreea Barbulescu 				  (swstat_reg & SWSTAT_SWDONE_ACK_MASK) != SWSTAT_SW_NOT_DONE,
194*a60aeae7SKhristine Andreea Barbulescu 				  timeout);
195*a60aeae7SKhristine Andreea Barbulescu 	if (err != 0) {
196*a60aeae7SKhristine Andreea Barbulescu 		ERROR("Failed to wait for SWSTAT.sw_done\n");
197*a60aeae7SKhristine Andreea Barbulescu 		return TIMEOUT_ERR;
198*a60aeae7SKhristine Andreea Barbulescu 	}
199*a60aeae7SKhristine Andreea Barbulescu 
200*a60aeae7SKhristine Andreea Barbulescu 	/* Wait DFISTAT.dfi_init_complete to 1 */
201*a60aeae7SKhristine Andreea Barbulescu 	err = mmio_read_32_poll_timeout(DDRC_BASE + OFFSET_DDRC_DFISTAT, dfistat_reg,
202*a60aeae7SKhristine Andreea Barbulescu 				  (dfistat_reg & DFISTAT_DFI_INIT_DONE) != DFISTAT_DFI_INIT_INCOMPLETE,
203*a60aeae7SKhristine Andreea Barbulescu 				  timeout);
204*a60aeae7SKhristine Andreea Barbulescu 	if (err != 0) {
205*a60aeae7SKhristine Andreea Barbulescu 		ERROR("DDRC DFI initialization not complete\n");
206*a60aeae7SKhristine Andreea Barbulescu 		return TIMEOUT_ERR;
207*a60aeae7SKhristine Andreea Barbulescu 	}
208*a60aeae7SKhristine Andreea Barbulescu 
209*a60aeae7SKhristine Andreea Barbulescu 	/* Set SWCTL.sw_done to 0 */
210*a60aeae7SKhristine Andreea Barbulescu 	mmio_write_32(DDRC_BASE + OFFSET_DDRC_SWCTL, SWCTL_SWDONE_ENABLE);
211*a60aeae7SKhristine Andreea Barbulescu 	err = mmio_read_32_poll_timeout(DDRC_BASE + OFFSET_DDRC_SWSTAT, swctl_reg,
212*a60aeae7SKhristine Andreea Barbulescu 				  (swctl_reg & SWSTAT_SWDONE_ACK_MASK) == SWSTAT_SW_NOT_DONE,
213*a60aeae7SKhristine Andreea Barbulescu 				  timeout);
214*a60aeae7SKhristine Andreea Barbulescu 	if (err != 0) {
215*a60aeae7SKhristine Andreea Barbulescu 		ERROR("Failed to clear register DDRC SWCTL.sw_done\n");
216*a60aeae7SKhristine Andreea Barbulescu 		return TIMEOUT_ERR;
217*a60aeae7SKhristine Andreea Barbulescu 	}
218*a60aeae7SKhristine Andreea Barbulescu 
219*a60aeae7SKhristine Andreea Barbulescu 	/* Set dfi_init_start to 0 */
220*a60aeae7SKhristine Andreea Barbulescu 	mmio_clrbits_32(DDRC_BASE + OFFSET_DDRC_DFIMISC, DFIMISC_DFI_INIT_START_MASK);
221*a60aeae7SKhristine Andreea Barbulescu 
222*a60aeae7SKhristine Andreea Barbulescu 	/* Enable PHY Master. */
223*a60aeae7SKhristine Andreea Barbulescu 	mmio_setbits_32(DDRC_BASE + OFFSET_DFIPHYMSTR, DFIPHYMSTR_ENABLE);
224*a60aeae7SKhristine Andreea Barbulescu 
225*a60aeae7SKhristine Andreea Barbulescu 	/* Wait for PHY Master to be enabled. */
226*a60aeae7SKhristine Andreea Barbulescu 	err = mmio_read_32_poll_timeout(DDRC_BASE + OFFSET_DFIPHYMSTR, phymstr_reg,
227*a60aeae7SKhristine Andreea Barbulescu 				  (phymstr_reg & DFIPHYMSTR_ENABLE) == DFIPHYMSTR_ENABLE,
228*a60aeae7SKhristine Andreea Barbulescu 				  timeout);
229*a60aeae7SKhristine Andreea Barbulescu 	if (err != 0) {
230*a60aeae7SKhristine Andreea Barbulescu 		ERROR("Failed to enable PHY Master\n");
231*a60aeae7SKhristine Andreea Barbulescu 		return TIMEOUT_ERR;
232*a60aeae7SKhristine Andreea Barbulescu 	}
233*a60aeae7SKhristine Andreea Barbulescu 
234*a60aeae7SKhristine Andreea Barbulescu 	if ((options & ADJUST_DDRC_MASK) != ADJUST_DDRC_DISABLED) {
235*a60aeae7SKhristine Andreea Barbulescu 		/* Overwrite DDRC register based on post training_results */
236*a60aeae7SKhristine Andreea Barbulescu 		ret = adjust_ddrc_config();
237*a60aeae7SKhristine Andreea Barbulescu 		if (ret != NO_ERR) {
238*a60aeae7SKhristine Andreea Barbulescu 			return ret;
239*a60aeae7SKhristine Andreea Barbulescu 		}
240*a60aeae7SKhristine Andreea Barbulescu 	}
241*a60aeae7SKhristine Andreea Barbulescu 
242*a60aeae7SKhristine Andreea Barbulescu 	/* Set dfi_complete_en to 1 */
243*a60aeae7SKhristine Andreea Barbulescu 	mmio_setbits_32(DDRC_BASE + OFFSET_DDRC_DFIMISC, DFIMISC_DFI_INIT_COMPLETE_EN_MASK);
244*a60aeae7SKhristine Andreea Barbulescu 
245*a60aeae7SKhristine Andreea Barbulescu 	/* Set PWRCTL.selfref_sw to 0 */
246*a60aeae7SKhristine Andreea Barbulescu 	mmio_clrbits_32(DDRC_BASE + OFFSET_DDRC_PWRCTL, PWRCTL_SELFREF_SW_MASK);
247*a60aeae7SKhristine Andreea Barbulescu 
248*a60aeae7SKhristine Andreea Barbulescu 	/* Set SWCTL.sw_done to 1 */
249*a60aeae7SKhristine Andreea Barbulescu 	mmio_write_32(DDRC_BASE + OFFSET_DDRC_SWCTL, SWCTL_SWDONE_DONE);
250*a60aeae7SKhristine Andreea Barbulescu 	err = mmio_read_32_poll_timeout(DDRC_BASE + OFFSET_DDRC_SWSTAT, swctl_reg,
251*a60aeae7SKhristine Andreea Barbulescu 				  (swctl_reg & SWSTAT_SWDONE_ACK_MASK)
252*a60aeae7SKhristine Andreea Barbulescu 				  != SWSTAT_SW_NOT_DONE, timeout);
253*a60aeae7SKhristine Andreea Barbulescu 	if (err != 0) {
254*a60aeae7SKhristine Andreea Barbulescu 		ERROR("Failed to set SWCTL.sw_done to 1\n");
255*a60aeae7SKhristine Andreea Barbulescu 		return TIMEOUT_ERR;
256*a60aeae7SKhristine Andreea Barbulescu 	}
257*a60aeae7SKhristine Andreea Barbulescu 
258*a60aeae7SKhristine Andreea Barbulescu 	/* Wait for DWC_ddr_umctl2 to move to normal operating mode */
259*a60aeae7SKhristine Andreea Barbulescu 	err = mmio_read_32_poll_timeout(DDRC_BASE + OFFSET_DDRC_STAT, umctl2_reg,
260*a60aeae7SKhristine Andreea Barbulescu 				  (umctl2_reg & STAT_OPERATING_MODE_MASK)
261*a60aeae7SKhristine Andreea Barbulescu 				  != STAT_OPERATING_MODE_INIT, timeout);
262*a60aeae7SKhristine Andreea Barbulescu 	if (err != 0) {
263*a60aeae7SKhristine Andreea Barbulescu 		ERROR("DWC_ddr_umctl2 did not reach normal operating mode\n");
264*a60aeae7SKhristine Andreea Barbulescu 		return TIMEOUT_ERR;
265*a60aeae7SKhristine Andreea Barbulescu 	}
266*a60aeae7SKhristine Andreea Barbulescu 
267*a60aeae7SKhristine Andreea Barbulescu 	/* Enable auto-refresh: RFSHCTL3.dis_auto_refresh = 0 */
268*a60aeae7SKhristine Andreea Barbulescu 	mmio_clrbits_32(DDRC_BASE + OFFSET_DDRC_RFSHCTL3, RFSHCTL3_DIS_AUTO_REFRESH_MASK);
269*a60aeae7SKhristine Andreea Barbulescu 
270*a60aeae7SKhristine Andreea Barbulescu 	/* Enable power down: PWRCTL.powerdown_en = 1 */
271*a60aeae7SKhristine Andreea Barbulescu 	mmio_setbits_32(DDRC_BASE + OFFSET_DDRC_PWRCTL, PWRCTL_POWER_DOWN_ENABLE_MASK);
272*a60aeae7SKhristine Andreea Barbulescu 
273*a60aeae7SKhristine Andreea Barbulescu 	/* Enable self-refresh: PWRCTL.selfref_en = 1 */
274*a60aeae7SKhristine Andreea Barbulescu 	mmio_setbits_32(DDRC_BASE + OFFSET_DDRC_PWRCTL, PWRCTL_SELF_REFRESH_ENABLE_MASK);
275*a60aeae7SKhristine Andreea Barbulescu 
276*a60aeae7SKhristine Andreea Barbulescu 	/*
277*a60aeae7SKhristine Andreea Barbulescu 	 * Enable assertion of dfi_dram_clk_disable:
278*a60aeae7SKhristine Andreea Barbulescu 	 * PWRTL.en_dfi_dram_clk_disable = 1
279*a60aeae7SKhristine Andreea Barbulescu 	 */
280*a60aeae7SKhristine Andreea Barbulescu 	mmio_setbits_32(DDRC_BASE + OFFSET_DDRC_PWRCTL, PWRCTL_EN_DFI_DRAM_CLOCK_DIS_MASK);
281*a60aeae7SKhristine Andreea Barbulescu 
282*a60aeae7SKhristine Andreea Barbulescu 	/*
283*a60aeae7SKhristine Andreea Barbulescu 	 * Each platform has a different number of AXI ports so this
284*a60aeae7SKhristine Andreea Barbulescu 	 * method should be implemented in hardware specific source
285*a60aeae7SKhristine Andreea Barbulescu 	 */
286*a60aeae7SKhristine Andreea Barbulescu 	ret = enable_axi_ports();
287*a60aeae7SKhristine Andreea Barbulescu 
288*a60aeae7SKhristine Andreea Barbulescu 	return ret;
289*a60aeae7SKhristine Andreea Barbulescu }
290*a60aeae7SKhristine Andreea Barbulescu 
29130c8a20dSKhristine Andreea Barbulescu /* Wait until firmware finishes execution and return training result */
wait_firmware_execution(void)29230c8a20dSKhristine Andreea Barbulescu uint32_t wait_firmware_execution(void)
29330c8a20dSKhristine Andreea Barbulescu {
29430c8a20dSKhristine Andreea Barbulescu 	uint32_t timeout_us = DEFAULT_TIMEOUT_US, ret = NO_ERR, mail = 0;
29530c8a20dSKhristine Andreea Barbulescu 	uint64_t timeout = timeout_init_us(timeout_us);
29630c8a20dSKhristine Andreea Barbulescu 	bool loop_continue = true;
29730c8a20dSKhristine Andreea Barbulescu 	bool timeout_expired;
29830c8a20dSKhristine Andreea Barbulescu 
29930c8a20dSKhristine Andreea Barbulescu 	do {
30030c8a20dSKhristine Andreea Barbulescu 		ret = get_mail(&mail);
30130c8a20dSKhristine Andreea Barbulescu 		if (ret != NO_ERR) {
30230c8a20dSKhristine Andreea Barbulescu 			loop_continue = false;
30330c8a20dSKhristine Andreea Barbulescu 		} else if (mail == TRAINING_FAILED_MSG) {
30430c8a20dSKhristine Andreea Barbulescu 			/* Training stage failed */
30530c8a20dSKhristine Andreea Barbulescu 			ret = TRAINING_FAILED;
30630c8a20dSKhristine Andreea Barbulescu 			loop_continue = false;
30730c8a20dSKhristine Andreea Barbulescu 		} else if (mail == TRAINING_OK_MSG) {
30830c8a20dSKhristine Andreea Barbulescu 			loop_continue = false;
30930c8a20dSKhristine Andreea Barbulescu 		} else {
31030c8a20dSKhristine Andreea Barbulescu 			/* Continue waiting for training result */
31130c8a20dSKhristine Andreea Barbulescu 		}
31230c8a20dSKhristine Andreea Barbulescu 		timeout_expired = timeout_elapsed(timeout);
31330c8a20dSKhristine Andreea Barbulescu 		if (timeout_expired) {
31430c8a20dSKhristine Andreea Barbulescu 			ret = TRAINING_FAILED;
31530c8a20dSKhristine Andreea Barbulescu 			loop_continue = false;
31630c8a20dSKhristine Andreea Barbulescu 		}
31730c8a20dSKhristine Andreea Barbulescu 		/* Continue loop if no exit condition met and timeout not elapsed */
31830c8a20dSKhristine Andreea Barbulescu 	} while (loop_continue);
31930c8a20dSKhristine Andreea Barbulescu 
32030c8a20dSKhristine Andreea Barbulescu 	return ret;
32130c8a20dSKhristine Andreea Barbulescu }
32230c8a20dSKhristine Andreea Barbulescu 
32330c8a20dSKhristine Andreea Barbulescu /* Acknowledge received message */
ack_mail(void)32430c8a20dSKhristine Andreea Barbulescu static uint32_t ack_mail(void)
32530c8a20dSKhristine Andreea Barbulescu {
32630c8a20dSKhristine Andreea Barbulescu 	uint32_t timeout = DEFAULT_TIMEOUT_US;
32730c8a20dSKhristine Andreea Barbulescu 	uint32_t uct_reg;
32830c8a20dSKhristine Andreea Barbulescu 	int err;
32930c8a20dSKhristine Andreea Barbulescu 
33030c8a20dSKhristine Andreea Barbulescu 	/* ACK message */
33130c8a20dSKhristine Andreea Barbulescu 	mmio_write_32(DDR_PHYA_DCTWRITEPROT, APBONLY_DCTWRITEPROT_ACK_EN);
33230c8a20dSKhristine Andreea Barbulescu 
33330c8a20dSKhristine Andreea Barbulescu 	err = mmio_read_32_poll_timeout(DDR_PHYA_APBONLY_UCTSHADOWREGS, uct_reg,
33430c8a20dSKhristine Andreea Barbulescu 					(uct_reg & UCT_WRITE_PROT_SHADOW_MASK) !=
33530c8a20dSKhristine Andreea Barbulescu 					UCT_WRITE_PROT_SHADOW_ACK,
33630c8a20dSKhristine Andreea Barbulescu 					timeout);
33730c8a20dSKhristine Andreea Barbulescu 	if (err != 0) {
33830c8a20dSKhristine Andreea Barbulescu 		ERROR("DDR PHY did not acknowledge write protection\n");
33930c8a20dSKhristine Andreea Barbulescu 		return TIMEOUT_ERR;
34030c8a20dSKhristine Andreea Barbulescu 	}
34130c8a20dSKhristine Andreea Barbulescu 
34230c8a20dSKhristine Andreea Barbulescu 	mmio_write_32(DDR_PHYA_DCTWRITEPROT, APBONLY_DCTWRITEPROT_ACK_DIS);
34330c8a20dSKhristine Andreea Barbulescu 
34430c8a20dSKhristine Andreea Barbulescu 	return NO_ERR;
34530c8a20dSKhristine Andreea Barbulescu }
34630c8a20dSKhristine Andreea Barbulescu 
34730c8a20dSKhristine Andreea Barbulescu /* Read available message from DDR PHY microcontroller */
get_mail(uint32_t * mail)34830c8a20dSKhristine Andreea Barbulescu static uint32_t get_mail(uint32_t *mail)
34930c8a20dSKhristine Andreea Barbulescu {
35030c8a20dSKhristine Andreea Barbulescu 	uint32_t uct_reg, timeout = DEFAULT_TIMEOUT_US;
35130c8a20dSKhristine Andreea Barbulescu 	int err;
35230c8a20dSKhristine Andreea Barbulescu 
35330c8a20dSKhristine Andreea Barbulescu 	err = mmio_read_32_poll_timeout(DDR_PHYA_APBONLY_UCTSHADOWREGS, uct_reg,
35430c8a20dSKhristine Andreea Barbulescu 					(uct_reg & UCT_WRITE_PROT_SHADOW_MASK) ==
35530c8a20dSKhristine Andreea Barbulescu 					UCT_WRITE_PROT_SHADOW_ACK,
35630c8a20dSKhristine Andreea Barbulescu 					timeout);
35730c8a20dSKhristine Andreea Barbulescu 	if (err != 0) {
35830c8a20dSKhristine Andreea Barbulescu 		ERROR("DDR PHY did not acknowledge UCT write protection\n");
35930c8a20dSKhristine Andreea Barbulescu 		return TIMEOUT_ERR;
36030c8a20dSKhristine Andreea Barbulescu 	}
36130c8a20dSKhristine Andreea Barbulescu 
36230c8a20dSKhristine Andreea Barbulescu 	*mail = mmio_read_32(DDR_PHYA_APBONLY_UCTWRITEONLYSHADOW);
36330c8a20dSKhristine Andreea Barbulescu 	/* ACK */
36430c8a20dSKhristine Andreea Barbulescu 	return ack_mail();
36530c8a20dSKhristine Andreea Barbulescu }
366a4efd428SKhristine Andreea Barbulescu 
367a4efd428SKhristine Andreea Barbulescu /* Read Critical Delay Differences from message block and store max values */
read_cdds(void)368a4efd428SKhristine Andreea Barbulescu void read_cdds(void)
369a4efd428SKhristine Andreea Barbulescu {
370a4efd428SKhristine Andreea Barbulescu 	const uint32_t rank0_rw_addr[] = {CDD_CHA_RW_0_0, CDD_CHB_RW_0_0};
371a4efd428SKhristine Andreea Barbulescu 	const uint32_t rank0_wr_addr[] = {CDD_CHA_WR_0_0, CDD_CHB_WR_0_0};
372a4efd428SKhristine Andreea Barbulescu 	uint8_t cdd_rr = 0, cdd_ww = 0, cdd_wr = 0, cdd_rw = 0;
373a4efd428SKhristine Andreea Barbulescu 	uint32_t mstr;
374a4efd428SKhristine Andreea Barbulescu 
375a4efd428SKhristine Andreea Barbulescu 	/* Max CDD values for single-rank */
376a4efd428SKhristine Andreea Barbulescu 	tr_res.cdd.rr = cdd_rr;
377a4efd428SKhristine Andreea Barbulescu 	tr_res.cdd.ww = cdd_ww;
378a4efd428SKhristine Andreea Barbulescu 	tr_res.cdd.rw = is_lpddr4() ?
379a4efd428SKhristine Andreea Barbulescu 			get_max_cdd(rank0_rw_addr, ARRAY_SIZE(rank0_rw_addr)) :
380a4efd428SKhristine Andreea Barbulescu 			mmio_read_8(CDD_CHA_RW_0_0_DDR3);
381a4efd428SKhristine Andreea Barbulescu 	tr_res.cdd.wr = is_lpddr4() ?
382a4efd428SKhristine Andreea Barbulescu 			get_max_cdd(rank0_wr_addr, ARRAY_SIZE(rank0_wr_addr)) :
383a4efd428SKhristine Andreea Barbulescu 			mmio_read_8(CDD_CHA_WR_0_0_DDR3);
384a4efd428SKhristine Andreea Barbulescu 
385a4efd428SKhristine Andreea Barbulescu 	/* Check MSTR.active_ranks to identify multi-rank configurations */
386a4efd428SKhristine Andreea Barbulescu 	mstr = mmio_read_32(DDRC_BASE);
387a4efd428SKhristine Andreea Barbulescu 	if ((mstr & MSTR_ACT_RANKS_MASK) == MSTR_DUAL_RANK_VAL) {
388a4efd428SKhristine Andreea Barbulescu 		/* Compute max CDDs for both ranks depending on memory type */
389a4efd428SKhristine Andreea Barbulescu 		if (is_lpddr4()) {
390a4efd428SKhristine Andreea Barbulescu 			const uint32_t rr_addr[] = {
391a4efd428SKhristine Andreea Barbulescu 				CDD_CHA_RR_1_0, CDD_CHA_RR_0_1,
392a4efd428SKhristine Andreea Barbulescu 				CDD_CHB_RR_1_0, CDD_CHB_RR_0_1
393a4efd428SKhristine Andreea Barbulescu 				};
394a4efd428SKhristine Andreea Barbulescu 			const uint32_t ww_addr[] = {
395a4efd428SKhristine Andreea Barbulescu 				CDD_CHA_WW_1_0, CDD_CHA_WW_0_1,
396a4efd428SKhristine Andreea Barbulescu 				CDD_CHB_WW_1_0, CDD_CHB_WW_0_1
397a4efd428SKhristine Andreea Barbulescu 				};
398a4efd428SKhristine Andreea Barbulescu 			const uint32_t rw_addr[] = {
399a4efd428SKhristine Andreea Barbulescu 				CDD_CHA_RW_1_1, CDD_CHA_RW_1_0,
400a4efd428SKhristine Andreea Barbulescu 				CDD_CHA_RW_0_1, CDD_CHB_RW_1_1,
401a4efd428SKhristine Andreea Barbulescu 				CDD_CHB_RW_1_0, CDD_CHB_RW_0_1
402a4efd428SKhristine Andreea Barbulescu 				};
403a4efd428SKhristine Andreea Barbulescu 			const uint32_t wr_addr[] = {
404a4efd428SKhristine Andreea Barbulescu 				CDD_CHA_WR_1_1, CDD_CHA_WR_1_0,
405a4efd428SKhristine Andreea Barbulescu 				CDD_CHA_WR_0_1, CDD_CHB_WR_1_1,
406a4efd428SKhristine Andreea Barbulescu 				CDD_CHB_WR_1_0, CDD_CHB_WR_0_1
407a4efd428SKhristine Andreea Barbulescu 				};
408a4efd428SKhristine Andreea Barbulescu 
409a4efd428SKhristine Andreea Barbulescu 			cdd_rr = get_max_cdd(rr_addr, ARRAY_SIZE(rr_addr));
410a4efd428SKhristine Andreea Barbulescu 			cdd_rw = get_max_cdd(rw_addr, ARRAY_SIZE(rw_addr));
411a4efd428SKhristine Andreea Barbulescu 			cdd_wr = get_max_cdd(wr_addr, ARRAY_SIZE(wr_addr));
412a4efd428SKhristine Andreea Barbulescu 			cdd_ww = get_max_cdd(ww_addr, ARRAY_SIZE(ww_addr));
413a4efd428SKhristine Andreea Barbulescu 		} else {
414a4efd428SKhristine Andreea Barbulescu 			const uint32_t rr_addr[] = {CDD_CHA_RR_1_0_DDR3,
415a4efd428SKhristine Andreea Barbulescu 						    CDD_CHA_RR_0_1_DDR3};
416a4efd428SKhristine Andreea Barbulescu 			const uint32_t ww_addr[] = {CDD_CHA_WW_1_0_DDR3,
417a4efd428SKhristine Andreea Barbulescu 						    CDD_CHA_WW_0_1_DDR3};
418a4efd428SKhristine Andreea Barbulescu 			const uint32_t rw_addr[] = {CDD_CHA_RW_1_1_DDR3,
419a4efd428SKhristine Andreea Barbulescu 						    CDD_CHA_RW_1_0_DDR3,
420a4efd428SKhristine Andreea Barbulescu 						    CDD_CHA_RW_0_1_DDR3};
421a4efd428SKhristine Andreea Barbulescu 			const uint32_t wr_addr[] = {CDD_CHA_WR_1_1_DDR3,
422a4efd428SKhristine Andreea Barbulescu 						    CDD_CHA_WR_1_0_DDR3,
423a4efd428SKhristine Andreea Barbulescu 						    CDD_CHA_WR_0_1_DDR3};
424a4efd428SKhristine Andreea Barbulescu 
425a4efd428SKhristine Andreea Barbulescu 			cdd_rr = get_max_cdd(rr_addr, ARRAY_SIZE(rr_addr));
426a4efd428SKhristine Andreea Barbulescu 			cdd_rw = get_max_cdd(rw_addr, ARRAY_SIZE(rw_addr));
427a4efd428SKhristine Andreea Barbulescu 			cdd_wr = get_max_cdd(wr_addr, ARRAY_SIZE(wr_addr));
428a4efd428SKhristine Andreea Barbulescu 			cdd_ww = get_max_cdd(ww_addr, ARRAY_SIZE(ww_addr));
429a4efd428SKhristine Andreea Barbulescu 		}
430a4efd428SKhristine Andreea Barbulescu 
431a4efd428SKhristine Andreea Barbulescu 		/* Update max CDD values if needed */
432a4efd428SKhristine Andreea Barbulescu 		if (cdd_rr > tr_res.cdd.rr) {
433a4efd428SKhristine Andreea Barbulescu 			tr_res.cdd.rr = cdd_rr;
434a4efd428SKhristine Andreea Barbulescu 		}
435a4efd428SKhristine Andreea Barbulescu 		if (cdd_rw > tr_res.cdd.rw) {
436a4efd428SKhristine Andreea Barbulescu 			tr_res.cdd.rw = cdd_rw;
437a4efd428SKhristine Andreea Barbulescu 		}
438a4efd428SKhristine Andreea Barbulescu 		if (cdd_wr > tr_res.cdd.wr) {
439a4efd428SKhristine Andreea Barbulescu 			tr_res.cdd.wr = cdd_wr;
440a4efd428SKhristine Andreea Barbulescu 		}
441a4efd428SKhristine Andreea Barbulescu 		if (cdd_ww > tr_res.cdd.ww) {
442a4efd428SKhristine Andreea Barbulescu 			tr_res.cdd.ww = cdd_ww;
443a4efd428SKhristine Andreea Barbulescu 		}
444a4efd428SKhristine Andreea Barbulescu 	}
445a4efd428SKhristine Andreea Barbulescu }
446a4efd428SKhristine Andreea Barbulescu 
447a4efd428SKhristine Andreea Barbulescu /* Read trained VrefCA from message block and store average value */
read_vref_ca(void)448a4efd428SKhristine Andreea Barbulescu void read_vref_ca(void)
449a4efd428SKhristine Andreea Barbulescu {
450a4efd428SKhristine Andreea Barbulescu 	const uint32_t rank0_vref_addr[] = {VREF_CA_A0, VREF_CA_B0};
451a4efd428SKhristine Andreea Barbulescu 	const uint32_t rank01_vref_addr[] = {VREF_CA_A0, VREF_CA_A1,
452a4efd428SKhristine Andreea Barbulescu 					     VREF_CA_B0, VREF_CA_B1};
453a4efd428SKhristine Andreea Barbulescu 	uint32_t mstr;
454a4efd428SKhristine Andreea Barbulescu 
455a4efd428SKhristine Andreea Barbulescu 	/* Check MSTR.active_ranks to identify multi-rank configurations */
456a4efd428SKhristine Andreea Barbulescu 	mstr = mmio_read_32(DDRC_BASE);
457a4efd428SKhristine Andreea Barbulescu 	if ((mstr & MSTR_ACT_RANKS_MASK) == MSTR_DUAL_RANK_VAL) {
458a4efd428SKhristine Andreea Barbulescu 		tr_res.vref_ca = get_avg_vref(rank01_vref_addr,
459a4efd428SKhristine Andreea Barbulescu 					      ARRAY_SIZE(rank01_vref_addr));
460a4efd428SKhristine Andreea Barbulescu 	} else {
461a4efd428SKhristine Andreea Barbulescu 		tr_res.vref_ca = get_avg_vref(rank0_vref_addr,
462a4efd428SKhristine Andreea Barbulescu 					      ARRAY_SIZE(rank0_vref_addr));
463a4efd428SKhristine Andreea Barbulescu 	}
464a4efd428SKhristine Andreea Barbulescu }
465a4efd428SKhristine Andreea Barbulescu 
466a4efd428SKhristine Andreea Barbulescu /* Read trained VrefDQ from message block and store average value*/
read_vref_dq(void)467a4efd428SKhristine Andreea Barbulescu void read_vref_dq(void)
468a4efd428SKhristine Andreea Barbulescu {
469a4efd428SKhristine Andreea Barbulescu 	const uint32_t rank0_vref_addr[] = {VREF_DQ_A0, VREF_DQ_B0};
470a4efd428SKhristine Andreea Barbulescu 	const uint32_t rank01_vref_addr[] = {VREF_DQ_A0, VREF_DQ_A1,
471a4efd428SKhristine Andreea Barbulescu 					     VREF_DQ_B0, VREF_DQ_B1};
472a4efd428SKhristine Andreea Barbulescu 	uint32_t mstr;
473a4efd428SKhristine Andreea Barbulescu 
474a4efd428SKhristine Andreea Barbulescu 	/* Check MSTR.active_ranks to identify multi-rank configurations */
475a4efd428SKhristine Andreea Barbulescu 	mstr = mmio_read_32(DDRC_BASE);
476a4efd428SKhristine Andreea Barbulescu 	if ((mstr & MSTR_ACT_RANKS_MASK) == MSTR_DUAL_RANK_VAL) {
477a4efd428SKhristine Andreea Barbulescu 		tr_res.vref_dq = get_avg_vref(rank01_vref_addr,
478a4efd428SKhristine Andreea Barbulescu 					      ARRAY_SIZE(rank01_vref_addr));
479a4efd428SKhristine Andreea Barbulescu 	} else {
480a4efd428SKhristine Andreea Barbulescu 		tr_res.vref_dq = get_avg_vref(rank0_vref_addr,
481a4efd428SKhristine Andreea Barbulescu 					      ARRAY_SIZE(rank0_vref_addr));
482a4efd428SKhristine Andreea Barbulescu 	}
483a4efd428SKhristine Andreea Barbulescu }
484a4efd428SKhristine Andreea Barbulescu 
485a4efd428SKhristine Andreea Barbulescu /* Calculate DFITMG1.dfi_t_wrdata_delay */
compute_tphy_wrdata_delay(void)486a4efd428SKhristine Andreea Barbulescu void compute_tphy_wrdata_delay(void)
487a4efd428SKhristine Andreea Barbulescu {
488a4efd428SKhristine Andreea Barbulescu 	uint16_t tx_dqsdly, tx_dqsdly_tg1, tctrl_delay, burst_length,
489a4efd428SKhristine Andreea Barbulescu 		 wrdata_use_dfi_phy_clk;
490a4efd428SKhristine Andreea Barbulescu 
491a4efd428SKhristine Andreea Barbulescu 	const uint32_t single_rank_dly_addr[] = {
492a4efd428SKhristine Andreea Barbulescu 		DBYTE0_TXDQSDLYTG0_U0, DBYTE0_TXDQSDLYTG0_U1,
493a4efd428SKhristine Andreea Barbulescu 		DBYTE1_TXDQSDLYTG0_U0, DBYTE1_TXDQSDLYTG0_U1,
494a4efd428SKhristine Andreea Barbulescu 		DBYTE2_TXDQSDLYTG0_U0, DBYTE2_TXDQSDLYTG0_U1,
495a4efd428SKhristine Andreea Barbulescu 		DBYTE3_TXDQSDLYTG0_U0, DBYTE3_TXDQSDLYTG0_U1
496a4efd428SKhristine Andreea Barbulescu 	};
497a4efd428SKhristine Andreea Barbulescu 
498a4efd428SKhristine Andreea Barbulescu 	const uint32_t dual_rank_dly_addr[] = {
499a4efd428SKhristine Andreea Barbulescu 		DBYTE0_TXDQSDLYTG1_U0, DBYTE0_TXDQSDLYTG1_U1,
500a4efd428SKhristine Andreea Barbulescu 		DBYTE1_TXDQSDLYTG1_U0, DBYTE1_TXDQSDLYTG1_U1,
501a4efd428SKhristine Andreea Barbulescu 		DBYTE2_TXDQSDLYTG1_U0, DBYTE2_TXDQSDLYTG1_U1,
502a4efd428SKhristine Andreea Barbulescu 		DBYTE3_TXDQSDLYTG1_U0, DBYTE3_TXDQSDLYTG1_U1
503a4efd428SKhristine Andreea Barbulescu 	};
504a4efd428SKhristine Andreea Barbulescu 
505a4efd428SKhristine Andreea Barbulescu 	uint32_t mstr, dfitmg0;
506a4efd428SKhristine Andreea Barbulescu 
507a4efd428SKhristine Andreea Barbulescu 	/* Compute max tx_dqdqsdly for rank 0 */
508a4efd428SKhristine Andreea Barbulescu 	tx_dqsdly = get_max_delay(single_rank_dly_addr,
509a4efd428SKhristine Andreea Barbulescu 				  ARRAY_SIZE(single_rank_dly_addr));
510a4efd428SKhristine Andreea Barbulescu 
511a4efd428SKhristine Andreea Barbulescu 	/* Check MSTR.active_ranks to identify multi-rank configurations */
512a4efd428SKhristine Andreea Barbulescu 	mstr = mmio_read_32(DDRC_BASE);
513a4efd428SKhristine Andreea Barbulescu 	if ((mstr & MSTR_ACT_RANKS_MASK) == MSTR_DUAL_RANK_VAL) {
514a4efd428SKhristine Andreea Barbulescu 		/* Compute max tx_dqdqsdly for rank 1 */
515a4efd428SKhristine Andreea Barbulescu 		tx_dqsdly_tg1 = get_max_delay(dual_rank_dly_addr,
516a4efd428SKhristine Andreea Barbulescu 					      ARRAY_SIZE(dual_rank_dly_addr));
517a4efd428SKhristine Andreea Barbulescu 		if (tx_dqsdly_tg1 > tx_dqsdly) {
518a4efd428SKhristine Andreea Barbulescu 			tx_dqsdly = tx_dqsdly_tg1;
519a4efd428SKhristine Andreea Barbulescu 		}
520a4efd428SKhristine Andreea Barbulescu 	}
521a4efd428SKhristine Andreea Barbulescu 
522a4efd428SKhristine Andreea Barbulescu 	/* Extract coarse delay value + 1 for fine delay */
523a4efd428SKhristine Andreea Barbulescu 	tx_dqsdly = (tx_dqsdly >> TXDQDLY_COARSE) + 1U;
524a4efd428SKhristine Andreea Barbulescu 
525a4efd428SKhristine Andreea Barbulescu 	/* Compute tctrl_delay */
526a4efd428SKhristine Andreea Barbulescu 	tctrl_delay = (uint16_t)((mmio_read_16(ARDPTR_INITVAL_ADDR) / 2U) +
527a4efd428SKhristine Andreea Barbulescu 				 (DDRPHY_PIPE_DFI_MISC * 2U) + 3U);
528a4efd428SKhristine Andreea Barbulescu 
529a4efd428SKhristine Andreea Barbulescu 	burst_length = (uint16_t)(mstr >> MSTR_BURST_RDWR_POS) &
530a4efd428SKhristine Andreea Barbulescu 		       MSTR_BURST_RDWR_MASK;
531a4efd428SKhristine Andreea Barbulescu 	dfitmg0 = mmio_read_16(DDRC_BASE + OFFSET_DDRC_DFITMG0);
532a4efd428SKhristine Andreea Barbulescu 	wrdata_use_dfi_phy_clk = (uint16_t)(dfitmg0 >> DFITMG0_PHY_CLK_POS) &
533a4efd428SKhristine Andreea Barbulescu 				 DFITMG0_PHY_CLK_MASK;
534a4efd428SKhristine Andreea Barbulescu 
535a4efd428SKhristine Andreea Barbulescu 	/* Program */
536a4efd428SKhristine Andreea Barbulescu 	tr_res.tphy_wrdata_delay = tctrl_delay + 6U + burst_length +
537a4efd428SKhristine Andreea Barbulescu 				   wrdata_use_dfi_phy_clk + tx_dqsdly;
538a4efd428SKhristine Andreea Barbulescu 	tr_res.tphy_wrdata_delay = (tr_res.tphy_wrdata_delay / 2U) +
539a4efd428SKhristine Andreea Barbulescu 				   (tr_res.tphy_wrdata_delay % 2U);
540a4efd428SKhristine Andreea Barbulescu }
541a4efd428SKhristine Andreea Barbulescu 
542*a60aeae7SKhristine Andreea Barbulescu /* Re-program some of the DDRC registers based on post-training results. */
adjust_ddrc_config(void)543*a60aeae7SKhristine Andreea Barbulescu static uint32_t adjust_ddrc_config(void)
544*a60aeae7SKhristine Andreea Barbulescu {
545*a60aeae7SKhristine Andreea Barbulescu 	uint8_t wr_gap_ddr3 = 3, min_lp4 = 7, min_ddr3 = 0xe, max = 0xf;
546*a60aeae7SKhristine Andreea Barbulescu 	uint8_t rd_gap, wr_gap, rd_gap_new, wr_gap_new, delta, min;
547*a60aeae7SKhristine Andreea Barbulescu 	uint8_t rd_gap_lp4 = 4, rd_gap_ddr3 = 2, wr_gap_lp4 = 5;
548*a60aeae7SKhristine Andreea Barbulescu 	uint32_t dramtmg2_reg, rankctl_reg, mstr_reg;
549*a60aeae7SKhristine Andreea Barbulescu 	uint32_t ret = NO_ERR;
550*a60aeae7SKhristine Andreea Barbulescu 
551*a60aeae7SKhristine Andreea Barbulescu 	/* DRAMTMG2.rd2wr & DRAMTMG2.wr2rd */
552*a60aeae7SKhristine Andreea Barbulescu 	dramtmg2_reg = mmio_read_32(DDRC_BASE + OFFSET_DDRC_DRAMTMG2);
553*a60aeae7SKhristine Andreea Barbulescu 	delta = (uint8_t)((tr_res.cdd.rw + (tr_res.cdd.rw % 2U)) / 2U);
554*a60aeae7SKhristine Andreea Barbulescu 	if (!update_bf(&dramtmg2_reg, DRAMTMG2_RD_WR_POS, DRAMTMG2_RD_WR_MASK,
555*a60aeae7SKhristine Andreea Barbulescu 		       (int32_t)delta)) {
556*a60aeae7SKhristine Andreea Barbulescu 		return BITFIELD_EXCEEDED;
557*a60aeae7SKhristine Andreea Barbulescu 	}
558*a60aeae7SKhristine Andreea Barbulescu 	delta = (uint8_t)((tr_res.cdd.ww + (tr_res.cdd.ww % 2U)) / 2U);
559*a60aeae7SKhristine Andreea Barbulescu 	if (!update_bf(&dramtmg2_reg, DRAMTMG2_WR_RD_POS, DRAMTMG2_WR_RD_MASK,
560*a60aeae7SKhristine Andreea Barbulescu 		       (int32_t)delta)) {
561*a60aeae7SKhristine Andreea Barbulescu 		return BITFIELD_EXCEEDED;
562*a60aeae7SKhristine Andreea Barbulescu 	}
563*a60aeae7SKhristine Andreea Barbulescu 	mmio_write_32(DDRC_BASE + OFFSET_DDRC_DRAMTMG2, dramtmg2_reg);
564*a60aeae7SKhristine Andreea Barbulescu 
565*a60aeae7SKhristine Andreea Barbulescu 	/* For LPDDR4 overwrite INIT6 and INIT7 DDRC registers. */
566*a60aeae7SKhristine Andreea Barbulescu 	if (is_lpddr4()) {
567*a60aeae7SKhristine Andreea Barbulescu 		/* INIT6.mr5 */
568*a60aeae7SKhristine Andreea Barbulescu 		mmio_clrsetbits_32(DDRC_BASE + OFFSET_DDRC_INIT6, INIT6_MR5_MASK, tr_res.vref_ca);
569*a60aeae7SKhristine Andreea Barbulescu 
570*a60aeae7SKhristine Andreea Barbulescu 		/* INIT7.mr6 */
571*a60aeae7SKhristine Andreea Barbulescu 		mmio_clrsetbits_32(DDRC_BASE + OFFSET_DDRC_INIT7, INIT7_MR6_MASK, tr_res.vref_dq);
572*a60aeae7SKhristine Andreea Barbulescu 	}
573*a60aeae7SKhristine Andreea Barbulescu 
574*a60aeae7SKhristine Andreea Barbulescu 	/* DFITMG1.dfi_t_wrdata_delay */
575*a60aeae7SKhristine Andreea Barbulescu 	mmio_clrsetbits_32(DDRC_BASE + OFFSET_DDRC_DFITMG1,
576*a60aeae7SKhristine Andreea Barbulescu 			   (DFITMG1_WRDATA_DELAY_MASK << DFITMG1_WRDATA_DELAY_POS),
577*a60aeae7SKhristine Andreea Barbulescu 			   (((uint32_t)tr_res.tphy_wrdata_delay) << DFITMG1_WRDATA_DELAY_POS));
578*a60aeae7SKhristine Andreea Barbulescu 
579*a60aeae7SKhristine Andreea Barbulescu 	/* For multi-rank systems */
580*a60aeae7SKhristine Andreea Barbulescu 	mstr_reg = mmio_read_32(DDRC_BASE);
581*a60aeae7SKhristine Andreea Barbulescu 	if ((mstr_reg & MSTR_ACT_RANKS_MASK) == MSTR_DUAL_RANK_VAL) {
582*a60aeae7SKhristine Andreea Barbulescu 		uint8_t rd_gap_ct = is_lpddr4() ? rd_gap_lp4 : rd_gap_ddr3;
583*a60aeae7SKhristine Andreea Barbulescu 		uint8_t wr_gap_ct = is_lpddr4() ? wr_gap_lp4 : wr_gap_ddr3;
584*a60aeae7SKhristine Andreea Barbulescu 
585*a60aeae7SKhristine Andreea Barbulescu 		min = is_lpddr4() ? min_lp4 : min_ddr3;
586*a60aeae7SKhristine Andreea Barbulescu 		rankctl_reg = mmio_read_32(DDRC_BASE + OFFSET_DDRC_RANKCTL);
587*a60aeae7SKhristine Andreea Barbulescu 		/* RANKCTL.diff_rank_rd_gap */
588*a60aeae7SKhristine Andreea Barbulescu 		rd_gap = (uint8_t)((rankctl_reg >> RANKCTL_RD_GAP_POS) &
589*a60aeae7SKhristine Andreea Barbulescu 				   RANKCTL_RD_GAP_MASK);
590*a60aeae7SKhristine Andreea Barbulescu 		rd_gap_new = (uint8_t)((rd_gap_ct + tr_res.cdd.rr +
591*a60aeae7SKhristine Andreea Barbulescu 					(tr_res.cdd.rr % 2U)) / 2U);
592*a60aeae7SKhristine Andreea Barbulescu 
593*a60aeae7SKhristine Andreea Barbulescu 		/* ensure min and max of rd_gap field */
594*a60aeae7SKhristine Andreea Barbulescu 		rd_gap_new = (rd_gap_new < min) ? min : ((rd_gap_new > max) ?
595*a60aeae7SKhristine Andreea Barbulescu 							 max : rd_gap_new);
596*a60aeae7SKhristine Andreea Barbulescu 		if (rd_gap_new > rd_gap) {
597*a60aeae7SKhristine Andreea Barbulescu 			delta = (uint8_t)(rd_gap_new - rd_gap);
598*a60aeae7SKhristine Andreea Barbulescu 			if (!update_bf(&rankctl_reg, RANKCTL_RD_GAP_POS,
599*a60aeae7SKhristine Andreea Barbulescu 				       RANKCTL_RD_GAP_MASK, (int32_t)delta)) {
600*a60aeae7SKhristine Andreea Barbulescu 				return BITFIELD_EXCEEDED;
601*a60aeae7SKhristine Andreea Barbulescu 			}
602*a60aeae7SKhristine Andreea Barbulescu 		}
603*a60aeae7SKhristine Andreea Barbulescu 
604*a60aeae7SKhristine Andreea Barbulescu 		/* RANKCTL.diff_rank_wr_gap */
605*a60aeae7SKhristine Andreea Barbulescu 		wr_gap = (uint8_t)((rankctl_reg >> RANKCTL_WR_GAP_POS) &
606*a60aeae7SKhristine Andreea Barbulescu 				   RANKCTL_WR_GAP_MASK);
607*a60aeae7SKhristine Andreea Barbulescu 		wr_gap_new = (uint8_t)((wr_gap_ct + tr_res.cdd.ww +
608*a60aeae7SKhristine Andreea Barbulescu 					(tr_res.cdd.ww % 2U)) / 2U);
609*a60aeae7SKhristine Andreea Barbulescu 
610*a60aeae7SKhristine Andreea Barbulescu 		/* ensure min and max of wr_gap field */
611*a60aeae7SKhristine Andreea Barbulescu 		wr_gap_new = (wr_gap_new < min) ? min : ((wr_gap_new > max) ?
612*a60aeae7SKhristine Andreea Barbulescu 							 max : wr_gap_new);
613*a60aeae7SKhristine Andreea Barbulescu 		if (wr_gap_new > wr_gap) {
614*a60aeae7SKhristine Andreea Barbulescu 			delta = (uint8_t)(wr_gap_new - wr_gap);
615*a60aeae7SKhristine Andreea Barbulescu 			if (!update_bf(&rankctl_reg, RANKCTL_WR_GAP_POS,
616*a60aeae7SKhristine Andreea Barbulescu 				       RANKCTL_WR_GAP_MASK, (int32_t)delta)) {
617*a60aeae7SKhristine Andreea Barbulescu 				return BITFIELD_EXCEEDED;
618*a60aeae7SKhristine Andreea Barbulescu 			}
619*a60aeae7SKhristine Andreea Barbulescu 		}
620*a60aeae7SKhristine Andreea Barbulescu 
621*a60aeae7SKhristine Andreea Barbulescu 		if ((rd_gap_new > rd_gap) || (wr_gap_new > wr_gap)) {
622*a60aeae7SKhristine Andreea Barbulescu 			mmio_write_32(DDRC_BASE + OFFSET_DDRC_RANKCTL, rankctl_reg);
623*a60aeae7SKhristine Andreea Barbulescu 		}
624*a60aeae7SKhristine Andreea Barbulescu 	}
625*a60aeae7SKhristine Andreea Barbulescu 
626*a60aeae7SKhristine Andreea Barbulescu 	return ret;
627*a60aeae7SKhristine Andreea Barbulescu }
628*a60aeae7SKhristine Andreea Barbulescu 
629a4efd428SKhristine Andreea Barbulescu /* Check if memory type is LPDDR4 using MSTR register */
is_lpddr4(void)630a4efd428SKhristine Andreea Barbulescu static bool is_lpddr4(void)
631a4efd428SKhristine Andreea Barbulescu {
632a4efd428SKhristine Andreea Barbulescu 	uint32_t mstr;
633a4efd428SKhristine Andreea Barbulescu 
634a4efd428SKhristine Andreea Barbulescu 	mstr = mmio_read_32(DDRC_BASE);
635a4efd428SKhristine Andreea Barbulescu 	return ((mstr & MSTR_DRAM_MASK) == MSTR_LPDDR4_VAL);
636a4efd428SKhristine Andreea Barbulescu }
637a4efd428SKhristine Andreea Barbulescu 
638a4efd428SKhristine Andreea Barbulescu /*
639a4efd428SKhristine Andreea Barbulescu  * Get maximum critical delay difference value.
640a4efd428SKhristine Andreea Barbulescu  * @param cdd_addr[] - list of CDD memory addresses
641a4efd428SKhristine Andreea Barbulescu  * @param size - number of CDDs to be read
642a4efd428SKhristine Andreea Barbulescu  * @return max CDD value
643a4efd428SKhristine Andreea Barbulescu  */
get_max_cdd(const uint32_t cdd_addr[],size_t size)644a4efd428SKhristine Andreea Barbulescu static uint8_t get_max_cdd(const uint32_t cdd_addr[], size_t size)
645a4efd428SKhristine Andreea Barbulescu {
646a4efd428SKhristine Andreea Barbulescu 	uint8_t cdd, max = 0;
647a4efd428SKhristine Andreea Barbulescu 	int8_t signed_cdd;
648a4efd428SKhristine Andreea Barbulescu 	size_t i;
649a4efd428SKhristine Andreea Barbulescu 
650a4efd428SKhristine Andreea Barbulescu 	for (i = 0; i < size; i++) {
651a4efd428SKhristine Andreea Barbulescu 		/* CDD has type int8_t - read as unsigned and cast to signed */
652a4efd428SKhristine Andreea Barbulescu 		signed_cdd = (int8_t)(mmio_read_8(cdd_addr[i]));
653a4efd428SKhristine Andreea Barbulescu 		/* We need to use absolute value */
654a4efd428SKhristine Andreea Barbulescu 		cdd = (uint8_t)((signed_cdd >= 0) ? signed_cdd : -signed_cdd);
655a4efd428SKhristine Andreea Barbulescu 		max = MAX(cdd, max);
656a4efd428SKhristine Andreea Barbulescu 	}
657a4efd428SKhristine Andreea Barbulescu 	return max;
658a4efd428SKhristine Andreea Barbulescu }
659a4efd428SKhristine Andreea Barbulescu 
660a4efd428SKhristine Andreea Barbulescu /*
661a4efd428SKhristine Andreea Barbulescu  * Get maximum delay value.
662a4efd428SKhristine Andreea Barbulescu  * @param delay_addr[] - list of CDD memory addresses
663a4efd428SKhristine Andreea Barbulescu  * @param size - number of values to be read
664a4efd428SKhristine Andreea Barbulescu  * @return max delay value
665a4efd428SKhristine Andreea Barbulescu  */
get_max_delay(const uint32_t delay_addr[],size_t size)666a4efd428SKhristine Andreea Barbulescu static uint16_t get_max_delay(const uint32_t delay_addr[], size_t size)
667a4efd428SKhristine Andreea Barbulescu {
668a4efd428SKhristine Andreea Barbulescu 	uint16_t value, max = 0;
669a4efd428SKhristine Andreea Barbulescu 	size_t i;
670a4efd428SKhristine Andreea Barbulescu 
671a4efd428SKhristine Andreea Barbulescu 	for (i = 0; i < size; i++) {
672a4efd428SKhristine Andreea Barbulescu 		value = mmio_read_16(delay_addr[i]);
673a4efd428SKhristine Andreea Barbulescu 		max = MAX(value, max);
674a4efd428SKhristine Andreea Barbulescu 	}
675a4efd428SKhristine Andreea Barbulescu 	return max;
676a4efd428SKhristine Andreea Barbulescu }
677a4efd428SKhristine Andreea Barbulescu 
678a4efd428SKhristine Andreea Barbulescu /*
679a4efd428SKhristine Andreea Barbulescu  * Compute average vref value.
680a4efd428SKhristine Andreea Barbulescu  * @param vref_addr[] - list of vref memory addresses
681a4efd428SKhristine Andreea Barbulescu  * @param size - number of values to be read
682a4efd428SKhristine Andreea Barbulescu  * @return average vref value
683a4efd428SKhristine Andreea Barbulescu  */
get_avg_vref(const uint32_t vref_addr[],size_t size)684a4efd428SKhristine Andreea Barbulescu static uint8_t get_avg_vref(const uint32_t vref_addr[], size_t size)
685a4efd428SKhristine Andreea Barbulescu {
686a4efd428SKhristine Andreea Barbulescu 	uint32_t sum = 0;
687a4efd428SKhristine Andreea Barbulescu 	size_t i;
688a4efd428SKhristine Andreea Barbulescu 
689a4efd428SKhristine Andreea Barbulescu 	for (i = 0; i < size; i++) {
690a4efd428SKhristine Andreea Barbulescu 		sum += mmio_read_8(vref_addr[i]);
691a4efd428SKhristine Andreea Barbulescu 	}
692a4efd428SKhristine Andreea Barbulescu 
693a4efd428SKhristine Andreea Barbulescu 	assert((sum / size) <= UINT8_MAX);
694a4efd428SKhristine Andreea Barbulescu 
695a4efd428SKhristine Andreea Barbulescu 	return (uint8_t)(sum / size);
696a4efd428SKhristine Andreea Barbulescu }
697