/*
 * Minerva Training Cell
 * DRAM Training for Tegra X1 SoC. Supports LPDDR4.
 *
 * Copyright (c) 2018-2022 CTCaer  <ctcaer@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>
#include <stdlib.h>
#include "mtc.h"
#include "mtc_mc_emc_regs.h"
#include "mtc_switch_tables.h"
#include "types.h"
#include <module.h>

#define EPRINTF(...)
#define EPRINTFARGS(...)

#define MAX_FREQ_T210     1600000
//#define OVERCLOCK_FREQ    1862400
//#define OVERCLOCK_VOLTAGE 1200000 // Default is 1100mV and in HOS 1125mV.

#define PERF_HACK

bool train_ram_patterns;

/*
 * REF:  PLL Input reference (OSC_FREQ).
 * DIVN: PLL feedback divider.
 * DIVM: PLL input divider.
 * DIVP: PLL post divider.
 * PLL_OUT = (REF / DIVM) * DIVN / DIVP
 *
 * DIVP    | DIVP
 * Encoded | Real
 * ----------------------
 * 0       | 1 (DIVP off)
 * 1       | 2
 * 2       | 3
 * 3       | 4
 * 4       | 5
 * 5       | 6
 * 6       | 8
 * 7       | 10
 * 8       | 12
 * 9       | 16
 * 10      | 12
 * 11      | 16
 * 12      | 20
 * 13      | 24
 * 14      | 32
 */
static pllm_clk_config_t pllm_clk_config_table[] =
{
	// pll_osc_in, pll_out, pll_feedback_div, pll_input_div, pll_post_div.
	// f_in,       f_out,   n,                m,             p.
	// f_out = ((f_in / m) * n) / p. Example: 1600000 = (38400 / 2) * 97.
	{38400, 297600,  93,  4, 2},
	{38400, 400000,  125, 4, 2},
	{38400, 408000,  85,  4, 1},
	{38400, 532800,  111, 4, 1},
	{38400, 665600,  104, 3, 1},
	{38400, 800000,  125, 3, 1},
	{38400, 931200,  97,  4, 0},
	{38400, 1065600, 111, 4, 0},
	{38400, 1200000, 125, 4, 0},
	{38400, 1331200, 104, 3, 0},
	{38400, 1459200, 76,  2, 0},
	{38400, 1600000, 125, 3, 0},
	{38400, 1728000, 90,  2, 0}, // Custom. Normalized 1733 MHz.
	{38400, 1795200, 187, 4, 0}, // Custom.
	{38400, 1862400, 97,  2, 0}, // JEDEC Standard. (T210 official max).
	{38400, 1894400, 148, 3, 0}, // Custom.
	{38400, 1932800, 151, 3, 0}, // Custom.
	{38400, 1996800, 104, 2, 0}, // Custom. Normalized 2000 MHz.
	{38400, 2064000, 215, 4, 0}, // Custom.
	{38400, 2099200, 164, 3, 0}, // Custom.
	{38400, 2131200, 111, 2, 0}, // JEDEC Standard. (T210B01 official max).
 	{0,     0,       0,   0, 0}
};

static const u32 burst_regs_emc_addr_table[221] = {
	EMC_RC,
	EMC_RFC,
	EMC_RFCPB,
	EMC_REFCTRL2,
	EMC_RFC_SLR,
	EMC_RAS,
	EMC_RP,
	EMC_R2W,
	EMC_W2R,
	EMC_R2P,
	EMC_W2P,
	EMC_R2R,
	EMC_TPPD,
	EMC_CCDMW,
	EMC_RD_RCD,
	EMC_WR_RCD,
	EMC_RRD,
	EMC_REXT,
	EMC_WEXT,
	EMC_WDV_CHK,
	EMC_WDV,
	EMC_WSV,
	EMC_WEV,
	EMC_WDV_MASK,
	EMC_WS_DURATION,
	EMC_WE_DURATION,
	EMC_QUSE,
	EMC_QUSE_WIDTH,
	EMC_IBDLY,
	EMC_OBDLY,
	EMC_EINPUT,
	EMC_MRW6,
	EMC_EINPUT_DURATION,
	EMC_PUTERM_EXTRA,
	EMC_PUTERM_WIDTH,
	EMC_QRST,
	EMC_QSAFE,
	EMC_RDV,
	EMC_RDV_MASK,
	EMC_RDV_EARLY,
	EMC_RDV_EARLY_MASK,
	EMC_REFRESH,
	EMC_BURST_REFRESH_NUM,
	EMC_PRE_REFRESH_REQ_CNT,
	EMC_PDEX2WR,
	EMC_PDEX2RD,
	EMC_PCHG2PDEN,
	EMC_ACT2PDEN,
	EMC_AR2PDEN,
	EMC_RW2PDEN,
	EMC_CKE2PDEN,
	EMC_PDEX2CKE,
	EMC_PDEX2MRR,
	EMC_TXSR,
	EMC_TXSRDLL,
	EMC_TCKE,
	EMC_TCKESR,
	EMC_TPD,
	EMC_TFAW,
	EMC_TRPAB,
	EMC_TCLKSTABLE,
	EMC_TCLKSTOP,
	EMC_MRW7,
	EMC_TREFBW,
	EMC_ODT_WRITE,
	EMC_FBIO_CFG5,
	EMC_FBIO_CFG7,
	EMC_CFG_DIG_DLL,
	EMC_CFG_DIG_DLL_PERIOD,
	EMC_PMACRO_IB_RXRT,
	EMC_CFG_PIPE_1,
	EMC_CFG_PIPE_2,
	EMC_PMACRO_QUSE_DDLL_RANK0_4,
	EMC_PMACRO_QUSE_DDLL_RANK0_5,
	EMC_PMACRO_QUSE_DDLL_RANK1_4,
	EMC_PMACRO_QUSE_DDLL_RANK1_5,
	EMC_MRW8,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_4,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_5,
	EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_0,
	EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_1,
	EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_2,
	EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_3,
	EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_4,
	EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_5,
	EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_0,
	EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_1,
	EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_2,
	EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_3,
	EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_4,
	EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_5,
	EMC_PMACRO_DDLL_LONG_CMD_0,
	EMC_PMACRO_DDLL_LONG_CMD_1,
	EMC_PMACRO_DDLL_LONG_CMD_2,
	EMC_PMACRO_DDLL_LONG_CMD_3,
	EMC_PMACRO_DDLL_LONG_CMD_4,
	EMC_PMACRO_DDLL_SHORT_CMD_0,
	EMC_PMACRO_DDLL_SHORT_CMD_1,
	EMC_PMACRO_DDLL_SHORT_CMD_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_3,
	EMC_TXDSRVTTGEN,
	EMC_FDPD_CTRL_DQ,
	EMC_FDPD_CTRL_CMD,
	EMC_FBIO_SPARE,
	EMC_ZCAL_INTERVAL,
	EMC_ZCAL_WAIT_CNT,
	EMC_MRS_WAIT_CNT,
	EMC_MRS_WAIT_CNT2,
	EMC_AUTO_CAL_CHANNEL,
	EMC_DLL_CFG_0,
	EMC_DLL_CFG_1,
	EMC_PMACRO_AUTOCAL_CFG_COMMON,
	EMC_PMACRO_ZCTRL,
	EMC_CFG,
	EMC_CFG_PIPE,
	EMC_DYN_SELF_REF_CONTROL,
	EMC_QPOP,
	EMC_DQS_BRLSHFT_0,
	EMC_DQS_BRLSHFT_1,
	EMC_CMD_BRLSHFT_2,
	EMC_CMD_BRLSHFT_3,
	EMC_PMACRO_PAD_CFG_CTRL,
	EMC_PMACRO_DATA_PAD_RX_CTRL,
	EMC_PMACRO_CMD_PAD_RX_CTRL,
	EMC_PMACRO_DATA_RX_TERM_MODE,
	EMC_PMACRO_CMD_RX_TERM_MODE,
	EMC_PMACRO_CMD_PAD_TX_CTRL,
	EMC_PMACRO_DATA_PAD_TX_CTRL,
	EMC_PMACRO_COMMON_PAD_TX_CTRL,
	EMC_PMACRO_VTTGEN_CTRL_0,
	EMC_PMACRO_VTTGEN_CTRL_1,
	EMC_PMACRO_VTTGEN_CTRL_2,
	EMC_PMACRO_BRICK_CTRL_RFU1,
	EMC_PMACRO_CMD_BRICK_CTRL_FDPD,
	EMC_PMACRO_BRICK_CTRL_RFU2,
	EMC_PMACRO_DATA_BRICK_CTRL_FDPD,
	EMC_PMACRO_BG_BIAS_CTRL_0,
	EMC_CFG_3,
	EMC_PMACRO_TX_PWRD_0,
	EMC_PMACRO_TX_PWRD_1,
	EMC_PMACRO_TX_PWRD_2,
	EMC_PMACRO_TX_PWRD_3,
	EMC_PMACRO_TX_PWRD_4,
	EMC_PMACRO_TX_PWRD_5,
	EMC_CONFIG_SAMPLE_DELAY,
	EMC_PMACRO_TX_SEL_CLK_SRC_0,
	EMC_PMACRO_TX_SEL_CLK_SRC_1,
	EMC_PMACRO_TX_SEL_CLK_SRC_2,
	EMC_PMACRO_TX_SEL_CLK_SRC_3,
	EMC_PMACRO_TX_SEL_CLK_SRC_4,
	EMC_PMACRO_TX_SEL_CLK_SRC_5,
	EMC_PMACRO_DDLL_BYPASS,
	EMC_PMACRO_DDLL_PWRD_0,
	EMC_PMACRO_DDLL_PWRD_1,
	EMC_PMACRO_DDLL_PWRD_2,
	EMC_PMACRO_CMD_CTRL_0,
	EMC_PMACRO_CMD_CTRL_1,
	EMC_PMACRO_CMD_CTRL_2,
	EMC_TR_TIMING_0,
	EMC_TR_DVFS,
	EMC_TR_CTRL_1,
	EMC_TR_RDV,
	EMC_TR_QPOP,
	EMC_TR_RDV_MASK,
	EMC_MRW14,
	EMC_TR_QSAFE,
	EMC_TR_QRST,
	EMC_TRAINING_CTRL,
	EMC_TRAINING_SETTLE,
	EMC_TRAINING_VREF_SETTLE,
	EMC_TRAINING_CA_FINE_CTRL,
	EMC_TRAINING_CA_CTRL_MISC,
	EMC_TRAINING_CA_CTRL_MISC1,
	EMC_TRAINING_CA_VREF_CTRL,
	EMC_TRAINING_QUSE_CORS_CTRL,
	EMC_TRAINING_QUSE_FINE_CTRL,
	EMC_TRAINING_QUSE_CTRL_MISC,
	EMC_TRAINING_QUSE_VREF_CTRL,
	EMC_TRAINING_READ_FINE_CTRL,
	EMC_TRAINING_READ_CTRL_MISC,
	EMC_TRAINING_READ_VREF_CTRL,
	EMC_TRAINING_WRITE_FINE_CTRL,
	EMC_TRAINING_WRITE_CTRL_MISC,
	EMC_TRAINING_WRITE_VREF_CTRL,
	EMC_TRAINING_MPC,
	EMC_MRW15
};

static const u32 burst_reg_per_ch_emc01_addr_table[8] = {
	EMC0_MRW10,
	EMC1_MRW10,
	EMC0_MRW11,
	EMC1_MRW11,
	EMC0_MRW12,
	EMC1_MRW12,
	EMC0_MRW13,
	EMC1_MRW13
};

static const u32 vref_perch_regs_emc01_addr_table[4] = {
	EMC0_TRAINING_OPT_DQS_IB_VREF_RANK0,
	EMC1_TRAINING_OPT_DQS_IB_VREF_RANK0,
	EMC0_TRAINING_OPT_DQS_IB_VREF_RANK1,
	EMC1_TRAINING_OPT_DQS_IB_VREF_RANK1
};

static const u32 training_mod_regs_emc01_addr_table[20] = {
	EMC0_TRAINING_RW_OFFSET_IB_BYTE0,
	EMC1_TRAINING_RW_OFFSET_IB_BYTE0,
	EMC0_TRAINING_RW_OFFSET_IB_BYTE1,
	EMC1_TRAINING_RW_OFFSET_IB_BYTE1,
	EMC0_TRAINING_RW_OFFSET_IB_BYTE2,
	EMC1_TRAINING_RW_OFFSET_IB_BYTE2,
	EMC0_TRAINING_RW_OFFSET_IB_BYTE3,
	EMC1_TRAINING_RW_OFFSET_IB_BYTE3,
	EMC0_TRAINING_RW_OFFSET_IB_MISC,
	EMC1_TRAINING_RW_OFFSET_IB_MISC,
	EMC0_TRAINING_RW_OFFSET_OB_BYTE0,
	EMC1_TRAINING_RW_OFFSET_OB_BYTE0,
	EMC0_TRAINING_RW_OFFSET_OB_BYTE1,
	EMC1_TRAINING_RW_OFFSET_OB_BYTE1,
	EMC0_TRAINING_RW_OFFSET_OB_BYTE2,
	EMC1_TRAINING_RW_OFFSET_OB_BYTE2,
	EMC0_TRAINING_RW_OFFSET_OB_BYTE3,
	EMC1_TRAINING_RW_OFFSET_OB_BYTE3,
	EMC0_TRAINING_RW_OFFSET_OB_MISC,
	EMC1_TRAINING_RW_OFFSET_OB_MISC
};

static const u32 trim_regs_emc_addr_table[138] = {
	EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_0,
	EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_1,
	EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_2,
	EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_3,
	EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_0,
	EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_1,
	EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_2,
	EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_3,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_0,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_1,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_2,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_0,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_1,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_2,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_0,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_1,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_2,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_0,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_1,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_2,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_0,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_1,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_2,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_0,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_1,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_2,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_0,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_1,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_2,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_0,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_1,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_2,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_0,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_1,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_2,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_0,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_1,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_2,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_0,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_1,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_2,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_0,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_1,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_2,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_0,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_1,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_2,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_0,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_1,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_2,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_0,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_1,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_2,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_0,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_1,
	EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_2,
	EMC_PMACRO_IB_VREF_DQS_0,
	EMC_PMACRO_IB_VREF_DQS_1,
	EMC_PMACRO_IB_VREF_DQ_0,
	EMC_PMACRO_IB_VREF_DQ_1,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_4,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_5,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_2,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_0,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_1,
	EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_2,
	EMC_PMACRO_QUSE_DDLL_RANK0_0,
	EMC_PMACRO_QUSE_DDLL_RANK0_1,
	EMC_PMACRO_QUSE_DDLL_RANK0_2,
	EMC_PMACRO_QUSE_DDLL_RANK0_3,
	EMC_PMACRO_QUSE_DDLL_RANK1_0,
	EMC_PMACRO_QUSE_DDLL_RANK1_1,
	EMC_PMACRO_QUSE_DDLL_RANK1_2,
	EMC_PMACRO_QUSE_DDLL_RANK1_3
};

static const u32 trim_perch_regs_emc01_addr_table[10] = {
	EMC0_CMD_BRLSHFT_0,
	EMC1_CMD_BRLSHFT_1,
	EMC0_DATA_BRLSHFT_0,
	EMC1_DATA_BRLSHFT_0,
	EMC0_DATA_BRLSHFT_1,
	EMC1_DATA_BRLSHFT_1,
	EMC0_QUSE_BRLSHFT_0,
	EMC1_QUSE_BRLSHFT_1,
	EMC0_QUSE_BRLSHFT_2,
	EMC1_QUSE_BRLSHFT_3
};

static const u32 burst_mc_regs_addr_table[33] = {
	MC_EMEM_ARB_CFG,
	MC_EMEM_ARB_OUTSTANDING_REQ,
	MC_EMEM_ARB_REFPB_HP_CTRL,
	MC_EMEM_ARB_REFPB_BANK_CTRL,
	MC_EMEM_ARB_TIMING_RCD,
	MC_EMEM_ARB_TIMING_RP,
	MC_EMEM_ARB_TIMING_RC,
	MC_EMEM_ARB_TIMING_RAS,
	MC_EMEM_ARB_TIMING_FAW,
	MC_EMEM_ARB_TIMING_RRD,
	MC_EMEM_ARB_TIMING_RAP2PRE,
	MC_EMEM_ARB_TIMING_WAP2PRE,
	MC_EMEM_ARB_TIMING_R2R,
	MC_EMEM_ARB_TIMING_W2W,
	MC_EMEM_ARB_TIMING_R2W,
	MC_EMEM_ARB_TIMING_CCDMW,
	MC_EMEM_ARB_TIMING_W2R,
	MC_EMEM_ARB_TIMING_RFCPB,
	MC_EMEM_ARB_DA_TURNS,
	MC_EMEM_ARB_DA_COVERS,
	MC_EMEM_ARB_MISC0,
	MC_EMEM_ARB_MISC1,
	MC_EMEM_ARB_MISC2,
	MC_EMEM_ARB_RING1_THROTTLE,
	MC_EMEM_ARB_DHYST_CTRL,
	MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_0,
	MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_1,
	MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_2,
	MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_3,
	MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_4,
	MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_5,
	MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6,
	MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7
};

static const u32 la_scale_regs_mc_addr_table[24] = {
	MC_MLL_MPCORER_PTSA_RATE,
	MC_FTOP_PTSA_RATE,
	MC_PTSA_GRANT_DECREMENT,
	MC_LATENCY_ALLOWANCE_XUSB_0,
	MC_LATENCY_ALLOWANCE_XUSB_1,
	MC_LATENCY_ALLOWANCE_TSEC_0,
	MC_LATENCY_ALLOWANCE_SDMMCA_0,
	MC_LATENCY_ALLOWANCE_SDMMCAA_0,
	MC_LATENCY_ALLOWANCE_SDMMC_0,
	MC_LATENCY_ALLOWANCE_SDMMCAB_0,
	MC_LATENCY_ALLOWANCE_PPCS_0,
	MC_LATENCY_ALLOWANCE_PPCS_1,
	MC_LATENCY_ALLOWANCE_MPCORE_0,
	MC_LATENCY_ALLOWANCE_HC_0,
	MC_LATENCY_ALLOWANCE_HC_1,
	MC_LATENCY_ALLOWANCE_AVPC_0,
	MC_LATENCY_ALLOWANCE_GPU_0,
	MC_LATENCY_ALLOWANCE_GPU2_0,
	MC_LATENCY_ALLOWANCE_NVENC_0,
	MC_LATENCY_ALLOWANCE_NVDEC_0,
	MC_LATENCY_ALLOWANCE_VIC_0,
	MC_LATENCY_ALLOWANCE_VI2_0,
	MC_LATENCY_ALLOWANCE_ISP2_0,
	MC_LATENCY_ALLOWANCE_ISP2_1
};

static const u32 periodic_training_addr[10] =
{
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2,
	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3,
	EMC_DATA_BRLSHFT_0,
	EMC_DATA_BRLSHFT_1
};

