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