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