static const u32 ram_pattern_dq_table[0x500] = {
	/* DQ RAM Patterns Table 0 */
	0x18181818, 0x61616161, 0x85858585, 0x14141414, 0x51515151,
	0x47474747, 0x1E1E1E1E, 0x79797979, 0xE5E5E5E5, 0x94949494,
	0x51515151, 0x46464646, 0x19191919, 0x67676767, 0x9C9C9C9C,
	0x71717171, 0xC5C5C5C5, 0x17171717, 0x5F5F5F5F, 0x7E7E7E7E,
	0xFBFBFBFB, 0xEDEDEDED, 0xB4B4B4B4, 0xD2D2D2D2, 0x48484848,
	0x21212121, 0x85858585, 0x16161616, 0x59595959, 0x66666666,
	0x9A9A9A9A, 0x69696969, 0xA4A4A4A4, 0x93939393, 0x4F4F4F4F,
	0x3F3F3F3F, 0xFCFCFCFC, 0xF3F3F3F3, 0xCDCDCDCD, 0x37373737,
	0xDCDCDCDC, 0x70707070, 0xC3C3C3C3, 0x0F0F0F0F, 0x3E3E3E3E,
	0xFAFAFAFA, 0xEBEBEBEB, 0xACACACAC, 0xB3B3B3B3, 0xCCCCCCCC,
	0x31313131, 0xC5C5C5C5, 0x15151515, 0x57575757, 0x5F5F5F5F,
	0x7F7F7F7F, 0xFDFDFDFD, 0xF4F4F4F4, 0xD0D0D0D0, 0x42424242,
	0x08080808, 0x23232323, 0x8F8F8F8F, 0x3F3F3F3F, 0x18181818,
	0x61616161, 0x85858585, 0x14141414, 0x51515151, 0x47474747,
	0x1E1E1E1E, 0x79797979, 0xE5E5E5E5, 0x94949494, 0x51515151,
	0x46464646, 0x19191919, 0x67676767, 0x9C9C9C9C, 0x71717171,
	0xC5C5C5C5, 0x17171717, 0x5F5F5F5F, 0x7E7E7E7E, 0xFBFBFBFB,
	0xEDEDEDED, 0xB4B4B4B4, 0xD2D2D2D2, 0x48484848, 0x21212121,
	0x85858585, 0x16161616, 0x59595959, 0x66666666, 0x9A9A9A9A,
	0x69696969, 0xA4A4A4A4, 0x93939393, 0x4F4F4F4F, 0x3F3F3F3F,
	0xFCFCFCFC, 0xF3F3F3F3, 0xCDCDCDCD, 0x37373737, 0xDCDCDCDC,
	0x70707070, 0xC3C3C3C3, 0x0F0F0F0F, 0x3E3E3E3E, 0xFAFAFAFA,
	0xEBEBEBEB, 0xACACACAC, 0xB3B3B3B3, 0xCCCCCCCC, 0x31313131,
	0xC5C5C5C5, 0x15151515, 0x57575757, 0x5F5F5F5F, 0x7F7F7F7F,
	0xFDFDFDFD, 0xF4F4F4F4, 0xD0D0D0D0, 0x42424242, 0x08080808,
	0x23232323, 0x8F8F8F8F, 0x3F3F3F3F, 0x06060606, 0x18181818,
	0x21212121, 0x05050505, 0x14141414, 0x11111111, 0x07070707,
	0x1E1E1E1E, 0x39393939, 0x25252525, 0x14141414, 0x11111111,
	0x06060606, 0x19191919, 0x27272727, 0x1C1C1C1C, 0x31313131,
	0x05050505, 0x17171717, 0x1F1F1F1F, 0x3E3E3E3E, 0x3B3B3B3B,
	0x2D2D2D2D, 0x34343434, 0x12121212, 0x08080808, 0x21212121,
	0x05050505, 0x16161616, 0x19191919, 0x26262626, 0x1A1A1A1A,
	0x29292929, 0x24242424, 0x13131313, 0x0F0F0F0F, 0x3F3F3F3F,
	0x3C3C3C3C, 0x33333333, 0x0D0D0D0D, 0x37373737, 0x1C1C1C1C,
	0x30303030, 0x03030303, 0x0F0F0F0F, 0x3E3E3E3E, 0x3A3A3A3A,
	0x2B2B2B2B, 0x2C2C2C2C, 0x33333333, 0x0C0C0C0C, 0x31313131,
	0x05050505, 0x15151515, 0x17171717, 0x1F1F1F1F, 0x3F3F3F3F,
	0x3D3D3D3D, 0x34343434, 0x10101010, 0x02020202, 0x08080808,
	0x23232323, 0x0F0F0F0F, 0x06060606, 0x18181818, 0x21212121,
	0x05050505, 0x14141414, 0x11111111, 0x07070707, 0x1E1E1E1E,
	0x39393939, 0x25252525, 0x14141414, 0x11111111, 0x06060606,
	0x19191919, 0x27272727, 0x1C1C1C1C, 0x31313131, 0x05050505,
	0x17171717, 0x1F1F1F1F, 0x3E3E3E3E, 0x3B3B3B3B, 0x2D2D2D2D,
	0x34343434, 0x12121212, 0x08080808, 0x21212121, 0x05050505,
	0x16161616, 0x19191919, 0x26262626, 0x1A1A1A1A, 0x29292929,
	0x24242424, 0x13131313, 0x0F0F0F0F, 0x3F3F3F3F, 0x3C3C3C3C,
	0x33333333, 0x0D0D0D0D, 0x37373737, 0x1C1C1C1C, 0x30303030,
	0x03030303, 0x0F0F0F0F, 0x3E3E3E3E, 0x3A3A3A3A, 0x2B2B2B2B,
	0x2C2C2C2C, 0x33333333, 0x0C0C0C0C, 0x31313131, 0x05050505,
	0x15151515, 0x17171717, 0x1F1F1F1F, 0x3F3F3F3F, 0x3D3D3D3D,
	0x34343434, 0x10101010, 0x02020202, 0x08080808, 0x23232323,
	0x0F0F0F0F,

	/* DQ RAM Patterns Table 1 */
	0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000,
	0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF,
	0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000,
	0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
	0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000,
	0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
	0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
	0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000,
	0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
	0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000,
	0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
	0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000,
	0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000,
	0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
	0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000,
	0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF,
	0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
	0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF,
	0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
	0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
	0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF,
	0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
	0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
	0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
	0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000,
	0x3F3F3F3F, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
	0x00000000, 0x3F3F3F3F, 0x3F3F3F3F, 0x00000000, 0x00000000,
	0x00000000, 0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F,
	0x00000000, 0x00000000, 0x00000000, 0x3F3F3F3F, 0x3F3F3F3F,
	0x3F3F3F3F, 0x3F3F3F3F, 0x00000000, 0x00000000, 0x3F3F3F3F,
	0x00000000, 0x00000000, 0x00000000, 0x3F3F3F3F, 0x00000000,
	0x3F3F3F3F, 0x3F3F3F3F, 0x00000000, 0x00000000, 0x3F3F3F3F,
	0x3F3F3F3F, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000,
	0x3F3F3F3F, 0x00000000, 0x00000000, 0x3F3F3F3F, 0x3F3F3F3F,
	0x3F3F3F3F, 0x3F3F3F3F, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F,
	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3F3F3F3F,
	0x3F3F3F3F, 0x3F3F3F3F, 0x00000000, 0x00000000, 0x00000000,
	0x3F3F3F3F, 0x00000000, 0x00000000, 0x00000000, 0x3F3F3F3F,
	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
	0x3F3F3F3F, 0x3F3F3F3F, 0x00000000, 0x00000000, 0x00000000,
	0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000,
	0x00000000, 0x00000000, 0x3F3F3F3F, 0x3F3F3F3F, 0x3F3F3F3F,
	0x3F3F3F3F, 0x00000000, 0x00000000, 0x3F3F3F3F, 0x00000000,
	0x00000000, 0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F,
	0x3F3F3F3F, 0x00000000, 0x00000000, 0x3F3F3F3F, 0x3F3F3F3F,
	0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F,
	0x00000000, 0x00000000, 0x3F3F3F3F, 0x3F3F3F3F, 0x3F3F3F3F,
	0x3F3F3F3F, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000,
	0x00000000, 0x00000000, 0x00000000, 0x3F3F3F3F, 0x3F3F3F3F,
	0x3F3F3F3F, 0x00000000, 0x00000000, 0x00000000, 0x3F3F3F3F,
	0x00000000,

	/* DQ RAM Patterns Table 2 */
	0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
	0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
	0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
	0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
	0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
	0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
	0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
	0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
	0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
	0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
	0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
	0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
	0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
	0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
	0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
	0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
	0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
	0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
	0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
	0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
	0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
	0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
	0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
	0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,
	0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
	0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x3F3F3F3F,
	0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000,
	0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F,
	0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000,
	0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F,
	0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000,
	0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F,
	0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000,
	0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F,
	0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000,
	0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F,
	0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000,
	0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F,
	0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000,
	0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F,
	0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000,
	0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F,
	0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000,
	0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F,
	0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000,
	0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F,
	0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000,
	0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F,
	0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000,
	0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F,
	0x00000000, 0x3F3F3F3F, 0x00000000, 0x3F3F3F3F, 0x00000000,
	0x3F3F3F3F,

	/* DQ RAM Patterns Table 3 */
	0x80808080, 0x00000000, 0x80808080, 0x00000000, 0x80808080,
	0x00000000, 0x80808080, 0x40404040, 0x00000000, 0x40404040,
	0x00000000, 0x40404040, 0x00000000, 0x40404040, 0x20202020,
	0x00000000, 0x20202020, 0x00000000, 0x20202020, 0x00000000,
	0x20202020, 0x10101010, 0x00000000, 0x10101010, 0x00000000,
	0x10101010, 0x00000000, 0x10101010, 0x08080808, 0x00000000,
	0x08080808, 0x00000000, 0x08080808, 0x00000000, 0x08080808,
	0x04040404, 0x00000000, 0x04040404, 0x00000000, 0x04040404,
	0x00000000, 0x04040404, 0x02020202, 0x00000000, 0x02020202,
	0x00000000, 0x02020202, 0x00000000, 0x02020202, 0x01010101,
	0x00000000, 0x01010101, 0x00000000, 0x01010101, 0x00000000,
	0x01010101, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x80808080,
	0x00000000, 0x80808080, 0x00000000, 0x80808080, 0x00000000,
	0x80808080, 0x40404040, 0x00000000, 0x40404040, 0x00000000,
	0x40404040, 0x00000000, 0x40404040, 0x20202020, 0x00000000,
	0x20202020, 0x00000000, 0x20202020, 0x00000000, 0x20202020,
	0x10101010, 0x00000000, 0x10101010, 0x00000000, 0x10101010,
	0x00000000, 0x10101010, 0x08080808, 0x00000000, 0x08080808,
	0x00000000, 0x08080808, 0x00000000, 0x08080808, 0x04040404,
	0x00000000, 0x04040404, 0x00000000, 0x04040404, 0x00000000,
	0x04040404, 0x02020202, 0x00000000, 0x02020202, 0x00000000,
	0x02020202, 0x00000000, 0x02020202, 0x01010101, 0x00000000,
	0x01010101, 0x00000000, 0x01010101, 0x00000000, 0x01010101,
	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x20202020,
	0x00000000, 0x20202020, 0x00000000, 0x20202020, 0x00000000,
	0x20202020, 0x00000000, 0x20202020, 0x00000000, 0x10101010,
	0x00000000, 0x10101010, 0x00000000, 0x10101010, 0x00000000,
	0x10101010, 0x00000000, 0x10101010, 0x00000000, 0x08080808,
	0x00000000, 0x08080808, 0x00000000, 0x08080808, 0x00000000,
	0x08080808, 0x00000000, 0x08080808, 0x00000000, 0x04040404,
	0x00000000, 0x04040404, 0x00000000, 0x04040404, 0x00000000,
	0x04040404, 0x00000000, 0x04040404, 0x00000000, 0x02020202,
	0x00000000, 0x02020202, 0x00000000, 0x02020202, 0x00000000,
	0x02020202, 0x00000000, 0x02020202, 0x00000000, 0x01010101,
	0x00000000, 0x01010101, 0x00000000, 0x01010101, 0x00000000,
	0x01010101, 0x00000000, 0x01010101, 0x00000000, 0x00000000,
	0x00000000, 0x00000000, 0x00000000, 0x20202020, 0x00000000,
	0x20202020, 0x00000000, 0x20202020, 0x00000000, 0x20202020,
	0x00000000, 0x20202020, 0x00000000, 0x10101010, 0x00000000,
	0x10101010, 0x00000000, 0x10101010, 0x00000000, 0x10101010,
	0x00000000, 0x10101010, 0x00000000, 0x08080808, 0x00000000,
	0x08080808, 0x00000000, 0x08080808, 0x00000000, 0x08080808,
	0x00000000, 0x08080808, 0x00000000, 0x04040404, 0x00000000,
	0x04040404, 0x00000000, 0x04040404, 0x00000000, 0x04040404,
	0x00000000, 0x04040404, 0x00000000, 0x02020202, 0x00000000,
	0x02020202, 0x00000000, 0x02020202, 0x00000000, 0x02020202,
	0x00000000, 0x02020202, 0x00000000, 0x01010101, 0x00000000,
	0x01010101, 0x00000000, 0x01010101, 0x00000000, 0x01010101,
	0x00000000, 0x01010101, 0x00000000, 0x00000000, 0x00000000,
	0x00000000,

	/* DQ RAM Patterns Table 4 */
	0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA,
	0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555,
	0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC,
	0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333,
	0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA,
	0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555,
	0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC,
	0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333,
	0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA,
	0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555,
	0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC,
	0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333,
	0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA,
	0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555,
	0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC,
	0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333,
	0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA,
	0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555,
	0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC,
	0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333,
	0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA,
	0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555,
	0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC,
	0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333,
	0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA,
	0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555,
	0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC,
	0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333,
	0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA,
	0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555,
	0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC,
	0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333,
	0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA,
	0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555,
	0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC,
	0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333,
	0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA,
	0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555,
	0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC,
	0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333,
	0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA,
	0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555,
	0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC,
	0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333,
	0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA,
	0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555,
	0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC,
	0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333,
	0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA,
	0x55555555, 0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555,
	0xCCCCCCCC, 0x33333333, 0xAAAAAAAA, 0x55555555, 0xCCCCCCCC,
	0x33333333
};

static const u32 ram_pattern_dmi_table[0x500] = {
	/* DMI RAM Patterns Table 0 */
	0xF, 0xF, 0x0, 0xF, 0xF, 0x0, 0xF, 0xF,
	0x0, 0xF, 0x0, 0xF, 0xF, 0x0, 0xF, 0xF,
	0xF, 0xF, 0x0, 0xF, 0xF, 0x0, 0x0, 0x0,
	0xF, 0xF, 0x0, 0xF, 0x0, 0x0, 0xF, 0x0,
	0xF, 0xF, 0xF, 0x0, 0xF, 0xF, 0xF, 0x0,
	0x0, 0xF, 0xF, 0x0, 0x0, 0xF, 0x0, 0xF,
	0x0, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF,
	0x0, 0x0, 0x0, 0x0, 0xF, 0xF, 0xF, 0x0,
	0xF, 0xF, 0x0, 0xF, 0xF, 0x0, 0xF, 0xF,
	0x0, 0xF, 0x0, 0xF, 0xF, 0x0, 0xF, 0xF,
	0xF, 0xF, 0x0, 0xF, 0xF, 0x0, 0x0, 0x0,
	0xF, 0xF, 0x0, 0xF, 0x0, 0x0, 0xF, 0x0,
	0xF, 0xF, 0xF, 0x0, 0xF, 0xF, 0xF, 0x0,
	0x0, 0xF, 0xF, 0x0, 0x0, 0xF, 0x0, 0xF,
	0x0, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF,
	0x0, 0x0, 0x0, 0x0, 0xF, 0xF, 0xF, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,

	/* DMI RAM Patterns Table 1 */
	0x0, 0x0, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0,
	0xF, 0xF, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0,
	0xF, 0x0, 0x0, 0x0, 0xF, 0xF, 0xF, 0xF,
	0x0, 0x0, 0xF, 0x0, 0x0, 0x0, 0xF, 0x0,
	0xF, 0xF, 0x0, 0x0, 0xF, 0xF, 0xF, 0x0,
	0xF, 0x0, 0xF, 0x0, 0x0, 0xF, 0xF, 0xF,
	0xF, 0xF, 0x0, 0xF, 0x0, 0x0, 0x0, 0x0,
	0xF, 0xF, 0xF, 0x0, 0x0, 0x0, 0xF, 0x0,
	0x0, 0x0, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0,
	0xF, 0xF, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0,
	0xF, 0x0, 0x0, 0x0, 0xF, 0xF, 0xF, 0xF,
	0x0, 0x0, 0xF, 0x0, 0x0, 0x0, 0xF, 0x0,
	0xF, 0xF, 0x0, 0x0, 0xF, 0xF, 0xF, 0x0,
	0xF, 0x0, 0xF, 0x0, 0x0, 0xF, 0xF, 0xF,
	0xF, 0xF, 0x0, 0xF, 0x0, 0x0, 0x0, 0x0,
	0xF, 0xF, 0xF, 0x0, 0x0, 0x0, 0xF, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,

	/* DMI RAM Patterns Table 2 */
	0x0, 0xF, 0x0, 0xF, 0x0, 0xF, 0x0, 0xF,
	0x0, 0xF, 0x0, 0xF, 0x0, 0xF, 0x0, 0xF,
	0x0, 0xF, 0x0, 0xF, 0x0, 0xF, 0x0, 0xF,
	0x0, 0xF, 0x0, 0xF, 0x0, 0xF, 0x0, 0xF,
	0x0, 0xF, 0x0, 0xF, 0x0, 0xF, 0x0, 0xF,
	0x0, 0xF, 0x0, 0xF, 0x0, 0xF, 0x0, 0xF,
	0x0, 0xF, 0x0, 0xF, 0x0, 0xF, 0x0, 0xF,
	0x0, 0xF, 0x0, 0xF, 0x0, 0xF, 0x0, 0xF,
	0x0, 0xF, 0x0, 0xF, 0x0, 0xF, 0x0, 0xF,
	0x0, 0xF, 0x0, 0xF, 0x0, 0xF, 0x0, 0xF,
	0x0, 0xF, 0x0, 0xF, 0x0, 0xF, 0x0, 0xF,
	0x0, 0xF, 0x0, 0xF, 0x0, 0xF, 0x0, 0xF,
	0x0, 0xF, 0x0, 0xF, 0x0, 0xF, 0x0, 0xF,
	0x0, 0xF, 0x0, 0xF, 0x0, 0xF, 0x0, 0xF,
	0x0, 0xF, 0x0, 0xF, 0x0, 0xF, 0x0, 0xF,
	0x0, 0xF, 0x0, 0xF, 0x0, 0xF, 0x0, 0xF,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,

	/* DMI RAM Patterns Table 3 */
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0xF, 0x0, 0xF, 0x0, 0xF, 0x0, 0xF, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0xF, 0x0, 0xF, 0x0, 0xF, 0x0, 0xF, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,

	/* DMI RAM Patterns Table 4 */
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3,
	0xA, 0x5, 0xC, 0x3, 0xA, 0x5, 0xC, 0x3
};

static void _usleep(u32 microseconds)
{
	u32 start = TMR(0x10);
	while ((u32)(TMR(0x10) - start) <= microseconds)
		;
}

static u32 div_o3(u32 a, u32 b)
{
	u32 result = a / b;

	if ((b * result) < a)
		return result + 1;
	else
		return result;
}

static u32 _actual_osc_clocks(u32 in)
{
	u32 actual_clock;

	actual_clock = 16 * in;
	if (in > 63)
	{
		actual_clock = 2048;
		if (in > 127)
		{
			if (in >= 192)
				actual_clock = 8192;
			else
				actual_clock = 4096;
		}
	}

	return actual_clock;
}

static void _ccfifo_write(u32 addr, u32 data_val, u32 delay) //addr and delay are u16
{
	EMC(EMC_CCFIFO_DATA) = data_val;
	EMC(EMC_CCFIFO_ADDR) = (addr & 0xffff) | ((delay & 0x7FFF) << 16) | (1 << 31);
}

static bool _wait_emc_status(u32 reg_offset, u32 bit_mask, bool updated_state, u32 emc_channel)
{
	bool err = true;

	for (u32 i = 0; i < EMC_STATUS_UPDATE_TIMEOUT; i++)
	{
		if (emc_channel)
		{
			if (emc_channel != 1)
				goto done;

			if (((EMC_CH1(reg_offset) & bit_mask) != 0) == updated_state)
			{
				err = false;
				break;
			}
		}
		else if (((EMC(reg_offset) & bit_mask) != 0) == updated_state)
		{
			err = false;
			break;
		}
		_usleep(1);
	}

done:
	return err;
}

static void _request_mmr_data(u32 data, bool dual_channel)
{
	EMC(EMC_MRR) = data;
	_wait_emc_status(EMC_EMC_STATUS, MRR_DIVLD, true, EMC_CHANNEL0);
	if (dual_channel)
		_wait_emc_status(EMC_EMC_STATUS, MRR_DIVLD, true, EMC_CHANNEL1);
}

static void _start_periodic_compensation()
{
	EMC(EMC_MPC) = 0x4B;
	(void)EMC(EMC_MPC);
}

static bool _timing_update(u32 dual_channel)
{
	bool err = 0;

	EMC(EMC_TIMING_CONTROL) = 1;
	err = _wait_emc_status(EMC_EMC_STATUS, TIMING_UPDATE_STALLED, false, EMC_CHANNEL0);
	if (dual_channel)
		err |= _wait_emc_status(EMC_EMC_STATUS, TIMING_UPDATE_STALLED, false, EMC_CHANNEL1);

	return err;
}

static u32 _get_dram_temperature()
{
	u32 mr4_0 = 0;
	u32 mr4_1 = 0;

	bool channel1_enabled = (EMC(EMC_FBIO_CFG7) >> 2) & 1;
	u32 emc_cfg_o = EMC(EMC_CFG);

	_wait_emc_status(EMC_EMC_STATUS, MRR_DIVLD, false, EMC_CHANNEL0);

	if (emc_cfg_o & 0x20000000)
	{
		EMC(EMC_CFG) = emc_cfg_o & 0xDFFFFFFF;
		_timing_update(channel1_enabled);
	}

	_request_mmr_data(0x80040000, EMC_CHANNEL0);
	mr4_0 = EMC(EMC_MRR) & 0xFFFF;

	if (mr4_0 < 0xF001)
		mr4_0 &= 0x7;
	else
	{
		mr4_0 = -1;
		goto out;
	}

	if (channel1_enabled)
	{
		_request_mmr_data(0x40040000, EMC_CHANNEL1);
		mr4_1 = EMC(EMC_MRR);

		if (mr4_1 < 0xF001)
			mr4_1 &= 0x7;
		else
			goto out;

		if (mr4_1 > mr4_0)
			mr4_0 = mr4_1;
	}

out:
	if (emc_cfg_o & 0x20000000)
	{
		EMC(EMC_CFG) = emc_cfg_o;
		_timing_update(channel1_enabled);
	}

	return mr4_0;
}

static u32 _pllm_clk_base_cfg(u32 rate_KHz, u32 clk_src_emc, bool new_src_is_PLLMB)
{
	u32 dividers = 0;
	u32 i = 0;
	u32 pll_ref = 38400; // Only 38.4MHz crystal is supported for T210.

	pllm_clk_config_t *pllm_clk_config;

	for (i = 0; pllm_clk_config_table[i].pll_osc_in; i++)
	{
		if (pllm_clk_config_table[i].pll_osc_in == pll_ref && pllm_clk_config_table[i].pll_out == rate_KHz)
			break;
	}

	pllm_clk_config = &pllm_clk_config_table[i];
	if (pllm_clk_config->pll_osc_in)
	{
		dividers = pllm_clk_config->pll_input_div | (pllm_clk_config->pll_feedback_div << 8) | ((pllm_clk_config->pll_post_div & 0x1F) << 20);
		if (new_src_is_PLLMB)
		{
			CLOCK(CLK_RST_CONTROLLER_PLLMB_BASE) = dividers;
			CLOCK(CLK_RST_CONTROLLER_PLLMB_BASE) |= PLLM_ENABLE;

			if ((clk_src_emc >> EMC_2X_CLK_SRC_SHIFT) == PLLM_UD)
				clk_src_emc = (clk_src_emc & 0x1FFFFFFF) | (PLLMB_UD << EMC_2X_CLK_SRC_SHIFT);
			else if (!(clk_src_emc >> EMC_2X_CLK_SRC_SHIFT))
				clk_src_emc |= (PLLMB_OUT0 << EMC_2X_CLK_SRC_SHIFT);

			while (!(CLOCK(CLK_RST_CONTROLLER_PLLMB_BASE) & PLLM_LOCK))
				;
		}
		else
		{
			CLOCK(CLK_RST_CONTROLLER_PLLM_BASE) = dividers;
			CLOCK(CLK_RST_CONTROLLER_PLLM_MISC2) |= PLLM_EN_LCKDET;
			CLOCK(CLK_RST_CONTROLLER_PLLM_BASE) |= PLLM_ENABLE;

			if ((clk_src_emc >> EMC_2X_CLK_SRC_SHIFT) == PLLM_UD)
				clk_src_emc = (clk_src_emc & 0x1FFFFFFF) | (PLLM_UD << EMC_2X_CLK_SRC_SHIFT);
			while (!(CLOCK(CLK_RST_CONTROLLER_PLLM_BASE) & PLLM_LOCK))
				;
		}
	}
	return clk_src_emc;
}

static void _change_dll_src(emc_table_t *mtc_table_entry, u32 clk_src_emc)
{
	u32 emc_2x_clk_src = clk_src_emc >> EMC_2X_CLK_SRC_SHIFT;

	u32 dll_setting = ((((mtc_table_entry->dll_clk_src & 0x1FFFFFFF)
		| (emc_2x_clk_src << EMC_2X_CLK_SRC_SHIFT)) & 0xFFFFFF00)
		| (clk_src_emc & 0xFF)) & 0xFFFFF3FF;

	if (emc_2x_clk_src == PLLMB_UD)
		dll_setting |= EMC_DLL_PLLM_VCOB;
	else if (emc_2x_clk_src != PLLM_UD)
		dll_setting |= EMC_DLL_SWITCH_OUT;

	CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_EMC_DLL) = dll_setting;

	// Commit clock write.
	(void)CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_X);
	_usleep(2);

	// Enable/Disable EMC DLL.
	if (mtc_table_entry->clk_out_enb_x_0_clk_enb_emc_dll)
		CLOCK(CLK_RST_CONTROLLER_CLK_ENB_X_SET) = (1 << 14);
	else
		CLOCK(CLK_RST_CONTROLLER_CLK_ENB_X_CLR) = (1 << 14);

	// Commit clock write.
	(void)CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_X);
	_usleep(2);
}

