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