static u32 _digital_dll_prelock(emc_table_t *mtc_table_entry, u32 needs_tristate_training, u32 selected_clk_src_emc)
{
	u32 dual_channel = (EMC(EMC_FBIO_CFG7) >> 1) & ((EMC(EMC_FBIO_CFG7) >> 2) & 1);

	EMC(EMC_CFG_DIG_DLL) = (EMC(EMC_CFG_DIG_DLL) & 0xFFFFF824) | 0x3C8;

	_timing_update(dual_channel);

	while (EMC(EMC_CFG_DIG_DLL) & 1)
		;
	if (dual_channel)
		while (EMC_CH1(EMC_CFG_DIG_DLL) & 1)
			;

	EMC(EMC_DLL_CFG_0) = mtc_table_entry->burst_regs.emc_dll_cfg_0_idx;
	EMC(EMC_DLL_CFG_1) = mtc_table_entry->burst_regs.emc_dll_cfg_1_idx;

	_change_dll_src(mtc_table_entry, selected_clk_src_emc);

	EMC(EMC_CFG_DIG_DLL) |= 1;

	_timing_update(dual_channel);

	while (!(EMC(EMC_CFG_DIG_DLL) & 1))
		;
	if (dual_channel)
		while (!(EMC_CH1(EMC_CFG_DIG_DLL) & 1))
			;

	while ((((EMC(EMC_DIG_DLL_STATUS) >> 17) & 1) ^ 1) | (((EMC(EMC_DIG_DLL_STATUS) >> 15) & 1) ^ 1))
		;

	if (needs_tristate_training)
	{
		EMC(EMC_DBG) |= 2u;
		EMC(EMC_CFG_DIG_DLL) &= 0xFFFFFFFE; //Disable CFG_DLL_EN: [PMC] Enable digital DLL.
		EMC(EMC_DBG) &= 0xFFFFFFFD;

		while (EMC(EMC_CFG_DIG_DLL) & 1)
			;
		if (dual_channel)
			while (EMC_CH1(EMC_CFG_DIG_DLL) & 1)
				;
	}

	return EMC(EMC_DIG_DLL_STATUS) & 0x7FF;
}

static void _digital_dll_disable()
{
	bool dual_channel = (EMC(EMC_FBIO_CFG7) >> 1) & ((EMC(EMC_FBIO_CFG7) >> 2) & 1);

	EMC(EMC_CFG_DIG_DLL) &= 0xFFFFFFFE;

	_timing_update(dual_channel);

	while (EMC(EMC_CFG_DIG_DLL) & 1)
		;
	if (dual_channel)
		while (EMC_CH1(EMC_CFG_DIG_DLL) & 1)
			;
}

static void _digital_dll_enable_rs(u32 channel1_enabled)
{
	EMC(EMC_CFG_DIG_DLL) = (EMC(EMC_CFG_DIG_DLL) & 0xFFFFFF24) | 0x89;

	_timing_update(channel1_enabled);

	while (!(EMC(EMC_CFG_DIG_DLL) & 1))
		;
	if (channel1_enabled)
		while (!(EMC_CH1(EMC_CFG_DIG_DLL) & 1))
			;
}

static u32 _dvfs_power_ramp_down(bool flip_backward, emc_table_t *src_emc_table_entry, emc_table_t *dst_emc_table_entry, u32 src_clock_period)
{
	u32 pmacro_cmd_pad;
	u32 pmacro_rfu1;
	u32 pmacro_cfg5;
	u32 pmacro_common_tx;
	u32 pmacro_dq_pad;

	u32 src_clk_per_pc = (100000 / src_clock_period) + 1;

	if (flip_backward)
	{
		pmacro_cmd_pad = dst_emc_table_entry->burst_regs.emc_pmacro_cmd_pad_tx_ctrl_idx;
		pmacro_dq_pad = dst_emc_table_entry->burst_regs.emc_pmacro_data_pad_tx_ctrl_idx;
		pmacro_rfu1 = dst_emc_table_entry->burst_regs.emc_pmacro_brick_ctrl_rfu1_idx;
		pmacro_cfg5 = dst_emc_table_entry->burst_regs.emc_fbio_cfg5_idx;
		pmacro_common_tx = dst_emc_table_entry->burst_regs.emc_pmacro_common_pad_tx_ctrl_idx;
	}
	else
	{
		pmacro_cmd_pad = src_emc_table_entry->burst_regs.emc_pmacro_cmd_pad_tx_ctrl_idx;
		pmacro_dq_pad = (dst_emc_table_entry->burst_regs.emc_pmacro_data_pad_tx_ctrl_idx & 0x101) | src_emc_table_entry->burst_regs.emc_pmacro_data_pad_tx_ctrl_idx;
		pmacro_rfu1 = src_emc_table_entry->burst_regs.emc_pmacro_brick_ctrl_rfu1_idx;
		pmacro_cfg5 = src_emc_table_entry->burst_regs.emc_fbio_cfg5_idx;
		pmacro_common_tx = src_emc_table_entry->burst_regs.emc_pmacro_common_pad_tx_ctrl_idx;
	}

	u32 pmacro_cmd_pad_drvforceon = pmacro_cmd_pad | 0x4000000;

	u32 ramp_down_wait = src_clock_period * 12;

	_ccfifo_write(EMC_PMACRO_CMD_PAD_TX_CTRL, pmacro_cmd_pad_drvforceon, 0);
	_ccfifo_write(EMC_FBIO_CFG5, pmacro_cfg5 | 0x100, 12);

	if (src_clock_period >= 1000) // Dvfs high speed threshold.
	{
		_ccfifo_write(EMC_PMACRO_BRICK_CTRL_RFU1, pmacro_rfu1 & 0xF800F800, (u32)(src_clk_per_pc + 19));
		ramp_down_wait += 100000 + (src_clock_period * 20);
	}
	else
	{
		if (src_clock_period >= 416) // Iobrick dcc threshold.
			_ccfifo_write(EMC_PMACRO_BRICK_CTRL_RFU1, pmacro_rfu1 & 0xFEEDFEED, (u32)src_clk_per_pc);
		else
		{
			pmacro_dq_pad = (pmacro_dq_pad & 0xFEFEFDFD) | 0x10200;
			pmacro_cmd_pad_drvforceon = (pmacro_cmd_pad & 0xFAFEFDFD) | 0x4010200;
			_ccfifo_write(EMC_PMACRO_CMD_PAD_TX_CTRL, pmacro_cmd_pad_drvforceon, (u32)src_clk_per_pc);
			_ccfifo_write(EMC_PMACRO_DATA_PAD_TX_CTRL, pmacro_dq_pad, 0);
			_ccfifo_write(EMC_PMACRO_BRICK_CTRL_RFU1, pmacro_rfu1 & 0xFEEDFEED, 0);
		}
		ramp_down_wait += 300000;
		_ccfifo_write(EMC_PMACRO_BRICK_CTRL_RFU1, pmacro_rfu1 & 0xFE40FE40, (u32)src_clk_per_pc);

		if (src_clock_period >= 416) // Iobrick dcc threshold.
			_ccfifo_write(EMC_PMACRO_BRICK_CTRL_RFU1, pmacro_rfu1 & 0xF800F800, (u32)src_clk_per_pc);
		else
		{
			_ccfifo_write(EMC_PMACRO_CMD_PAD_TX_CTRL, pmacro_cmd_pad_drvforceon & 0xFEFEFDFD, (u32)src_clk_per_pc);
			_ccfifo_write(EMC_PMACRO_DATA_PAD_TX_CTRL, pmacro_dq_pad & 0xFEFEFDFD, 0);
			_ccfifo_write(EMC_PMACRO_BRICK_CTRL_RFU1, pmacro_rfu1 & 0xF800F800, 0);
		}
	}

	if (src_clock_period >= 1666) // Dvfs mid speed threshold.
		_ccfifo_write(EMC_PMACRO_COMMON_PAD_TX_CTRL, pmacro_common_tx & 0xFFFFFFF0, (u32)src_clk_per_pc);
	else
	{
		ramp_down_wait += 400000;
		_ccfifo_write(EMC_PMACRO_COMMON_PAD_TX_CTRL, pmacro_common_tx & 0xFFFFFFFA, (u32)src_clk_per_pc);
		_ccfifo_write(EMC_PMACRO_COMMON_PAD_TX_CTRL, pmacro_common_tx & 0xFFFFFFF0, (u32)src_clk_per_pc);
		_ccfifo_write(EMC_INTSTATUS, 0, (u32)src_clk_per_pc);
	}

	return ramp_down_wait;
}

static u32 _dvfs_power_ramp_up(bool flip_backward, emc_table_t *src_emc_table_entry, emc_table_t *dst_emc_table_entry, u32 needs_training, u32 dst_clock_period)
{
	u32 pmacro_cmd_pad;
	u32 pmacro_dq_pad;
	u32 pmacro_rfu1;
	u32 pmacro_cfg5;
	u32 pmacro_common_tx;
	u32 pmacro_cmd_pad_data;
	u32 ramp_up_wait = 0;

	u32 dst_clk_per_pc = (100000 / dst_clock_period) + 1;

	if (flip_backward)
	{
		pmacro_cmd_pad = src_emc_table_entry->burst_regs.emc_pmacro_cmd_pad_tx_ctrl_idx;
		pmacro_dq_pad = src_emc_table_entry->burst_regs.emc_pmacro_data_pad_tx_ctrl_idx;
		pmacro_rfu1 = src_emc_table_entry->burst_regs.emc_pmacro_brick_ctrl_rfu1_idx;
		pmacro_cfg5 = src_emc_table_entry->burst_regs.emc_fbio_cfg5_idx;
		pmacro_common_tx = src_emc_table_entry->burst_regs.emc_pmacro_common_pad_tx_ctrl_idx;
	}
	else if (needs_training & NEEDS_TRAINING_CA_COMBO)
	{
		pmacro_cmd_pad = dst_emc_table_entry->shadow_regs_ca_train.emc_pmacro_cmd_pad_tx_ctrl_idx;
		pmacro_dq_pad = dst_emc_table_entry->shadow_regs_ca_train.emc_pmacro_data_pad_tx_ctrl_idx;
		pmacro_rfu1 = dst_emc_table_entry->shadow_regs_ca_train.emc_pmacro_brick_ctrl_rfu1_idx;
		pmacro_cfg5 = dst_emc_table_entry->shadow_regs_ca_train.emc_fbio_cfg5_idx;
		pmacro_common_tx = dst_emc_table_entry->shadow_regs_ca_train.emc_pmacro_common_pad_tx_ctrl_idx;
	}
	else if (needs_training & NEEDS_TRAINING_QUSE_COMBO)
	{
		pmacro_cmd_pad = dst_emc_table_entry->shadow_regs_quse_train.emc_pmacro_cmd_pad_tx_ctrl_idx;
		pmacro_dq_pad = dst_emc_table_entry->shadow_regs_quse_train.emc_pmacro_data_pad_tx_ctrl_idx;
		pmacro_rfu1 = dst_emc_table_entry->shadow_regs_quse_train.emc_pmacro_brick_ctrl_rfu1_idx;
		pmacro_cfg5 = dst_emc_table_entry->shadow_regs_quse_train.emc_fbio_cfg5_idx;
		pmacro_common_tx = dst_emc_table_entry->shadow_regs_quse_train.emc_pmacro_common_pad_tx_ctrl_idx;
	}
	else if (needs_training & (NEEDS_TRAINING_WR_COMBO | NEEDS_TRAINING_RD_COMBO))
	{
		pmacro_cmd_pad = dst_emc_table_entry->shadow_regs_rdwr_train.emc_pmacro_cmd_pad_tx_ctrl_idx;
		pmacro_dq_pad = dst_emc_table_entry->shadow_regs_rdwr_train.emc_pmacro_data_pad_tx_ctrl_idx;
		pmacro_rfu1 = dst_emc_table_entry->shadow_regs_rdwr_train.emc_pmacro_brick_ctrl_rfu1_idx;
		pmacro_cfg5 = dst_emc_table_entry->shadow_regs_rdwr_train.emc_fbio_cfg5_idx;
		pmacro_common_tx = dst_emc_table_entry->shadow_regs_rdwr_train.emc_pmacro_common_pad_tx_ctrl_idx;
	}
	else
	{
		pmacro_cmd_pad = dst_emc_table_entry->burst_regs.emc_pmacro_cmd_pad_tx_ctrl_idx;
		pmacro_dq_pad = dst_emc_table_entry->burst_regs.emc_pmacro_data_pad_tx_ctrl_idx;
		pmacro_rfu1 = dst_emc_table_entry->burst_regs.emc_pmacro_brick_ctrl_rfu1_idx;
		pmacro_cfg5 = dst_emc_table_entry->burst_regs.emc_fbio_cfg5_idx;
		pmacro_common_tx = dst_emc_table_entry->burst_regs.emc_pmacro_common_pad_tx_ctrl_idx;
	}

	pmacro_cmd_pad_data = (pmacro_cmd_pad & 0xFAFEFDFD) | 0x4000000;

	if (dst_clock_period >= 1666) // Dvfs mid speed threshold.
	{
		_ccfifo_write(EMC_PMACRO_COMMON_PAD_TX_CTRL, pmacro_common_tx | 8, 0);

		_ccfifo_write(EMC_PMACRO_BRICK_CTRL_RFU1, pmacro_rfu1 | 0x600, 0);
		_ccfifo_write(EMC_FBIO_CFG5, pmacro_cfg5 & 0xFFFFFEFF, 12);

		ramp_up_wait = (dst_clock_period * 12) + 0;
	}
	else
	{
		_ccfifo_write(EMC_PMACRO_COMMON_PAD_TX_CTRL, pmacro_common_tx & 0xA, 0);
		_ccfifo_write(EMC_PMACRO_COMMON_PAD_TX_CTRL, pmacro_common_tx & 0xF, dst_clk_per_pc);

		if (dst_clock_period < 1000) // Dvfs high speed threshold.
		{
			if (dst_clock_period >= 416) // Iobrick dcc threshold.
				_ccfifo_write(EMC_PMACRO_BRICK_CTRL_RFU1, pmacro_rfu1 & 0xFE40FE40, dst_clk_per_pc);
			else
			{
				_ccfifo_write(EMC_PMACRO_CMD_PAD_TX_CTRL, (pmacro_cmd_pad & 0xFAFEFDFD) | 0x4010200, dst_clk_per_pc);
				_ccfifo_write(EMC_PMACRO_DATA_PAD_TX_CTRL, (pmacro_dq_pad & 0xFEFEFDFD) | 0x10200, 0);
				_ccfifo_write(EMC_PMACRO_BRICK_CTRL_RFU1, pmacro_rfu1 & 0xFE40FE40, 0);
			}

			_ccfifo_write(EMC_PMACRO_BRICK_CTRL_RFU1, pmacro_rfu1 & 0xFEEDFEED, dst_clk_per_pc);

			if (dst_clock_period >= 416) // Iobrick dcc threshold.
				_ccfifo_write(EMC_PMACRO_BRICK_CTRL_RFU1, pmacro_rfu1, dst_clk_per_pc);
			else
			{
				pmacro_cmd_pad_data = pmacro_cmd_pad | 0x5010202;
				_ccfifo_write(EMC_PMACRO_CMD_PAD_TX_CTRL, pmacro_cmd_pad_data, dst_clk_per_pc);
				_ccfifo_write(EMC_PMACRO_DATA_PAD_TX_CTRL, pmacro_dq_pad | 0x1010202, 0);
				_ccfifo_write(EMC_PMACRO_BRICK_CTRL_RFU1, pmacro_rfu1, 0);
			}

			ramp_up_wait = 500000 + (dst_clock_period * 10);
		}
		else // 1000 > dst_clock_period < 1666.
		{
			_ccfifo_write(EMC_PMACRO_BRICK_CTRL_RFU1, pmacro_rfu1 | 0x6000600, dst_clk_per_pc);

			ramp_up_wait = 200000 + (dst_clock_period * 10);
		}

		_ccfifo_write(EMC_FBIO_CFG5, pmacro_cfg5 & 0xFFFFFEFF, dst_clk_per_pc + 9);
	}

	_ccfifo_write(EMC_PMACRO_CMD_PAD_TX_CTRL, pmacro_cmd_pad_data & 0xFBFFFFFF, 5);

	return ramp_up_wait;
}

static u32 _minerva_update_clock_tree_delay(emc_table_t *src_emc_entry, emc_table_t *dst_emc_entry, u32 dram_dev_num, u32 channel1_enabled, enum tree_update_mode_t update_type)
{
	u32 cval = 0;
	u32 adelta = 0;
	s32 tdelta = 0;

	u32 temp_ch0_0 = 0;
	u32 temp_ch0_1 = 0;
	u32 temp_ch1_0 = 0;
	u32 temp_ch1_1 = 0;

	u32 upd_type_bits = 1 << update_type;
	u32 dst_rate_mhz = dst_emc_entry->rate_khz / 1000;
	u32 src_rate_mhz = src_emc_entry->rate_khz / 1000;

	u32 tval = 1000000 * _actual_osc_clocks(src_emc_entry->run_clocks) / 2;

	if (update_type > PERIODIC_TRAINING_UPDATE)
		return 0;

	if (upd_type_bits & 0x5400)
	{
		_request_mmr_data(0x80130000, channel1_enabled); // Dev0 MRR 19.
		temp_ch0_0 = (EMC(EMC_MRR) & 0xFF) << 8;
		temp_ch0_1 = EMC(EMC_MRR) & 0xFF00;
		if (channel1_enabled)
		{
			temp_ch1_0 = (EMC_CH1(EMC_MRR) & 0xFF) << 8;
			temp_ch1_1 = EMC_CH1(EMC_MRR) & 0xFF00;
		}

		_request_mmr_data(0x80120000, channel1_enabled); // Dev0 MRR 18.
		temp_ch0_0 |= EMC(EMC_MRR) & 0xFF;
		temp_ch0_1 |= (EMC(EMC_MRR) & 0xFF00) >> 8;
		if (channel1_enabled)
		{
			temp_ch1_0 |= EMC_CH1(EMC_MRR) & 0xFF;
			temp_ch1_1 |= (EMC_CH1(EMC_MRR) & 0xFF00) >> 8;
		}
	}

	cval = tval / (src_rate_mhz * temp_ch0_0);
	switch (update_type)
	{
	case DVFS_PT1:
	case TRAINING_PT1:
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u0_idx += 100 * cval;
		if (update_type > PERIODIC_TRAINING_UPDATE || !(upd_type_bits & 0x6800))
			goto calc_td0_0;
		break;
	case DVFS_UPDATE:
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u0_idx =
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u0_idx / dst_emc_entry->ptfv_list.ptfv_dvfs_samples_idx;
		break;
	case TRAINING_UPDATE:
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u0_idx =
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u0_idx / dst_emc_entry->ptfv_list.ptfv_write_samples_idx;
		break;
	case PERIODIC_TRAINING_UPDATE:
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u0_idx =
			(100 * cval + dst_emc_entry->ptfv_list.ptfv_movavg_weight_idx * dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u0_idx)
			/ (dst_emc_entry->ptfv_list.ptfv_movavg_weight_idx + 1);
		break;
	default:
		if (update_type > PERIODIC_TRAINING_UPDATE || !(upd_type_bits & 0x6800))
			goto calc_td0_0;
		break;
	}

	tdelta = dst_emc_entry->current_dram_clktree_c0d0u0 - (dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u0_idx / 100);
	if (tdelta < 0)
		tdelta *= -1;
	adelta = tdelta;
	if (update_type == TRAINING_UPDATE || ((dst_rate_mhz * tdelta * 128) / 1000000) > dst_emc_entry->tree_margin)
		dst_emc_entry->current_dram_clktree_c0d0u0 = dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u0_idx / 100;

calc_td0_0:
	cval = tval / (src_rate_mhz * temp_ch0_1);
	switch (update_type)
	{
	case DVFS_PT1:
	case TRAINING_PT1:
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u1_idx += 100 * cval;
		if (update_type > PERIODIC_TRAINING_UPDATE || !(upd_type_bits & 0x6800))
			goto calc_td1_0;
		break;
	case DVFS_UPDATE:
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u1_idx =
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u1_idx / dst_emc_entry->ptfv_list.ptfv_dvfs_samples_idx;
		break;
	case TRAINING_UPDATE:
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u1_idx =
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u1_idx / dst_emc_entry->ptfv_list.ptfv_write_samples_idx;
		break;
	case PERIODIC_TRAINING_UPDATE:
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u1_idx =
			(100 * cval + dst_emc_entry->ptfv_list.ptfv_movavg_weight_idx * dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u1_idx)
			/ (dst_emc_entry->ptfv_list.ptfv_movavg_weight_idx + 1);
		break;
	default:
		if (update_type > PERIODIC_TRAINING_UPDATE || !(upd_type_bits & 0x6800))
			goto calc_td1_0;
		break;
	}

	tdelta = dst_emc_entry->current_dram_clktree_c0d0u1 - (dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u1_idx / 100);
	if (tdelta < 0)
		tdelta *= -1;
	if ((u32)tdelta > adelta)
		adelta = tdelta;
	if (update_type == TRAINING_UPDATE || ((dst_rate_mhz * tdelta * 128) / 1000000) > dst_emc_entry->tree_margin)
		dst_emc_entry->current_dram_clktree_c0d0u1 = dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u1_idx / 100;

calc_td1_0:
	if (channel1_enabled)
	{
		cval = tval / (src_rate_mhz * temp_ch1_0);
		switch (update_type)
		{
		case DVFS_PT1:
		case TRAINING_PT1:
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u0_idx += 100 * cval;
			if (update_type > PERIODIC_TRAINING_UPDATE || !(upd_type_bits & 0x6800))
				goto calc_td1_1;
			break;
		case DVFS_UPDATE:
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u0_idx =
				dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u0_idx / dst_emc_entry->ptfv_list.ptfv_dvfs_samples_idx;
			break;
		case TRAINING_UPDATE:
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u0_idx =
				dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u0_idx / dst_emc_entry->ptfv_list.ptfv_write_samples_idx;
			break;
		case PERIODIC_TRAINING_UPDATE:
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u0_idx =
				(100 * cval + dst_emc_entry->ptfv_list.ptfv_movavg_weight_idx * dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u0_idx)
				/ (dst_emc_entry->ptfv_list.ptfv_movavg_weight_idx + 1);
			break;
		default:
			if (update_type > PERIODIC_TRAINING_UPDATE || !(upd_type_bits & 0x6800))
				goto calc_td1_1;
			break;
		}

		tdelta = dst_emc_entry->current_dram_clktree_c1d0u0 - (dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u0_idx / 100);
		if (tdelta < 0)
			tdelta *= -1;
		if ((u32)tdelta > adelta)
			adelta = tdelta;
		if (update_type == TRAINING_UPDATE || ((dst_rate_mhz * tdelta * 128) / 1000000) > dst_emc_entry->tree_margin)
			dst_emc_entry->current_dram_clktree_c1d0u0 = dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u0_idx / 100;

calc_td1_1:
		cval = tval / (src_rate_mhz * temp_ch1_1);
		switch (update_type)
		{
		case DVFS_PT1:
		case TRAINING_PT1:
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u1_idx += 100 * cval;
			if (update_type > PERIODIC_TRAINING_UPDATE || !(upd_type_bits & 0x6800))
				goto calc_dev2;
			break;
		case DVFS_UPDATE:
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u1_idx =
				dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u1_idx / dst_emc_entry->ptfv_list.ptfv_dvfs_samples_idx;
			break;
		case TRAINING_UPDATE:
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u1_idx =
				dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u1_idx / dst_emc_entry->ptfv_list.ptfv_write_samples_idx;
			break;
		case PERIODIC_TRAINING_UPDATE:
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u1_idx =
				(100 * cval + dst_emc_entry->ptfv_list.ptfv_movavg_weight_idx * dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u1_idx)
				/ (dst_emc_entry->ptfv_list.ptfv_movavg_weight_idx + 1);
			break;
		default:
			if (update_type > PERIODIC_TRAINING_UPDATE || !(upd_type_bits & 0x6800))
				goto calc_dev2;
			break;
		}

		tdelta = dst_emc_entry->current_dram_clktree_c1d0u1 - (dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u1_idx / 100);
		if (tdelta < 0)
			tdelta *= -1;
		if ((u32)tdelta > adelta)
			adelta = tdelta;
		if (update_type == TRAINING_UPDATE || ((dst_rate_mhz * tdelta * 128) / 1000000) > dst_emc_entry->tree_margin)
			dst_emc_entry->current_dram_clktree_c1d0u1 = dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u1_idx / 100;
	}

calc_dev2:
	if (dram_dev_num != TWO_RANK)
		goto out;

	if (update_type <= PERIODIC_TRAINING_UPDATE && upd_type_bits & 0x5400)
	{
		_request_mmr_data(0x40130000, channel1_enabled); // Dev1 MRR 19.
		temp_ch0_0 = (EMC(EMC_MRR) & 0xFF) << 8;
		temp_ch0_1 = EMC(EMC_MRR) & 0xFF00;
		if (channel1_enabled)
		{
			temp_ch1_0 = (EMC_CH1(EMC_MRR) & 0xFF) << 8;
			temp_ch1_1 = EMC_CH1(EMC_MRR) & 0xFF00;
		}

		_request_mmr_data(0x40120000, channel1_enabled); // Dev1 MRR 18
		temp_ch0_0 |= EMC(EMC_MRR) & 0xFF;
		temp_ch0_1 |= ((EMC(EMC_MRR) & 0xFF00) >> 8);
		if (channel1_enabled)
		{
			temp_ch1_0 |= EMC_CH1(EMC_MRR) & 0xFF;
			temp_ch1_1 |= (EMC_CH1(EMC_MRR) & 0xFF00) >> 8;
		}
	}

	cval = tval / (src_rate_mhz * temp_ch0_0);
	switch (update_type )
	{
	case DVFS_PT1:
	case TRAINING_PT1:
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u0_idx += 100 * cval;
		if (update_type > PERIODIC_TRAINING_UPDATE || !(upd_type_bits & 0x6800))
			goto calc_tmp_td0_1;
		break;
	case DVFS_UPDATE:
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u0_idx =
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u0_idx / dst_emc_entry->ptfv_list.ptfv_dvfs_samples_idx;
		break;
	case TRAINING_UPDATE:
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u0_idx =
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u0_idx / dst_emc_entry->ptfv_list.ptfv_write_samples_idx;
		break;
	case PERIODIC_TRAINING_UPDATE:
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u0_idx =
			(100 * cval + dst_emc_entry->ptfv_list.ptfv_movavg_weight_idx * dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u0_idx)
			/ (dst_emc_entry->ptfv_list.ptfv_movavg_weight_idx + 1);
		break;
	default:
		if (update_type > PERIODIC_TRAINING_UPDATE || !(upd_type_bits & 0x6800))
			goto calc_tmp_td0_1;
		break;
	}

	tdelta = dst_emc_entry->current_dram_clktree_c0d1u0 - (dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u0_idx / 100);
	if (tdelta < 0)
		tdelta *= -1;
	if ((u32)tdelta > adelta)
		adelta = tdelta;
	if (update_type == TRAINING_UPDATE || ((dst_rate_mhz * tdelta * 128) / 1000000) > dst_emc_entry->tree_margin)
		dst_emc_entry->current_dram_clktree_c0d1u0 = dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u0_idx / 100;

calc_tmp_td0_1:
	cval = tval / (src_rate_mhz * temp_ch0_1);
	switch (update_type)
	{
	case DVFS_PT1:
	case TRAINING_PT1:
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u1_idx += 100 * cval;
		if (update_type > PERIODIC_TRAINING_UPDATE || !(upd_type_bits & 0x6800))
			goto calc_tmp_td1_0;
		break;
	case DVFS_UPDATE:
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u1_idx =
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u1_idx / dst_emc_entry->ptfv_list.ptfv_dvfs_samples_idx;
		break;
	case TRAINING_UPDATE:
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u1_idx =
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u1_idx / dst_emc_entry->ptfv_list.ptfv_write_samples_idx;
		break;
	case PERIODIC_TRAINING_UPDATE:
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u1_idx =
			(100 * cval + dst_emc_entry->ptfv_list.ptfv_movavg_weight_idx * dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u1_idx)
			/ (dst_emc_entry->ptfv_list.ptfv_movavg_weight_idx + 1);
		break;
	default:
		if (update_type > PERIODIC_TRAINING_UPDATE || !(upd_type_bits & 0x6800))
			goto calc_tmp_td1_0;
		break;
	}

	tdelta = dst_emc_entry->current_dram_clktree_c0d1u1 - (dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u1_idx / 100);
	if (tdelta < 0)
		tdelta *= -1;
	if ((u32)tdelta > adelta)
		adelta = tdelta;
	if (update_type == TRAINING_UPDATE || ((dst_rate_mhz * tdelta * 128) / 1000000) > dst_emc_entry->tree_margin)
		dst_emc_entry->current_dram_clktree_c0d1u1 = dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u1_idx / 100;

calc_tmp_td1_0:
	if (channel1_enabled)
	{
		cval = tval / (src_rate_mhz * temp_ch1_0);
		switch (update_type)
		{
		case DVFS_PT1:
		case TRAINING_PT1:
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u0_idx += 100 * cval;
			if (update_type > PERIODIC_TRAINING_UPDATE || !(upd_type_bits & 0x6800))
				goto calc_tmp_td1_1;
			break;
		case DVFS_UPDATE:
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u0_idx =
				dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u0_idx / dst_emc_entry->ptfv_list.ptfv_dvfs_samples_idx;
			break;
		case TRAINING_UPDATE:
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u0_idx =
				dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u0_idx / dst_emc_entry->ptfv_list.ptfv_write_samples_idx;
			break;
		case PERIODIC_TRAINING_UPDATE:
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u0_idx =
				(100 * cval + dst_emc_entry->ptfv_list.ptfv_movavg_weight_idx * dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u0_idx)
				/ (dst_emc_entry->ptfv_list.ptfv_movavg_weight_idx + 1);
			break;
		default:
			if (update_type > PERIODIC_TRAINING_UPDATE || !(upd_type_bits & 0x6800))
				goto calc_tmp_td1_1;
			break;
		}

		tdelta = dst_emc_entry->current_dram_clktree_c1d1u0 - (dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u0_idx / 100);
		if (tdelta < 0)
			tdelta *= -1;
		if ((u32)tdelta > adelta)
			adelta = tdelta;
		if (update_type == TRAINING_UPDATE || ((dst_rate_mhz * tdelta * 128) / 1000000) > dst_emc_entry->tree_margin)
			dst_emc_entry->current_dram_clktree_c1d1u0 = dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u0_idx / 100;

calc_tmp_td1_1:
		cval = tval / (src_rate_mhz * temp_ch1_1);
		switch (update_type)
		{
		case DVFS_PT1:
		case TRAINING_PT1:
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u1_idx += 100 * cval;
			if (update_type > PERIODIC_TRAINING_UPDATE || !(upd_type_bits & 0x6800))
				goto out;
			break;
		case DVFS_UPDATE:
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u1_idx =
				dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u1_idx / dst_emc_entry->ptfv_list.ptfv_dvfs_samples_idx;
			break;
		case TRAINING_UPDATE:
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u1_idx =
				dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u1_idx / dst_emc_entry->ptfv_list.ptfv_write_samples_idx;
			break;
		case PERIODIC_TRAINING_UPDATE:
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u1_idx =
				(100 * cval + dst_emc_entry->ptfv_list.ptfv_movavg_weight_idx * dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u1_idx)
				/ (dst_emc_entry->ptfv_list.ptfv_movavg_weight_idx + 1);
			break;
		default:
			if (update_type > PERIODIC_TRAINING_UPDATE || !(upd_type_bits & 0x6800))
				goto out;
			break;
		}

		tdelta = dst_emc_entry->current_dram_clktree_c1d1u1 - (dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u1_idx / 100);
		if (tdelta < 0)
			tdelta *= -1;
		if ((u32)tdelta > adelta)
			adelta = tdelta;
		if (update_type == TRAINING_UPDATE || ((dst_rate_mhz * tdelta * 128) / 1000000) > dst_emc_entry->tree_margin)
			dst_emc_entry->current_dram_clktree_c1d1u1 = dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u1_idx / 100;
	}

out:
	if (update_type == TRAINING_UPDATE)
	{
		dst_emc_entry->trained_dram_clktree_c0d0u0 = dst_emc_entry->current_dram_clktree_c0d0u0;
		dst_emc_entry->trained_dram_clktree_c0d0u1 = dst_emc_entry->current_dram_clktree_c0d0u1;
		dst_emc_entry->trained_dram_clktree_c0d1u0 = dst_emc_entry->current_dram_clktree_c0d1u0;
		dst_emc_entry->trained_dram_clktree_c0d1u1 = dst_emc_entry->current_dram_clktree_c0d1u1;
		dst_emc_entry->trained_dram_clktree_c1d0u0 = dst_emc_entry->current_dram_clktree_c1d0u0;
		dst_emc_entry->trained_dram_clktree_c1d0u1 = dst_emc_entry->current_dram_clktree_c1d0u1;
		dst_emc_entry->trained_dram_clktree_c1d1u0 = dst_emc_entry->current_dram_clktree_c1d1u0;
		dst_emc_entry->trained_dram_clktree_c1d1u1 = dst_emc_entry->current_dram_clktree_c1d1u1;
	}

	return (u32)adelta;
}

static u32 _minerva_periodic_compensation_handler(emc_table_t *src_emc_entry, emc_table_t *dst_emc_entry, u32 dram_dev_num, u32 channel1_enabled, enum comp_seq_t seq_type)
{
	if (!dst_emc_entry->periodic_training)
		return 0;

	u32 delay = 1000 * _actual_osc_clocks(src_emc_entry->run_clocks) / src_emc_entry->rate_khz + 2;

	if (seq_type == DVFS_SEQUENCE)
	{
		if (src_emc_entry->periodic_training && dst_emc_entry->ptfv_list.ptfv_config_ctrl_idx & 1)
		{
			u32 samples = dst_emc_entry->ptfv_list.ptfv_dvfs_samples_idx;
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u0_idx = src_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u0_idx * samples;
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u1_idx = src_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u1_idx * samples;
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u0_idx = src_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u0_idx * samples;
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u1_idx = src_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u1_idx * samples;
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u0_idx = src_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u0_idx * samples;
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u1_idx = src_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u1_idx * samples;
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u0_idx = src_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u0_idx * samples;
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u1_idx = src_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u1_idx * samples;
		}
		else
		{
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u0_idx = 0;
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u1_idx = 0;
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u0_idx = 0;
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u1_idx = 0;
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u0_idx = 0;
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u1_idx = 0;
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u0_idx = 0;
			dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u1_idx = 0;

			for (u32 i = 0; i < dst_emc_entry->ptfv_list.ptfv_dvfs_samples_idx; i++)
			{
				_start_periodic_compensation();
				_usleep(delay);
				_minerva_update_clock_tree_delay(src_emc_entry, dst_emc_entry, dram_dev_num, channel1_enabled, DVFS_PT1);
			}
		}

		return _minerva_update_clock_tree_delay(src_emc_entry, dst_emc_entry, dram_dev_num, channel1_enabled, DVFS_UPDATE);
	}
	else if (seq_type == WRITE_TRAINING_SEQUENCE)
	{
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u0_idx = 0;
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d0u1_idx = 0;
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u0_idx = 0;
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d0u1_idx = 0;
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u0_idx = 0;
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c0d1u1_idx = 0;
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u0_idx = 0;
		dst_emc_entry->ptfv_list.ptfv_dqsosc_movavg_c1d1u1_idx = 0;

		for (u32 i = 0; i < dst_emc_entry->ptfv_list.ptfv_write_samples_idx; i++)
		{
			_start_periodic_compensation();
			_usleep(delay);
			_minerva_update_clock_tree_delay(src_emc_entry, dst_emc_entry, dram_dev_num, channel1_enabled, TRAINING_PT1);
		}

		return _minerva_update_clock_tree_delay(src_emc_entry, dst_emc_entry, dram_dev_num, channel1_enabled, TRAINING_UPDATE);
	}
	else if (seq_type == PERIODIC_TRAINING_SEQUENCE)
	{
		_start_periodic_compensation();
		_usleep(delay);
		return _minerva_update_clock_tree_delay(src_emc_entry, dst_emc_entry, dram_dev_num, channel1_enabled, PERIODIC_TRAINING_UPDATE);
	}

	return seq_type;
}

#define STORE_TRIM_VAL(chan, rank, reg, byte) \
		((mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_long_dq_rank##rank##_##reg##_idx >>	\
	 		EMC_PMACRO_OB_DDLL_LONG_DQ_BYTE##byte##_SHIFT) & 0x7FF) \
		+ \
		(((mtc_table_entry->trim_perch_regs.emc##chan##_data_brlshft_##rank##_idx >>	\
			EMC_DATA_BRLSHFT_##rank##_RANK##rank##_BYTE##byte##_DATA_BRLSHFT_SHIFT) & 0x7) << 6)

static u32 _minerva_apply_periodic_compensation_trimmer(emc_table_t *mtc_table_entry, u32 trim_emc_reg_addr)
{
	u32 trimmer = 0;
	s32 tree_delta[4] = {0};
	s32 tree_delta_taps[4] = {0};
	s32 new_trim[] = {
		// chan, rank, reg, byte.
		STORE_TRIM_VAL(0, 0, 0, 0),
		STORE_TRIM_VAL(0, 0, 0, 1),
		STORE_TRIM_VAL(0, 0, 1, 2),
		STORE_TRIM_VAL(0, 0, 1, 3),

		STORE_TRIM_VAL(1, 0, 2, 4),
		STORE_TRIM_VAL(1, 0, 2, 5),
		STORE_TRIM_VAL(1, 0, 3, 6),
		STORE_TRIM_VAL(1, 0, 3, 7),

		STORE_TRIM_VAL(0, 1, 0, 0),
		STORE_TRIM_VAL(0, 1, 0, 1),
		STORE_TRIM_VAL(0, 1, 1, 2),
		STORE_TRIM_VAL(0, 1, 1, 3),

		STORE_TRIM_VAL(1, 1, 2, 4),
		STORE_TRIM_VAL(1, 1, 2, 5),
		STORE_TRIM_VAL(1, 1, 3, 6),
		STORE_TRIM_VAL(1, 1, 3, 7)
	};

	u32 dst_rate_mhz = mtc_table_entry->rate_khz / 1000;

	switch (trim_emc_reg_addr)
	{
	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0:
	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1:
	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2:
	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3:
	case EMC_DATA_BRLSHFT_0:
		tree_delta[0] = (mtc_table_entry->current_dram_clktree_c0d0u0 - mtc_table_entry->trained_dram_clktree_c0d0u0) * 128;
		tree_delta[1] = (mtc_table_entry->current_dram_clktree_c0d0u1 - mtc_table_entry->trained_dram_clktree_c0d0u1) * 128;
		tree_delta[2] = (mtc_table_entry->current_dram_clktree_c1d0u0 - mtc_table_entry->trained_dram_clktree_c1d0u0) * 128;
		tree_delta[3] = (mtc_table_entry->current_dram_clktree_c1d0u1 - mtc_table_entry->trained_dram_clktree_c1d0u1) * 128;
		tree_delta_taps[0] = (tree_delta[0] * (s32)dst_rate_mhz) / 1000000;
		tree_delta_taps[1] = (tree_delta[1] * (s32)dst_rate_mhz) / 1000000;
		tree_delta_taps[2] = (tree_delta[2] * (s32)dst_rate_mhz) / 1000000;
		tree_delta_taps[3] = (tree_delta[3] * (s32)dst_rate_mhz) / 1000000;
		for (u32 i = 0; i < 4; i++)
		{
			// Check if tap exceeds margins and apply it.
			if ((tree_delta_taps[i] > (s32)mtc_table_entry->tree_margin) || (tree_delta_taps[i] < (-1 * (s32)mtc_table_entry->tree_margin)))
			{
				new_trim[i * 2]     += tree_delta_taps[i];
				new_trim[i * 2 + 1] += tree_delta_taps[i];
			}
		}
		if (trim_emc_reg_addr == EMC_DATA_BRLSHFT_0)
		{
			for (u32 i = 0; i < 8; i++)
				new_trim[i] /= 64;
		}
		else
		{
			for (u32 i = 0; i < 8; i++)
				new_trim[i] %= 64;
		}
		break;
	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0:
	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1:
	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2:
	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3:
	case EMC_DATA_BRLSHFT_1:
		tree_delta[0] = (mtc_table_entry->current_dram_clktree_c0d1u0 - mtc_table_entry->trained_dram_clktree_c0d1u0) * 128;
		tree_delta[1] = (mtc_table_entry->current_dram_clktree_c0d1u1 - mtc_table_entry->trained_dram_clktree_c0d1u1) * 128;
		tree_delta[2] = (mtc_table_entry->current_dram_clktree_c1d1u0 - mtc_table_entry->trained_dram_clktree_c1d1u0) * 128;
		tree_delta[3] = (mtc_table_entry->current_dram_clktree_c1d1u1 - mtc_table_entry->trained_dram_clktree_c1d1u1) * 128;
		tree_delta_taps[0] = (tree_delta[0] * (s32)dst_rate_mhz) / 1000000;
		tree_delta_taps[1] = (tree_delta[1] * (s32)dst_rate_mhz) / 1000000;
		tree_delta_taps[2] = (tree_delta[2] * (s32)dst_rate_mhz) / 1000000;
		tree_delta_taps[3] = (tree_delta[3] * (s32)dst_rate_mhz) / 1000000;
		for (u32 i = 0; i < 4; i++)
		{
			// Check if tap exceeds margins and apply it.
			if ((tree_delta_taps[i] > (s32)mtc_table_entry->tree_margin) || (tree_delta_taps[i] < (-1 * (s32)mtc_table_entry->tree_margin)))
			{
				new_trim[8 + i * 2]     += tree_delta_taps[i];
				new_trim[8 + i * 2 + 1] += tree_delta_taps[i];
			}
		}
		if (trim_emc_reg_addr == EMC_DATA_BRLSHFT_1)
		{
			for (u32 i = 0; i < 8; i++)
				new_trim[i + 8] /= 64;
		}
		else
		{
			for (u32 i = 0; i < 8; i++)
				new_trim[i + 8] %= 64;
		}
		break;
	default:
		break;
	}

	switch (trim_emc_reg_addr)
	{
	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0:
		trimmer = (new_trim[0] & 0x7FF) | ((new_trim[1] & 0x7FF) << 16);
		break;
	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1:
		trimmer = (new_trim[2] & 0x7FF) | ((new_trim[3] & 0x7FF) << 16);
		break;
	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2:
		trimmer = (new_trim[4] & 0x7FF) | ((new_trim[5] & 0x7FF) << 16);
		break;
	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3:
		trimmer = (new_trim[6] & 0x7FF) | ((new_trim[7] & 0x7FF) << 16);
		break;
	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0:
		trimmer = (new_trim[8] & 0x7FF) | ((new_trim[9] & 0x7FF) << 16);
		break;
	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1:
		trimmer = (new_trim[10] & 0x7FF) | ((new_trim[11] & 0x7FF) << 16);
		break;
	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2:
		trimmer = (new_trim[12] & 0x7FF) | ((new_trim[13] & 0x7FF) << 16);
		break;
	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3:
		trimmer = (new_trim[14] & 0x7FF) | ((new_trim[15] & 0x7FF) << 16);
		break;
	case EMC_DATA_BRLSHFT_0:
		trimmer = ((new_trim[0] & 7) << EMC_DATA_BRLSHFT_0_RANK0_BYTE0_DATA_BRLSHFT_SHIFT)
				| ((new_trim[1] & 7) << EMC_DATA_BRLSHFT_0_RANK0_BYTE1_DATA_BRLSHFT_SHIFT)
				| ((new_trim[2] & 7) << EMC_DATA_BRLSHFT_0_RANK0_BYTE2_DATA_BRLSHFT_SHIFT)
				| ((new_trim[3] & 7) << EMC_DATA_BRLSHFT_0_RANK0_BYTE3_DATA_BRLSHFT_SHIFT)
				| ((new_trim[4] & 7) << EMC_DATA_BRLSHFT_0_RANK0_BYTE4_DATA_BRLSHFT_SHIFT)
				| ((new_trim[5] & 7) << EMC_DATA_BRLSHFT_0_RANK0_BYTE5_DATA_BRLSHFT_SHIFT)
				| ((new_trim[6] & 7) << EMC_DATA_BRLSHFT_0_RANK0_BYTE6_DATA_BRLSHFT_SHIFT)
				| ((new_trim[7] & 7) << EMC_DATA_BRLSHFT_0_RANK0_BYTE7_DATA_BRLSHFT_SHIFT);
		break;
	case EMC_DATA_BRLSHFT_1:
		trimmer = ((new_trim[8]  & 7) << EMC_DATA_BRLSHFT_1_RANK1_BYTE0_DATA_BRLSHFT_SHIFT)
				| ((new_trim[9]  & 7) << EMC_DATA_BRLSHFT_1_RANK1_BYTE1_DATA_BRLSHFT_SHIFT)
				| ((new_trim[10] & 7) << EMC_DATA_BRLSHFT_1_RANK1_BYTE2_DATA_BRLSHFT_SHIFT)
				| ((new_trim[11] & 7) << EMC_DATA_BRLSHFT_1_RANK1_BYTE3_DATA_BRLSHFT_SHIFT)
				| ((new_trim[12] & 7) << EMC_DATA_BRLSHFT_1_RANK1_BYTE4_DATA_BRLSHFT_SHIFT)
				| ((new_trim[13] & 7) << EMC_DATA_BRLSHFT_1_RANK1_BYTE5_DATA_BRLSHFT_SHIFT)
				| ((new_trim[14] & 7) << EMC_DATA_BRLSHFT_1_RANK1_BYTE6_DATA_BRLSHFT_SHIFT)
				| ((new_trim[15] & 7) << EMC_DATA_BRLSHFT_1_RANK1_BYTE7_DATA_BRLSHFT_SHIFT);
		break;
	default:
		break;
	}

	return trimmer;
}

static bool _check_freq_changed(u32 dst_entry_rate_KHz, u32 dst_entry_clk_src_emc, u32 src_entry_rate_KHz, u32 src_entry_clk_src_emc)
{
	u64 dst_div_clock;
	u64 src_div_clock;
	u32 src_end_div_clk_ratio;

	u32 src_entry_emc_2X_clk_src = src_entry_clk_src_emc >> EMC_2X_CLK_SRC_SHIFT;
	u32 dst_entry_emc_2X_clk_src = dst_entry_clk_src_emc >> EMC_2X_CLK_SRC_SHIFT;
	u32 src_entry_emc_2X_clk_src_div = src_entry_clk_src_emc & 0xFF;
	u32 dst_entry_emc_2X_clk_src_div = dst_entry_clk_src_emc & 0xFF;
	u32 pll_post_divider = 0;

	switch (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_EMC) >> EMC_2X_CLK_SRC_SHIFT)
	{
		case PLLM_OUT0:
		case PLLM_UD:
			pll_post_divider = (CLOCK(CLK_RST_CONTROLLER_PLLM_BASE) >> 20) & 0x1F;
			break;
		case PLLMB_UD:
		case PLLMB_OUT0:
			pll_post_divider = (CLOCK(CLK_RST_CONTROLLER_PLLMB_BASE) >> 20) & 0x1F;
			break;
		default:
			break;
	}

	// Hang if post div is wrong.
	if (pll_post_divider > 5)
		while (true)
			;

	if (src_entry_emc_2X_clk_src <= PLLMB_UD)
		src_entry_emc_2X_clk_src_div = 0;
	if (dst_entry_emc_2X_clk_src <= PLLMB_UD)
		dst_entry_emc_2X_clk_src_div = 0;

	if (dst_entry_emc_2X_clk_src != src_entry_emc_2X_clk_src
		&& (dst_entry_emc_2X_clk_src & 0xFFFFFFFB || src_entry_emc_2X_clk_src & 0xFFFFFFFB))
			return true;

	dst_div_clock = dst_entry_rate_KHz * (pll_post_divider + 1)
		* ((dst_entry_emc_2X_clk_src_div >> 1) * 10 + (dst_entry_emc_2X_clk_src_div & 1) * 5 + 10) / 10; // Accounting for 7.1 div.
	src_div_clock = src_entry_rate_KHz * (pll_post_divider + 1)
		* ((src_entry_emc_2X_clk_src_div >> 1) * 10 + (src_entry_emc_2X_clk_src_div & 1) * 5 + 10) / 10; // Accounting for 7.1 div.

	src_end_div_clk_ratio = (src_div_clock * 1000) / dst_div_clock;

	if (src_end_div_clk_ratio > 1010 || src_end_div_clk_ratio < 990)
		return true;
	else
		return false;
}

static void _save_train_results(emc_table_t *mtc_table_entry, u32 needs_training, u32 dram_dev_num, bool channel1_enabled)
{
	bool needs_ca_training = !!(needs_training & NEEDS_TRAINING_CA);
	bool needs_ca_vref_training = !!(needs_training & NEEDS_TRAINING_CA_VREF);
	bool needs_quse_training = !!(needs_training & NEEDS_TRAINING_QUSE);
	bool needs_quse_vref_training = !!(needs_training & NEEDS_TRAINING_QUSE_VREF);
	bool needs_wr_training = !!(needs_training & NEEDS_TRAINING_WR);
	bool needs_wr_vref_training = !!(needs_training & NEEDS_TRAINING_WR_VREF);
	bool needs_rd_training = !!(needs_training & NEEDS_TRAINING_RD);
	bool needs_rd_vref_training = !!(needs_training & NEEDS_TRAINING_RD_VREF);
	bool needs_training_in_self_refresh = !!(needs_training & NEEDS_TRAINING_IN_SELF_REFRESH);

	if (needs_ca_training)
	{
		mtc_table_entry->trim_perch_regs.emc_cmd_brlshft_0_idx = EMC_CH0(EMC_CMD_BRLSHFT_0);
		mtc_table_entry->trim_perch_regs.emc_cmd_brlshft_1_idx = channel1_enabled ? EMC_CH1(EMC_CMD_BRLSHFT_1) : 0;
		mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_long_dq_rank0_4_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_4);
		mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_long_dq_rank0_5_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_5) : 0;

		if (needs_training_in_self_refresh)
		{
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_cmd0_0_idx = EMC(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_0);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_cmd0_1_idx = EMC(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_1);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_cmd0_2_idx = EMC(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_2);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_cmd1_0_idx = EMC(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_0);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_cmd1_1_idx = EMC(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_1);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_cmd1_2_idx = EMC(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_2);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_cmd2_0_idx = EMC(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_0);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_cmd2_1_idx = EMC(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_1);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_cmd2_2_idx = EMC(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_2);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_cmd3_0_idx = EMC(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_0);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_cmd3_1_idx = EMC(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_1);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_cmd3_2_idx = EMC(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_2);
		}
	}

	if (needs_ca_vref_training)
	{
		mtc_table_entry->burst_reg_per_ch.emc0_mrw10_idx = (EMC_CH0(EMC_TRAINING_OPT_CA_VREF) & 0xFFFF) | 0x880C0000;
		mtc_table_entry->burst_reg_per_ch.emc1_mrw10_idx = (channel1_enabled ? EMC_CH1(EMC_TRAINING_OPT_CA_VREF) & 0xFFFF : 0) | 0x880C0000;

		u32 mrw11_dev_selectn;
		if (dram_dev_num == TWO_RANK)
			mrw11_dev_selectn = 0x480C0000;
		else
			mrw11_dev_selectn = 0xC80C0000;

		mtc_table_entry->burst_reg_per_ch.emc0_mrw11_idx =
			((EMC_CH0(EMC_TRAINING_OPT_CA_VREF) >> 16) & 0xFF)
			| (EMC_CH0(EMC_TRAINING_OPT_CA_VREF) >> 24 << 8)
			| (mrw11_dev_selectn & 0xFFFFFF00);

		mtc_table_entry->burst_reg_per_ch.emc1_mrw11_idx =
			(((channel1_enabled ? EMC_CH1(EMC_TRAINING_OPT_CA_VREF) : 0) >> 16) & 0xFF)
			| ((channel1_enabled ? EMC_CH1(EMC_TRAINING_OPT_CA_VREF) : 0) >> 24 << 8)
			| (mrw11_dev_selectn & 0xFFFFFF00);
	}

	if (needs_quse_training || needs_rd_training)
	{
		mtc_table_entry->trim_perch_regs.emc_quse_brlshft_0_idx = EMC_CH0(EMC_QUSE_BRLSHFT_0);
		mtc_table_entry->trim_perch_regs.emc_quse_brlshft_1_idx = channel1_enabled ? EMC_CH1(EMC_QUSE_BRLSHFT_1) : 0;

		mtc_table_entry->trim_regs.emc_pmacro_quse_ddll_rank0_0_idx = EMC_CH0(EMC_PMACRO_QUSE_DDLL_RANK0_0);
		mtc_table_entry->trim_regs.emc_pmacro_quse_ddll_rank0_1_idx = EMC_CH0(EMC_PMACRO_QUSE_DDLL_RANK0_1);
		mtc_table_entry->trim_regs.emc_pmacro_quse_ddll_rank0_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_QUSE_DDLL_RANK0_2) : 0;
		mtc_table_entry->trim_regs.emc_pmacro_quse_ddll_rank0_3_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_QUSE_DDLL_RANK0_3) : 0;

		if (dram_dev_num == TWO_RANK)
		{
			mtc_table_entry->trim_perch_regs.emc_quse_brlshft_2_idx = EMC_CH0(EMC_QUSE_BRLSHFT_2);
			mtc_table_entry->trim_perch_regs.emc_quse_brlshft_3_idx = channel1_enabled ? EMC_CH1(EMC_QUSE_BRLSHFT_3) : 0;

			mtc_table_entry->trim_regs.emc_pmacro_quse_ddll_rank1_0_idx = EMC_CH0(EMC_PMACRO_QUSE_DDLL_RANK1_0);
			mtc_table_entry->trim_regs.emc_pmacro_quse_ddll_rank1_1_idx = EMC_CH0(EMC_PMACRO_QUSE_DDLL_RANK1_1);
			mtc_table_entry->trim_regs.emc_pmacro_quse_ddll_rank1_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_QUSE_DDLL_RANK1_2) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_quse_ddll_rank1_3_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_QUSE_DDLL_RANK1_3) : 0;
		}
	}

	if (needs_quse_vref_training)
	{
		if (dram_dev_num == TWO_RANK)
		{
			u32 emc0_opt_dqs_array[4] = {0};
			u32 emc1_opt_dqs_array[4] = {0};
			u32 emc1_training_opt_dqs_ib_vref_rank0_val = channel1_enabled ? EMC_CH1(EMC_TRAINING_OPT_DQS_IB_VREF_RANK0) : 0;
			u32 emc1_training_opt_dqs_ib_vref_rank1_val = channel1_enabled ? EMC_CH1(EMC_TRAINING_OPT_DQS_IB_VREF_RANK1) : 0;

			for (u32 i = 0; i < 4; i++)
			{
				emc0_opt_dqs_array[i] = (EMC_CH0(EMC_TRAINING_OPT_DQS_IB_VREF_RANK0) >> (8 * i)) & 0xFF;
				emc1_opt_dqs_array[i] = (emc1_training_opt_dqs_ib_vref_rank0_val >> (8 * i)) & 0xFF;
			}

			u32 ib_vref_dqs_0 = 0;
			u32 ib_vref_dqs_1 = 0;
			for (u32 i = 0; i < 4; i++)
			{
				ib_vref_dqs_0 |= (emc0_opt_dqs_array[i] + ((EMC_CH0(EMC_TRAINING_OPT_DQS_IB_VREF_RANK1) >> (8 * i)) & 0xFF)) >> 1 << (8 * i);
				ib_vref_dqs_1 |= (emc1_opt_dqs_array[i] + ((emc1_training_opt_dqs_ib_vref_rank1_val >> (8 * i)) & 0xFF)) >> 1 << (8 * i);
			}

			mtc_table_entry->trim_regs.emc_pmacro_ib_vref_dqs_0_idx = ib_vref_dqs_0;
			mtc_table_entry->trim_regs.emc_pmacro_ib_vref_dqs_1_idx = ib_vref_dqs_1;
		}
		else
		{
			mtc_table_entry->trim_regs.emc_pmacro_ib_vref_dqs_0_idx = EMC(EMC_PMACRO_IB_VREF_DQS_0);
			mtc_table_entry->trim_regs.emc_pmacro_ib_vref_dqs_1_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_VREF_DQS_1) : 0;
		}
	}

	if (needs_rd_training)
	{
		mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_long_dqs_rank0_0_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_0);
		mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_long_dqs_rank0_1_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_1);
		mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_long_dqs_rank0_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_2) : 0;
		mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_long_dqs_rank0_3_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_3) : 0;

		if (dram_dev_num == TWO_RANK)
		{
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_long_dqs_rank1_0_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_0);
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_long_dqs_rank1_1_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_1);
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_long_dqs_rank1_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_2) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_long_dqs_rank1_3_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_3) : 0;
		}

		if (needs_training_in_self_refresh)
		{
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte0_0_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_0);
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte0_1_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_1);
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte0_2_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_2);
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte1_0_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_0);
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte1_1_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_1);
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte1_2_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_2);
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte2_0_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_0);
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte2_1_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_1);
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte2_2_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_2);
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte3_0_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_0);
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte3_1_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_1);
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte3_2_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_2);
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte4_0_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_0) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte4_1_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_1) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte4_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_2) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte5_0_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_0) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte5_1_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_1) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte5_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_2) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte6_0_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_0) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte6_1_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_1) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte6_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_2) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte7_0_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_0) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte7_1_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_1) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank0_byte7_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_2) : 0;

			if (dram_dev_num == TWO_RANK)
			{
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte0_0_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_0);
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte0_1_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_1);
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte0_2_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_2);
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte1_0_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_0);
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte1_1_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_1);
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte1_2_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_2);
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte2_0_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_0);
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte2_1_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_1);
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte2_2_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_2);
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte3_0_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_0);
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte3_1_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_1);
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte3_2_idx = EMC_CH0(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_2);
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte4_0_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_0) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte4_1_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_1) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte4_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_2) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte5_0_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_0) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte5_1_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_1) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte5_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_2) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte6_0_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_0) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte6_1_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_1) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte6_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_2) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte7_0_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_0) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte7_1_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_1) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ib_ddll_short_dq_rank1_byte7_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_2) : 0;
			}
		}

		if (needs_rd_vref_training)
		{
			char ib_vref_dq_byte0_icr = (EMC(EMC_PMACRO_IB_VREF_DQ_0) & 0x7F) + (mtc_table_entry->save_restore_mod_regs[0] & 0x7F);
			if (mtc_table_entry->save_restore_mod_regs[0] & 0x80000000) // < 0 check.
				ib_vref_dq_byte0_icr = (EMC(EMC_PMACRO_IB_VREF_DQ_0) & 0x7F) - (mtc_table_entry->save_restore_mod_regs[0] & 0x7F);

			char ib_vref_dq_byte1_icr = ((EMC(EMC_PMACRO_IB_VREF_DQ_0) >> 8) & 0x7F) + (mtc_table_entry->save_restore_mod_regs[1] & 0x7F);
			if (mtc_table_entry->save_restore_mod_regs[1] & 0x80000000)
				ib_vref_dq_byte1_icr = ((EMC(EMC_PMACRO_IB_VREF_DQ_0) >> 8) & 0x7F) - (mtc_table_entry->save_restore_mod_regs[1] & 0x7F);

			char ib_vref_dq_byte2_icr = ((EMC(EMC_PMACRO_IB_VREF_DQ_0) >> 16) & 0x7F) + (mtc_table_entry->save_restore_mod_regs[2] & 0x7F);
			if (mtc_table_entry->save_restore_mod_regs[2] & 0x80000000)
				ib_vref_dq_byte2_icr = ((EMC(EMC_PMACRO_IB_VREF_DQ_0) >> 16) & 0x7F) - (mtc_table_entry->save_restore_mod_regs[2] & 0x7F);

			char ib_vref_dq_byte3_icr = ((EMC(EMC_PMACRO_IB_VREF_DQ_0) >> 24) & 0x7F) + (mtc_table_entry->save_restore_mod_regs[3] & 0x7F);
			if (mtc_table_entry->save_restore_mod_regs[3] & 0x80000000)
				ib_vref_dq_byte3_icr = ((EMC(EMC_PMACRO_IB_VREF_DQ_0) >> 24) & 0x7F) - (mtc_table_entry->save_restore_mod_regs[3] & 0x7F);

			mtc_table_entry->trim_regs.emc_pmacro_ib_vref_dq_0_idx =
				 ((ib_vref_dq_byte0_icr & 0x7F)
				| (ib_vref_dq_byte1_icr & 0x7F) << 8)
				| ((ib_vref_dq_byte2_icr & 0x7F) << 16)
				| ((ib_vref_dq_byte3_icr & 0x7F) << 24);

			char ib_vref_dq_byte4_icr = (EMC(EMC_PMACRO_IB_VREF_DQ_1) & 0x7F) + (mtc_table_entry->save_restore_mod_regs[4] & 0x7F);
			if (mtc_table_entry->save_restore_mod_regs[4] & 0x80000000)
				ib_vref_dq_byte4_icr = (EMC(EMC_PMACRO_IB_VREF_DQ_1) & 0x7F) - (mtc_table_entry->save_restore_mod_regs[4] & 0x7F);

			char ib_vref_dq_byte5_icr = ((EMC(EMC_PMACRO_IB_VREF_DQ_1) >> 8) & 0x7F) + (mtc_table_entry->save_restore_mod_regs[5] & 0x7F);
			if (mtc_table_entry->save_restore_mod_regs[5] & 0x80000000)
				ib_vref_dq_byte5_icr = ((EMC(EMC_PMACRO_IB_VREF_DQ_1) >> 8) & 0x7F) - (mtc_table_entry->save_restore_mod_regs[5] & 0x7F);

			char ib_vref_dq_byte6_icr = ((EMC(EMC_PMACRO_IB_VREF_DQ_1) >> 16) & 0x7F) + (mtc_table_entry->save_restore_mod_regs[6] & 0x7F);
			if (mtc_table_entry->save_restore_mod_regs[6] & 0x80000000)
				ib_vref_dq_byte6_icr = ((EMC(EMC_PMACRO_IB_VREF_DQ_1) >> 16) & 0x7F) - (mtc_table_entry->save_restore_mod_regs[6] & 0x7F);

			char ib_vref_dq_byte7_icr = ((EMC(EMC_PMACRO_IB_VREF_DQ_1) >> 24) & 0x7F) + (mtc_table_entry->save_restore_mod_regs[7] & 0x7F);
			if (mtc_table_entry->save_restore_mod_regs[7] & 0x80000000)
				ib_vref_dq_byte7_icr = ((EMC(EMC_PMACRO_IB_VREF_DQ_1) >> 24) & 0x7F) - (mtc_table_entry->save_restore_mod_regs[7] & 0x7F);

			mtc_table_entry->trim_regs.emc_pmacro_ib_vref_dq_1_idx =
				 ((ib_vref_dq_byte4_icr & 0x7F)
				| (ib_vref_dq_byte5_icr & 0x7F) << 8)
				| ((ib_vref_dq_byte6_icr & 0x7F) << 16)
				| ((ib_vref_dq_byte7_icr & 0x7F) << 24);
		}
	}

	if (needs_wr_training)
	{
		mtc_table_entry->trim_perch_regs.emc0_data_brlshft_0_idx = EMC_CH0(EMC_DATA_BRLSHFT_0);
		mtc_table_entry->trim_perch_regs.emc1_data_brlshft_0_idx = channel1_enabled ? EMC_CH1(EMC_DATA_BRLSHFT_0) : 0;

		if (dram_dev_num == TWO_RANK)
		{
			mtc_table_entry->trim_perch_regs.emc0_data_brlshft_1_idx = EMC_CH0(EMC_DATA_BRLSHFT_1);
			mtc_table_entry->trim_perch_regs.emc1_data_brlshft_1_idx = channel1_enabled ? EMC_CH1(EMC_DATA_BRLSHFT_1) : 0;
		}

		mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_long_dq_rank0_0_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0);
		mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_long_dq_rank0_1_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1);
		mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_long_dq_rank0_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2) : 0;
		mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_long_dq_rank0_3_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3) : 0;

		if (dram_dev_num == TWO_RANK)
		{
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_long_dq_rank1_0_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_long_dq_rank1_1_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_long_dq_rank1_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_long_dq_rank1_3_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3) : 0;
		}

		if (needs_training_in_self_refresh)
		{
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte0_0_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_0);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte0_1_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_1);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte0_2_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_2);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte1_0_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_0);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte1_1_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_1);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte1_2_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_2);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte2_0_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_0);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte2_1_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_1);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte2_2_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_2);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte3_0_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_0);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte3_1_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_1);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte3_2_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_2);
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte4_0_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_0) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte4_1_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_1) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte4_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_2) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte5_0_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_0) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte5_1_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_1) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte5_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_2) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte6_0_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_0) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte6_1_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_1) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte6_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_2) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte7_0_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_0) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte7_1_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_1) : 0;
			mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank0_byte7_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_2) : 0;

			if (dram_dev_num == TWO_RANK)
			{
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte0_0_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_0);
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte0_1_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_1);
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte0_2_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_2);
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte1_0_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_0);
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte1_1_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_1);
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte1_2_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_2);
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte2_0_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_0);
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte2_1_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_1);
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte2_2_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_2);
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte3_0_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_0);
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte3_1_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_1);
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte3_2_idx = EMC_CH0(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_2);
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte4_0_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_0) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte4_1_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_1) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte4_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_2) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte5_0_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_0) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte5_1_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_1) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte5_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_2) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte6_0_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_0) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte6_1_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_1) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte6_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_2) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte7_0_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_0) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte7_1_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_1) : 0;
				mtc_table_entry->trim_regs.emc_pmacro_ob_ddll_short_dq_rank1_byte7_2_idx = channel1_enabled ? EMC_CH1(EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_2) : 0;
			}
		}

		if (needs_wr_vref_training) // mode 12/13 (MRW).
		{
			u32 emc1_ranks_sub_partitions = 0;
			emc1_ranks_sub_partitions = channel1_enabled ? EMC_CH1(EMC_TRAINING_OPT_DQ_OB_VREF) : 0;

			u8 emc0_ib_vref_dq_byte8_modded_plus = mtc_table_entry->save_restore_mod_regs[8] + EMC_CH0(EMC_TRAINING_OPT_DQ_OB_VREF);
			if (mtc_table_entry->save_restore_mod_regs[8] & 0x80000000) // < 0 check.
				emc0_ib_vref_dq_byte8_modded_plus = EMC_CH0(EMC_TRAINING_OPT_DQ_OB_VREF) - mtc_table_entry->save_restore_mod_regs[8];

			u8 emc0_mrw12_op_sp1 = ((EMC_CH0(EMC_TRAINING_OPT_DQ_OB_VREF) & 0xFFFF) >> 8) + mtc_table_entry->save_restore_mod_regs[9];
			if (mtc_table_entry->save_restore_mod_regs[9] & 0x80000000)
				emc0_mrw12_op_sp1 = ((EMC_CH0(EMC_TRAINING_OPT_DQ_OB_VREF) & 0xFFFF) >> 8) - mtc_table_entry->save_restore_mod_regs[9];

			u8 emc0_mrw13_op_sp0 = ((EMC_CH0(EMC_TRAINING_OPT_DQ_OB_VREF) >> 16) & 0xFF) + mtc_table_entry->save_restore_mod_regs[8];
			if (mtc_table_entry->save_restore_mod_regs[8] & 0x80000000)
				emc0_mrw13_op_sp0 = ((EMC_CH0(EMC_TRAINING_OPT_DQ_OB_VREF) >> 16) & 0xFF) - mtc_table_entry->save_restore_mod_regs[8];

			u8 emc0_ib_vref_dq_byte9_modded_a_plus = mtc_table_entry->save_restore_mod_regs[9] + (EMC_CH0(EMC_TRAINING_OPT_DQ_OB_VREF) >> 24);
			if (mtc_table_entry->save_restore_mod_regs[9] & 0x80000000)
				emc0_ib_vref_dq_byte9_modded_a_plus = (EMC_CH0(EMC_TRAINING_OPT_DQ_OB_VREF) >> 24) - (u8)mtc_table_entry->save_restore_mod_regs[9];

			u8 emc0_ib_vref_dq_byte10_modded_plus = emc1_ranks_sub_partitions + mtc_table_entry->save_restore_mod_regs[10];
			if (mtc_table_entry->save_restore_mod_regs[10] & 0x80000000)
				emc0_ib_vref_dq_byte10_modded_plus = emc1_ranks_sub_partitions - mtc_table_entry->save_restore_mod_regs[10];

			u8 emc0_ib_vref_dq_byte11_modded_plus = ((emc1_ranks_sub_partitions & 0xFFFF) >> 8) + mtc_table_entry->save_restore_mod_regs[11];
			if (mtc_table_entry->save_restore_mod_regs[11] & 0x80000000)
				emc0_ib_vref_dq_byte11_modded_plus = ((emc1_ranks_sub_partitions & 0xFFFF) >> 8) - mtc_table_entry->save_restore_mod_regs[11];

			u8 emc1_mrw13_op_sp0 = ((emc1_ranks_sub_partitions >> 16) & 0xFF) + mtc_table_entry->save_restore_mod_regs[10];
			if (mtc_table_entry->save_restore_mod_regs[10] & 0x80000000)
				emc1_mrw13_op_sp0 = ((emc1_ranks_sub_partitions >> 16) & 0xFF) - mtc_table_entry->save_restore_mod_regs[10];

			u8 emc1_mrw13_op_sp1 = (emc1_ranks_sub_partitions >> 24) + mtc_table_entry->save_restore_mod_regs[11];
			if (mtc_table_entry->save_restore_mod_regs[11] & 0x80000000)
				emc1_mrw13_op_sp1 = (emc1_ranks_sub_partitions >> 24) - mtc_table_entry->save_restore_mod_regs[11];

			u32 mr13_dev_ext_cnt_sp_addr = 0xC80E0000;
			if (dram_dev_num == TWO_RANK)
				mr13_dev_ext_cnt_sp_addr = 0x480E0000;

			mtc_table_entry->burst_reg_per_ch.emc1_mrw12_idx = (u8)emc0_ib_vref_dq_byte10_modded_plus | 0x880E0000 | (emc0_ib_vref_dq_byte11_modded_plus << 8);
			mtc_table_entry->burst_reg_per_ch.emc0_mrw12_idx = emc0_ib_vref_dq_byte8_modded_plus | 0x880E0000 | (emc0_mrw12_op_sp1 << 8);
			mtc_table_entry->burst_reg_per_ch.emc0_mrw13_idx = emc0_ib_vref_dq_byte9_modded_a_plus << 8 | emc0_mrw13_op_sp0 | mr13_dev_ext_cnt_sp_addr;
			mtc_table_entry->burst_reg_per_ch.emc1_mrw13_idx = (emc1_mrw13_op_sp1 << 8) | emc1_mrw13_op_sp0 | mr13_dev_ext_cnt_sp_addr;
		}
	}
}

static u32 _minerva_set_clock(emc_table_t *src_emc_entry, emc_table_t *dst_emc_entry, u32 needs_training, u32 selected_clk_src_emc)
{
	u32 emc_dbg_o;
	u32 emc_pin_o;
	u32 emc_cfg_pipe_clk_o;
	u32 emc_sel_dpd_ctrl;
	u32 emc_cfg;
	u32 emc_dbg_val;
	u32 emc_zq_cal = 0;
	u32 ramp_up_wait;
	u32 ramp_down_wait;
	u32 bg_regulator_mode_change;
	u32 mr13_flip_fspop;
	u32 mr13_flip_fspwr;
	u32 mr13_catr_enable;

	/* needs_training flags */
	/*
	 | bit | Description                      |
	 |-----|----------------------------------|
	 |  0  | Needs CA              training   |
	 |  1  | Needs CA_VREF         training   |
	 |  2  | Needs QUSE            training   |
	 |  3  | Needs QUSE_VREF       training   |
	 |  4  | Needs WR              training   |
	 |  5  | Needs WR_VREF         training   |
	 |  6  | Needs RD              training   |
	 |  7  | Needs RD_VREF         training   |
	 |  8  | Needs SWAP_RANK       training   |
	 |  9  | Needs IN_SELF_REFRESH training   |
	 */

	bool compensate_trimmer_applicable = false;
	bool needs_ca_combo_training = !!(needs_training & NEEDS_TRAINING_CA_COMBO);
	bool needs_tristate_training = !!(needs_training & NEEDS_TRISTATE_TRAINING);

	bool needs_ca_training = !!(needs_training & NEEDS_TRAINING_CA);
	bool needs_ca_vref_training = !!(needs_training & NEEDS_TRAINING_CA_VREF);
	bool needs_quse_training = !!(needs_training & NEEDS_TRAINING_QUSE);
	bool needs_quse_vref_training = !!(needs_training & NEEDS_TRAINING_QUSE_VREF);
	bool needs_wr_training = !!(needs_training & NEEDS_TRAINING_WR);
	bool needs_wr_vref_training = !!(needs_training & NEEDS_TRAINING_WR_VREF);
	bool needs_rd_training = !!(needs_training & NEEDS_TRAINING_RD);
	bool needs_rd_vref_training = !!(needs_training & NEEDS_TRAINING_RD_VREF);
	bool needs_swap_rank_training = !!(needs_training & NEEDS_TRAINING_SWAP_RANK);

	bool zcal_resistor_shared = (src_emc_entry->burst_regs.emc_zcal_wait_cnt_idx >> 31) & 1;
	bool enable_bg_regulator = (dst_emc_entry->burst_regs.emc_pmacro_bg_bias_ctrl_0_idx & 1) ^ 1;
	bool channel1_enabled = (src_emc_entry->burst_regs.emc_fbio_cfg7_idx >> 2) & 1;
	u32  dram_type = EMC(EMC_FBIO_CFG5) & 3;
	u32  dram_dev_num = (MC(MC_EMEM_ADR_CFG) & 1) + 1;

	u32 src_clock_period = 1000000000 / src_emc_entry->rate_khz; // In picoseconds.
	u32 dst_clock_period = 1000000000 / dst_emc_entry->rate_khz; // In picoseconds.

	// Get current FSP op/write value.
	bool enable_fsp_opwr = !(EMC(EMC_MRW3) & 0xC0);

	if (dram_type != DRAM_TYPE_LPDDR4)
	{
		EPRINTF("MTC Error: DRAM is not LPDDR4");
		return 5;
	}

	u32 tFC_lpddr4 = dst_emc_entry->dram_timings.t_fc_lpddr4 * 1000;
	u32 tZQCAL_lpddr4 = 1000000;
	if (dst_clock_period <= 2000)
		tZQCAL_lpddr4 -= tFC_lpddr4;
	s32 tZQCAL_lpddr4_fc_adj = tZQCAL_lpddr4 / dst_clock_period;

	(void)EMC(EMC_CFG);
	(void)EMC(EMC_AUTO_CAL_CONFIG);

	// Step 1 - Pre DVFS SW sequence.
	EPRINTF("Step 1");
	emc_dbg_o = EMC(EMC_DBG);
	emc_pin_o = EMC(EMC_PIN);
	emc_cfg = dst_emc_entry->burst_regs.emc_cfg_idx & 0xFFFFFFF;
	emc_sel_dpd_ctrl = dst_emc_entry->emc_sel_dpd_ctrl & 0xFFFFFEC3;
	emc_cfg_pipe_clk_o = EMC(EMC_CFG_PIPE_CLK);
	_digital_dll_disable();

	// Step 1.2 - Disable AUTOCAL temporarily.
	EPRINTF("Step 1.2");
	EMC(EMC_AUTO_CAL_CONFIG) = (dst_emc_entry->emc_auto_cal_config & 0x7FFFF9FF) | 0x600;
	(void)EMC(EMC_AUTO_CAL_CONFIG);

	// Step 1.3 - Disable other power features.
	EPRINTF("Step 1.3");
	EMC(EMC_DBG) = emc_dbg_o | 2;
	EMC(EMC_CFG) = emc_cfg;
	EMC(EMC_SEL_DPD_CTRL) = emc_sel_dpd_ctrl;
	EMC(EMC_DBG) = emc_dbg_o;

	if (!needs_tristate_training && dst_emc_entry->periodic_training)
	{
		if (dram_dev_num == TWO_RANK)
		{
			_wait_emc_status(EMC_EMC_STATUS, IN_POWERDOWN_BOTH_MASK, false, EMC_CHANNEL0);
			if (channel1_enabled)
				_wait_emc_status(EMC_EMC_STATUS, IN_POWERDOWN_BOTH_MASK, false, EMC_CHANNEL1);
		}
		else
		{
			_wait_emc_status(EMC_EMC_STATUS, IN_POWERDOWN_1DEV_MASK, false, EMC_CHANNEL0);
			if (channel1_enabled)
				_wait_emc_status(EMC_EMC_STATUS, IN_POWERDOWN_1DEV_MASK, false, EMC_CHANNEL1);
		}

		_wait_emc_status(EMC_EMC_STATUS, IN_SELF_REFRESH_MASK, false, EMC_CHANNEL0);
		if (channel1_enabled)
			_wait_emc_status(EMC_EMC_STATUS, IN_SELF_REFRESH_MASK, false, EMC_CHANNEL1);

		// Reset clock tree delays.
		dst_emc_entry->current_dram_clktree_c0d0u0 = dst_emc_entry->trained_dram_clktree_c0d0u0;
		dst_emc_entry->current_dram_clktree_c0d0u1 = dst_emc_entry->trained_dram_clktree_c0d0u1;
		dst_emc_entry->current_dram_clktree_c0d1u0 = dst_emc_entry->trained_dram_clktree_c0d1u0;
		dst_emc_entry->current_dram_clktree_c0d1u1 = dst_emc_entry->trained_dram_clktree_c0d1u1;
		dst_emc_entry->current_dram_clktree_c1d0u0 = dst_emc_entry->trained_dram_clktree_c1d0u0;
		dst_emc_entry->current_dram_clktree_c1d0u1 = dst_emc_entry->trained_dram_clktree_c1d0u1;
		dst_emc_entry->current_dram_clktree_c1d1u0 = dst_emc_entry->trained_dram_clktree_c1d1u0;
		dst_emc_entry->current_dram_clktree_c1d1u1 = dst_emc_entry->trained_dram_clktree_c1d1u1;

		u32 adelta = _minerva_periodic_compensation_handler(src_emc_entry, dst_emc_entry, dram_dev_num, channel1_enabled, DVFS_SEQUENCE);

		if (((dst_emc_entry->rate_khz / 1000) * 128) * adelta / 1000000 > dst_emc_entry->tree_margin)
			compensate_trimmer_applicable = true;
	}

	EMC(EMC_INTSTATUS) = CLKCHANGE_COMPLETE_INT;
	EMC(EMC_DBG) = emc_dbg_o | 2;
	EMC(EMC_CFG) = emc_cfg;
	EMC(EMC_SEL_DPD_CTRL) = emc_sel_dpd_ctrl;
	EMC(EMC_CFG_PIPE_CLK) = emc_cfg_pipe_clk_o | 1; // CLK_ALWAYS_ON.
	EMC(EMC_FDPD_CTRL_CMD_NO_RAMP) = dst_emc_entry->emc_fdpd_ctrl_cmd_no_ramp & 0xFFFFFFFE;

	bg_regulator_mode_change = src_emc_entry->burst_regs.emc_pmacro_bg_bias_ctrl_0_idx ^ dst_emc_entry->burst_regs.emc_pmacro_bg_bias_ctrl_0_idx;
	bg_regulator_mode_change = (bg_regulator_mode_change | (bg_regulator_mode_change >> 2)) & 1;

	if (bg_regulator_mode_change)
	{
		EMC(EMC_DBG) = emc_dbg_o | 2;
		if (enable_bg_regulator)
			EMC(EMC_PMACRO_BG_BIAS_CTRL_0) = src_emc_entry->burst_regs.emc_pmacro_bg_bias_ctrl_0_idx & 0xFFFFFFFE;
		else
			EMC(EMC_PMACRO_BG_BIAS_CTRL_0) = src_emc_entry->burst_regs.emc_pmacro_bg_bias_ctrl_0_idx & 0xFFFFFFFB;
	}

	// Check if we need to turn on VREF generator.
	if ((!(src_emc_entry->burst_regs.emc_pmacro_data_pad_tx_ctrl_idx & 0x100)
		&& (dst_emc_entry->burst_regs.emc_pmacro_data_pad_tx_ctrl_idx & 0x100))
		|| (!(src_emc_entry->burst_regs.emc_pmacro_data_pad_tx_ctrl_idx & 1)
		&& (dst_emc_entry->burst_regs.emc_pmacro_data_pad_tx_ctrl_idx & 1)))
	{
		EMC(EMC_PMACRO_DATA_PAD_TX_CTRL) =
			(((dst_emc_entry->burst_regs.emc_pmacro_data_pad_tx_ctrl_idx & 1) | (src_emc_entry->burst_regs.emc_pmacro_data_pad_tx_ctrl_idx & 0xFFFFFFFE)) & 0xFFFFFEFF)
			| (((dst_emc_entry->burst_regs.emc_pmacro_data_pad_tx_ctrl_idx >> 8) & 0x1) << 8);
	}

	_usleep(1);

	EMC(EMC_DBG) = emc_dbg_o;

	// Step 2 - Prelock the DLL.
	EPRINTF("Step 2");
	if (dst_emc_entry->burst_regs.emc_cfg_dig_dll_idx & 1)
		_digital_dll_prelock(dst_emc_entry, needs_tristate_training, selected_clk_src_emc); // Prelock enabled for target frequency.
	else
	{
		_change_dll_src(dst_emc_entry, selected_clk_src_emc);
		_digital_dll_disable(); // Disabling DLL for target frequency.
	}

	// Step 3 - Prepare autocal for the clock change.
	EPRINTF("Step 3");
	EMC(EMC_AUTO_CAL_CONFIG) = (dst_emc_entry->emc_auto_cal_config & 0x7FFFF9FF) | 0x600;
	EMC(EMC_DBG) = emc_dbg_o | 2;
	EMC(EMC_AUTO_CAL_CONFIG2) = dst_emc_entry->emc_auto_cal_config2;
	EMC(EMC_AUTO_CAL_CONFIG3) = dst_emc_entry->emc_auto_cal_config3;
	EMC(EMC_AUTO_CAL_CONFIG4) = dst_emc_entry->emc_auto_cal_config4;
	EMC(EMC_AUTO_CAL_CONFIG5) = dst_emc_entry->emc_auto_cal_config5;
	EMC(EMC_AUTO_CAL_CONFIG6) = dst_emc_entry->emc_auto_cal_config6;
	EMC(EMC_AUTO_CAL_CONFIG7) = dst_emc_entry->emc_auto_cal_config7;
	EMC(EMC_AUTO_CAL_CONFIG8) = dst_emc_entry->emc_auto_cal_config8;
	EMC(EMC_DBG) = emc_dbg_o;
	EMC(EMC_AUTO_CAL_CONFIG) = (dst_emc_entry->emc_auto_cal_config & 0x7FFFF9FE) | 0x601;

	// Step 4 - Update EMC_CFG.
	EPRINTF("Step 4");
	if (src_clock_period <= 50000)
		EMC(EMC_CFG_2) = dst_emc_entry->emc_cfg_2;
	else
		_ccfifo_write(EMC_SELF_REF, 1, 0);

	// Step 5 - Prepare reference variables for ZQCAL regs.
	EPRINTF("Step 5");
	// u32 zq_wait_long = 0;
	// u32 zq_wait_short = 0;

	// zq_wait_long = _fceil(1000.0f / dst_clock_period);

	// Step 7 - Bug 200024907 - Patch RP R2P.
	EPRINTF("Step 7");
	if (needs_ca_combo_training && dram_dev_num == TWO_RANK)
		EMC(EMC_PIN) = 0x107;

	u32 R2P_war = 0;
	u32 TRPab_war = 0;
	u32 RP_war = 0;
	u32 W2P_war = 0;

	u32 nRTP = 8;  // <= 1066MHz.
	if (   src_clock_period < 1000000 /  266
		&& src_clock_period < 1000000 /  533
		&& src_clock_period < 1000000 /  800
		&& src_clock_period < 1000000 / 1066  )
		nRTP = 10; // 1067MHz < x <= 1333MHz.
	if (src_clock_period < 1000000 / 1333)
		nRTP = 12; // 1333MHz < x <= 1600MHz.
	if (src_clock_period < 1000000 / 1600)
		nRTP = 14; // 1600MHz < x <= 1866MHz.
	if (src_clock_period < 1000000 / 1866)
		nRTP = 16; // > 1866MHz

	u32 tRPST = (src_emc_entry->emc_mrw >> 7) & 1;

	u32 deltaTWATM = div_o3(7500, src_clock_period);
	if (deltaTWATM < 8)
		deltaTWATM = 8;

	u32 tRTM = src_emc_entry->dram_timings.rl + div_o3(3600, src_clock_period) + deltaTWATM + tRPST + nRTP + 1;

	if (tRTM <= src_emc_entry->burst_regs.emc_rp_idx + src_emc_entry->burst_regs.emc_r2p_idx)
	{
		TRPab_war = src_emc_entry->burst_regs.emc_trpab_idx;
		R2P_war = src_emc_entry->burst_regs.emc_r2p_idx;
		RP_war = src_emc_entry->burst_regs.emc_rp_idx;
	}
	else
	{
		R2P_war = tRTM - src_emc_entry->burst_regs.emc_rp_idx;
		TRPab_war = src_emc_entry->burst_regs.emc_trpab_idx;
		RP_war = src_emc_entry->burst_regs.emc_rp_idx;
		if (R2P_war > 63)
		{
			RP_war = tRTM - 63;
			R2P_war = 63;
			if (src_emc_entry->burst_regs.emc_trpab_idx < tRTM - 63)
				TRPab_war = tRTM - 63;
			else
				TRPab_war = src_emc_entry->burst_regs.emc_trpab_idx;
		}
	}

	if (RP_war >= deltaTWATM)
		W2P_war = src_emc_entry->burst_regs.emc_w2p_idx;
	else
	{
		u32 W2P_war_temp = deltaTWATM + src_emc_entry->burst_regs.emc_w2p_idx;
		W2P_war = W2P_war_temp - RP_war;
		if (W2P_war > 63)
		{
			RP_war = W2P_war_temp - 63;
			W2P_war = 63;
			if (TRPab_war < RP_war)
				TRPab_war = RP_war;
		}
	}

	if ( src_emc_entry->burst_regs.emc_w2p_idx != W2P_war
		|| src_emc_entry->burst_regs.emc_rp_idx != RP_war
		|| src_emc_entry->burst_regs.emc_r2p_idx != R2P_war
		|| src_emc_entry->burst_regs.emc_trpab_idx != TRPab_war)
	{
		EMC(EMC_DBG) = emc_dbg_o | 2;
		EMC(EMC_RP) = RP_war;
		EMC(EMC_R2P) = R2P_war;
		EMC(EMC_W2P) = W2P_war;
		EMC(EMC_TRPAB) = TRPab_war;
		EMC(EMC_DBG) = emc_dbg_o;
		(void)EMC(EMC_TRPAB);
		_usleep(1);
	}

	// Step 7.2 - Program FSP reference registers and send MRWs to new FSPWR.
	EPRINTF("Step 7.2");
	if (enable_fsp_opwr)
	{
		mr13_flip_fspop = dst_emc_entry->emc_mrw3 | 0xC0;
		mr13_flip_fspwr = (dst_emc_entry->emc_mrw3 & 0xFFFFFF3F) | 0x40;
	}
	else
	{
		mr13_flip_fspop = dst_emc_entry->emc_mrw3 & 0xFFFFFF3F;
		mr13_flip_fspwr = mr13_flip_fspop | 0x80;
	}

	if (dram_dev_num == TWO_RANK)
	{
		if (needs_swap_rank_training)
			mr13_catr_enable = (mr13_flip_fspwr & 0x3FFFFFFF) | 0x40000001;
		else
			mr13_catr_enable = (mr13_flip_fspwr & 0x3FFFFFFF) | 0x80000001;

		if (needs_ca_combo_training)
		{
			if (needs_swap_rank_training)
				mr13_flip_fspop = (mr13_flip_fspop & 0x3FFFFFFF) | 0x80000000;
			else
				mr13_flip_fspop = (mr13_flip_fspop & 0x3FFFFFFF) | 0x40000000;
		}
	}
	else
		mr13_catr_enable = mr13_flip_fspwr | 1;

	EMC(EMC_MRW3) = mr13_flip_fspwr;
	EMC(EMC_MRW) = dst_emc_entry->emc_mrw;
	EMC(EMC_MRW2) = dst_emc_entry->emc_mrw2;

	// Step 8 - Program the shadow registers.
	EPRINTF("Step 8");
	// Writing burst_regs.
	u32 reg_addr = 0;
	u32 reg_val = 0;
	u32 reg_check = false;
	burst_regs_table_t *dst_burst_regs = (burst_regs_table_t *)&dst_emc_entry->burst_regs;

	for (u32 i = 0; dst_emc_entry->num_burst > i; i++)
	{
		reg_check = false;
		reg_addr = burst_regs_emc_addr_table[i];
		if (needs_tristate_training)
		{
			if (needs_ca_combo_training)
				reg_val = dst_burst_regs->shadow_regs_ca_train[i];
			else if (needs_training & NEEDS_TRAINING_QUSE_COMBO)
				reg_val = dst_burst_regs->shadow_regs_quse_train[i];
			else if (needs_training & (NEEDS_TRAINING_WR_COMBO | NEEDS_TRAINING_RD_COMBO))
				reg_val = dst_burst_regs->shadow_regs_rdwr_train[i];
			else
				continue;
		}
		else
			reg_val = dst_burst_regs->burst_regs[i];

		if ((reg_addr & 0xFFF7) != EMC_MRW6
			&& (reg_addr - EMC_MRW7) & 0xFFFF7
			//&& (reg_addr & 0xEFF7) != 0x34B4 // EMC_MRW10.
			&& ((reg_addr & 0xEFFF) - 0x34B8) & 0xFFF7 // EMC_MRW11.
			&& reg_addr != EMC_TRAINING_CTRL
			&& reg_addr != EMC_MRW14
			&& reg_addr != EMC_MRW15)
		{
			reg_check = true;
		}

		if (reg_check && reg_addr == EMC_CFG)
		{
			reg_val &= 0xFFFFFFF;

			EMC(reg_addr) = reg_val;
			continue;
		}

		if (reg_addr != EMC_CFG)// EMC_CFG
		{
			if (reg_addr != EMC_ZCAL_INTERVAL)
			{
				switch ( reg_addr )
				{
				case EMC_PMACRO_AUTOCAL_CFG_COMMON:
					reg_val |= 0x10000;
					break;
				case EMC_PMACRO_DATA_PAD_TX_CTRL:
					reg_val &= 0xFEFEFDFD;
					break;
				case EMC_PMACRO_CMD_PAD_TX_CTRL:
					reg_val = (reg_val & 0xFAFEFDFD) | 0x4000000;
					break;
				case EMC_PMACRO_BRICK_CTRL_RFU1:
					reg_val &= 0xF800F800;
					break;
				case EMC_PMACRO_COMMON_PAD_TX_CTRL:
					reg_val &= 0xFFFFFFF0;
					break;
				case EMC_TRAINING_CTRL:
					reg_val |= needs_swap_rank_training << 14;// bit15 is TR_IN_SELF_REFRESH
					break;
				}
			}
			else
				reg_val = 0;
		}
		else
			reg_val &= 0xFFFFFFF;

		EMC(reg_addr) = reg_val;
	}

	if (needs_tristate_training)
		EMC(EMC_MRW) = (src_emc_entry->run_clocks & 0xFF) | 0x170000;
	else
		EMC(EMC_MRW) = (dst_emc_entry->run_clocks & 0xFF) | 0x170000;

	// Writing burst_regs_per_ch.
	for (u32 i = 0; dst_emc_entry->num_burst_per_ch > i; i++)
	{
		reg_addr = burst_reg_per_ch_emc01_addr_table[i];
		if (reg_addr && (channel1_enabled || ((reg_addr - 0x4000) > 0xFFF)))
		{
			EMC(reg_addr) = dst_burst_regs->burst_reg_per_ch[i];
		}
	}

	// Writing vref_regs.
	trim_regs_table_t *trim_regs_table = (trim_regs_table_t *)&dst_emc_entry->trim_regs;
	for (u32 i = 0; dst_emc_entry->vref_num > i; i++)
	{
		reg_addr = vref_perch_regs_emc01_addr_table[i];
		if (reg_addr && (channel1_enabled || (reg_addr - 0x4000) > 0xFFF))
			EMC(reg_addr) = trim_regs_table->vref_perch_regs[i];
	}

	// Writing training mod regs.
	if (needs_tristate_training)
	{
		for (u32 i = 0; dst_emc_entry->training_mod_num > i; i++)
		{
			reg_addr = training_mod_regs_emc01_addr_table[i];
			if (reg_addr && (channel1_enabled || (reg_addr - 0x4000) > 0xFFF))
				EMC(reg_addr) = dst_emc_entry->training_mod_regs[i];
		}
	}

	// Writing trim_regs
	for (u32 i = 0; dst_emc_entry->num_trim > i; i++)
	{
		reg_addr = trim_regs_emc_addr_table[i];
		if (reg_addr)
		{
			if (((reg_addr & 0xFFFFFFF3) == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0
				 || (reg_addr & 0xFFFFFFF3) == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0
				 || (reg_addr & 0xFFFFFFFB) == EMC_DATA_BRLSHFT_0)
				&& compensate_trimmer_applicable)
			{
				EMC(reg_addr) = _minerva_apply_periodic_compensation_trimmer(dst_emc_entry, reg_addr);
			}
			else
				EMC(reg_addr) = trim_regs_table->trim_regs[i];
		}
	}

	// Writing trim_regs_per_ch
	reg_val = 0;
	for (u32 i = 0; dst_emc_entry->num_trim_per_ch > i; i++)
	{
		reg_addr = trim_perch_regs_emc01_addr_table[i];
		if (reg_addr && (channel1_enabled || reg_addr - 0x4000 > 0xFFF))
		{
			if (((reg_addr & 0xFFFFFFF3) == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0
				 || (reg_addr & 0xFFFFFFF3) == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0
				 || (reg_addr & 0xFFFFFFFB) == EMC_DATA_BRLSHFT_0)
				&& compensate_trimmer_applicable )
			{
				reg_val = _minerva_apply_periodic_compensation_trimmer(dst_emc_entry, reg_addr & 0xFFF);
			}
			else if (((reg_addr & 0xFFFFFFFB) == 0x3660
				 || (reg_addr & 0xFFFFFFDF) == 0x3648
				 || (reg_addr & 0xFFFFFFF7) == 0x3644
				 || reg_addr == 0x366C
				 || reg_addr == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0
				 || (reg_addr & 0xFFFFFFFB) == 0x3588)
				&& compensate_trimmer_applicable )
			{
				reg_val = _minerva_apply_periodic_compensation_trimmer(dst_emc_entry, reg_addr & 0xFFF);
			}
			else if (((reg_addr & 0xFFFFFFF3) == 0x4660
				 || (reg_addr & 0xFFFFFFF3) == 0x4640
				 || (reg_addr & 0xFFFFFFFB) == 0x4588)
				&& compensate_trimmer_applicable)
			{
				reg_val = _minerva_apply_periodic_compensation_trimmer(dst_emc_entry, reg_addr & 0xFFF);
			}
			else
			{
				reg_val = trim_regs_table->trim_perch_regs[i];
			}
			EMC(reg_addr) = reg_val;
		}
	}

	if (needs_tristate_training)
	{
		// Check delta wrt previous values (save value if margin exceeds what is set in table).
		if (needs_wr_training && dst_emc_entry->periodic_training)
			_minerva_periodic_compensation_handler(src_emc_entry, dst_emc_entry, dram_dev_num, channel1_enabled, WRITE_TRAINING_SEQUENCE);
	}
	else
	{
		// Writing burst_mc_regs.
		for (u32 i = 0; dst_emc_entry->num_mc_regs > i; i++)
			MC(burst_mc_regs_addr_table[i]) = dst_emc_entry->burst_mc_regs[i];

		// Writing la_scale_regs.
		if (dst_emc_entry->rate_khz < src_emc_entry->rate_khz)
		{
			for (u32 i = 0; dst_emc_entry->num_up_down > i; i++)
				MC(la_scale_regs_mc_addr_table[i]) = dst_emc_entry->la_scale_regs[i];
		}
	}

	// Step 9 - LPDDR4.
	EPRINTF("Step 9");
	EMC(EMC_ZCAL_INTERVAL) = src_emc_entry->burst_regs.emc_zcal_interval_idx & 0xFF000000;
	EMC(EMC_ZCAL_WAIT_CNT) = dst_emc_entry->burst_regs.emc_zcal_wait_cnt_idx & 0xFFFFF800;
	EMC(EMC_DBG) = emc_dbg_o | 0x40000002;
	EMC(EMC_ZCAL_INTERVAL) = src_emc_entry->burst_regs.emc_zcal_interval_idx & 0xFF000000;
	EMC(EMC_DBG) = emc_dbg_o;

	if (needs_tristate_training)
	{
		EMC(EMC_DBG) = emc_dbg_o | 2;
		EMC(EMC_PMACRO_AUTOCAL_CFG_COMMON) = dst_emc_entry->burst_regs.emc_pmacro_autocal_cfg_common_idx | 0x10000;

		if (needs_ca_combo_training)
			EMC(EMC_FBIO_CFG5) = src_emc_entry->burst_regs.emc_fbio_cfg5_idx | 0x8000000;

		EMC(EMC_DBG) = emc_dbg_o;

		if (channel1_enabled)
			_ccfifo_write(EMC_CFG_SYNC, 0, 0);

		_ccfifo_write(EMC_DBG, (emc_dbg_o & 0xF3FFFFFF) | 0x4000000, 0);
	}

	// Step 10 - Self refresh
	EPRINTF("Step 10");
	_ccfifo_write(EMC_SELF_REF, 0x101, 0);

	if (!needs_ca_combo_training && (dst_clock_period <= 2000))
	{
		_ccfifo_write(EMC_MRW3, mr13_flip_fspwr ^ 0x40, 0);
		_ccfifo_write(EMC_MRW6, (src_emc_entry->burst_regs.emc_mrw6_idx & 0xC0C0) | (dst_emc_entry->burst_regs.emc_mrw6_idx & 0xFFFF3F3F), 0);
		_ccfifo_write(EMC_MRW14, (src_emc_entry->burst_regs.emc_mrw14_idx & 0x3838) | (dst_emc_entry->burst_regs.emc_mrw14_idx & 0xFFFF0707), 0);
		if (dram_dev_num == TWO_RANK)
		{
			_ccfifo_write(EMC_MRW7, (src_emc_entry->burst_regs.emc_mrw7_idx & 0xC0C0) | (dst_emc_entry->burst_regs.emc_mrw7_idx & 0xFFFF3F3F), 0);
			_ccfifo_write(EMC_MRW15, (src_emc_entry->burst_regs.emc_mrw15_idx & 0x3838) | (dst_emc_entry->burst_regs.emc_mrw15_idx & 0xFFFF0707), 0);
		}

		if (dram_dev_num == ONE_RANK || zcal_resistor_shared)
			emc_zq_cal = 0x80000001;
		else
			emc_zq_cal = 1;

		_ccfifo_write(EMC_ZQ_CAL, emc_zq_cal, 0);
	}

	emc_dbg_val = emc_dbg_o;
	u32 tRP_src_timing = (src_emc_entry->dram_timings.t_rp * 1000) / src_clock_period;
	bool in_self_refresh = false;
	u32 ref_delay = 0;

	if (needs_tristate_training)
	{
		emc_dbg_val = (emc_dbg_o & 0xF3FFFFFF) | 0x44000000;
		_ccfifo_write(EMC_DBG, emc_dbg_val, 0);
	}

	if (needs_ca_combo_training)
	{
		_ccfifo_write(EMC_PMACRO_DATA_RX_TERM_MODE, src_emc_entry->burst_regs.emc_pmacro_data_rx_term_mode_idx & 0xFFFFFCCC, 0);

		if (dram_dev_num == TWO_RANK && needs_swap_rank_training)
		{
			_ccfifo_write(EMC_MRW3, mr13_flip_fspop | 8, tRP_src_timing);
			_ccfifo_write(EMC_MRW3, mr13_catr_enable | 8, 0);
		}
		else
			_ccfifo_write(EMC_MRW3, mr13_catr_enable | 8, tRP_src_timing);

		_ccfifo_write(EMC_TR_CTRL_0, 0x15A, 0);
		ref_delay = 1000000 / src_clock_period;
	}
	else
	{
		_ccfifo_write(EMC_MRW3, mr13_flip_fspop | 8, tRP_src_timing);
		ref_delay = tFC_lpddr4 / src_clock_period;
	}

	_ccfifo_write(EMC_INTSTATUS, 0, ref_delay);
	_ccfifo_write(EMC_PIN, emc_pin_o & 0xFFFFFFF8, 30);

	// Step 11 - Ramp down.
	EPRINTF("Step 11");
	_ccfifo_write(EMC_CFG_SYNC, 0, 0);
	_ccfifo_write(EMC_DBG, emc_dbg_val | 0x40000002, 0); // WRITE_MUX_ACTIVE | WRITE_ACTIVE_ONLY

	ramp_down_wait = _dvfs_power_ramp_down(false, src_emc_entry, dst_emc_entry, src_clock_period);

	// Step 12 - Trigger clock change.
	EPRINTF("Step 12");
	_ccfifo_write(EMC_STALL_THEN_EXE_AFTER_CLKCHANGE, 1, 0);
	if (!needs_tristate_training)
		_ccfifo_write(EMC_DBG, (emc_dbg_val & 0xBFFFFFFF) | 2, 0);

	// Step 13 - Ramp up.
	EPRINTF("Step 13");
	ramp_up_wait = _dvfs_power_ramp_up(false, src_emc_entry, dst_emc_entry, needs_training, dst_clock_period);

	_ccfifo_write(EMC_DBG, emc_dbg_val, 0);

	// Step 14 - Bringup CKE pins.
	EPRINTF("Step 14");
	u32 emc_pin_val_final = 0;
	if (needs_ca_combo_training)
	{
		emc_pin_val_final = emc_pin_o & 0xFFFFFFF8;
		if (dram_dev_num == TWO_RANK)
		{
			if (needs_swap_rank_training)
				emc_pin_val_final |= 5;
			else
				emc_pin_val_final |= 6;
		}
	}
	else if (dram_dev_num == TWO_RANK)
		emc_pin_val_final = emc_pin_o | 7;
	else
		emc_pin_val_final = (emc_pin_o & 0xFFFFFFF8) | 1;

	_ccfifo_write(EMC_PIN, emc_pin_val_final, 0);

	// Step 15 - Zqlatch.
	EPRINTF("Step 15");
	if (!needs_ca_combo_training)
	{
		s32 zq_latch_dvfs_wait_time;
		u32 T_PDEX_timing = div_o3(dst_emc_entry->dram_timings.t_pdex * 1000, dst_clock_period);

		if (dst_clock_period > 2000)
			zq_latch_dvfs_wait_time = (s32)tZQCAL_lpddr4_fc_adj - (s32)T_PDEX_timing;
		else
			zq_latch_dvfs_wait_time =
				(s32)tZQCAL_lpddr4_fc_adj - (ramp_up_wait + ramp_down_wait) / dst_clock_period;

		if (dram_dev_num == ONE_RANK)
		{
			if (dst_clock_period > 2000)
				_ccfifo_write(EMC_ZQ_CAL, 0x80000001, T_PDEX_timing);

			if (!needs_tristate_training)
				_ccfifo_write(EMC_MRW3, (mr13_flip_fspop & 0xF3FFFFF7) | 0xC000000, T_PDEX_timing);

			emc_zq_cal = 0x80000002;
		}
		else if (zcal_resistor_shared)
		{
			if (dst_clock_period > 2000)
				_ccfifo_write(EMC_ZQ_CAL, 0x80000001, T_PDEX_timing);

			s32 T_PDEX_timing_final = zq_latch_dvfs_wait_time + (s32)T_PDEX_timing;

			if (T_PDEX_timing_final < 0)
				T_PDEX_timing_final = 0;

			_ccfifo_write(EMC_ZQ_CAL, 0x80000002, T_PDEX_timing_final);
			_ccfifo_write(EMC_ZQ_CAL, 0x40000001, 0);

			if (!needs_tristate_training)
				_ccfifo_write(EMC_MRW3, (mr13_flip_fspop & 0xF3FFFFF7) | 0xC000000, 0);

			emc_zq_cal = 0x40000002;
			zq_latch_dvfs_wait_time = 1000000 / dst_clock_period;
		}
		else
		{
			if (dst_clock_period > 2000)
				_ccfifo_write(EMC_ZQ_CAL, 1, T_PDEX_timing);

			if (!needs_tristate_training)
				_ccfifo_write(EMC_MRW3, (mr13_flip_fspop & 0xF3FFFFF7) | 0xC000000, T_PDEX_timing);

			emc_zq_cal = 2;
		}

		// Disable self-refresh.
		if (!needs_tristate_training)
		{
#ifdef PERF_HACK
			// HACK: Setting ACTIVE_SELF_REF increases perf by 1-2%.
			_ccfifo_write(EMC_SELF_REF, 0x100, 0);
#else
			_ccfifo_write(EMC_SELF_REF, 0, 0);
#endif
			_ccfifo_write(EMC_REF, 0, 0);
		}

		if (zq_latch_dvfs_wait_time < 0)
			zq_latch_dvfs_wait_time = 0;

		_ccfifo_write(EMC_ZQ_CAL, emc_zq_cal, (u32)zq_latch_dvfs_wait_time);
	}

	_ccfifo_write(EMC_INTSTATUS, 0, 10); // WAR: delay for zqlatch.

	// Step 16 - LPDDR4 Conditional training kickoff.
	EPRINTF("Step 16");
	if (needs_tristate_training)
	{
		_ccfifo_write(EMC_INTSTATUS, 0, 1020000 / dst_clock_period);

		u32 training_command = 0;

		if (needs_ca_training)
			training_command |= (1 << 1);  // CA: Initiates CA Training.
		if (needs_ca_vref_training)
			training_command |= (1 << 5);  // CA_VREF: Initiates CA_VREF Training.
		if (needs_quse_training)
			training_command |= (1 << 4);  // QUSE: Initiates QUSE Training.
		if (needs_quse_vref_training)
			training_command |= (1 << 8);  // QUSE_VREF: Initiates DQS_VREF Training.
		if (needs_wr_training)
			training_command |= (1 << 3);  // WR: Initiates WR Training.
		if (needs_wr_vref_training)
			training_command |= (1 << 6);  // WR_VREF: Initiates OB (wrire) DRAM_VREF Training.
		if (needs_rd_training)
			training_command |= (1 << 2);  // RD: Initiates RD Training.
		if (needs_rd_vref_training)
			training_command |= (1 << 7);  // RD_VREF: Initiates IB_DQ_VREF Training.
		training_command     |= (1 << 31); // GO: Start the Training.

		_ccfifo_write(EMC_TRAINING_CMD, training_command, 0);

		if (bg_regulator_mode_change)
		{
			if (enable_bg_regulator)
				_ccfifo_write(EMC_PMACRO_BG_BIAS_CTRL_0,
					src_emc_entry->burst_regs.emc_pmacro_bg_bias_ctrl_0_idx & 0xFFFFFFFE, 0);
			else
				_ccfifo_write(EMC_PMACRO_BG_BIAS_CTRL_0,
					src_emc_entry->burst_regs.emc_pmacro_bg_bias_ctrl_0_idx & 0xFFFFFFFB, 0);
		}

		_ccfifo_write(EMC_SWITCH_BACK_CTRL, 1, 0);

		if (!needs_ca_combo_training || needs_swap_rank_training)
		{
			_ccfifo_write(EMC_MRW3, mr13_flip_fspop ^ 0xC0, 0);
			_ccfifo_write(EMC_INTSTATUS, 0, 1000000 / dst_clock_period);
		}

		_ccfifo_write(EMC_PIN, emc_pin_o & 0xFFFFFFF8, 0);
		_ccfifo_write(EMC_CFG_SYNC, 0, 0);
		_ccfifo_write(EMC_DBG, emc_dbg_val | 0x40000002, 0);

		_dvfs_power_ramp_down(true, src_emc_entry, dst_emc_entry, dst_clock_period);

		_ccfifo_write(EMC_STALL_THEN_EXE_AFTER_CLKCHANGE, 1, 0);
		_ccfifo_write(EMC_DBG, (emc_dbg_val & 0xBFFFFFFF) | 2, 0);

		_dvfs_power_ramp_up(true, src_emc_entry, dst_emc_entry, needs_training, src_clock_period);

		_ccfifo_write(EMC_DBG, emc_dbg_val, 0);

		if (dram_dev_num == TWO_RANK)
			_ccfifo_write(EMC_PIN, emc_pin_o | 7, 0);
		else
			_ccfifo_write(EMC_PIN, (emc_pin_o & 0xFFFFFFF8) | 1, 0);

		if (needs_ca_combo_training)
		{
			_ccfifo_write(EMC_TR_CTRL_0, 0x4A, 200000 / src_clock_period);
			_ccfifo_write(EMC_TR_CTRL_0, 0x40, 1000000 / src_clock_period);
			_ccfifo_write(EMC_MRW3, mr13_catr_enable & 0xFFFFFFFE, 0);
			_ccfifo_write(EMC_INTSTATUS, 0, 1000000 / src_clock_period);
			_ccfifo_write(EMC_PMACRO_DATA_RX_TERM_MODE, src_emc_entry->burst_regs.emc_pmacro_data_rx_term_mode_idx, 0);
		}

		_ccfifo_write(EMC_DBG, emc_dbg_o, 0);

		_ccfifo_write(EMC_ZQ_CAL, 0x80000001, 0);
		_ccfifo_write(EMC_ZQ_CAL, 0x80000002, 1000000 / src_clock_period);

		if ((!needs_ca_combo_training || needs_swap_rank_training) && dram_dev_num == TWO_RANK)
		{

			_ccfifo_write(EMC_ZQ_CAL, 0x40000001, 0);
			_ccfifo_write(EMC_ZQ_CAL, 0x40000002, 1000000 / src_clock_period);
		}

		if (!needs_ca_combo_training)
			_ccfifo_write(EMC_MRW3, (mr13_flip_fspop & 0xF3FFFFF7) ^ 0xC0000C0, 0);

		_ccfifo_write(EMC_SELF_REF, 0, 0); // Was 0x100.
	}

	// Step 19.2.
	EPRINTF("Step 19.2");
	if (bg_regulator_mode_change)
	{
		_ccfifo_write(EMC_DBG, emc_dbg_o | 2, 0);

		u32 bg_regulator_switch_complete_wait_clks = 0;
		if (needs_tristate_training)
		{
			bg_regulator_switch_complete_wait_clks = 1250000 / src_clock_period;
			_ccfifo_write(EMC_PMACRO_BG_BIAS_CTRL_0,
				src_emc_entry->burst_regs.emc_pmacro_bg_bias_ctrl_0_idx, bg_regulator_switch_complete_wait_clks);
		}
		else
		{
			if (ramp_up_wait <= 1250000)
				bg_regulator_switch_complete_wait_clks = (1250000 - ramp_up_wait) / dst_clock_period;
			_ccfifo_write(EMC_PMACRO_BG_BIAS_CTRL_0,
				dst_emc_entry->burst_regs.emc_pmacro_bg_bias_ctrl_0_idx, bg_regulator_switch_complete_wait_clks);
		}

		_ccfifo_write(EMC_DBG, emc_dbg_o, 0);
	}

	// Step 20 - Issue ref and optional QRST.
	EPRINTF("Step 20");
	if (needs_tristate_training)
		_ccfifo_write(EMC_REF, 0, 0);

	// Step 21 - Restore ZCAL and ZCAL interval.
	EPRINTF("Step 21");
	_ccfifo_write(EMC_DBG, emc_dbg_o | 2, 0);

	if (needs_tristate_training)
		_ccfifo_write(EMC_ZCAL_INTERVAL, src_emc_entry->burst_regs.emc_zcal_interval_idx, 0);

	_ccfifo_write(EMC_CFG, dst_emc_entry->burst_regs.emc_cfg_idx & 0xEFFFFFFF, 0);

	// Step 22 - Restore EMC_CFG_PIPE_CLK.
	EPRINTF("Step 22");
	//if (needs_tristate_training && dram_type == DRAM_TYPE_LPDDR4)////////////////
	if (needs_tristate_training)
		_ccfifo_write(EMC_SEL_DPD_CTRL, src_emc_entry->emc_sel_dpd_ctrl, 0);

	_ccfifo_write(EMC_DBG, emc_dbg_o, 0);
	_ccfifo_write(EMC_CFG_PIPE_CLK, emc_cfg_pipe_clk_o, 0);

	if (bg_regulator_mode_change)
	{
		if (enable_bg_regulator)
			EMC(EMC_PMACRO_BG_BIAS_CTRL_0) = dst_emc_entry->burst_regs.emc_pmacro_bg_bias_ctrl_0_idx & 0xFFFFFFFB;
		else
			EMC(EMC_PMACRO_BG_BIAS_CTRL_0) = dst_emc_entry->burst_regs.emc_pmacro_bg_bias_ctrl_0_idx & 0xFFFFFFFE;
	}

	// Step 23 - Clock Change.
	EPRINTF("Step 23");
	// During training save current clock.
	if (needs_tristate_training)
	{
		u32 emc_clk_src = CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_EMC);
		CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_EMC_SAFE) = emc_clk_src;
		_change_dll_src(src_emc_entry, emc_clk_src);
	}

	// Set CFG_DLL_MODE to RUN_PERIODIC.
	EMC(EMC_CFG_DIG_DLL) = (EMC(EMC_CFG_DIG_DLL) & 0xFFFFFF24) | 0x88;
	(void)EMC(EMC_CFG_DIG_DLL);

	(void)EMC(EMC_FBIO_CFG7);
	(void)MC(MC_EMEM_ADR_CFG);
	(void)EMC(EMC_INTSTATUS);

	// Do clock change.
	CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_EMC) = selected_clk_src_emc;
	(void)CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_EMC);

	if (_wait_emc_status(EMC_INTSTATUS, CLKCHANGE_COMPLETE_INT, true, 0))
		return 4; // Clkchange handshake timeout error.

	// Step 24 - Save training results.
	EPRINTF("Step 24");
	if (needs_tristate_training)
	{
		(void)MC(MC_EMEM_ADR_CFG);
		emc_dbg_val = EMC(EMC_DBG);
		EMC(EMC_DBG) |= 1;

		_save_train_results(dst_emc_entry, needs_training, dram_dev_num, channel1_enabled);

		EMC(EMC_DBG) = emc_dbg_val;
	}

	// Step 25 - Program MC updown regs.
	EPRINTF("Step 25");
	if ((dst_emc_entry->rate_khz > src_emc_entry->rate_khz) && !needs_tristate_training)
	{
		for (u32 i = 0; dst_emc_entry->num_up_down > i; i++)
			MC(la_scale_regs_mc_addr_table[i]) = dst_emc_entry->la_scale_regs[i];

		bool dual_channel = (EMC(EMC_FBIO_CFG7) >> 1) & ((EMC(EMC_FBIO_CFG7) >> 2) & 1);
		if (_timing_update(dual_channel))
			return 4;
	}

	// Step 26 - Restore ZCAL regs.
	EPRINTF("Step 26");
	if (!in_self_refresh)
	{
		EMC(EMC_DBG) = emc_dbg_o | 2;
		EMC(EMC_ZCAL_WAIT_CNT) = dst_emc_entry->burst_regs.emc_zcal_wait_cnt_idx;
		EMC(EMC_ZCAL_INTERVAL) = dst_emc_entry->burst_regs.emc_zcal_interval_idx;
		EMC(EMC_DBG) = emc_dbg_o;
	}

	// Step 27 - Restore EMC_CFG, FDPD regs.
	EPRINTF("Step 27");
	EMC(EMC_DBG) = emc_dbg_o | 2;
	EMC(EMC_CFG) = dst_emc_entry->burst_regs.emc_cfg_idx;
	EMC(EMC_DBG) = emc_dbg_o;
	EMC(EMC_FDPD_CTRL_CMD_NO_RAMP) = dst_emc_entry->emc_fdpd_ctrl_cmd_no_ramp;
	EMC(EMC_SEL_DPD_CTRL) = dst_emc_entry->emc_sel_dpd_ctrl;

	// Step 28 - Training recover.
	EPRINTF("Step 28");
	if (needs_tristate_training)
	{
		EMC(EMC_DBG) = emc_dbg_o | 2;
		EMC(EMC_CFG) = dst_emc_entry->burst_regs.emc_cfg_idx;
		EMC(EMC_SEL_DPD_CTRL) = dst_emc_entry->emc_sel_dpd_ctrl;
		EMC(EMC_ZCAL_WAIT_CNT) = src_emc_entry->burst_regs.emc_zcal_wait_cnt_idx;
		EMC(EMC_ZCAL_INTERVAL) = src_emc_entry->burst_regs.emc_zcal_interval_idx;
		EMC(EMC_AUTO_CAL_CONFIG2) = src_emc_entry->emc_auto_cal_config2;
		EMC(EMC_AUTO_CAL_CONFIG3) = src_emc_entry->emc_auto_cal_config3;
		EMC(EMC_AUTO_CAL_CONFIG4) = src_emc_entry->emc_auto_cal_config4;
		EMC(EMC_AUTO_CAL_CONFIG5) = src_emc_entry->emc_auto_cal_config5;
		EMC(EMC_AUTO_CAL_CONFIG6) = src_emc_entry->emc_auto_cal_config6;
		EMC(EMC_AUTO_CAL_CONFIG7) = src_emc_entry->emc_auto_cal_config7;
		EMC(EMC_AUTO_CAL_CONFIG8) = src_emc_entry->emc_auto_cal_config8;
		EMC(EMC_DBG) = emc_dbg_o;
		EMC(EMC_TR_DVFS) = dst_emc_entry->burst_regs.emc_tr_dvfs_idx & 0xFFFFFFFE;
	}

	EMC(EMC_DBG) = emc_dbg_o | 2;
	EMC(EMC_PMACRO_AUTOCAL_CFG_COMMON) = dst_emc_entry->burst_regs.emc_pmacro_autocal_cfg_common_idx;
	EMC(EMC_DBG) = emc_dbg_o;

	// Step 29 - Power fix WAR.
	EPRINTF("Step 29");
	EMC(EMC_PMACRO_CFG_PM_GLOBAL_0) = 0xFF0000;
	EMC(EMC_PMACRO_TRAINING_CTRL_0) = CH0_TRAINING_E_WRPTR;
	EMC(EMC_PMACRO_TRAINING_CTRL_1) = CH0_TRAINING_E_WRPTR;
	EMC(EMC_PMACRO_CFG_PM_GLOBAL_0) = 0;

	// Step 30 - Re-enable autocal.
	EPRINTF("Step 30");
	if (needs_tristate_training)
		EMC(EMC_AUTO_CAL_CONFIG) = src_emc_entry->emc_auto_cal_config;
	else
	{
		if (dst_emc_entry->burst_regs.emc_cfg_dig_dll_idx & 1)
			_digital_dll_enable_rs(channel1_enabled);
		EMC(EMC_AUTO_CAL_CONFIG) = dst_emc_entry->emc_auto_cal_config;
	}

	return 0;
}

static void _minerva_train_patterns(emc_table_t *src_emc_entry, emc_table_t *dst_emc_entry, bool switch_rate, u32 selected_clk_src_emc)
{
	u32 needs_training_idx = 0;
	u32 emc_cfg_dig_dll_val = 0;
	u32 needs_training_emc_table[8] = {0};

	u32 needs_training = dst_emc_entry->needs_training;

	 // Must start as true.
	if (train_ram_patterns)
	{
		u32 train_table_off = dst_emc_entry->training_pattern * 256;
		for (u32 i = 0; i < 256; i++)
		{
			EMC(EMC_TRAINING_PATRAM_DQ) = ram_pattern_dq_table[train_table_off + i];
			EMC(EMC_TRAINING_PATRAM_DMI) = ram_pattern_dmi_table[train_table_off + i] & 0xF;
			EMC(EMC_TRAINING_PATRAM_CTRL) = 0x80000000 + i;
		}
		train_ram_patterns = false;
	}

	if (!dst_emc_entry->trained)
	{
		if (needs_training & NEEDS_TRAINING_CA_COMBO)
		{
			needs_training_emc_table[needs_training_idx++] =
				needs_training & (NEEDS_TRAINING_CA_COMBO | NEEDS_TRAINING_IN_SELF_REFRESH);
			if (MC(MC_EMEM_ADR_CFG) & 1) // if mapping W8 (1KB page).
				needs_training_emc_table[needs_training_idx++] =
					needs_training & (NEEDS_TRAINING_CA_COMBO | NEEDS_TRAINING_SWAP_RANK | NEEDS_TRAINING_IN_SELF_REFRESH);
		}

		if (needs_training & NEEDS_TRAINING_QUSE_COMBO)
		{
			needs_training_emc_table[needs_training_idx++] =
				needs_training & (NEEDS_TRAINING_QUSE_COMBO | NEEDS_TRAINING_IN_SELF_REFRESH);
			if (MC(MC_EMEM_ADR_CFG) & 1)
				needs_training_emc_table[needs_training_idx++] =
					needs_training & (NEEDS_TRAINING_QUSE | NEEDS_TRAINING_IN_SELF_REFRESH);
		}

		if (needs_training & (NEEDS_TRAINING_WR_COMBO | NEEDS_TRAINING_RD_COMBO))
			needs_training_emc_table[needs_training_idx++] =
				needs_training & (NEEDS_TRAINING_WR_COMBO | NEEDS_TRAINING_RD_COMBO | NEEDS_TRAINING_IN_SELF_REFRESH);

		for (u32 i = 0; needs_training_idx > i; i++) // Runs more than once for needs_training CA/QUSE/WR/RD.
		{
			_minerva_set_clock(src_emc_entry, dst_emc_entry, needs_training_emc_table[i], selected_clk_src_emc);

			bool dual_channel = (EMC(EMC_FBIO_CFG7) >> 1) & ((EMC(EMC_FBIO_CFG7) >> 2) & 1);

			EMC(EMC_DBG) = (EMC(EMC_DBG) & 0xF3FFFFFF) | 0x8000000;
			EMC(EMC_CFG_UPDATE) = (EMC(EMC_CFG_UPDATE) & 0xFFFFFFF9) | 4;
			_timing_update(dual_channel);

			EMC(EMC_CFG_UPDATE) &= 0xFFFFFFF9;
			EMC(EMC_DBG) &= 0xF3FFFFFF;
			EMC(EMC_CFG_DIG_DLL) = (EMC(EMC_CFG_DIG_DLL) & 0xFFFFFF3E) | 0x80;
			_timing_update(dual_channel);

			emc_cfg_dig_dll_val = EMC(EMC_CFG_DIG_DLL) & 0xFFFFFFFE;
			if (dst_emc_entry->burst_regs.emc_cfg_dig_dll_idx == 1)
				emc_cfg_dig_dll_val = EMC(EMC_CFG_DIG_DLL) | 1;
			EMC(EMC_CFG_DIG_DLL) = (emc_cfg_dig_dll_val & 0xFFFFFF3F) | 0x80;
			_timing_update(dual_channel);

			while (!(EMC(EMC_DIG_DLL_STATUS) & 0x8000))
				;

			// Bug 200024907.
			EMC(EMC_RP) = src_emc_entry->burst_regs.emc_rp_idx;
			EMC(EMC_R2P) = src_emc_entry->burst_regs.emc_r2p_idx;
			EMC(EMC_W2P) = src_emc_entry->burst_regs.emc_w2p_idx;
			EMC(EMC_TRPAB) = src_emc_entry->burst_regs.emc_trpab_idx;

			_timing_update(dual_channel);
		}

		EPRINTF("Trained");
		dst_emc_entry->trained = 1;
	}

	if (switch_rate)
		_minerva_set_clock(src_emc_entry, dst_emc_entry, 0, selected_clk_src_emc);
}

void _minerva_do_over_temp_compensation(mtc_config_t *mtc_cfg)
{
	u32 dram_type = EMC(EMC_FBIO_CFG5) & 3;

	// Only LPDDR chips are supported.
	if (dram_type != DRAM_TYPE_LPDDR4)
		return;

	u32 dram_temp = _get_dram_temperature();

	if (mtc_cfg->prev_temp == dram_temp || dram_temp == (u32)-1)
		return;

	u32 refr = mtc_cfg->current_emc_table->burst_regs.emc_refresh_idx;
	u32 pre_refr = mtc_cfg->current_emc_table->burst_regs.emc_pre_refresh_req_cnt_idx;
	u32 dyn_self_ref = mtc_cfg->current_emc_table->burst_regs.emc_dyn_self_ref_control_idx;

	switch (dram_temp)
	{
	// Normal temp (<= 85 oC).
	case 0:
	case 1:
	case 2:
	case 3:
		if (mtc_cfg->prev_temp < 4)
		{
			mtc_cfg->prev_temp = dram_temp;
			return;
		}
		break;
	// Over temp (> 85 oC).
	case 4: // 2x refresh.
		refr = (refr & 0xFFFF0000) | ((refr & 0xFFFF) >> REFRESH_X2);
		pre_refr = (pre_refr & 0xFFFF0000) | ((pre_refr & 0xFFFF) >> REFRESH_X2);
		dyn_self_ref = (dyn_self_ref & 0xFFFF0000) | ((dyn_self_ref & 0xFFFF) >> REFRESH_X2);
		break;
	case 5: // 4x refresh.
	case 6: // Temp 6 normally needs a derating emc table.
		refr = (refr & 0xFFFF0000) | ((refr & 0xFFFF) >> REFRESH_X4);
		pre_refr = (pre_refr & 0xFFFF0000) | ((pre_refr & 0xFFFF) >> REFRESH_X4);
		dyn_self_ref = (dyn_self_ref & 0xFFFF0000) | ((dyn_self_ref & 0xFFFF) >> REFRESH_X4);
		break;
	default:
		break;
	}

	mtc_cfg->prev_temp = dram_temp;

	EMC(EMC_REFRESH) = refr;
	EMC(EMC_PRE_REFRESH_REQ_CNT) = pre_refr;
	EMC(EMC_DYN_SELF_REF_CONTROL) = dyn_self_ref;
}

u32 _minerva_do_periodic_compensation(emc_table_t *mtc_table_entry)
{
	if (mtc_table_entry && mtc_table_entry->periodic_training)
	{
		u32  dram_dev_num = (MC(MC_EMEM_ADR_CFG) & 1) + 1;
		u32  pd_mask = (dram_dev_num == TWO_RANK) ? IN_POWERDOWN_BOTH_MASK : IN_POWERDOWN_1DEV_MASK;
		bool channel1_enabled = (mtc_table_entry->burst_regs.emc_fbio_cfg7_idx >> 2) & 1;

		(void)EMC(EMC_DBG);

		// Safekeep current config.
		u32 emc_cfg_o = EMC(EMC_CFG);
		u32 emc_cfg_dig_dll_o = EMC(EMC_CFG_DIG_DLL);
		u32 emc_cfg_update_o = EMC(EMC_CFG_UPDATE);

		// Step 1 - Disable digital DLL.
		EMC(EMC_CFG_DIG_DLL) = emc_cfg_dig_dll_o & 0xFFFFFFFE;

		// Step 1.2 - Always update auto cal in clock change.
		EMC(EMC_CFG_UPDATE) = (emc_cfg_update_o & 0xFFFFF9FF) | 0x400;

		// Step 1.3 - Disable other power features.
		EMC(EMC_CFG) = emc_cfg_o & 0xFFFFFFF;

		// Timing update and wait for everything to power down.
		_timing_update(channel1_enabled);

		_wait_emc_status(EMC_EMC_STATUS, pd_mask, 0, EMC_CHANNEL0);
		if (channel1_enabled)
			_wait_emc_status(EMC_EMC_STATUS, pd_mask, 0, EMC_CHANNEL1);

		_wait_emc_status(EMC_EMC_STATUS, IN_SELF_REFRESH_MASK, 0, EMC_CHANNEL0);
		if (channel1_enabled)
			_wait_emc_status(EMC_EMC_STATUS, IN_SELF_REFRESH_MASK, 0, EMC_CHANNEL1);

		_wait_emc_status(EMC_CFG_DIG_DLL, 1, 0, EMC_CHANNEL0);
		if (channel1_enabled)
			_wait_emc_status(EMC_CFG_DIG_DLL, 1, 0, EMC_CHANNEL1);

		// Step 2 - Osc kick off - this assumes training and dvfs have set correct MR23.
		_start_periodic_compensation();

		// Step 3 - Let dram capture its clock tree delays.
		_usleep(1000 * _actual_osc_clocks(mtc_table_entry->run_clocks) / mtc_table_entry->rate_khz + 2);

		// Step 4 - Check delta wrt previous values (save value if margin exceeds what is set in table).
		u32 adelta = _minerva_update_clock_tree_delay(mtc_table_entry, mtc_table_entry, dram_dev_num, channel1_enabled, PERIODIC_TRAINING_UPDATE);

		// Step 5 - Apply compensation w.r.t. trained values (if clock tree has drifted more than the set margin).
		if (adelta && ((mtc_table_entry->rate_khz / 1000) * 128) * adelta / 1000000 > mtc_table_entry->tree_margin)
		{
			for (u32 i = 0; i < 10; i++)
			{
				EMC(periodic_training_addr[i]) =
					_minerva_apply_periodic_compensation_trimmer(mtc_table_entry, periodic_training_addr[i]);
			}
		}

		// Step 6 - Restore other power features.
		EMC(EMC_CFG) = emc_cfg_o;

		// Step 6.1 - Restore the DLL.
		EMC(EMC_CFG_DIG_DLL) = emc_cfg_dig_dll_o;

		// Step 6.2 - Timing update for applying the new trimmers.
		_timing_update(channel1_enabled);

		// Step 6.3 - Restore the UPDATE_DLL_IN_UPDATE field.
		EMC(EMC_CFG_UPDATE) = emc_cfg_update_o;
	}

	return 0;
}

static u32 _minerva_set_rate(mtc_config_t *mtc_cfg)
{
	u32 src_emc_entry_idx = 999;
	u32 dst_emc_entry_idx = 999;
	u32 selected_clk_src_emc;
	u32 emc_clk_src;
	bool freq_changed = false;
	bool src_is_pllmb;
	emc_table_t *src_emc_entry;
	emc_table_t *dst_emc_entry;

	if (mtc_cfg->table_entries > 900)
		return 4;

	for (u32 i = 0; i < mtc_cfg->table_entries; i++)
	{
		u32 table_entry_rate = mtc_cfg->mtc_table[i].rate_khz;
		if (mtc_cfg->rate_from == table_entry_rate)
			src_emc_entry_idx = i;
		if (mtc_cfg->rate_to == table_entry_rate)
			dst_emc_entry_idx = i;
	}

	if (src_emc_entry_idx >= mtc_cfg->table_entries)
		return 4;

	if (dst_emc_entry_idx >= mtc_cfg->table_entries)
		return 4;

	src_emc_entry = (emc_table_t *)&mtc_cfg->mtc_table[src_emc_entry_idx];
	dst_emc_entry = (emc_table_t *)&mtc_cfg->mtc_table[dst_emc_entry_idx];

	u32 src_rate_khz = src_emc_entry->rate_khz;
	u32 dst_rate_khz = dst_emc_entry->rate_khz;
	u32 src_clk_src_emc = src_emc_entry->clk_src_emc;
	u32 dst_clk_src_emc = dst_emc_entry->clk_src_emc;

	freq_changed = _check_freq_changed(dst_rate_khz, dst_clk_src_emc, src_rate_khz, src_clk_src_emc);
	EPRINTFARGS("Requested freq change from %d to %d.", src_rate_khz, dst_rate_khz);

	// Get current clock source.
	emc_clk_src = CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_EMC) >> EMC_2X_CLK_SRC_SHIFT;
	src_is_pllmb = emc_clk_src == PLLMB_UD || emc_clk_src == PLLMB_OUT0;

	if (freq_changed)
	{
		if (emc_clk_src == PLLM_UD ||
			emc_clk_src == PLLM_OUT0) // Clock source is PLLM. Switch based on src_is_pllmb.
		{
			src_is_pllmb = !src_is_pllmb;
		}
		else if (emc_clk_src == PLLMB_UD ||
				 emc_clk_src == PLLMB_OUT0) // Clock source is PLLMB. Switch to PLLM.
		{
			src_is_pllmb = false;
		}
		selected_clk_src_emc = _pllm_clk_base_cfg(dst_rate_khz, dst_clk_src_emc, src_is_pllmb);
	}
	else
	{
		selected_clk_src_emc = dst_clk_src_emc;
		emc_clk_src = selected_clk_src_emc >> EMC_2X_CLK_SRC_SHIFT;
		if (src_is_pllmb)
		{
			if (emc_clk_src == PLLM_UD || emc_clk_src == PLLMB_UD)
				selected_clk_src_emc = (selected_clk_src_emc & 0x1FFFFFFF) | (PLLMB_UD << EMC_2X_CLK_SRC_SHIFT);
			else if (emc_clk_src == PLLM_OUT0 || emc_clk_src == PLLMB_OUT0)
				selected_clk_src_emc = (selected_clk_src_emc & 0x1FFFFFFF) | (PLLMB_OUT0 << EMC_2X_CLK_SRC_SHIFT);
		}
	}

	switch (mtc_cfg->train_mode)
	{
	case OP_SWITCH:
		_minerva_set_clock(src_emc_entry, dst_emc_entry, 0, selected_clk_src_emc);
		mtc_cfg->current_emc_table = dst_emc_entry;
		mtc_cfg->rate_from = dst_emc_entry->rate_khz;
		if (dst_emc_entry->periodic_training)
			_minerva_do_periodic_compensation(dst_emc_entry);
		return 0;
	case OP_TRAIN:
		_minerva_train_patterns(src_emc_entry, dst_emc_entry, false, selected_clk_src_emc);
		return 0;
	case OP_TRAIN_SWITCH:
		_minerva_train_patterns(src_emc_entry, dst_emc_entry, true, selected_clk_src_emc);
		mtc_cfg->current_emc_table = dst_emc_entry;
		mtc_cfg->rate_from = dst_emc_entry->rate_khz;
		if (dst_emc_entry->periodic_training)
			_minerva_do_periodic_compensation(dst_emc_entry);
		return 0;
	default:
		return 4;
	}
}

static void _minerva_get_table(mtc_config_t *mtc_cfg)
{
	switch (mtc_cfg->sdram_id)
	{
	case DRAM_4GB_HYNIX_H9HCNNNBPUMLHR_NLN:
		memcpy(mtc_cfg->mtc_table, nx_abca2_2_10NoCfgVersion_V9_8_7_V1_6, EMC_TABLE_SIZE_R7);
		break;
	case DRAM_4GB_SAMSUNG_K4F6E304HB_MGCH:
	case DRAM_4GB_MICRON_MT53B512M32D2NP_062_WT:
	case DRAM_4GB_COPPER_SAMSUNG:
	case DRAM_6GB_SAMSUNG_K4FHE3D4HM_MFCH:
	default:
		memcpy(mtc_cfg->mtc_table, nx_abca2_0_3_10NoCfgVersion_V9_8_7_V1_6, EMC_TABLE_SIZE_R7);
		break;
	}

	mtc_cfg->table_entries = 10;
	mtc_cfg->rate_to = 0;
	mtc_cfg->rate_from = 0;
	mtc_cfg->train_mode = 0;
	mtc_cfg->prev_temp = 0;
	mtc_cfg->current_emc_table = NULL;

	// Important!
	mtc_cfg->train_ram_patterns = true;
	mtc_cfg->init_done = MTC_INIT_MAGIC;
}

void _minerva_init(mtc_config_t *mtc_cfg, bdkParams_t bp)
{
	EPRINTF("-- Minerva Training Cell --");

	train_ram_patterns = mtc_cfg->train_ram_patterns;

	if (mtc_cfg->init_done != MTC_INIT_MAGIC)
	{
		if (mtc_cfg->init_done == MTC_NEW_MAGIC)
		{
			_minerva_get_table(mtc_cfg);
#ifdef OVERCLOCK_VOLTAGE
			// Set SD1 regulator voltage.
			if ((bp->extension_magic & 0xF0FFFFFF) == IANOS_EXT0)
				bp->reg_voltage_set(1, OVERCLOCK_VOLTAGE);
#endif
		}
		return;
	}

#ifdef OVERCLOCK_FREQ
	// Change max rate in table.
	mtc_cfg->mtc_table[mtc_cfg->table_entries - 1].rate_khz = OVERCLOCK_FREQ;

	// Change rates for OC RAM.
	if (mtc_cfg->rate_from == MAX_FREQ_T210)
		mtc_cfg->rate_from = OVERCLOCK_FREQ;
	if (mtc_cfg->rate_to == MAX_FREQ_T210)
		mtc_cfg->rate_to = OVERCLOCK_FREQ;
#endif

	switch (mtc_cfg->train_mode)
	{
	case OP_SWITCH:
		EPRINTF("Switching..");
		_minerva_set_rate(mtc_cfg);
		break;
	case OP_TRAIN:
		EPRINTF("Training..");
		_minerva_set_rate(mtc_cfg);
		break;
	case OP_TRAIN_SWITCH:
		EPRINTF("Training and switching..");
		_minerva_set_rate(mtc_cfg);
		break;
	case OP_PERIODIC_TRAIN:
		EPRINTF("Periodic training..");
		_minerva_do_periodic_compensation(mtc_cfg->current_emc_table);
		break;
	case OP_TEMP_COMP:
		EPRINTF("Over temperature compensation..");
		_minerva_do_over_temp_compensation(mtc_cfg);
		break;
	}

#ifdef OVERCLOCK_FREQ
	// Restore rates for OC RAM.
	if (mtc_cfg->rate_from == OVERCLOCK_FREQ)
		mtc_cfg->rate_from = MAX_FREQ_T210;
	if (mtc_cfg->rate_to == OVERCLOCK_FREQ)
		mtc_cfg->rate_to = MAX_FREQ_T210;
#endif

	mtc_cfg->train_ram_patterns = train_ram_patterns;
}
